diff --git a/src/cart/crt_bulk.c b/src/cart/crt_bulk.c index af7d3a918ac..a20108520ee 100644 --- a/src/cart/crt_bulk.c +++ b/src/cart/crt_bulk.c @@ -253,16 +253,18 @@ crt_bulk_free_common(struct crt_bulk *bulk) static inline bool crt_bulk_desc_expired(struct crt_bulk_desc *bulk_desc) { - struct timespec now; + uint64_t now_sec; struct crt_rpc_priv *rpc_priv; - clock_gettime(CLOCK_REALTIME_COARSE, &now); + now_sec = crt_realtime_sec(); rpc_priv = container_of(bulk_desc->bd_rpc, struct crt_rpc_priv, crp_pub); /* Deadline expired */ - if (now.tv_sec > rpc_priv->crp_deadline_sec) { - RPC_TRACE(DB_NET, rpc_priv, "Deadline expired for bulk. Deadline=%d, now=%ld\n", - rpc_priv->crp_deadline_sec, now.tv_sec); + if (now_sec > rpc_priv->crp_deadline_sec) { + RPC_TRACE(DB_NET, rpc_priv, + "Deadline expired for bulk. Deadline=" DF_U64 ", now=" DF_U64 + " seconds\n", + rpc_priv->crp_deadline_sec, now_sec); return true; } diff --git a/src/cart/crt_context.c b/src/cart/crt_context.c index 56e6d3dfbfe..e5055e8b532 100644 --- a/src/cart/crt_context.c +++ b/src/cart/crt_context.c @@ -513,7 +513,7 @@ crt_rpc_complete_and_unlock(struct crt_rpc_priv *rpc_priv, int rc) cbinfo.cci_rc = rc; if (cbinfo.cci_rc == 0) - cbinfo.cci_rc = rpc_priv->crp_reply_hdr.cch_rc; + cbinfo.cci_rc = rpc_priv->crp_reply_hdr.crh_status; if (cbinfo.cci_rc != 0) RPC_CWARN(crt_quiet_error(cbinfo.cci_rc), DB_NET, rpc_priv, "failed, " DF_RC "\n", @@ -1051,7 +1051,6 @@ crt_req_timeout_track(struct crt_rpc_priv *rpc_priv) if (rpc_priv->crp_in_binheap == 1) D_GOTO(out, rc = 0); - /* Set the deadline as RPC is starting to be tracked */ rpc_priv->crp_deadline_sec = crt_timeout_to_deadline(rpc_priv->crp_timeout_sec); /* add to binheap for timeout tracking */ @@ -1274,19 +1273,23 @@ crt_context_timeout_check(struct crt_context *crt_ctx) if (!print_once) { print_once = true; - RPC_WARN(rpc_priv, - "ctx_id %d, (status: %#x) timed out (%d seconds) [deadline: %d], " - "target (%d:%d)\n", - crt_ctx->cc_idx, rpc_priv->crp_state, rpc_priv->crp_timeout_sec, - rpc_priv->crp_deadline_sec, rpc_priv->crp_pub.cr_ep.ep_rank, - rpc_priv->crp_pub.cr_ep.ep_tag); + RPC_WARN( + rpc_priv, + "ctx_id %d, (status: %#x) timed out (%d seconds) [deadline_sec: " DF_U64 + "], " + "target (%d:%d)\n", + crt_ctx->cc_idx, rpc_priv->crp_state, rpc_priv->crp_timeout_sec, + rpc_priv->crp_deadline_sec, rpc_priv->crp_pub.cr_ep.ep_rank, + rpc_priv->crp_pub.cr_ep.ep_tag); } else { - RPC_INFO(rpc_priv, - "ctx_id %d, (status: %#x) timed out (%d seconds) [deadline: %d], " - "target (%d:%d)\n", - crt_ctx->cc_idx, rpc_priv->crp_state, rpc_priv->crp_timeout_sec, - rpc_priv->crp_deadline_sec, rpc_priv->crp_pub.cr_ep.ep_rank, - rpc_priv->crp_pub.cr_ep.ep_tag); + RPC_INFO( + rpc_priv, + "ctx_id %d, (status: %#x) timed out (%d seconds) [deadline_sec: " DF_U64 + "], " + "target (%d:%d)\n", + crt_ctx->cc_idx, rpc_priv->crp_state, rpc_priv->crp_timeout_sec, + rpc_priv->crp_deadline_sec, rpc_priv->crp_pub.cr_ep.ep_rank, + rpc_priv->crp_pub.cr_ep.ep_tag); } crt_req_timeout_hdlr(rpc_priv); diff --git a/src/cart/crt_corpc.c b/src/cart/crt_corpc.c index 5ffd83a1186..97abd5df277 100644 --- a/src/cart/crt_corpc.c +++ b/src/cart/crt_corpc.c @@ -522,7 +522,7 @@ corpc_del_child_rpc_locked(struct crt_rpc_priv *parent_rpc_priv, static inline void crt_corpc_fail_parent_rpc(struct crt_rpc_priv *parent_rpc_priv, int failed_rc) { - parent_rpc_priv->crp_reply_hdr.cch_rc = failed_rc; + parent_rpc_priv->crp_reply_hdr.crh_status = failed_rc; parent_rpc_priv->crp_corpc_info->co_rc = failed_rc; if (failed_rc != -DER_GRPVER) RPC_ERROR(parent_rpc_priv, @@ -662,9 +662,9 @@ crt_corpc_reply_hdlr(const struct crt_cb_info *cb_info) co_info->co_rc = rc; } /* propagate failure rc to parent */ - if (child_rpc_priv->crp_reply_hdr.cch_rc != 0) + if (child_rpc_priv->crp_reply_hdr.crh_status != 0) crt_corpc_fail_parent_rpc(parent_rpc_priv, - child_rpc_priv->crp_reply_hdr.cch_rc); + child_rpc_priv->crp_reply_hdr.crh_status); co_ops = opc_info->coi_co_ops; if (co_ops == NULL) { @@ -900,8 +900,11 @@ crt_corpc_req_hdlr(struct crt_rpc_priv *rpc_priv) /* Set child's deadline same as parent and calculate timeout left from it */ child_rpc_priv->crp_deadline_sec = rpc_priv->crp_deadline_sec; - child_rpc_priv->crp_timeout_sec = - crt_deadline_to_timeout(rpc_priv->crp_deadline_sec); + { + int64_t timeout_sec = crt_deadline_to_timeout(rpc_priv->crp_deadline_sec); + + child_rpc_priv->crp_timeout_sec = timeout_sec > 0 ? timeout_sec : 1; + } rc = crt_req_send(child_rpc, crt_corpc_reply_hdlr, rpc_priv); if (rc != 0) { diff --git a/src/cart/crt_hg.c b/src/cart/crt_hg.c index f0428021221..1d06d724558 100644 --- a/src/cart/crt_hg.c +++ b/src/cart/crt_hg.c @@ -1253,11 +1253,12 @@ crt_rpc_handler_common(hg_handle_t hg_hdl) rc = crt_hg_unpack_header(hg_hdl, &rpc_tmp, &proc); if (unlikely(rc != 0)) { D_ERROR("crt_hg_unpack_header failed, rc: %d.\n", rc); - D_GOTO(reply_error, rc = -DER_MISC); + HG_Destroy(rpc_tmp.crp_hg_hdl); + return HG_PROTOCOL_ERROR; } D_ASSERT(proc != NULL); - opc = rpc_tmp.crp_req_hdr.cch_opc; + opc = rpc_tmp.crp_req_hdr.crh_opc; rpc_tmp.crp_pub.cr_opc = opc; /* allocate rpc struct for a given opcode; in/out size will vary per opc */ @@ -1276,14 +1277,11 @@ crt_rpc_handler_common(hg_handle_t hg_hdl) opc_info = rpc_priv->crp_opc_info; rpc_pub = &rpc_priv->crp_pub; - /* populate rpc_priv based on raw header values in rpc_tmp. - * perform conversion to either a timeout or deadline based on header - * feature flag and set rpc_priv fields accordingly - * returns error if an rpc is determined to be expired already - */ + /* Populate RPC state and reject requests whose absolute deadline has expired. */ rc = crt_hg_process_header(&rpc_tmp, rpc_priv); if (unlikely(rc != 0)) { - RPC_WARN(rpc_priv, "RPC expired. Deadline was %d\n", rpc_priv->crp_deadline_sec); + RPC_WARN(rpc_priv, "RPC expired. Deadline was " DF_U64 " seconds\n", + rpc_priv->crp_deadline_sec); crt_hg_unpack_cleanup(proc); crt_rpc_priv_free(rpc_priv); @@ -1296,8 +1294,8 @@ crt_rpc_handler_common(hg_handle_t hg_hdl) } rpc_priv->crp_fail_hlc = rpc_tmp.crp_fail_hlc; - rpc_pub->cr_ep.ep_rank = rpc_priv->crp_req_hdr.cch_dst_rank; - rpc_pub->cr_ep.ep_tag = rpc_priv->crp_req_hdr.cch_dst_tag; + rpc_pub->cr_ep.ep_rank = crt_req_dst_rank(&rpc_priv->crp_req_hdr); + rpc_pub->cr_ep.ep_tag = crt_req_dst_tag(&rpc_priv->crp_req_hdr); RPC_TRACE(DB_ALL, rpc_priv, "(opc: %#x rpc_pub: %p) allocated per RPC request received.\n", rpc_priv->crp_opc_info->coi_opc, &rpc_priv->crp_pub); @@ -1313,8 +1311,7 @@ crt_rpc_handler_common(hg_handle_t hg_hdl) rc = crt_hg_unpack_body(rpc_priv, proc); if (rc == 0) { rpc_priv->crp_input_got = 1; - rpc_pub->cr_ep.ep_grp = NULL; - /* TODO lookup by rpc_priv->crp_req_hdr.cch_grp_id */ + rpc_pub->cr_ep.ep_grp = NULL; } else { DHL_ERROR(rpc_priv, rc, "crt_hg_unpack_body failed, opc: %#x", rpc_pub->cr_opc); @@ -1522,7 +1519,7 @@ crt_hg_req_send_cb(const struct hg_cb_info *hg_cbinfo) hg_ret = HG_Get_output(hg_cbinfo->info.forward.handle, &rpc_pub->cr_output); if (hg_ret == HG_SUCCESS) { rpc_priv->crp_output_got = 1; - rc = rpc_priv->crp_reply_hdr.cch_rc; + rc = rpc_priv->crp_reply_hdr.crh_status; } else { rc = crt_hgret_2_der(hg_ret); RPC_ERROR(rpc_priv, "HG_Get_output failed, hg_ret: " DF_HG_RC "\n", @@ -1708,7 +1705,7 @@ crt_hg_reply_error_send(struct crt_rpc_priv *rpc_priv, int error_code) D_ASSERT(error_code != 0); hg_out_struct = &rpc_priv->crp_pub.cr_output; - rpc_priv->crp_reply_hdr.cch_rc = error_code; + rpc_priv->crp_reply_hdr.crh_status = error_code; hg_ret = HG_Respond(rpc_priv->crp_hg_hdl, NULL, NULL, hg_out_struct); if (hg_ret != HG_SUCCESS) { CRT_METRIC_INC(crt_ctx, CM_RPC_REPLY_FAILED); diff --git a/src/cart/crt_hg.h b/src/cart/crt_hg.h index 7ff992e5443..e50b3ffbbad 100644 --- a/src/cart/crt_hg.h +++ b/src/cart/crt_hg.h @@ -22,9 +22,13 @@ #include +/** Legacy HG RPC ID, pre 3.0 **/ +#define CRT_HG_RPCID_LEGACY (0xDA036868) +#define CRT_HG_ONEWAY_RPCID_LEGACY (0xDA036869) + /** the shared HG RPC ID used for all CRT opc */ -#define CRT_HG_RPCID (0xDA036868) -#define CRT_HG_ONEWAY_RPCID (0xDA036869) +#define CRT_HG_RPCID (0xDA03686A) +#define CRT_HG_ONEWAY_RPCID (0xDA03686B) /** MAX number of HG handles in pool */ #define CRT_HG_POOL_MAX_NUM (512) @@ -45,9 +49,15 @@ #define CRT_UCX_STR "ucx" struct crt_rpc_priv; -struct crt_common_hdr; +struct crt_req_hdr; +struct crt_reply_hdr; struct crt_corpc_hdr; +int +crt_proc_req_hdr(crt_proc_t proc, struct crt_req_hdr *hdr); +int +crt_proc_reply_hdr(crt_proc_t proc, struct crt_reply_hdr *hdr); + /** * Enumeration specifying providers supported by the library */ diff --git a/src/cart/crt_hg_proc.c b/src/cart/crt_hg_proc.c index 967c07505af..7a3d5a92021 100644 --- a/src/cart/crt_hg_proc.c +++ b/src/cart/crt_hg_proc.c @@ -153,6 +153,7 @@ crt_proc_crt_bulk_t(crt_proc_t proc, crt_proc_op_t proc_op, crt_bulk_t *pcrt_bul struct crt_bulk *bulk = NULL; hg_bulk_t hg_bulk; hg_return_t hg_ret; + uint32_t tmp = 0; int rc; /* @@ -178,12 +179,25 @@ crt_proc_crt_bulk_t(crt_proc_t proc, crt_proc_op_t proc_op, crt_bulk_t *pcrt_bul /* Pack mercury bulk handle to send over the wire */ hg_ret = hg_proc_hg_bulk_t(proc, &hg_bulk); - return (hg_ret == HG_SUCCESS) ? 0 : -DER_HG; + if (unlikely(hg_ret != HG_SUCCESS)) + return -DER_HG; + + /* Pack primary/secondary provider identifier with bulk */ + rc = crt_proc_uint32_t(proc, proc_op, &tmp); + if (unlikely(rc)) + return rc; + + return 0; case CRT_PROC_DECODE: /* unpack mercury handle and wrap it around crt_bulk_t struct */ hg_ret = hg_proc_hg_bulk_t(proc, &hg_bulk); - if (hg_ret != HG_SUCCESS) + if (unlikely(hg_ret != HG_SUCCESS)) + return -DER_HG; + + /* unpack primary/secondary provider identifier with bulk */ + rc = crt_proc_uint32_t(proc, proc_op, &tmp); + if (unlikely(rc)) return -DER_HG; /* don't create a bulk wrapper if null bulk was transmitted */ @@ -194,7 +208,7 @@ crt_proc_crt_bulk_t(crt_proc_t proc, crt_proc_op_t proc_op, crt_bulk_t *pcrt_bul /* Allocate space for a wrapper struct */ D_ALLOC_PTR(bulk); - if (bulk == NULL) + if (unlikely(bulk == NULL)) return -DER_NOMEM; *bulk = (struct crt_bulk){.hg_bulk_hdl = hg_bulk, @@ -207,6 +221,7 @@ crt_proc_crt_bulk_t(crt_proc_t proc, crt_proc_op_t proc_op, crt_bulk_t *pcrt_bul .deferred = false}; *pcrt_bulk = bulk; + return 0; case CRT_PROC_FREE: @@ -465,26 +480,98 @@ crt_proc_corpc_hdr(crt_proc_t proc, struct crt_corpc_hdr *hdr) return rc; } -static inline int -crt_proc_common_hdr(crt_proc_t proc, struct crt_common_hdr *hdr) +#define CRT_HDR_MAX_WIRE_BYTES 512U + +/* Supports newer minor version by skipping unknown header parts */ +static int +crt_proc_hdr_validate_skip(crt_proc_t proc, crt_proc_op_t proc_op, + const struct crt_hdr_common *common, size_t known_bytes) { - crt_proc_op_t proc_op; - int rc; + uint8_t skip[CRT_HDR_MAX_WIRE_BYTES]; + uint16_t header_bytes = common->chc_envelope.che_header_bytes; + size_t extra_bytes; - if (unlikely(hdr == NULL)) - D_GOTO(out, rc = -DER_INVAL); + if (common->chc_envelope.che_major != CRT_HDR_MAJOR || header_bytes < known_bytes || + header_bytes > CRT_HDR_MAX_WIRE_BYTES || header_bytes % sizeof(uint32_t) != 0) + return -DER_PROTO; - /* - * D_DEBUG("in crt_proc_common_hdr, opc: %#x.\n", hdr->cch_opc); - */ + if (common->chc_envelope.che_minor == CRT_HDR_MINOR && header_bytes != known_bytes) + return -DER_PROTO; + + extra_bytes = header_bytes - known_bytes; + if (extra_bytes == 0) + return 0; + + /* We get here if the incoming minor version is newer */ + if (hg_proc_get_size_left(proc) < extra_bytes) + return -DER_PROTO; + + return crt_proc_memcpy(proc, proc_op, skip, extra_bytes); +} + +int +crt_proc_req_hdr(crt_proc_t proc, struct crt_req_hdr *hdr) +{ + crt_proc_op_t proc_op; + int rc; rc = crt_proc_get_op(proc, &proc_op); - if (unlikely(rc)) - D_GOTO(out, rc); + if (rc != 0) + return rc; + + if (hdr == NULL) + return -DER_INVAL; + + if (FREEING(proc_op)) + return 0; + + if (ENCODING(proc_op)) { + hdr->crh_common.chc_envelope.che_major = CRT_HDR_MAJOR; + hdr->crh_common.chc_envelope.che_minor = CRT_HDR_MINOR; + hdr->crh_common.chc_envelope.che_header_bytes = sizeof(*hdr); + } + + if (DECODING(proc_op) && hg_proc_get_size_left(proc) < sizeof(*hdr)) + return -DER_PROTO; rc = crt_proc_memcpy(proc, proc_op, hdr, sizeof(*hdr)); + if (rc != 0 || ENCODING(proc_op)) + return rc; -out: + rc = crt_proc_hdr_validate_skip(proc, proc_op, &hdr->crh_common, sizeof(*hdr)); + return rc; +} + +int +crt_proc_reply_hdr(crt_proc_t proc, struct crt_reply_hdr *hdr) +{ + crt_proc_op_t proc_op; + int rc; + + rc = crt_proc_get_op(proc, &proc_op); + if (rc != 0) + return rc; + + if (hdr == NULL) + return -DER_INVAL; + + if (FREEING(proc_op)) + return 0; + + if (ENCODING(proc_op)) { + hdr->crh_common.chc_envelope.che_major = CRT_HDR_MAJOR; + hdr->crh_common.chc_envelope.che_minor = CRT_HDR_MINOR; + hdr->crh_common.chc_envelope.che_header_bytes = sizeof(*hdr); + } + + if (DECODING(proc_op) && hg_proc_get_size_left(proc) < sizeof(*hdr)) + return -DER_PROTO; + + rc = crt_proc_memcpy(proc, proc_op, hdr, sizeof(*hdr)); + if (rc != 0 || ENCODING(proc_op)) + return rc; + + rc = crt_proc_hdr_validate_skip(proc, proc_op, &hdr->crh_common, sizeof(*hdr)); return rc; } @@ -562,36 +649,38 @@ crt_hg_unpack_header(hg_handle_t handle, struct crt_rpc_priv *rpc_priv, } /* Decode header */ - rc = crt_proc_common_hdr(hg_proc, &rpc_priv->crp_req_hdr); + rc = crt_proc_req_hdr(hg_proc, &rpc_priv->crp_req_hdr); if (rc != 0) { - RPC_ERROR(rpc_priv, "crt_proc_common_hdr failed: " DF_RC "\n", DP_RC(rc)); + RPC_ERROR(rpc_priv, "crt_proc_req_hdr failed: " DF_RC "\n", DP_RC(rc)); + hg_proc_free(hg_proc); D_GOTO(out, rc); } /* Sync the HLC. Clients never decode requests. */ D_ASSERT(crt_is_service()); - rc = d_hlc_get_msg(rpc_priv->crp_req_hdr.cch_hlc, NULL /* hlc_out */, &clock_offset); + rc = d_hlc_get_msg(rpc_priv->crp_req_hdr.crh_common.chc_hlc, NULL /* hlc_out */, + &clock_offset); if (rc != 0) { - REPORT_HLC_SYNC_ERR("failed to sync HLC for request: opc=%x ts=" - DF_U64" offset="DF_U64" from=%u\n", - rpc_priv->crp_req_hdr.cch_opc, - rpc_priv->crp_req_hdr.cch_hlc, - clock_offset, - rpc_priv->crp_req_hdr.cch_src_rank); + REPORT_HLC_SYNC_ERR("failed to sync HLC for request: opc=%x ts=" DF_U64 + " offset=" DF_U64 " from=%u\n", + rpc_priv->crp_req_hdr.crh_opc, + rpc_priv->crp_req_hdr.crh_common.chc_hlc, clock_offset, + rpc_priv->crp_req_hdr.crh_src_rank); /* Fail all but SWIM requests. */ - if (!crt_opc_is_swim(rpc_priv->crp_req_hdr.cch_opc)) + if (!crt_opc_is_swim(rpc_priv->crp_req_hdr.crh_opc)) rpc_priv->crp_fail_hlc = 1; rc = 0; } - rpc_priv->crp_flags = rpc_priv->crp_req_hdr.cch_flags; + rpc_priv->crp_flags = rpc_priv->crp_req_hdr.crh_flags; if (rpc_priv->crp_flags & CRT_RPC_FLAG_COLL) { rc = crt_proc_corpc_hdr(hg_proc, &rpc_priv->crp_coreq_hdr); if (rc != 0) { RPC_ERROR(rpc_priv, "crt_proc_corpc_hdr failed: " DF_RC"\n", DP_RC(rc)); + hg_proc_free(hg_proc); D_GOTO(out, rc); } } @@ -605,17 +694,12 @@ crt_hg_unpack_header(hg_handle_t handle, struct crt_rpc_priv *rpc_priv, /* Process header. * Populate 'out' fields of rpc_priv struct based on incoming rpc header 'in' * - * Treat incoming 'cch_src_deadline_sec' header value as either a timeout or a - * deadline based on a feature flag set in the RPC header. - * - * * Returns 0 on success or -DER_TIMEDOUT if RPC is determined to be expired already. */ int crt_hg_process_header(struct crt_rpc_priv *in, struct crt_rpc_priv *out) { - int src_val; - int timeout; + int64_t timeout; int rc = 0; out->crp_hg_addr = in->crp_hg_addr; @@ -623,31 +707,14 @@ crt_hg_process_header(struct crt_rpc_priv *in, struct crt_rpc_priv *out) out->crp_pub.cr_ctx = in->crp_pub.cr_ctx; out->crp_flags = in->crp_flags; out->crp_req_hdr = in->crp_req_hdr; - out->crp_reply_hdr.cch_hlc = in->crp_reply_hdr.cch_hlc; - - src_val = in->crp_req_hdr.cch_src_deadline_sec; - - /* Treat header value as a deadline or a timeout depending on the feat flag */ - if (in->crp_flags & CRT_RPC_FLAG_DEADLINES_USED) { - out->crp_deadline_sec = src_val; - - timeout = crt_deadline_to_timeout(src_val); - /* - * TODO: need a better way in future to handle an edge case where a client - * can be running ahead of the server, but within hlc_epsilon allowed. Also - * need to account for 1 second granularity of deadlines. - */ - if (timeout == 0) - timeout = 1; - - if (timeout > 0) - out->crp_timeout_sec = timeout; - else - D_GOTO(exit, rc = -DER_TIMEDOUT); - } else { - out->crp_deadline_sec = crt_timeout_to_deadline(src_val); - out->crp_timeout_sec = src_val; - } + out->crp_reply_hdr.crh_common.chc_rpcid = in->crp_req_hdr.crh_common.chc_rpcid; + out->crp_deadline_sec = in->crp_req_hdr.crh_deadline_unix_sec; + timeout = crt_deadline_to_timeout(out->crp_deadline_sec); + if (timeout == 0) + timeout = 1; + if (timeout < 0) + D_GOTO(exit, rc = -DER_TIMEDOUT); + out->crp_timeout_sec = timeout; if (!(out->crp_flags & CRT_RPC_FLAG_COLL)) return 0; @@ -715,7 +782,7 @@ int crt_proc_in_common(crt_proc_t proc, crt_rpc_input_t *data) { struct crt_rpc_priv *rpc_priv; - struct crt_common_hdr *hdr; + struct crt_req_hdr *hdr; crt_proc_op_t proc_op; int rc; @@ -733,40 +800,35 @@ crt_proc_in_common(crt_proc_t proc, crt_rpc_input_t *data) if (ENCODING(proc_op)) { hdr = &rpc_priv->crp_req_hdr; - hdr->cch_flags = rpc_priv->crp_flags; - hdr->cch_dst_rank = crt_grp_priv_get_primary_rank( - rpc_priv->crp_grp_priv, rpc_priv->crp_pub.cr_ep.ep_rank); - hdr->cch_dst_tag = rpc_priv->crp_pub.cr_ep.ep_tag; + hdr->crh_flags = rpc_priv->crp_flags; + rc = crt_req_dst_set( + hdr, + crt_grp_priv_get_primary_rank(rpc_priv->crp_grp_priv, + rpc_priv->crp_pub.cr_ep.ep_rank), + rpc_priv->crp_pub.cr_ep.ep_tag); + if (rc != 0) + D_GOTO(out, rc); - if (rpc_priv->crp_flags & CRT_RPC_FLAG_DEADLINES_USED) { - hdr->cch_src_deadline_sec = rpc_priv->crp_deadline_sec; - } else { - /* Support forwarding of rpc timeout for deprecated clients */ - hdr->cch_src_deadline_sec = rpc_priv->crp_timeout_sec; - } + hdr->crh_deadline_unix_sec = rpc_priv->crp_deadline_sec; if (crt_is_service()) { - hdr->cch_src_rank = - crt_grp_priv_get_primary_rank( - rpc_priv->crp_grp_priv, - rpc_priv->crp_grp_priv->gp_self - ); - hdr->cch_hlc = d_hlc_get(); + hdr->crh_src_rank = crt_grp_priv_get_primary_rank( + rpc_priv->crp_grp_priv, rpc_priv->crp_grp_priv->gp_self); + hdr->crh_common.chc_hlc = d_hlc_get(); } else { - hdr->cch_src_rank = CRT_NO_RANK; + hdr->crh_src_rank = CRT_NO_RANK; /* * Because client HLC timestamps shall never be * used to sync server HLCs, we forward the * HLCT reading, which must be either zero or a * server HLC timestamp. */ - hdr->cch_hlc = d_hlct_get(); + hdr->crh_common.chc_hlc = d_hlct_get(); } } - rc = crt_proc_common_hdr(proc, &rpc_priv->crp_req_hdr); + rc = crt_proc_req_hdr(proc, &rpc_priv->crp_req_hdr); if (rc != 0) { - RPC_ERROR(rpc_priv, "crt_proc_common_hdr failed: " - DF_RC"\n", DP_RC(rc)); + RPC_ERROR(rpc_priv, "crt_proc_req_hdr failed: " DF_RC "\n", DP_RC(rc)); D_GOTO(out, rc); } /** @@ -795,7 +857,7 @@ crt_proc_in_common(crt_proc_t proc, crt_rpc_input_t *data) if (*data == NULL) { /* D_DEBUG("crt_proc_in_common, opc: %#x, NULL input.\n", - rpc_priv->crp_req_hdr.cch_opc); + rpc_priv->crp_req_hdr.crh_opc); */ D_GOTO(out, rc); } @@ -833,45 +895,45 @@ crt_proc_out_common(crt_proc_t proc, crt_rpc_output_t *data) if (ENCODING(proc_op)) { /* Clients never encode replies. */ D_ASSERT(crt_is_service()); - rpc_priv->crp_reply_hdr.cch_hlc = d_hlc_get(); + rpc_priv->crp_reply_hdr.crh_common.chc_hlc = d_hlc_get(); + rpc_priv->crp_reply_hdr.crh_common.chc_rpcid = + rpc_priv->crp_req_hdr.crh_common.chc_rpcid; } - rc = crt_proc_common_hdr(proc, &rpc_priv->crp_reply_hdr); + rc = crt_proc_reply_hdr(proc, &rpc_priv->crp_reply_hdr); if (rc != 0) { - RPC_ERROR(rpc_priv, "crt_proc_common_hdr failed: " - DF_RC"\n", DP_RC(rc)); + RPC_ERROR(rpc_priv, "crt_proc_reply_hdr failed: " DF_RC "\n", DP_RC(rc)); D_GOTO(out, rc); } if (DECODING(proc_op)) { - struct crt_common_hdr *hdr = &rpc_priv->crp_reply_hdr; + struct crt_reply_hdr *hdr = &rpc_priv->crp_reply_hdr; + + if (hdr->crh_common.chc_rpcid != rpc_priv->crp_req_hdr.crh_common.chc_rpcid) + D_GOTO(out, rc = -DER_PROTO); if (crt_is_service()) { uint64_t clock_offset; - rc = d_hlc_get_msg(hdr->cch_hlc, - NULL /* hlc_out */, - &clock_offset); + rc = d_hlc_get_msg(hdr->crh_common.chc_hlc, NULL /* hlc_out */, + &clock_offset); if (rc != 0) { - REPORT_HLC_SYNC_ERR("failed to sync " - "HLC for reply: " - "opc=%x ts="DF_U64 - " offset="DF_U64 - " from=%u\n", - hdr->cch_opc, - hdr->cch_hlc, - clock_offset, - hdr->cch_dst_rank); + REPORT_HLC_SYNC_ERR( + "failed to sync " + "HLC for reply: " + "opc=%x ts=" DF_U64 " offset=" DF_U64 " from=%u\n", + rpc_priv->crp_pub.cr_opc, hdr->crh_common.chc_hlc, + clock_offset, rpc_priv->crp_pub.cr_ep.ep_rank); /* Fail all but SWIM replies. */ - if (!crt_opc_is_swim(hdr->cch_opc)) + if (!crt_opc_is_swim(rpc_priv->crp_pub.cr_opc)) rpc_priv->crp_fail_hlc = 1; rc = 0; } } else { - d_hlct_sync(hdr->cch_hlc); + d_hlct_sync(hdr->crh_common.chc_hlc); } } - rc2 = rpc_priv->crp_reply_hdr.cch_rc; + rc2 = rpc_priv->crp_reply_hdr.crh_status; if (rc2 != 0) { RPC_CWARN(crt_quiet_error(rc2), DB_NET, rpc_priv, "RPC failed to execute on target. " @@ -885,7 +947,7 @@ crt_proc_out_common(crt_proc_t proc, crt_rpc_output_t *data) if (*data == NULL) { /* D_DEBUG("crt_proc_out_common, opc: %#x, NULL output.\n", - rpc_priv->crp_req_hdr.cch_opc); + rpc_priv->crp_req_hdr.crh_opc); */ D_GOTO(out, rc); } diff --git a/src/cart/crt_internal.h b/src/cart/crt_internal.h index e9cb462c56f..ca9289f7f81 100644 --- a/src/cart/crt_internal.h +++ b/src/cart/crt_internal.h @@ -41,13 +41,13 @@ if ((rpc)->crp_coll) { \ D_TRACE_DEBUG(mask, (rpc), "[opc=%#x (%s:%s) rpcid=%#lx CORPC] " fmt, \ (rpc)->crp_pub.cr_opc, _module, _opc, \ - (rpc)->crp_req_hdr.cch_rpcid, ##__VA_ARGS__); \ + (rpc)->crp_req_hdr.crh_common.chc_rpcid, ##__VA_ARGS__); \ } else { \ D_TRACE_DEBUG(mask, (rpc), \ "[opc=%#x (%s:%s) rpcid=%#lx rank:tag=%d:%d orig=%s] " fmt, \ (rpc)->crp_pub.cr_opc, _module, _opc, \ - (rpc)->crp_req_hdr.cch_rpcid, (rpc)->crp_pub.cr_ep.ep_rank, \ - (rpc)->crp_pub.cr_ep.ep_tag, \ + (rpc)->crp_req_hdr.crh_common.chc_rpcid, \ + (rpc)->crp_pub.cr_ep.ep_rank, (rpc)->crp_pub.cr_ep.ep_tag, \ crt_rpc_priv_get_origin_addr((rpc)), ##__VA_ARGS__); \ } \ } while (0) @@ -62,13 +62,14 @@ if ((rpc)->crp_coll) { \ D_TRACE_ERROR((rpc), "[opc=%#x (%s:%s) rpcid=%#lx CORPC] " fmt, \ (rpc)->crp_pub.cr_opc, _module, _opc, \ - (rpc)->crp_req_hdr.cch_rpcid, ##__VA_ARGS__); \ + (rpc)->crp_req_hdr.crh_common.chc_rpcid, ##__VA_ARGS__); \ } else { \ - D_TRACE_ERROR( \ - (rpc), "[opc=%#x (%s:%s) rpcid=%#lx rank:tag=%d:%d orig=%s] " fmt, \ - (rpc)->crp_pub.cr_opc, _module, _opc, (rpc)->crp_req_hdr.cch_rpcid, \ - (rpc)->crp_pub.cr_ep.ep_rank, (rpc)->crp_pub.cr_ep.ep_tag, \ - crt_rpc_priv_get_origin_addr((rpc)), ##__VA_ARGS__); \ + D_TRACE_ERROR((rpc), \ + "[opc=%#x (%s:%s) rpcid=%#lx rank:tag=%d:%d orig=%s] " fmt, \ + (rpc)->crp_pub.cr_opc, _module, _opc, \ + (rpc)->crp_req_hdr.crh_common.chc_rpcid, \ + (rpc)->crp_pub.cr_ep.ep_rank, (rpc)->crp_pub.cr_ep.ep_tag, \ + crt_rpc_priv_get_origin_addr((rpc)), ##__VA_ARGS__); \ } \ } while (0) @@ -82,13 +83,14 @@ if ((rpc)->crp_coll) { \ D_TRACE_WARN((rpc), "[opc=%#x (%s:%s) rpcid=%#lx CORPC] " fmt, \ (rpc)->crp_pub.cr_opc, _module, _opc, \ - (rpc)->crp_req_hdr.cch_rpcid, ##__VA_ARGS__); \ + (rpc)->crp_req_hdr.crh_common.chc_rpcid, ##__VA_ARGS__); \ } else { \ - D_TRACE_WARN( \ - (rpc), "[opc=%#x (%s:%s) rpcid=%#lx rank:tag=%d:%d orig=%s] " fmt, \ - (rpc)->crp_pub.cr_opc, _module, _opc, (rpc)->crp_req_hdr.cch_rpcid, \ - (rpc)->crp_pub.cr_ep.ep_rank, (rpc)->crp_pub.cr_ep.ep_tag, \ - crt_rpc_priv_get_origin_addr((rpc)), ##__VA_ARGS__); \ + D_TRACE_WARN((rpc), \ + "[opc=%#x (%s:%s) rpcid=%#lx rank:tag=%d:%d orig=%s] " fmt, \ + (rpc)->crp_pub.cr_opc, _module, _opc, \ + (rpc)->crp_req_hdr.crh_common.chc_rpcid, \ + (rpc)->crp_pub.cr_ep.ep_rank, (rpc)->crp_pub.cr_ep.ep_tag, \ + crt_rpc_priv_get_origin_addr((rpc)), ##__VA_ARGS__); \ } \ } while (0) @@ -102,13 +104,14 @@ if ((rpc)->crp_coll) { \ D_TRACE_INFO((rpc), "[opc=%#x (%s:%s) rpcid=%#lx CORPC] " fmt, \ (rpc)->crp_pub.cr_opc, _module, _opc, \ - (rpc)->crp_req_hdr.cch_rpcid, ##__VA_ARGS__); \ + (rpc)->crp_req_hdr.crh_common.chc_rpcid, ##__VA_ARGS__); \ } else { \ - D_TRACE_INFO( \ - (rpc), "[opc=%#x (%s:%s) rpcid=%#lx rank:tag=%d:%d] orig=%s" fmt, \ - (rpc)->crp_pub.cr_opc, _module, _opc, (rpc)->crp_req_hdr.cch_rpcid, \ - (rpc)->crp_pub.cr_ep.ep_rank, (rpc)->crp_pub.cr_ep.ep_tag, \ - crt_rpc_priv_get_origin_addr((rpc)), ##__VA_ARGS__); \ + D_TRACE_INFO((rpc), \ + "[opc=%#x (%s:%s) rpcid=%#lx rank:tag=%d:%d] orig=%s" fmt, \ + (rpc)->crp_pub.cr_opc, _module, _opc, \ + (rpc)->crp_req_hdr.crh_common.chc_rpcid, \ + (rpc)->crp_pub.cr_ep.ep_rank, (rpc)->crp_pub.cr_ep.ep_tag, \ + crt_rpc_priv_get_origin_addr((rpc)), ##__VA_ARGS__); \ } \ } while (0) /** diff --git a/src/cart/crt_rpc.c b/src/cart/crt_rpc.c index 27534bf3a36..1726ebadfd3 100644 --- a/src/cart/crt_rpc.c +++ b/src/cart/crt_rpc.c @@ -1668,17 +1668,16 @@ crt_rpc_inout_buff_init(struct crt_rpc_priv *rpc_priv) } static inline void -crt_common_hdr_init(struct crt_rpc_priv *rpc_priv, crt_opcode_t opc) +crt_rpc_hdr_init(struct crt_rpc_priv *rpc_priv, crt_opcode_t opc) { uint64_t rpcid; rpcid = atomic_fetch_add(&crt_gdata.cg_rpcid, 1); - rpc_priv->crp_req_hdr.cch_opc = opc; - rpc_priv->crp_req_hdr.cch_rpcid = rpcid; + rpc_priv->crp_req_hdr.crh_opc = opc; + rpc_priv->crp_req_hdr.crh_common.chc_rpcid = rpcid; - rpc_priv->crp_reply_hdr.cch_opc = opc; - rpc_priv->crp_reply_hdr.cch_rpcid = rpcid; + rpc_priv->crp_reply_hdr.crh_common.chc_rpcid = rpcid; } void @@ -1696,13 +1695,12 @@ crt_rpc_priv_init(struct crt_rpc_priv *rpc_priv, crt_context_t crt_ctx, bool srv rpc_priv->crp_completed = 0; if (!srv_flag) { /* avoid checksum warning */ - crt_common_hdr_init(rpc_priv, opc); + crt_rpc_hdr_init(rpc_priv, opc); } rpc_priv->crp_state = RPC_STATE_INITED; rpc_priv->crp_hdl_reuse = NULL; rpc_priv->crp_srv = srv_flag; - rpc_priv->crp_ul_retry = 0; - rpc_priv->crp_flags |= CRT_RPC_FLAG_DEADLINES_USED; + rpc_priv->crp_ul_retry = 0; if (srv_flag) { rpc_priv->crp_src_is_primary = ctx->cc_primary; @@ -1803,12 +1801,13 @@ crt_rpc_common_hdlr(struct crt_rpc_priv *rpc_priv) goto skip_check; } - if ((self_rank != CRT_NO_RANK && self_rank != rpc_priv->crp_req_hdr.cch_dst_rank) || - crt_ctx->cc_idx != rpc_priv->crp_req_hdr.cch_dst_tag) { + if ((self_rank != CRT_NO_RANK && self_rank != crt_req_dst_rank(&rpc_priv->crp_req_hdr)) || + crt_ctx->cc_idx != crt_req_dst_tag(&rpc_priv->crp_req_hdr)) { D_ERROR("Mismatch rpc: %p opc: %x rank:%d tag:%d " "self:%d cc_idx:%d ep_rank:%d ep_tag:%d\n", - rpc_priv, rpc_priv->crp_pub.cr_opc, rpc_priv->crp_req_hdr.cch_dst_rank, - rpc_priv->crp_req_hdr.cch_dst_tag, self_rank, crt_ctx->cc_idx, + rpc_priv, rpc_priv->crp_pub.cr_opc, + crt_req_dst_rank(&rpc_priv->crp_req_hdr), + crt_req_dst_tag(&rpc_priv->crp_req_hdr), self_rank, crt_ctx->cc_idx, rpc_priv->crp_pub.cr_ep.ep_rank, rpc_priv->crp_pub.cr_ep.ep_tag); D_GOTO(out, rc = -DER_BAD_TARGET); @@ -1820,7 +1819,7 @@ crt_rpc_common_hdlr(struct crt_rpc_priv *rpc_priv) rpc_priv->crp_reply_pending = 1; if (crt_rpc_cb_customized(crt_ctx, &rpc_priv->crp_pub) && - !crt_opc_is_swim(rpc_priv->crp_req_hdr.cch_opc)) { + !crt_opc_is_swim(rpc_priv->crp_req_hdr.crh_opc)) { /* Corresponding decref in crt_handle_rpc() */ RPC_ADDREF(rpc_priv); rc = crt_ctx->cc_rpc_cb((crt_context_t)crt_ctx, @@ -1912,7 +1911,7 @@ crt_req_src_rank_get(crt_rpc_t *rpc, d_rank_t *rank) rpc_priv = container_of(rpc, struct crt_rpc_priv, crp_pub); - *rank = rpc_priv->crp_req_hdr.cch_src_rank; + *rank = rpc_priv->crp_req_hdr.crh_src_rank; out: return rc; @@ -1936,7 +1935,7 @@ crt_req_dst_rank_get(crt_rpc_t *rpc, d_rank_t *rank) rpc_priv = container_of(rpc, struct crt_rpc_priv, crp_pub); - *rank = rpc_priv->crp_req_hdr.cch_dst_rank; + *rank = crt_req_dst_rank(&rpc_priv->crp_req_hdr); out: return rc; @@ -1959,7 +1958,7 @@ crt_req_dst_tag_get(crt_rpc_t *rpc, uint32_t *tag) } rpc_priv = container_of(rpc, struct crt_rpc_priv, crp_pub); - *tag = rpc_priv->crp_req_hdr.cch_dst_tag; + *tag = crt_req_dst_tag(&rpc_priv->crp_req_hdr); out: return rc; } diff --git a/src/cart/crt_rpc.h b/src/cart/crt_rpc.h index 75bf0c8b7fa..4777adb89a1 100644 --- a/src/cart/crt_rpc.h +++ b/src/cart/crt_rpc.h @@ -38,8 +38,32 @@ enum crt_rpc_flags_internal { CRT_RPC_FLAG_COLL = (1U << 16), /* flag of targeting primary group */ CRT_RPC_FLAG_PRIMARY_GRP = (1U << 17), - /* flag of using deadlines instead of timeouts in RPC headers */ - CRT_RPC_FLAG_DEADLINES_USED = (1U << 18), + /* Legacy pre 3.0 flag of using deadlines instead of timeouts in RPC headers */ + CRT_RPC_FLAG_DEADLINES_USED_LEGACY = (1U << 18), +}; + +/* Legacy pre 3.0 common RPC header used for both request and response */ +struct crt_common_hdr_legacy { + uint32_t cch_opc; + /* RPC request flag, see enum crt_rpc_flags_internal */ + uint32_t cch_flags; + /* HLC timestamp */ + uint64_t cch_hlc; + /* RPC id */ + uint64_t cch_rpcid; + /* destination rank in default primary group */ + d_rank_t cch_dst_rank; + /* originator rank in default primary group */ + d_rank_t cch_src_rank; + /* destination tag */ + uint32_t cch_dst_tag; + + /* used in crp_reply_hdr to propagate rpc failure back to sender */ + /* TODO: workaround for DAOS-13973 */ + union { + uint32_t cch_src_deadline_sec; + uint32_t cch_rc; + }; }; struct crt_corpc_hdr { @@ -59,49 +83,64 @@ struct crt_corpc_hdr { uint32_t coh_padding; }; -/* CaRT layer common header */ -struct crt_common_hdr { - uint32_t cch_opc; - /* RPC request flag, see enum crt_rpc_flags_internal */ - uint32_t cch_flags; - /* HLC timestamp */ - uint64_t cch_hlc; - /* RPC id */ - uint64_t cch_rpcid; - /* destination rank in default primary group */ - d_rank_t cch_dst_rank; - /* originator rank in default primary group */ - d_rank_t cch_src_rank; - /* destination tag */ - uint32_t cch_dst_tag; +struct __attribute__((packed)) crt_hdr_envelope { + uint8_t che_major; + uint8_t che_minor; + uint16_t che_header_bytes; +}; +struct __attribute__((packed)) crt_hdr_common { + struct crt_hdr_envelope chc_envelope; + uint64_t chc_hlc; + uint64_t chc_rpcid; +}; - /* used in crp_reply_hdr to propagate rpc failure back to sender */ - /* TODO: workaround for DAOS-13973 */ - union { - uint32_t cch_src_deadline_sec; - uint32_t cch_rc; - }; +/* rank:tag are packed as (rank << 8) | tag: + * upper 24 bits - rank + * lower 8 bits - tag + */ +union crt_dst_rank_tag { + uint32_t packed; }; -static inline int32_t -crt_deadline_to_timeout(uint32_t deadline_sec) -{ - struct timespec now; +struct __attribute__((packed)) crt_req_hdr { + struct crt_hdr_common crh_common; + uint32_t crh_opc; + uint32_t crh_flags; + uint32_t crh_src_rank; + union crt_dst_rank_tag crh_dst; + uint32_t crh_deadline_unix_sec; +}; - clock_gettime(CLOCK_REALTIME_COARSE, &now); - return deadline_sec - now.tv_sec - 1; -} +struct __attribute__((packed)) crt_reply_hdr { + struct crt_hdr_common crh_common; + int32_t crh_status; +}; + +#define CRT_HDR_MAJOR 1U +#define CRT_HDR_MINOR 0U +#define CRT_REQ_HDR_V1_BYTES sizeof(struct crt_req_hdr) +#define CRT_REPLY_HDR_V1_BYTES sizeof(struct crt_reply_hdr) static inline uint32_t -crt_timeout_to_deadline(int timeout_sec) +crt_req_dst_rank(const struct crt_req_hdr *hdr) { - struct timespec now; + return hdr->crh_dst.packed >> 8; +} - clock_gettime(CLOCK_REALTIME_COARSE, &now); +static inline uint8_t +crt_req_dst_tag(const struct crt_req_hdr *hdr) +{ + return hdr->crh_dst.packed & 0xffU; +} - /* Deadline is the next second after timeout */ - return now.tv_sec + timeout_sec + 1; +static inline int +crt_req_dst_set(struct crt_req_hdr *hdr, uint32_t rank, uint32_t tag) +{ + if (rank > 0x00ffffffU || tag > 0xffU) + return -DER_INVAL; + hdr->crh_dst.packed = (rank << 8) | tag; + return 0; } typedef enum { @@ -166,8 +205,8 @@ struct crt_rpc_priv { struct d_binheap_node crp_timeout_bp_node; /* the timeout in seconds set by user */ uint32_t crp_timeout_sec; - /* the deadline corresponding to crp_timeout_sec */ - uint32_t crp_deadline_sec; + /* the absolute Unix deadline in seconds */ + uint64_t crp_deadline_sec; /* time stamp to be timeout, the key of timeout binheap */ uint64_t crp_timeout_ts; crt_cb_t crp_complete_cb; @@ -190,7 +229,7 @@ struct crt_rpc_priv { struct crt_grp_priv *crp_grp_priv; /* group private pointer */ /* * RPC request flag, see enum crt_rpc_flags/crt_rpc_flags_internal, - * match with crp_req_hdr.cch_flags. + * match with crp_req_hdr.crh_flags. */ uint32_t crp_flags; uint32_t crp_srv : 1, /* flag of server received request */ @@ -235,8 +274,8 @@ struct crt_rpc_priv { * crt_gdata.cg_rwlock */ pthread_mutex_t crp_mutex; - struct crt_common_hdr crp_reply_hdr; /* common header for reply */ - struct crt_common_hdr crp_req_hdr; /* common header for request */ + struct crt_reply_hdr crp_reply_hdr; /* header for reply */ + struct crt_req_hdr crp_req_hdr; /* header for request */ struct crt_corpc_hdr crp_coreq_hdr; /* collective request header */ }; diff --git a/src/cart/crt_swim.c b/src/cart/crt_swim.c index 70f5f334246..4059dbf5b7e 100644 --- a/src/cart/crt_swim.c +++ b/src/cart/crt_swim.c @@ -52,7 +52,7 @@ * (This exclusion detection protocol could be piggybacked on all RPCs, after * optimizing away the rank lookup when group versions match and speeding up * the rank lookup when group versions differ. The main difficulty is that we - * would need to expand crt_common_hdr.) + * would need to expand the request and reply headers.) */ #define CRT_OSEQ_RPC_SWIM /* output fields */ \ ((int32_t) (rc) CRT_VAR) \ @@ -421,7 +421,7 @@ static void crt_swim_srv_cb(crt_rpc_t *rpc) crt_swim_update_last_unpack_hlc(csm, hlc); - from_id = rpc_priv->crp_req_hdr.cch_src_rank; + from_id = rpc_priv->crp_req_hdr.crh_src_rank; /* Initialize empty array in case of error in reply */ rpc_out->upds.ca_arrays = NULL; @@ -445,8 +445,8 @@ static void crt_swim_srv_cb(crt_rpc_t *rpc) * crt_hg_unpack_header may have failed to synchronize the HLC with * this request. */ - if (hlc > rpc_priv->crp_req_hdr.cch_hlc) - rcv_delay = d_hlc2msec(hlc - rpc_priv->crp_req_hdr.cch_hlc); + if (hlc > rpc_priv->crp_req_hdr.crh_common.chc_hlc) + rcv_delay = d_hlc2msec(hlc - rpc_priv->crp_req_hdr.crh_common.chc_hlc); RPC_TRACE(DB_NET, rpc_priv, "incoming %s with %zu updates with %u ms delay. %lu: %lu <= %lu\n", @@ -607,8 +607,8 @@ static void crt_swim_cli_cb(const struct crt_cb_info *cb_info) break; } - if (hlc > rpc_priv->crp_reply_hdr.cch_hlc) - rcv_delay = d_hlc2msec(hlc - rpc_priv->crp_reply_hdr.cch_hlc); + if (hlc > rpc_priv->crp_reply_hdr.crh_common.chc_hlc) + rcv_delay = d_hlc2msec(hlc - rpc_priv->crp_reply_hdr.crh_common.chc_hlc); RPC_TRACE(DB_NET, rpc_priv, "complete %s with %zu/%zu updates with %u ms delay. %lu: %lu => %lu " diff --git a/src/include/gurt/common.h b/src/include/gurt/common.h index a0ed3132af4..b5b1619ab9a 100644 --- a/src/include/gurt/common.h +++ b/src/include/gurt/common.h @@ -809,6 +809,33 @@ d_timeleft_ns(const struct timespec *expiration) return ns; } +static inline uint64_t +crt_realtime_sec(void) +{ + struct timespec now; + + clock_gettime(CLOCK_REALTIME_COARSE, &now); + return (uint64_t)now.tv_sec; +} + +static inline int64_t +crt_deadline_to_timeout(uint64_t deadline_sec) +{ + uint64_t now_sec = crt_realtime_sec(); + uint64_t timeout_sec; + + if (deadline_sec <= now_sec) + return -1; + timeout_sec = deadline_sec - now_sec - 1; + return timeout_sec > UINT32_MAX ? UINT32_MAX : (int64_t)timeout_sec; +} + +static inline uint64_t +crt_timeout_to_deadline(uint32_t timeout_sec) +{ + return crt_realtime_sec() + (uint64_t)timeout_sec + 1; +} + static inline struct timespec d_time_elapsed(const struct timespec start) { diff --git a/src/tests/rpc/SConscript b/src/tests/rpc/SConscript index e6b80fa6992..8f77cca9acd 100644 --- a/src/tests/rpc/SConscript +++ b/src/tests/rpc/SConscript @@ -16,6 +16,11 @@ def scons(): denv.d_test_program('rpc_tests', sources, LIBS=libs) + henv = denv.Clone() + henv.require('mercury') + henv.AppendUnique(LIBS=['cart', 'gurt', 'cmocka', 'pthread']) + henv.d_test_program('cart_hdr_tests', ['cart_hdr_tests.c']) + if __name__ == "SCons.Script": scons() diff --git a/src/tests/rpc/cart_hdr_tests.c b/src/tests/rpc/cart_hdr_tests.c new file mode 100644 index 00000000000..b7243ab498d --- /dev/null +++ b/src/tests/rpc/cart_hdr_tests.c @@ -0,0 +1,221 @@ +/** + * (C) Copyright 2026 Hewlett Packard Enterprise Development LP + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ + +/* This file implements CaRT RPC header tests */ +#include +#include +#include +#include + +#include "cart/crt_internal.h" + +static hg_class_t *header_class; + +static int +header_setup(void **state) +{ + (void)state; + header_class = HG_Init("na+sm", HG_FALSE); + assert_non_null(header_class); + return 0; +} + +static int +header_teardown(void **state) +{ + (void)state; + assert_int_equal(HG_Finalize(header_class), HG_SUCCESS); + return 0; +} + +static const uint8_t request_bytes[CRT_REQ_HDR_V1_BYTES] = { + 0x01, 0x00, 0x28, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x21, 0x22, 0x23, 0x24, 0x00, 0x00, 0x03, 0x00, + 0x31, 0x32, 0x33, 0x34, 0x51, 0x42, 0x43, 0x44, 0x5d, 0x5e, 0x5f, 0x60, +}; + +static hg_proc_t +header_proc(void *buffer, size_t bytes, hg_proc_op_t op) +{ + hg_proc_t proc; + + assert_int_equal(hg_proc_create_set(header_class, buffer, bytes, op, HG_NOHASH, &proc), + HG_SUCCESS); + return proc; +} + +static int +decode_request(void *buffer, size_t bytes, struct crt_req_hdr *hdr, size_t *used) +{ + hg_proc_t proc = header_proc(buffer, bytes, HG_DECODE); + int rc = crt_proc_req_hdr(proc, hdr); + + if (used != NULL) + *used = hg_proc_get_size_used(proc); + assert_int_equal(hg_proc_free(proc), HG_SUCCESS); + return rc; +} + +static void +request_roundtrip(void **state) +{ + struct crt_req_hdr request = { + .crh_common = + { + .chc_hlc = 0x0807060504030201ULL, + .chc_rpcid = 0x1817161514131211ULL, + }, + .crh_opc = 0x24232221, + .crh_flags = CRT_RPC_FLAG_COLL | CRT_RPC_FLAG_PRIMARY_GRP, + .crh_src_rank = 0x34333231, + .crh_dst = {.packed = (0x444342U << 8) | 0x51U}, + .crh_deadline_unix_sec = 0x605f5e5dULL, + }; + struct crt_req_hdr decoded = {0}; + uint8_t buffer[CRT_REQ_HDR_V1_BYTES]; + hg_proc_t proc = header_proc(buffer, sizeof(buffer), HG_ENCODE); + size_t used; + + (void)state; + assert_int_equal(crt_proc_req_hdr(proc, &request), 0); + assert_int_equal(hg_proc_get_size_used(proc), sizeof(buffer)); + assert_memory_equal(buffer, request_bytes, sizeof(buffer)); + assert_int_equal(hg_proc_free(proc), HG_SUCCESS); + assert_int_equal(decode_request(buffer, sizeof(buffer), &decoded, &used), 0); + assert_int_equal(used, sizeof(buffer)); + assert_int_equal(decoded.crh_common.chc_hlc, request.crh_common.chc_hlc); + assert_int_equal(decoded.crh_common.chc_rpcid, request.crh_common.chc_rpcid); + assert_int_equal(decoded.crh_opc, request.crh_opc); + assert_int_equal(decoded.crh_flags, request.crh_flags); + assert_int_equal(decoded.crh_src_rank, request.crh_src_rank); + assert_int_equal(crt_req_dst_rank(&decoded), crt_req_dst_rank(&request)); + assert_int_equal(crt_req_dst_tag(&decoded), crt_req_dst_tag(&request)); + assert_int_equal(decoded.crh_deadline_unix_sec, request.crh_deadline_unix_sec); +} + +static void +request_validation(void **state) +{ + struct crt_req_hdr decoded = {0}; + uint8_t buffer[CRT_REQ_HDR_V1_BYTES + 4] = {0}; + size_t bytes; + size_t used; + + (void)state; + memcpy(buffer, request_bytes, sizeof(request_bytes)); + for (bytes = 0; bytes < sizeof(request_bytes); bytes++) + assert_int_equal(decode_request(buffer, bytes, &decoded, NULL), -DER_PROTO); + buffer[0] = CRT_HDR_MAJOR + 1; + assert_int_equal(decode_request(buffer, sizeof(buffer), &decoded, NULL), -DER_PROTO); + buffer[0] = CRT_HDR_MAJOR; + buffer[2] = CRT_REQ_HDR_V1_BYTES - 4; + assert_int_equal(decode_request(buffer, sizeof(buffer), &decoded, NULL), -DER_PROTO); + buffer[2] = CRT_REQ_HDR_V1_BYTES + 4; + assert_int_equal(decode_request(buffer, sizeof(buffer), &decoded, NULL), -DER_PROTO); + memcpy(buffer, request_bytes, sizeof(request_bytes)); + buffer[1] = CRT_HDR_MINOR + 1; + buffer[2] = CRT_REQ_HDR_V1_BYTES; + assert_int_equal(decode_request(buffer, CRT_REQ_HDR_V1_BYTES, &decoded, &used), 0); + assert_int_equal(used, CRT_REQ_HDR_V1_BYTES); + memcpy(buffer, request_bytes, sizeof(request_bytes)); + buffer[1] = CRT_HDR_MINOR + 1; + buffer[2] = CRT_REQ_HDR_V1_BYTES + 4; + memset(buffer + CRT_REQ_HDR_V1_BYTES, 0xa5, 4); + assert_int_equal(decode_request(buffer, sizeof(buffer), &decoded, &used), 0); + assert_int_equal(used, sizeof(buffer)); + assert_int_equal(crt_req_dst_rank(&decoded), 0x444342U); + assert_int_equal(crt_req_dst_tag(&decoded), 0x51U); + memcpy(buffer, request_bytes, sizeof(request_bytes)); + buffer[2] = CRT_REQ_HDR_V1_BYTES + 4; + assert_int_equal(decode_request(buffer, sizeof(buffer), &decoded, NULL), -DER_PROTO); + buffer[2] = CRT_REQ_HDR_V1_BYTES + 1; + assert_int_equal(decode_request(buffer, sizeof(buffer), &decoded, NULL), -DER_PROTO); + buffer[2] = 4; + buffer[3] = 2; + assert_int_equal(decode_request(buffer, sizeof(buffer), &decoded, NULL), -DER_PROTO); + memcpy(buffer, request_bytes, sizeof(request_bytes)); +} + +static void +reply_roundtrip(void **state) +{ + const int32_t statuses[] = {0, -DER_TIMEDOUT, INT32_MIN, INT32_MAX, -1}; + const uint8_t prefix[] = {1, 0, 24, 0}; + struct crt_reply_hdr reply = { + .crh_common = + { + .chc_hlc = 0x0807060504030201ULL, + .chc_rpcid = 0x1817161514131211ULL, + }, + }; + struct crt_reply_hdr decoded; + uint8_t buffer[CRT_REPLY_HDR_V1_BYTES]; + hg_proc_t proc; + size_t index; + size_t bytes; + + (void)state; + for (index = 0; index < ARRAY_SIZE(statuses); index++) { + reply.crh_status = statuses[index]; + proc = header_proc(buffer, sizeof(buffer), HG_ENCODE); + assert_int_equal(crt_proc_reply_hdr(proc, &reply), 0); + assert_int_equal(hg_proc_get_size_used(proc), sizeof(buffer)); + assert_memory_equal(buffer, prefix, sizeof(prefix)); + assert_memory_equal(buffer + 4, request_bytes + 4, 16); + for (bytes = 0; bytes < 4; bytes++) + assert_int_equal(buffer[20 + bytes], + (uint8_t)((uint32_t)statuses[index] >> (bytes * 8))); + assert_int_equal(hg_proc_free(proc), HG_SUCCESS); + proc = header_proc(buffer, sizeof(buffer), HG_DECODE); + assert_int_equal(crt_proc_reply_hdr(proc, &decoded), 0); + assert_int_equal(decoded.crh_status, statuses[index]); + assert_int_equal(decoded.crh_common.chc_hlc, reply.crh_common.chc_hlc); + assert_int_equal(decoded.crh_common.chc_rpcid, reply.crh_common.chc_rpcid); + assert_int_equal(hg_proc_free(proc), HG_SUCCESS); + } + for (bytes = 0; bytes < sizeof(buffer); bytes++) { + proc = header_proc(buffer, bytes, HG_DECODE); + assert_int_equal(crt_proc_reply_hdr(proc, &decoded), -DER_PROTO); + assert_int_equal(hg_proc_free(proc), HG_SUCCESS); + } +} + +static void +deadline_conversion(void **state) +{ + uint64_t now_sec = crt_realtime_sec(); + + (void)state; + assert_int_equal(crt_deadline_to_timeout(0), -1); + assert_int_equal(crt_deadline_to_timeout(now_sec - 1), -1); + assert_int_equal(crt_deadline_to_timeout(UINT64_MAX), UINT32_MAX); + assert_true(crt_timeout_to_deadline(2) >= now_sec + 3); +} + +static void +destination_rank_tag(void **state) +{ + struct crt_req_hdr hdr = {0}; + + (void)state; + assert_int_equal(crt_req_dst_set(&hdr, 0x00ffffffU, 0xffU), 0); + assert_int_equal(crt_req_dst_rank(&hdr), 0x00ffffffU); + assert_int_equal(crt_req_dst_tag(&hdr), 0xffU); + assert_int_equal(crt_req_dst_set(&hdr, 0x01000000U, 0), -DER_INVAL); + assert_int_equal(crt_req_dst_set(&hdr, 0, 0x100U), -DER_INVAL); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(request_roundtrip), cmocka_unit_test(request_validation), + cmocka_unit_test(reply_roundtrip), cmocka_unit_test(deadline_conversion), + cmocka_unit_test(destination_rank_tag), + }; + + return cmocka_run_group_tests(tests, header_setup, header_teardown); +}