From 5fa905a760ebe03c767a6e824568cfdff7d6e04b Mon Sep 17 00:00:00 2001 From: Jeffrey Mahou Date: Tue, 21 Jul 2026 12:40:35 +0300 Subject: [PATCH 01/27] UCP/PROTO/RNDV: Isolate remote system device namespace --- AUTHORS | 1 + src/ucp/rndv/proto_rndv.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 71715f1edb6..76d668f028b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -58,6 +58,7 @@ Ivan Kochin Jakir Kham Jason Gunthorpe Jeff Daily +Jeffrey Mahou Jianxin Xiong John Snyder Jonas Zhou diff --git a/src/ucp/rndv/proto_rndv.c b/src/ucp/rndv/proto_rndv.c index 5c7e0dea312..3ab7a504fa8 100644 --- a/src/ucp/rndv/proto_rndv.c +++ b/src/ucp/rndv/proto_rndv.c @@ -331,9 +331,13 @@ static ucp_proto_select_param_t ucp_proto_rndv_remote_select_param_init( &mem_info, select_param->sg_count); } else { /* If we know the remote buffer parameters, these are actually the local - * parameters for the remote protocol + * parameters for the remote protocol. The rkey system device belongs + * to the remote topology namespace, so it cannot represent a local + * device while estimating the protocol that the peer will select. + * The peer will use its actual local system device when selecting the + * protocol. */ - mem_info.sys_dev = init_params->rkey_config_key->sys_dev; + mem_info.sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; mem_info.type = init_params->rkey_config_key->mem_type; mem_info.flags = ucp_proto_rndv_rkey_mem_flags_estimate(init_params); ucp_proto_select_param_init(&remote_select_param, params->remote_op_id, From 8e90a98b03fc17bc7fb5f04692da05302a441f8c Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Tue, 25 Aug 2026 09:44:51 +0300 Subject: [PATCH 02/27] UCP/TEST: Cover remote RNDV sysdev namespace --- test/gtest/ucp/test_ucp_proto_mock.cc | 66 +++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index bc784f4a830..1393688ae10 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -13,6 +13,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -867,6 +868,71 @@ UCS_TEST_P(test_ucp_proto_mock_rcx, rma_put_2_lanes, }, key, 0); } +UCS_TEST_P(test_ucp_proto_mock_rcx, rndv_remote_sys_dev_namespace, + "IB_NUM_PATHS?=1", "RNDV_SCHEME=put_zcopy") +{ + constexpr size_t select_size = UCS_MBYTE; + uint8_t remote = 0; + auto memh = mem_map(receiver(), &remote, + sizeof(remote)); + auto rkey_packed = rkey_pack(receiver(), memh); + auto rkey = rkey_unpack(sender().ep(), rkey_packed); + ucp_worker_h worker = sender().worker(); + ucp_worker_cfg_index_t ep_cfg_index = ep_config_index(sender()); + const ucs_sys_device_t remote_sys_dev = + get_mock_sys_dev_by_name("mock_0:1"); + const ucp_rkey_config_t *base_rkey_config = + &ucs_array_elem(&worker->rkey_config, rkey->cfg_index); + const ucp_ep_config_t *ep_config = + ucp_worker_ep_config(worker, ep_cfg_index); + ucp_rkey_config_key_t rkey_config_key = base_rkey_config->key; + ucs_sys_dev_distance_t lanes_distance[UCP_MAX_LANES]; + ucp_worker_cfg_index_t rkey_cfg_index; + ucp_rkey_config_t *rkey_config; + ucp_proto_select_param_t select_param; + ucp_memory_info_t mem_info; + const ucp_proto_select_elem_t *select_elem; + const ucp_proto_threshold_elem_t *threshold; + const ucp_proto_rndv_ctrl_priv_t *rpriv; + ucp_lane_index_t lane; + + ASSERT_EQ(UCS_SYS_DEVICE_ID_UNKNOWN, rkey_config_key.sys_dev); + ASSERT_NE(UCS_SYS_DEVICE_ID_UNKNOWN, remote_sys_dev); + + for (lane = 0; lane < ep_config->key.num_lanes; ++lane) { + lanes_distance[lane] = base_rkey_config->lanes_distance[lane]; + } + + /* Simulate an rkey received from a peer whose process-local sys_dev value + * happens to name a device in this process as well. */ + rkey_config_key.sys_dev = remote_sys_dev; + ASSERT_UCS_OK(ucp_worker_rkey_config_get(worker, &rkey_config_key, + lanes_distance, + &rkey_cfg_index)); + + mem_info.type = UCS_MEMORY_TYPE_HOST; + mem_info.sys_dev = get_mock_sys_dev_by_name("mock_1:1"); + mem_info.flags = UCS_MEM_FLAG_REGISTRABLE; + ucp_proto_select_param_init(&select_param, UCP_OP_ID_RNDV_RECV, 0, 0, + UCP_DATATYPE_CONTIG, &mem_info, 1); + + rkey_config = &ucs_array_elem(&worker->rkey_config, rkey_cfg_index); + select_elem = ucp_proto_select_lookup_slow( + worker, &rkey_config->proto_select, 1, ep_cfg_index, + rkey_cfg_index, &select_param); + ASSERT_NE(nullptr, select_elem); + + threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, + select_size); + ASSERT_STREQ("rndv/rtr", threshold->proto_config.proto->name); + rpriv = static_cast( + threshold->proto_config.priv); + + EXPECT_EQ(remote_sys_dev, rkey_config->key.sys_dev); + EXPECT_EQ(UCS_SYS_DEVICE_ID_UNKNOWN, + rpriv->remote_proto_config.select_param.sys_dev); +} + UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_rcx, rcx, "rc_x") class test_ucp_proto_mock_rcx2 : public test_ucp_proto_mock { From d093cd3d8b582b0f5d67e8bab646df9a6665d742 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Wed, 26 Aug 2026 16:03:06 +0300 Subject: [PATCH 03/27] UCP/RNDV: Model mtype lanes by staging device --- src/ucp/rndv/rndv_get.c | 7 ++++++- src/ucp/rndv/rndv_mtype.inl | 14 ++++++++++++++ src/ucp/rndv/rndv_put.c | 8 +++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/ucp/rndv/rndv_get.c b/src/ucp/rndv/rndv_get.c index 1c56943f71a..e6eba14d446 100644 --- a/src/ucp/rndv/rndv_get.c +++ b/src/ucp/rndv/rndv_get.c @@ -326,6 +326,8 @@ static void ucp_proto_rndv_get_mtype_probe(const ucp_proto_init_params_t *init_params) { ucp_context_t *context = init_params->worker->context; + ucp_proto_init_params_t mtype_init_params; + ucp_proto_select_param_t mtype_select_param; ucs_memory_type_t frag_mem_type; ucp_md_map_t mdesc_md_map; ucs_status_t status; @@ -347,7 +349,10 @@ ucp_proto_rndv_get_mtype_probe(const ucp_proto_init_params_t *init_params) continue; } - ucp_proto_rndv_get_common_probe(init_params, + ucp_proto_rndv_mtype_init_params_copy(init_params, &frag_mem_info, + &mtype_select_param, + &mtype_init_params); + ucp_proto_rndv_get_common_probe(&mtype_init_params, UCS_BIT(UCP_RNDV_MODE_GET_PIPELINE), frag_size, UCT_EP_OP_PUT_ZCOPY, 0, mdesc_md_map, 1, &frag_mem_info); diff --git a/src/ucp/rndv/rndv_mtype.inl b/src/ucp/rndv/rndv_mtype.inl index 4b11edb3fd8..2e0d757c9e5 100644 --- a/src/ucp/rndv/rndv_mtype.inl +++ b/src/ucp/rndv/rndv_mtype.inl @@ -44,6 +44,20 @@ ucp_proto_rndv_mtype_init(const ucp_proto_init_params_t *init_params, return UCS_OK; } +static UCS_F_ALWAYS_INLINE void ucp_proto_rndv_mtype_init_params_copy( + const ucp_proto_init_params_t *init_params, + const ucp_memory_info_t *frag_mem_info, + ucp_proto_select_param_t *select_param, + ucp_proto_init_params_t *mtype_init_params) +{ + /* Use the staging buffer device for local lane modeling without changing + * the protocol selection lookup key. */ + *mtype_init_params = *init_params; + *select_param = *init_params->select_param; + select_param->sys_dev = frag_mem_info->sys_dev; + mtype_init_params->select_param = select_param; +} + static UCS_F_ALWAYS_INLINE ucs_status_t ucp_proto_rndv_mtype_request_init(ucp_request_t *req, ucs_memory_type_t frag_mem_type, diff --git a/src/ucp/rndv/rndv_put.c b/src/ucp/rndv/rndv_put.c index f7244ab1e35..b28e0ba3cfe 100644 --- a/src/ucp/rndv/rndv_put.c +++ b/src/ucp/rndv/rndv_put.c @@ -597,6 +597,8 @@ static void ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) { ucp_context_t *context = init_params->worker->context; + ucp_proto_init_params_t mtype_init_params; + ucp_proto_select_param_t mtype_select_param; ucs_memory_type_t frag_mem_type; uct_completion_callback_t comp_cb; ucp_md_map_t mdesc_md_map; @@ -632,6 +634,10 @@ ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) return; } + ucp_proto_rndv_mtype_init_params_copy(init_params, &frag_mem_info, + &mtype_select_param, + &mtype_init_params); + flags = context->config.ext.rndv_errh_ppln_enable ? UCP_PROTO_COMMON_INIT_FLAG_ERR_HANDLING : 0; @@ -642,7 +648,7 @@ ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) } ucp_proto_rndv_put_common_probe( - init_params, UCS_BIT(UCP_RNDV_MODE_PUT_PIPELINE), frag_size, + &mtype_init_params, UCS_BIT(UCP_RNDV_MODE_PUT_PIPELINE), frag_size, UCT_EP_OP_GET_ZCOPY, flags, mdesc_md_map, comp_cb, 1, UCP_WORKER_STAT_RNDV_PUT_MTYPE_ZCOPY, &frag_mem_info); } From 638f131798ead075b32f3458ef7d24d36faf3739 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 27 Aug 2026 13:13:29 +0300 Subject: [PATCH 04/27] UCP/RNDV: Own mtype probe selection parameters --- src/ucp/rndv/rndv_get.c | 6 +- src/ucp/rndv/rndv_mtype.inl | 17 ++- src/ucp/rndv/rndv_put.c | 13 +- test/gtest/ucp/test_ucp_proto_mock.cc | 201 +++++++++++++++++++++++++- 4 files changed, 217 insertions(+), 20 deletions(-) diff --git a/src/ucp/rndv/rndv_get.c b/src/ucp/rndv/rndv_get.c index e6eba14d446..f3d4e4c17be 100644 --- a/src/ucp/rndv/rndv_get.c +++ b/src/ucp/rndv/rndv_get.c @@ -326,8 +326,7 @@ static void ucp_proto_rndv_get_mtype_probe(const ucp_proto_init_params_t *init_params) { ucp_context_t *context = init_params->worker->context; - ucp_proto_init_params_t mtype_init_params; - ucp_proto_select_param_t mtype_select_param; + ucp_proto_rndv_mtype_init_params_t mtype_init_params; ucs_memory_type_t frag_mem_type; ucp_md_map_t mdesc_md_map; ucs_status_t status; @@ -350,9 +349,8 @@ ucp_proto_rndv_get_mtype_probe(const ucp_proto_init_params_t *init_params) } ucp_proto_rndv_mtype_init_params_copy(init_params, &frag_mem_info, - &mtype_select_param, &mtype_init_params); - ucp_proto_rndv_get_common_probe(&mtype_init_params, + ucp_proto_rndv_get_common_probe(&mtype_init_params.super, UCS_BIT(UCP_RNDV_MODE_GET_PIPELINE), frag_size, UCT_EP_OP_PUT_ZCOPY, 0, mdesc_md_map, 1, &frag_mem_info); diff --git a/src/ucp/rndv/rndv_mtype.inl b/src/ucp/rndv/rndv_mtype.inl index 2e0d757c9e5..1dc31e10fdb 100644 --- a/src/ucp/rndv/rndv_mtype.inl +++ b/src/ucp/rndv/rndv_mtype.inl @@ -44,18 +44,23 @@ ucp_proto_rndv_mtype_init(const ucp_proto_init_params_t *init_params, return UCS_OK; } +typedef struct { + ucp_proto_init_params_t super; + ucp_proto_select_param_t select_param; +} ucp_proto_rndv_mtype_init_params_t; + + static UCS_F_ALWAYS_INLINE void ucp_proto_rndv_mtype_init_params_copy( const ucp_proto_init_params_t *init_params, const ucp_memory_info_t *frag_mem_info, - ucp_proto_select_param_t *select_param, - ucp_proto_init_params_t *mtype_init_params) + ucp_proto_rndv_mtype_init_params_t *mtype_init_params) { /* Use the staging buffer device for local lane modeling without changing * the protocol selection lookup key. */ - *mtype_init_params = *init_params; - *select_param = *init_params->select_param; - select_param->sys_dev = frag_mem_info->sys_dev; - mtype_init_params->select_param = select_param; + mtype_init_params->super = *init_params; + mtype_init_params->select_param = *init_params->select_param; + mtype_init_params->select_param.sys_dev = frag_mem_info->sys_dev; + mtype_init_params->super.select_param = &mtype_init_params->select_param; } static UCS_F_ALWAYS_INLINE ucs_status_t diff --git a/src/ucp/rndv/rndv_put.c b/src/ucp/rndv/rndv_put.c index b28e0ba3cfe..4f286fd8ae0 100644 --- a/src/ucp/rndv/rndv_put.c +++ b/src/ucp/rndv/rndv_put.c @@ -597,8 +597,7 @@ static void ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) { ucp_context_t *context = init_params->worker->context; - ucp_proto_init_params_t mtype_init_params; - ucp_proto_select_param_t mtype_select_param; + ucp_proto_rndv_mtype_init_params_t mtype_init_params; ucs_memory_type_t frag_mem_type; uct_completion_callback_t comp_cb; ucp_md_map_t mdesc_md_map; @@ -634,10 +633,6 @@ ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) return; } - ucp_proto_rndv_mtype_init_params_copy(init_params, &frag_mem_info, - &mtype_select_param, - &mtype_init_params); - flags = context->config.ext.rndv_errh_ppln_enable ? UCP_PROTO_COMMON_INIT_FLAG_ERR_HANDLING : 0; @@ -647,9 +642,11 @@ ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) comp_cb = ucp_proto_rndv_put_mtype_completion; } + ucp_proto_rndv_mtype_init_params_copy(init_params, &frag_mem_info, + &mtype_init_params); ucp_proto_rndv_put_common_probe( - &mtype_init_params, UCS_BIT(UCP_RNDV_MODE_PUT_PIPELINE), frag_size, - UCT_EP_OP_GET_ZCOPY, flags, mdesc_md_map, comp_cb, 1, + &mtype_init_params.super, UCS_BIT(UCP_RNDV_MODE_PUT_PIPELINE), + frag_size, UCT_EP_OP_GET_ZCOPY, flags, mdesc_md_map, comp_cb, 1, UCP_WORKER_STAT_RNDV_PUT_MTYPE_ZCOPY, &frag_mem_info); } diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 1393688ae10..29ea0a304e9 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -558,16 +558,25 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { } void - send_recv_am(size_t size, ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST) + send_recv_am(size_t size, ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST, + ucs_sys_device_t sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN) { /* Prepare receiver data handler */ mem_buffer recv_buf(size, mem_type); + ucs::handle recv_memh; + if (sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { + recv_memh = mem_map(receiver(), recv_buf); + recv_memh->sys_dev = sys_dev; + } + struct ctx_t { mem_buffer *buf; bool received; ucp_worker_h worker; ucp_am_recv_data_nbx_callback_t cmpl; - } ctx = {&recv_buf, false, receiver().worker()}; + ucp_mem_h memh; + } ctx = {&recv_buf, false, receiver().worker(), nullptr, + recv_memh.get()}; ctx.cmpl = [](void *req, ucs_status_t status, size_t len, void *arg) { ((ctx_t *)arg)->received = true; @@ -590,6 +599,10 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { UCP_OP_ATTR_FIELD_USER_DATA; params.user_data = arg; params.cb.recv_am = ctx->cmpl; + if (ctx->memh != nullptr) { + params.op_attr_mask |= UCP_OP_ATTR_FIELD_MEMH; + params.memh = ctx->memh; + } auto sptr = ucp_am_recv_data_nbx(ctx->worker, data, ctx->buf->ptr(), len, ¶ms); @@ -611,7 +624,15 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { /* Send data */ mem_buffer buf(size, mem_type); + ucs::handle send_memh; ucp_request_param_t param = {}; + if (sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { + send_memh = mem_map(sender(), buf); + send_memh->sys_dev = sys_dev; + param.op_attr_mask = UCP_OP_ATTR_FIELD_MEMH; + param.memh = send_memh; + } + auto sptr = ucp_am_send_nbx(sender().ep(), AM_ID, NULL, 0ul, buf.ptr(), buf.size(), ¶m); EXPECT_FALSE(UCS_PTR_IS_ERR(sptr)); @@ -1346,6 +1367,182 @@ UCS_TEST_P(test_ucp_proto_mock_gpu, cuda_managed_ppln_host_frag, UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_gpu, rcx_gpu, "rc_x,cuda,rocm") +class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { +public: + test_ucp_proto_mock_mtype_sys_dev() + { + mock_transport("rc_mlx5"); + } + + virtual void init() override + { + auto iface_attr_func = [](uct_iface_attr_t &iface_attr) { + iface_attr.cap.am.max_short = 2000; + iface_attr.bandwidth.shared = 28e9; + iface_attr.latency.c = 600e-9; + iface_attr.latency.m = 1e-9; + }; + + add_mock_iface("mock_0:1", iface_attr_func); + add_mock_iface("mock_1:1", iface_attr_func); + test_ucp_proto_mock::init(); + + s_first_nic_sys_dev = get_mock_sys_dev_by_name("mock_0:1"); + s_frag_sys_dev = add_mock_sys_dev(0, "mock_frag"); + s_user_sys_dev = add_mock_sys_dev(1, "mock_user"); + + const ucs_sys_topo_ops_t topo_ops = { + .get_distance = get_distance, + .get_memory_distance = get_memory_distance, + .get_memory_distance_for_cpuset = get_memory_distance_for_cpuset + }; + ASSERT_UCS_OK(ucs_sys_topo_provider_push(&topo_ops)); + m_topo_provider_pushed = true; + + set_frag_sys_dev(sender()); + set_frag_sys_dev(receiver()); + } + + virtual void cleanup() override + { + if (m_topo_provider_pushed) { + ucs_sys_topo_provider_pop(); + m_topo_provider_pushed = false; + } + + s_first_nic_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + s_frag_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + s_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + test_ucp_proto_mock::cleanup(); + } + +protected: + void check_mtype_uses_frag_sys_dev(const char *operation_desc) + { + static constexpr size_t msg_size = UCS_MBYTE; + + ASSERT_NE(s_user_sys_dev, s_frag_sys_dev); + ASSERT_NE(s_first_nic_sys_dev, s_frag_sys_dev); + send_recv_am(msg_size, UCS_MEMORY_TYPE_CUDA_MANAGED, s_user_sys_dev); + + const ucp_ep_config_t *ep_config = ucp_worker_ep_config( + sender().worker(), ep_config_index(sender())); + ucp_proto_select_key_t select_key; + ucp_proto_select_elem_t select_elem; + bool found = false; + + kh_foreach(ep_config->proto_select.hash, select_key.u64, select_elem, { + if ((select_key.param.op_id_flags == UCP_OP_ID_AM_SEND) && + (select_key.param.op_attr == 0) && + (select_key.param.dt_class == UCP_DATATYPE_CONTIG) && + (select_key.param.mem_type == UCS_MEMORY_TYPE_CUDA_MANAGED) && + (select_key.param.sys_dev == s_user_sys_dev)) { + const ucp_proto_threshold_elem_t *threshold = + ucp_proto_thresholds_search_slow(select_elem.thresholds, + msg_size); + ucp_proto_query_attr_t attr; + + ASSERT_STREQ("am/rndv", threshold->proto_config.proto->name); + ucp_proto_config_query(sender().worker(), + &threshold->proto_config, msg_size, + &attr); + EXPECT_NE(std::string::npos, + std::string(attr.desc).find(operation_desc)); + EXPECT_STREQ("rc_mlx5/mock_1:1", attr.config); + found = true; + } + }) + + EXPECT_TRUE(found); + } + +private: + static ucs_sys_device_t add_mock_sys_dev(uint8_t function, + const char *name) + { + const ucs_sys_bus_id_t bus_id = { + .domain = 0xfffe, + .bus = 0xfe, + .slot = 0x1f, + .function = function, + }; + ucs_sys_device_t sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + + EXPECT_UCS_OK(ucs_topo_find_device_by_bus_id(&bus_id, &sys_dev)); + EXPECT_UCS_OK(ucs_topo_sys_device_set_name(sys_dev, name, 10)); + return sys_dev; + } + + static void set_frag_sys_dev(entity &e) + { + ucp_memory_info_t mem_info; + ucp_md_index_t md_index; + + ASSERT_UCS_OK(ucp_mm_get_alloc_md_index( + e.ucph(), UCS_MEMORY_TYPE_HOST, UCS_SYS_DEVICE_ID_UNKNOWN, + &md_index, &mem_info)); + e.ucph()->alloc_md[UCS_MEMORY_TYPE_HOST].sys_dev = s_frag_sys_dev; + } + + static ucs_status_t get_distance(ucs_sys_device_t device1, + ucs_sys_device_t device2, + ucs_sys_dev_distance_t *distance) + { + *distance = ucs_topo_default_distance; + if (((device1 == s_frag_sys_dev) && + (device2 == s_first_nic_sys_dev)) || + ((device2 == s_frag_sys_dev) && + (device1 == s_first_nic_sys_dev))) { + distance->latency = 1e-3; + } + return UCS_OK; + } + + static void + get_memory_distance(ucs_sys_device_t, ucs_sys_dev_distance_t *distance) + { + *distance = ucs_topo_default_distance; + } + + static void + get_memory_distance_for_cpuset(ucs_sys_device_t, const ucs_cpu_set_t *, + ucs_sys_dev_distance_t *distance) + { + *distance = ucs_topo_default_distance; + } + + bool m_topo_provider_pushed{false}; + static ucs_sys_device_t s_first_nic_sys_dev; + static ucs_sys_device_t s_frag_sys_dev; + static ucs_sys_device_t s_user_sys_dev; +}; + +ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_first_nic_sys_dev = + UCS_SYS_DEVICE_ID_UNKNOWN; +ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_frag_sys_dev = + UCS_SYS_DEVICE_ID_UNKNOWN; +ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_user_sys_dev = + UCS_SYS_DEVICE_ID_UNKNOWN; + +UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_uses_frag_sys_dev, + "RNDV_SCHEME=get_ppln", "RNDV_THRESH=1", + "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", + "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") +{ + check_mtype_uses_frag_sys_dev("read from remote"); +} + +UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_uses_frag_sys_dev, + "RNDV_SCHEME=put_ppln", "RNDV_THRESH=1", + "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", + "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") +{ + check_mtype_uses_frag_sys_dev("write to remote"); +} + +UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_mtype_sys_dev, rcx_gpu, + "rc_x,cuda,rocm") + class test_ucp_proto_mock_cuda_ipc : public test_ucp_proto_mock { public: test_ucp_proto_mock_cuda_ipc() From f1549fef87d45f80d5ae17151ce2a7dfe2264e42 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 27 Aug 2026 13:18:40 +0300 Subject: [PATCH 05/27] UCP/TEST: Gate RNDV mtype abort on selection --- src/ucp/rndv/rndv_mtype.inl | 2 +- test/gtest/ucp/test_ucp_peer_failure.cc | 29 ++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/ucp/rndv/rndv_mtype.inl b/src/ucp/rndv/rndv_mtype.inl index 1dc31e10fdb..45572a1808b 100644 --- a/src/ucp/rndv/rndv_mtype.inl +++ b/src/ucp/rndv/rndv_mtype.inl @@ -60,7 +60,7 @@ static UCS_F_ALWAYS_INLINE void ucp_proto_rndv_mtype_init_params_copy( mtype_init_params->super = *init_params; mtype_init_params->select_param = *init_params->select_param; mtype_init_params->select_param.sys_dev = frag_mem_info->sys_dev; - mtype_init_params->super.select_param = &mtype_init_params->select_param; + mtype_init_params->super.select_param = &mtype_init_params->select_param; } static UCS_F_ALWAYS_INLINE ucs_status_t diff --git a/test/gtest/ucp/test_ucp_peer_failure.cc b/test/gtest/ucp/test_ucp_peer_failure.cc index f814d792185..b1f237244c6 100644 --- a/test/gtest/ucp/test_ucp_peer_failure.cc +++ b/test/gtest/ucp/test_ucp_peer_failure.cc @@ -840,12 +840,15 @@ class test_ucp_peer_failure_rndv_abort : public test_ucp_peer_failure { ucs::mock mock; if (proto->name == self->m_proto_name) { - if (self->m_replace_ops) { - mock_rndv_ops(req->send.ep, mock); - } + self->m_target_proto_seen = true; + if (self->m_fail_target_proto) { + if (self->m_replace_ops) { + mock_rndv_ops(req->send.ep, mock); + } - if (stage == self->m_proto_xfer_stage) { - self->close_peer(); + if (stage == self->m_proto_xfer_stage) { + self->close_peer(); + } } } @@ -904,6 +907,20 @@ class test_ucp_peer_failure_rndv_abort : public test_ucp_peer_failure { setup_progress_mock(sender().worker(), m_progress_mock); setup_progress_mock(receiver().worker(), m_progress_mock); + if (mode == rndv_mode::put_ppln) { + /* The capability check in init() does not include staging-device + * reachability. Verify that this topology actually selected the + * protocol whose abort handling is under test. */ + m_fail_target_proto = false; + m_target_proto_seen = false; + smoke_test(true); + if (!m_target_proto_seen) { + m_progress_mock.cleanup(); + UCS_TEST_SKIP_R("rndv/put/mtype was not selected"); + } + } + + m_fail_target_proto = true; { scoped_log_handler err_wrapper(wrap_errors_logger); scoped_log_handler warn_wrapper(wrap_warns_logger); @@ -920,6 +937,8 @@ class test_ucp_peer_failure_rndv_abort : public test_ucp_peer_failure { ucs::mock m_progress_mock; bool m_is_peer_closed{false}; + bool m_fail_target_proto{true}; + bool m_target_proto_seen{false}; std::string m_proto_name{}; /* Protocol stage during which data transfer happens */ uint8_t m_proto_xfer_stage{}; From d32210a87b6d7203f7775e8ee9e0ad26bb6a9f12 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 27 Aug 2026 13:26:50 +0300 Subject: [PATCH 06/27] UCP/TEST: Directly check mtype staging lanes --- test/gtest/ucp/test_ucp_proto_mock.cc | 172 ++++++++++---------------- 1 file changed, 64 insertions(+), 108 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 29ea0a304e9..dc13911f618 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -558,25 +558,16 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { } void - send_recv_am(size_t size, ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST, - ucs_sys_device_t sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN) + send_recv_am(size_t size, ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST) { /* Prepare receiver data handler */ mem_buffer recv_buf(size, mem_type); - ucs::handle recv_memh; - if (sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { - recv_memh = mem_map(receiver(), recv_buf); - recv_memh->sys_dev = sys_dev; - } - struct ctx_t { mem_buffer *buf; bool received; ucp_worker_h worker; ucp_am_recv_data_nbx_callback_t cmpl; - ucp_mem_h memh; - } ctx = {&recv_buf, false, receiver().worker(), nullptr, - recv_memh.get()}; + } ctx = {&recv_buf, false, receiver().worker()}; ctx.cmpl = [](void *req, ucs_status_t status, size_t len, void *arg) { ((ctx_t *)arg)->received = true; @@ -599,10 +590,6 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { UCP_OP_ATTR_FIELD_USER_DATA; params.user_data = arg; params.cb.recv_am = ctx->cmpl; - if (ctx->memh != nullptr) { - params.op_attr_mask |= UCP_OP_ATTR_FIELD_MEMH; - params.memh = ctx->memh; - } auto sptr = ucp_am_recv_data_nbx(ctx->worker, data, ctx->buf->ptr(), len, ¶ms); @@ -624,15 +611,7 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { /* Send data */ mem_buffer buf(size, mem_type); - ucs::handle send_memh; ucp_request_param_t param = {}; - if (sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { - send_memh = mem_map(sender(), buf); - send_memh->sys_dev = sys_dev; - param.op_attr_mask = UCP_OP_ATTR_FIELD_MEMH; - param.memh = send_memh; - } - auto sptr = ucp_am_send_nbx(sender().ep(), AM_ID, NULL, 0ul, buf.ptr(), buf.size(), ¶m); EXPECT_FALSE(UCS_PTR_IS_ERR(sptr)); @@ -1387,17 +1366,8 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { add_mock_iface("mock_1:1", iface_attr_func); test_ucp_proto_mock::init(); - s_first_nic_sys_dev = get_mock_sys_dev_by_name("mock_0:1"); - s_frag_sys_dev = add_mock_sys_dev(0, "mock_frag"); - s_user_sys_dev = add_mock_sys_dev(1, "mock_user"); - - const ucs_sys_topo_ops_t topo_ops = { - .get_distance = get_distance, - .get_memory_distance = get_memory_distance, - .get_memory_distance_for_cpuset = get_memory_distance_for_cpuset - }; - ASSERT_UCS_OK(ucs_sys_topo_provider_push(&topo_ops)); - m_topo_provider_pushed = true; + s_user_sys_dev = add_mock_sys_dev(0, "mock_user"); + s_frag_sys_dev = add_mock_sys_dev(1, "mock_frag"); set_frag_sys_dev(sender()); set_frag_sys_dev(receiver()); @@ -1405,55 +1375,70 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { virtual void cleanup() override { - if (m_topo_provider_pushed) { - ucs_sys_topo_provider_pop(); - m_topo_provider_pushed = false; + if (s_user_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { + EXPECT_UCS_OK(ucs_topo_sys_device_set_class( + s_user_sys_dev, UCS_TOPO_DEVICE_CLASS_UNKNOWN)); + } + if (s_frag_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { + EXPECT_UCS_OK(ucs_topo_sys_device_set_class( + s_frag_sys_dev, UCS_TOPO_DEVICE_CLASS_UNKNOWN)); } - s_first_nic_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - s_frag_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - s_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + s_frag_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + s_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; test_ucp_proto_mock::cleanup(); } protected: - void check_mtype_uses_frag_sys_dev(const char *operation_desc) - { - static constexpr size_t msg_size = UCS_MBYTE; + void check_mtype_uses_frag_sys_dev(ucp_operation_id_t op_id, + const char *proto_name) + { + static constexpr size_t msg_size = UCS_KBYTE; + uint8_t remote = 0; + auto memh = mem_map(receiver(), &remote, + sizeof(remote)); + auto rkey_packed = rkey_pack(receiver(), memh); + auto rkey = rkey_unpack(sender().ep(), + rkey_packed); + ucp_worker_h worker = sender().worker(); + ucp_worker_cfg_index_t ep_cfg_index = + ep_config_index(sender()); + ucp_rkey_config_t *rkey_config = &ucs_array_elem( + &worker->rkey_config, rkey->cfg_index); + ucp_memory_info_t mem_info; + ucp_proto_select_param_t select_param; + const ucp_proto_select_elem_t *select_elem; + const ucp_proto_threshold_elem_t *threshold; + ucp_proto_query_attr_t attr; + unsigned user_ordinal, frag_ordinal; ASSERT_NE(s_user_sys_dev, s_frag_sys_dev); - ASSERT_NE(s_first_nic_sys_dev, s_frag_sys_dev); - send_recv_am(msg_size, UCS_MEMORY_TYPE_CUDA_MANAGED, s_user_sys_dev); - - const ucp_ep_config_t *ep_config = ucp_worker_ep_config( - sender().worker(), ep_config_index(sender())); - ucp_proto_select_key_t select_key; - ucp_proto_select_elem_t select_elem; - bool found = false; - - kh_foreach(ep_config->proto_select.hash, select_key.u64, select_elem, { - if ((select_key.param.op_id_flags == UCP_OP_ID_AM_SEND) && - (select_key.param.op_attr == 0) && - (select_key.param.dt_class == UCP_DATATYPE_CONTIG) && - (select_key.param.mem_type == UCS_MEMORY_TYPE_CUDA_MANAGED) && - (select_key.param.sys_dev == s_user_sys_dev)) { - const ucp_proto_threshold_elem_t *threshold = - ucp_proto_thresholds_search_slow(select_elem.thresholds, - msg_size); - ucp_proto_query_attr_t attr; - - ASSERT_STREQ("am/rndv", threshold->proto_config.proto->name); - ucp_proto_config_query(sender().worker(), - &threshold->proto_config, msg_size, - &attr); - EXPECT_NE(std::string::npos, - std::string(attr.desc).find(operation_desc)); - EXPECT_STREQ("rc_mlx5/mock_1:1", attr.config); - found = true; - } - }) - - EXPECT_TRUE(found); + user_ordinal = ucs_topo_sys_device_get_bdf_class_ordinal( + s_user_sys_dev); + frag_ordinal = ucs_topo_sys_device_get_bdf_class_ordinal( + s_frag_sys_dev); + ASSERT_NE(UCS_SYS_DEVICE_ORDINAL_INVALID, user_ordinal); + ASSERT_NE(UCS_SYS_DEVICE_ORDINAL_INVALID, frag_ordinal); + ASSERT_NE(user_ordinal % 2, frag_ordinal % 2); + + mem_info.type = UCS_MEMORY_TYPE_CUDA_MANAGED; + mem_info.sys_dev = s_user_sys_dev; + mem_info.flags = UCS_MEM_FLAG_REGISTRABLE; + ucp_proto_select_param_init(&select_param, op_id, 0, 0, + UCP_DATATYPE_CONTIG, &mem_info, 1); + select_elem = ucp_proto_select_lookup_slow( + worker, &rkey_config->proto_select, 1, ep_cfg_index, + rkey->cfg_index, &select_param); + ASSERT_NE(nullptr, select_elem); + + threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, + msg_size); + ASSERT_STREQ(proto_name, threshold->proto_config.proto->name); + ucp_proto_config_query(worker, &threshold->proto_config, msg_size, + &attr); + EXPECT_STREQ((frag_ordinal % 2) ? "rc_mlx5/mock_1:1" : + "rc_mlx5/mock_0:1", + attr.config); } private: @@ -1470,6 +1455,8 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { EXPECT_UCS_OK(ucs_topo_find_device_by_bus_id(&bus_id, &sys_dev)); EXPECT_UCS_OK(ucs_topo_sys_device_set_name(sys_dev, name, 10)); + EXPECT_UCS_OK(ucs_topo_sys_device_set_class( + sys_dev, UCS_TOPO_DEVICE_CLASS_ACC)); return sys_dev; } @@ -1484,41 +1471,10 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { e.ucph()->alloc_md[UCS_MEMORY_TYPE_HOST].sys_dev = s_frag_sys_dev; } - static ucs_status_t get_distance(ucs_sys_device_t device1, - ucs_sys_device_t device2, - ucs_sys_dev_distance_t *distance) - { - *distance = ucs_topo_default_distance; - if (((device1 == s_frag_sys_dev) && - (device2 == s_first_nic_sys_dev)) || - ((device2 == s_frag_sys_dev) && - (device1 == s_first_nic_sys_dev))) { - distance->latency = 1e-3; - } - return UCS_OK; - } - - static void - get_memory_distance(ucs_sys_device_t, ucs_sys_dev_distance_t *distance) - { - *distance = ucs_topo_default_distance; - } - - static void - get_memory_distance_for_cpuset(ucs_sys_device_t, const ucs_cpu_set_t *, - ucs_sys_dev_distance_t *distance) - { - *distance = ucs_topo_default_distance; - } - - bool m_topo_provider_pushed{false}; - static ucs_sys_device_t s_first_nic_sys_dev; static ucs_sys_device_t s_frag_sys_dev; static ucs_sys_device_t s_user_sys_dev; }; -ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_first_nic_sys_dev = - UCS_SYS_DEVICE_ID_UNKNOWN; ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_frag_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_user_sys_dev = @@ -1529,7 +1485,7 @@ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_uses_frag_sys_dev, "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") { - check_mtype_uses_frag_sys_dev("read from remote"); + check_mtype_uses_frag_sys_dev(UCP_OP_ID_RNDV_RECV, "rndv/get/mtype"); } UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_uses_frag_sys_dev, @@ -1537,7 +1493,7 @@ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_uses_frag_sys_dev, "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") { - check_mtype_uses_frag_sys_dev("write to remote"); + check_mtype_uses_frag_sys_dev(UCP_OP_ID_RNDV_SEND, "rndv/put/mtype"); } UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_mtype_sys_dev, rcx_gpu, From de96cc68e868db16bec8e063975cc3bfa2e17314 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 27 Aug 2026 15:57:36 +0300 Subject: [PATCH 07/27] UCP/RNDV: Fix mtype helper spacing --- src/ucp/rndv/rndv_mtype.inl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ucp/rndv/rndv_mtype.inl b/src/ucp/rndv/rndv_mtype.inl index 45572a1808b..f812e7fb5ad 100644 --- a/src/ucp/rndv/rndv_mtype.inl +++ b/src/ucp/rndv/rndv_mtype.inl @@ -49,7 +49,6 @@ typedef struct { ucp_proto_select_param_t select_param; } ucp_proto_rndv_mtype_init_params_t; - static UCS_F_ALWAYS_INLINE void ucp_proto_rndv_mtype_init_params_copy( const ucp_proto_init_params_t *init_params, const ucp_memory_info_t *frag_mem_info, From 2d5daedd2b36089ac19d2486f51d207831f9f3a5 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Fri, 28 Aug 2026 10:44:06 +0300 Subject: [PATCH 08/27] UCP/RNDV: Apply code formatting --- src/ucp/rndv/rndv_put.c | 10 +-- test/gtest/ucp/test_ucp_peer_failure.cc | 4 +- test/gtest/ucp/test_ucp_proto_mock.cc | 95 ++++++++++++------------- 3 files changed, 53 insertions(+), 56 deletions(-) diff --git a/src/ucp/rndv/rndv_put.c b/src/ucp/rndv/rndv_put.c index 4f286fd8ae0..62ed63bae05 100644 --- a/src/ucp/rndv/rndv_put.c +++ b/src/ucp/rndv/rndv_put.c @@ -644,10 +644,12 @@ ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) ucp_proto_rndv_mtype_init_params_copy(init_params, &frag_mem_info, &mtype_init_params); - ucp_proto_rndv_put_common_probe( - &mtype_init_params.super, UCS_BIT(UCP_RNDV_MODE_PUT_PIPELINE), - frag_size, UCT_EP_OP_GET_ZCOPY, flags, mdesc_md_map, comp_cb, 1, - UCP_WORKER_STAT_RNDV_PUT_MTYPE_ZCOPY, &frag_mem_info); + ucp_proto_rndv_put_common_probe(&mtype_init_params.super, + UCS_BIT(UCP_RNDV_MODE_PUT_PIPELINE), + frag_size, UCT_EP_OP_GET_ZCOPY, flags, + mdesc_md_map, comp_cb, 1, + UCP_WORKER_STAT_RNDV_PUT_MTYPE_ZCOPY, + &frag_mem_info); } static void diff --git a/test/gtest/ucp/test_ucp_peer_failure.cc b/test/gtest/ucp/test_ucp_peer_failure.cc index b1f237244c6..a6786ff0c8e 100644 --- a/test/gtest/ucp/test_ucp_peer_failure.cc +++ b/test/gtest/ucp/test_ucp_peer_failure.cc @@ -937,8 +937,8 @@ class test_ucp_peer_failure_rndv_abort : public test_ucp_peer_failure { ucs::mock m_progress_mock; bool m_is_peer_closed{false}; - bool m_fail_target_proto{true}; - bool m_target_proto_seen{false}; + bool m_fail_target_proto{true}; + bool m_target_proto_seen{false}; std::string m_proto_name{}; /* Protocol stage during which data transfer happens */ uint8_t m_proto_xfer_stage{}; diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index dc13911f618..8e557e5e6bc 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -873,18 +873,17 @@ UCS_TEST_P(test_ucp_proto_mock_rcx, rndv_remote_sys_dev_namespace, { constexpr size_t select_size = UCS_MBYTE; uint8_t remote = 0; - auto memh = mem_map(receiver(), &remote, - sizeof(remote)); - auto rkey_packed = rkey_pack(receiver(), memh); - auto rkey = rkey_unpack(sender().ep(), rkey_packed); - ucp_worker_h worker = sender().worker(); - ucp_worker_cfg_index_t ep_cfg_index = ep_config_index(sender()); - const ucs_sys_device_t remote_sys_dev = - get_mock_sys_dev_by_name("mock_0:1"); + auto memh = mem_map(receiver(), &remote, sizeof(remote)); + auto rkey_packed = rkey_pack(receiver(), memh); + auto rkey = rkey_unpack(sender().ep(), rkey_packed); + ucp_worker_h worker = sender().worker(); + ucp_worker_cfg_index_t ep_cfg_index = ep_config_index(sender()); + const ucs_sys_device_t remote_sys_dev = get_mock_sys_dev_by_name( + "mock_0:1"); const ucp_rkey_config_t *base_rkey_config = &ucs_array_elem(&worker->rkey_config, rkey->cfg_index); - const ucp_ep_config_t *ep_config = - ucp_worker_ep_config(worker, ep_cfg_index); + const ucp_ep_config_t *ep_config = ucp_worker_ep_config(worker, + ep_cfg_index); ucp_rkey_config_key_t rkey_config_key = base_rkey_config->key; ucs_sys_dev_distance_t lanes_distance[UCP_MAX_LANES]; ucp_worker_cfg_index_t rkey_cfg_index; @@ -907,8 +906,7 @@ UCS_TEST_P(test_ucp_proto_mock_rcx, rndv_remote_sys_dev_namespace, * happens to name a device in this process as well. */ rkey_config_key.sys_dev = remote_sys_dev; ASSERT_UCS_OK(ucp_worker_rkey_config_get(worker, &rkey_config_key, - lanes_distance, - &rkey_cfg_index)); + lanes_distance, &rkey_cfg_index)); mem_info.type = UCS_MEMORY_TYPE_HOST; mem_info.sys_dev = get_mock_sys_dev_by_name("mock_1:1"); @@ -917,15 +915,16 @@ UCS_TEST_P(test_ucp_proto_mock_rcx, rndv_remote_sys_dev_namespace, UCP_DATATYPE_CONTIG, &mem_info, 1); rkey_config = &ucs_array_elem(&worker->rkey_config, rkey_cfg_index); - select_elem = ucp_proto_select_lookup_slow( - worker, &rkey_config->proto_select, 1, ep_cfg_index, - rkey_cfg_index, &select_param); + select_elem = ucp_proto_select_lookup_slow(worker, + &rkey_config->proto_select, 1, + ep_cfg_index, rkey_cfg_index, + &select_param); ASSERT_NE(nullptr, select_elem); threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, - select_size); + select_size); ASSERT_STREQ("rndv/rtr", threshold->proto_config.proto->name); - rpriv = static_cast( + rpriv = static_cast( threshold->proto_config.priv); EXPECT_EQ(remote_sys_dev, rkey_config->key.sys_dev); @@ -1356,10 +1355,10 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { virtual void init() override { auto iface_attr_func = [](uct_iface_attr_t &iface_attr) { - iface_attr.cap.am.max_short = 2000; - iface_attr.bandwidth.shared = 28e9; - iface_attr.latency.c = 600e-9; - iface_attr.latency.m = 1e-9; + iface_attr.cap.am.max_short = 2000; + iface_attr.bandwidth.shared = 28e9; + iface_attr.latency.c = 600e-9; + iface_attr.latency.m = 1e-9; }; add_mock_iface("mock_0:1", iface_attr_func); @@ -1395,16 +1394,13 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { { static constexpr size_t msg_size = UCS_KBYTE; uint8_t remote = 0; - auto memh = mem_map(receiver(), &remote, - sizeof(remote)); - auto rkey_packed = rkey_pack(receiver(), memh); - auto rkey = rkey_unpack(sender().ep(), - rkey_packed); - ucp_worker_h worker = sender().worker(); - ucp_worker_cfg_index_t ep_cfg_index = - ep_config_index(sender()); - ucp_rkey_config_t *rkey_config = &ucs_array_elem( - &worker->rkey_config, rkey->cfg_index); + auto memh = mem_map(receiver(), &remote, sizeof(remote)); + auto rkey_packed = rkey_pack(receiver(), memh); + auto rkey = rkey_unpack(sender().ep(), rkey_packed); + ucp_worker_h worker = sender().worker(); + ucp_worker_cfg_index_t ep_cfg_index = ep_config_index(sender()); + ucp_rkey_config_t *rkey_config = &ucs_array_elem(&worker->rkey_config, + rkey->cfg_index); ucp_memory_info_t mem_info; ucp_proto_select_param_t select_param; const ucp_proto_select_elem_t *select_elem; @@ -1426,24 +1422,25 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { mem_info.flags = UCS_MEM_FLAG_REGISTRABLE; ucp_proto_select_param_init(&select_param, op_id, 0, 0, UCP_DATATYPE_CONTIG, &mem_info, 1); - select_elem = ucp_proto_select_lookup_slow( - worker, &rkey_config->proto_select, 1, ep_cfg_index, - rkey->cfg_index, &select_param); + select_elem = ucp_proto_select_lookup_slow(worker, + &rkey_config->proto_select, + 1, ep_cfg_index, + rkey->cfg_index, + &select_param); ASSERT_NE(nullptr, select_elem); threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, - msg_size); + msg_size); ASSERT_STREQ(proto_name, threshold->proto_config.proto->name); ucp_proto_config_query(worker, &threshold->proto_config, msg_size, &attr); EXPECT_STREQ((frag_ordinal % 2) ? "rc_mlx5/mock_1:1" : - "rc_mlx5/mock_0:1", + "rc_mlx5/mock_0:1", attr.config); } private: - static ucs_sys_device_t add_mock_sys_dev(uint8_t function, - const char *name) + static ucs_sys_device_t add_mock_sys_dev(uint8_t function, const char *name) { const ucs_sys_bus_id_t bus_id = { .domain = 0xfffe, @@ -1451,12 +1448,12 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { .slot = 0x1f, .function = function, }; - ucs_sys_device_t sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + ucs_sys_device_t sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; EXPECT_UCS_OK(ucs_topo_find_device_by_bus_id(&bus_id, &sys_dev)); EXPECT_UCS_OK(ucs_topo_sys_device_set_name(sys_dev, name, 10)); - EXPECT_UCS_OK(ucs_topo_sys_device_set_class( - sys_dev, UCS_TOPO_DEVICE_CLASS_ACC)); + EXPECT_UCS_OK(ucs_topo_sys_device_set_class(sys_dev, + UCS_TOPO_DEVICE_CLASS_ACC)); return sys_dev; } @@ -1465,9 +1462,9 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { ucp_memory_info_t mem_info; ucp_md_index_t md_index; - ASSERT_UCS_OK(ucp_mm_get_alloc_md_index( - e.ucph(), UCS_MEMORY_TYPE_HOST, UCS_SYS_DEVICE_ID_UNKNOWN, - &md_index, &mem_info)); + ASSERT_UCS_OK(ucp_mm_get_alloc_md_index(e.ucph(), UCS_MEMORY_TYPE_HOST, + UCS_SYS_DEVICE_ID_UNKNOWN, + &md_index, &mem_info)); e.ucph()->alloc_md[UCS_MEMORY_TYPE_HOST].sys_dev = s_frag_sys_dev; } @@ -1481,17 +1478,15 @@ ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_uses_frag_sys_dev, - "RNDV_SCHEME=get_ppln", "RNDV_THRESH=1", - "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", - "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") + "RNDV_SCHEME=get_ppln", "RNDV_THRESH=1", "RNDV_FRAG_MEM_TYPES=host", + "RNDV_FRAG_SIZE=host:8K", "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") { check_mtype_uses_frag_sys_dev(UCP_OP_ID_RNDV_RECV, "rndv/get/mtype"); } UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_uses_frag_sys_dev, - "RNDV_SCHEME=put_ppln", "RNDV_THRESH=1", - "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", - "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") + "RNDV_SCHEME=put_ppln", "RNDV_THRESH=1", "RNDV_FRAG_MEM_TYPES=host", + "RNDV_FRAG_SIZE=host:8K", "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") { check_mtype_uses_frag_sys_dev(UCP_OP_ID_RNDV_SEND, "rndv/put/mtype"); } From 893663761178a391e243c3a55408b5341468781f Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Sun, 30 Aug 2026 10:55:02 +0300 Subject: [PATCH 09/27] UCP/RNDV: Prepare mtype probe parameters --- src/ucp/rndv/rndv_get.c | 4 ++-- src/ucp/rndv/rndv_mtype.inl | 4 ++-- src/ucp/rndv/rndv_put.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ucp/rndv/rndv_get.c b/src/ucp/rndv/rndv_get.c index f3d4e4c17be..2fe8ee8022a 100644 --- a/src/ucp/rndv/rndv_get.c +++ b/src/ucp/rndv/rndv_get.c @@ -348,8 +348,8 @@ ucp_proto_rndv_get_mtype_probe(const ucp_proto_init_params_t *init_params) continue; } - ucp_proto_rndv_mtype_init_params_copy(init_params, &frag_mem_info, - &mtype_init_params); + ucp_proto_rndv_mtype_init_params_prepare(init_params, &frag_mem_info, + &mtype_init_params); ucp_proto_rndv_get_common_probe(&mtype_init_params.super, UCS_BIT(UCP_RNDV_MODE_GET_PIPELINE), frag_size, UCT_EP_OP_PUT_ZCOPY, 0, diff --git a/src/ucp/rndv/rndv_mtype.inl b/src/ucp/rndv/rndv_mtype.inl index f812e7fb5ad..f7ded08be0e 100644 --- a/src/ucp/rndv/rndv_mtype.inl +++ b/src/ucp/rndv/rndv_mtype.inl @@ -49,16 +49,16 @@ typedef struct { ucp_proto_select_param_t select_param; } ucp_proto_rndv_mtype_init_params_t; -static UCS_F_ALWAYS_INLINE void ucp_proto_rndv_mtype_init_params_copy( +static UCS_F_ALWAYS_INLINE void ucp_proto_rndv_mtype_init_params_prepare( const ucp_proto_init_params_t *init_params, const ucp_memory_info_t *frag_mem_info, ucp_proto_rndv_mtype_init_params_t *mtype_init_params) { /* Use the staging buffer device for local lane modeling without changing * the protocol selection lookup key. */ - mtype_init_params->super = *init_params; mtype_init_params->select_param = *init_params->select_param; mtype_init_params->select_param.sys_dev = frag_mem_info->sys_dev; + mtype_init_params->super = *init_params; mtype_init_params->super.select_param = &mtype_init_params->select_param; } diff --git a/src/ucp/rndv/rndv_put.c b/src/ucp/rndv/rndv_put.c index 62ed63bae05..c954b6367bb 100644 --- a/src/ucp/rndv/rndv_put.c +++ b/src/ucp/rndv/rndv_put.c @@ -642,8 +642,8 @@ ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) comp_cb = ucp_proto_rndv_put_mtype_completion; } - ucp_proto_rndv_mtype_init_params_copy(init_params, &frag_mem_info, - &mtype_init_params); + ucp_proto_rndv_mtype_init_params_prepare(init_params, &frag_mem_info, + &mtype_init_params); ucp_proto_rndv_put_common_probe(&mtype_init_params.super, UCS_BIT(UCP_RNDV_MODE_PUT_PIPELINE), frag_size, UCT_EP_OP_GET_ZCOPY, flags, From f8e568c9e3d225df2ba5d0b8faf3484d485de91e Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Sun, 30 Aug 2026 10:55:30 +0300 Subject: [PATCH 10/27] UCP/TEST: Gate mtype sysdev checks by endpoint --- test/gtest/ucp/test_ucp_proto_mock.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 8e557e5e6bc..14304380ea4 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -1394,10 +1394,15 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { { static constexpr size_t msg_size = UCS_KBYTE; uint8_t remote = 0; + ucp_worker_h worker = sender().worker(); + + if (worker->mem_type_ep[UCS_MEMORY_TYPE_CUDA_MANAGED] == NULL) { + UCS_TEST_SKIP_R("CUDA managed memory type endpoint is unavailable"); + } + auto memh = mem_map(receiver(), &remote, sizeof(remote)); auto rkey_packed = rkey_pack(receiver(), memh); auto rkey = rkey_unpack(sender().ep(), rkey_packed); - ucp_worker_h worker = sender().worker(); ucp_worker_cfg_index_t ep_cfg_index = ep_config_index(sender()); ucp_rkey_config_t *rkey_config = &ucs_array_elem(&worker->rkey_config, rkey->cfg_index); From 093ce688d73553c7e518ea0bae0dbde8a95957a1 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Sun, 30 Aug 2026 11:09:25 +0300 Subject: [PATCH 11/27] UCP/TEST: Align RNDV abort state members --- test/gtest/ucp/test_ucp_peer_failure.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/gtest/ucp/test_ucp_peer_failure.cc b/test/gtest/ucp/test_ucp_peer_failure.cc index a6786ff0c8e..b1f237244c6 100644 --- a/test/gtest/ucp/test_ucp_peer_failure.cc +++ b/test/gtest/ucp/test_ucp_peer_failure.cc @@ -937,8 +937,8 @@ class test_ucp_peer_failure_rndv_abort : public test_ucp_peer_failure { ucs::mock m_progress_mock; bool m_is_peer_closed{false}; - bool m_fail_target_proto{true}; - bool m_target_proto_seen{false}; + bool m_fail_target_proto{true}; + bool m_target_proto_seen{false}; std::string m_proto_name{}; /* Protocol stage during which data transfer happens */ uint8_t m_proto_xfer_stage{}; From 0555af1520b55b8a96e45c35b6c010adb0b5e5a8 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Mon, 31 Aug 2026 17:23:21 +0300 Subject: [PATCH 12/27] UCP/TEST: Reuse mtype mock system devices --- test/gtest/ucp/test_ucp_proto_mock.cc | 28 ++++++++------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 14304380ea4..ff141f291e9 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -1365,8 +1365,12 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { add_mock_iface("mock_1:1", iface_attr_func); test_ucp_proto_mock::init(); - s_user_sys_dev = add_mock_sys_dev(0, "mock_user"); - s_frag_sys_dev = add_mock_sys_dev(1, "mock_frag"); + s_user_sys_dev = get_mock_sys_dev_by_name("mock_0:1"); + s_frag_sys_dev = get_mock_sys_dev_by_name("mock_1:1"); + EXPECT_UCS_OK(ucs_topo_sys_device_set_class( + s_user_sys_dev, UCS_TOPO_DEVICE_CLASS_ACC)); + EXPECT_UCS_OK(ucs_topo_sys_device_set_class( + s_frag_sys_dev, UCS_TOPO_DEVICE_CLASS_ACC)); set_frag_sys_dev(sender()); set_frag_sys_dev(receiver()); @@ -1374,6 +1378,8 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { virtual void cleanup() override { + test_ucp_proto_mock::cleanup(); + if (s_user_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { EXPECT_UCS_OK(ucs_topo_sys_device_set_class( s_user_sys_dev, UCS_TOPO_DEVICE_CLASS_UNKNOWN)); @@ -1385,7 +1391,6 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { s_frag_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; s_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - test_ucp_proto_mock::cleanup(); } protected: @@ -1445,23 +1450,6 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { } private: - static ucs_sys_device_t add_mock_sys_dev(uint8_t function, const char *name) - { - const ucs_sys_bus_id_t bus_id = { - .domain = 0xfffe, - .bus = 0xfe, - .slot = 0x1f, - .function = function, - }; - ucs_sys_device_t sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - - EXPECT_UCS_OK(ucs_topo_find_device_by_bus_id(&bus_id, &sys_dev)); - EXPECT_UCS_OK(ucs_topo_sys_device_set_name(sys_dev, name, 10)); - EXPECT_UCS_OK(ucs_topo_sys_device_set_class(sys_dev, - UCS_TOPO_DEVICE_CLASS_ACC)); - return sys_dev; - } - static void set_frag_sys_dev(entity &e) { ucp_memory_info_t mem_info; From f2a4ba2fac84e631d47833fe15467d2d419a827c Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Mon, 31 Aug 2026 16:42:22 +0300 Subject: [PATCH 13/27] UCP/TEST: Drop unrelated RNDV abort test changes --- test/gtest/ucp/test_ucp_peer_failure.cc | 29 +++++-------------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/test/gtest/ucp/test_ucp_peer_failure.cc b/test/gtest/ucp/test_ucp_peer_failure.cc index b1f237244c6..f814d792185 100644 --- a/test/gtest/ucp/test_ucp_peer_failure.cc +++ b/test/gtest/ucp/test_ucp_peer_failure.cc @@ -840,15 +840,12 @@ class test_ucp_peer_failure_rndv_abort : public test_ucp_peer_failure { ucs::mock mock; if (proto->name == self->m_proto_name) { - self->m_target_proto_seen = true; - if (self->m_fail_target_proto) { - if (self->m_replace_ops) { - mock_rndv_ops(req->send.ep, mock); - } + if (self->m_replace_ops) { + mock_rndv_ops(req->send.ep, mock); + } - if (stage == self->m_proto_xfer_stage) { - self->close_peer(); - } + if (stage == self->m_proto_xfer_stage) { + self->close_peer(); } } @@ -907,20 +904,6 @@ class test_ucp_peer_failure_rndv_abort : public test_ucp_peer_failure { setup_progress_mock(sender().worker(), m_progress_mock); setup_progress_mock(receiver().worker(), m_progress_mock); - if (mode == rndv_mode::put_ppln) { - /* The capability check in init() does not include staging-device - * reachability. Verify that this topology actually selected the - * protocol whose abort handling is under test. */ - m_fail_target_proto = false; - m_target_proto_seen = false; - smoke_test(true); - if (!m_target_proto_seen) { - m_progress_mock.cleanup(); - UCS_TEST_SKIP_R("rndv/put/mtype was not selected"); - } - } - - m_fail_target_proto = true; { scoped_log_handler err_wrapper(wrap_errors_logger); scoped_log_handler warn_wrapper(wrap_warns_logger); @@ -937,8 +920,6 @@ class test_ucp_peer_failure_rndv_abort : public test_ucp_peer_failure { ucs::mock m_progress_mock; bool m_is_peer_closed{false}; - bool m_fail_target_proto{true}; - bool m_target_proto_seen{false}; std::string m_proto_name{}; /* Protocol stage during which data transfer happens */ uint8_t m_proto_xfer_stage{}; From c18ccceb676b749b115db5a4fda50c64dc294812 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Tue, 1 Sep 2026 11:01:02 +0300 Subject: [PATCH 14/27] UCP/PROTO: Separate mtype selection and reachability devices --- src/ucp/proto/proto_common.c | 32 +++++++++++++++++++++------ src/ucp/rndv/rndv_get.c | 5 +---- src/ucp/rndv/rndv_mtype.inl | 18 --------------- src/ucp/rndv/rndv_put.c | 13 ++++------- test/gtest/ucp/test_ucp_proto_mock.cc | 23 ++++++++++++++----- 5 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/ucp/proto/proto_common.c b/src/ucp/proto/proto_common.c index ba221ecc9e8..ae359d80b41 100644 --- a/src/ucp/proto/proto_common.c +++ b/src/ucp/proto/proto_common.c @@ -532,6 +532,17 @@ ucp_proto_common_filter_min_frag(const ucp_proto_init_params_t *params, ucs_memory_type_names[reg_mem_type]); return 0; } + + /* The lane's device and the memory that will actually be registered + * on it (which may differ from select_param's buffer, e.g. a staging + * fragment for mtype protocols) must be topologically reachable. */ + if (!ucs_topo_is_reachable( + context->tl_rscs[rsc_index].tl_rsc.sys_device, + common_params->reg_mem_info.sys_dev)) { + ucs_trace("%s: no reachability to reg mem sys_dev=%u", lane_desc, + common_params->reg_mem_info.sys_dev); + return 0; + } } iface_attr = ucp_proto_common_get_iface_attr(params, lane); @@ -716,13 +727,20 @@ ucp_proto_common_find_lanes(const ucp_proto_init_params_t *params, } } - /* The two devices must also have internal reachability */ - lane_sys_dev = context->tl_rscs[rsc_index].tl_rsc.sys_device; - if (!ucs_topo_is_reachable(lane_sys_dev, select_param->sys_dev)) { - ucs_trace("%s: no reachability between lane_sys_dev=%u and " - "sys_dev=%u", - lane_desc, lane_sys_dev, select_param->sys_dev); - continue; + /* The two devices must also have internal reachability. This is only + * meaningful when select_param->sys_dev is what actually gets + * registered on this lane. Otherwise, reachability is checked by the + * lane filter against the buffer that is actually registered. */ + if (!(flags & UCP_PROTO_COMMON_INIT_FLAG_MIN_FRAG) || + (flags & UCP_PROTO_COMMON_INIT_FLAG_SEND_ZCOPY)) { + lane_sys_dev = context->tl_rscs[rsc_index].tl_rsc.sys_device; + if (!ucs_topo_is_reachable(lane_sys_dev, + select_param->sys_dev)) { + ucs_trace("%s: no reachability between lane_sys_dev=%u and " + "sys_dev=%u", + lane_desc, lane_sys_dev, select_param->sys_dev); + continue; + } } ucs_trace("%s: added as lane %d", lane_desc, lane); diff --git a/src/ucp/rndv/rndv_get.c b/src/ucp/rndv/rndv_get.c index 2fe8ee8022a..1c56943f71a 100644 --- a/src/ucp/rndv/rndv_get.c +++ b/src/ucp/rndv/rndv_get.c @@ -326,7 +326,6 @@ static void ucp_proto_rndv_get_mtype_probe(const ucp_proto_init_params_t *init_params) { ucp_context_t *context = init_params->worker->context; - ucp_proto_rndv_mtype_init_params_t mtype_init_params; ucs_memory_type_t frag_mem_type; ucp_md_map_t mdesc_md_map; ucs_status_t status; @@ -348,9 +347,7 @@ ucp_proto_rndv_get_mtype_probe(const ucp_proto_init_params_t *init_params) continue; } - ucp_proto_rndv_mtype_init_params_prepare(init_params, &frag_mem_info, - &mtype_init_params); - ucp_proto_rndv_get_common_probe(&mtype_init_params.super, + ucp_proto_rndv_get_common_probe(init_params, UCS_BIT(UCP_RNDV_MODE_GET_PIPELINE), frag_size, UCT_EP_OP_PUT_ZCOPY, 0, mdesc_md_map, 1, &frag_mem_info); diff --git a/src/ucp/rndv/rndv_mtype.inl b/src/ucp/rndv/rndv_mtype.inl index f7ded08be0e..4b11edb3fd8 100644 --- a/src/ucp/rndv/rndv_mtype.inl +++ b/src/ucp/rndv/rndv_mtype.inl @@ -44,24 +44,6 @@ ucp_proto_rndv_mtype_init(const ucp_proto_init_params_t *init_params, return UCS_OK; } -typedef struct { - ucp_proto_init_params_t super; - ucp_proto_select_param_t select_param; -} ucp_proto_rndv_mtype_init_params_t; - -static UCS_F_ALWAYS_INLINE void ucp_proto_rndv_mtype_init_params_prepare( - const ucp_proto_init_params_t *init_params, - const ucp_memory_info_t *frag_mem_info, - ucp_proto_rndv_mtype_init_params_t *mtype_init_params) -{ - /* Use the staging buffer device for local lane modeling without changing - * the protocol selection lookup key. */ - mtype_init_params->select_param = *init_params->select_param; - mtype_init_params->select_param.sys_dev = frag_mem_info->sys_dev; - mtype_init_params->super = *init_params; - mtype_init_params->super.select_param = &mtype_init_params->select_param; -} - static UCS_F_ALWAYS_INLINE ucs_status_t ucp_proto_rndv_mtype_request_init(ucp_request_t *req, ucs_memory_type_t frag_mem_type, diff --git a/src/ucp/rndv/rndv_put.c b/src/ucp/rndv/rndv_put.c index c954b6367bb..f7244ab1e35 100644 --- a/src/ucp/rndv/rndv_put.c +++ b/src/ucp/rndv/rndv_put.c @@ -597,7 +597,6 @@ static void ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) { ucp_context_t *context = init_params->worker->context; - ucp_proto_rndv_mtype_init_params_t mtype_init_params; ucs_memory_type_t frag_mem_type; uct_completion_callback_t comp_cb; ucp_md_map_t mdesc_md_map; @@ -642,14 +641,10 @@ ucp_proto_rndv_put_mtype_probe(const ucp_proto_init_params_t *init_params) comp_cb = ucp_proto_rndv_put_mtype_completion; } - ucp_proto_rndv_mtype_init_params_prepare(init_params, &frag_mem_info, - &mtype_init_params); - ucp_proto_rndv_put_common_probe(&mtype_init_params.super, - UCS_BIT(UCP_RNDV_MODE_PUT_PIPELINE), - frag_size, UCT_EP_OP_GET_ZCOPY, flags, - mdesc_md_map, comp_cb, 1, - UCP_WORKER_STAT_RNDV_PUT_MTYPE_ZCOPY, - &frag_mem_info); + ucp_proto_rndv_put_common_probe( + init_params, UCS_BIT(UCP_RNDV_MODE_PUT_PIPELINE), frag_size, + UCT_EP_OP_GET_ZCOPY, flags, mdesc_md_map, comp_cb, 1, + UCP_WORKER_STAT_RNDV_PUT_MTYPE_ZCOPY, &frag_mem_info); } static void diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index ff141f291e9..72d70c5e5b0 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -1394,7 +1394,7 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { } protected: - void check_mtype_uses_frag_sys_dev(ucp_operation_id_t op_id, + void check_mtype_keeps_user_sys_dev(ucp_operation_id_t op_id, const char *proto_name) { static constexpr size_t msg_size = UCS_KBYTE; @@ -1442,9 +1442,20 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, msg_size); ASSERT_STREQ(proto_name, threshold->proto_config.proto->name); + EXPECT_EQ(s_user_sys_dev, + threshold->proto_config.select_param.sys_dev); + if (op_id == UCP_OP_ID_RNDV_RECV) { + const auto *rpriv = + static_cast( + threshold->proto_config.priv); + EXPECT_EQ(s_frag_sys_dev, rpriv->frag_sys_dev); + } + ucp_proto_config_query(worker, &threshold->proto_config, msg_size, &attr); - EXPECT_STREQ((frag_ordinal % 2) ? "rc_mlx5/mock_1:1" : + /* Keep the user device as the equal-lane spreading key. The fragment + * device is modeled separately through reg_mem_info. */ + EXPECT_STREQ((user_ordinal % 2) ? "rc_mlx5/mock_1:1" : "rc_mlx5/mock_0:1", attr.config); } @@ -1470,18 +1481,18 @@ ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_frag_sys_dev = ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; -UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_uses_frag_sys_dev, +UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_keeps_user_sys_dev, "RNDV_SCHEME=get_ppln", "RNDV_THRESH=1", "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") { - check_mtype_uses_frag_sys_dev(UCP_OP_ID_RNDV_RECV, "rndv/get/mtype"); + check_mtype_keeps_user_sys_dev(UCP_OP_ID_RNDV_RECV, "rndv/get/mtype"); } -UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_uses_frag_sys_dev, +UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_keeps_user_sys_dev, "RNDV_SCHEME=put_ppln", "RNDV_THRESH=1", "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") { - check_mtype_uses_frag_sys_dev(UCP_OP_ID_RNDV_SEND, "rndv/put/mtype"); + check_mtype_keeps_user_sys_dev(UCP_OP_ID_RNDV_SEND, "rndv/put/mtype"); } UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_mtype_sys_dev, rcx_gpu, From 2203456d55777dd68a42096d40dd095ad283e45d Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Tue, 1 Sep 2026 15:19:28 +0300 Subject: [PATCH 15/27] UCP/PROTO: Check selected buffer reachability for zcopy --- src/ucp/proto/proto_common.c | 11 +++++------ test/gtest/ucp/test_ucp_peer_failure.cc | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ucp/proto/proto_common.c b/src/ucp/proto/proto_common.c index ae359d80b41..31d8acfe9a6 100644 --- a/src/ucp/proto/proto_common.c +++ b/src/ucp/proto/proto_common.c @@ -727,12 +727,11 @@ ucp_proto_common_find_lanes(const ucp_proto_init_params_t *params, } } - /* The two devices must also have internal reachability. This is only - * meaningful when select_param->sys_dev is what actually gets - * registered on this lane. Otherwise, reachability is checked by the - * lane filter against the buffer that is actually registered. */ - if (!(flags & UCP_PROTO_COMMON_INIT_FLAG_MIN_FRAG) || - (flags & UCP_PROTO_COMMON_INIT_FLAG_SEND_ZCOPY)) { + /* Check internal reachability only when the selected buffer is + * registered on this lane. For non-zcopy protocols, either no buffer + * is registered or the lane filter checks the buffer described by + * reg_mem_info. */ + if (flags & UCP_PROTO_COMMON_INIT_FLAG_SEND_ZCOPY) { lane_sys_dev = context->tl_rscs[rsc_index].tl_rsc.sys_device; if (!ucs_topo_is_reachable(lane_sys_dev, select_param->sys_dev)) { diff --git a/test/gtest/ucp/test_ucp_peer_failure.cc b/test/gtest/ucp/test_ucp_peer_failure.cc index f814d792185..34617742e00 100644 --- a/test/gtest/ucp/test_ucp_peer_failure.cc +++ b/test/gtest/ucp/test_ucp_peer_failure.cc @@ -1014,7 +1014,7 @@ UCS_TEST_P(test_ucp_peer_failure_rndv_put_ppln_abort, rtr_mtype) } UCS_TEST_P(test_ucp_peer_failure_rndv_put_ppln_abort, pipeline, - "RNDV_FRAG_SIZE=host:8K,cuda:8K") + "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K") { rndv_progress_failure_test(rndv_mode::put_ppln, true); } From 9a1567580942b8e48877ec9ceb8c3086eda9b759 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Wed, 2 Sep 2026 10:40:06 +0300 Subject: [PATCH 16/27] UCP/TEST: Exercise mtype staging reachability --- test/gtest/ucp/test_ucp_proto_mock.cc | 196 +++++++++++++++++--------- 1 file changed, 132 insertions(+), 64 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 72d70c5e5b0..4fb196a0c81 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -6,14 +6,20 @@ #include "ucp_test.h" +#include + extern "C" { #include #include #include #include +#if HAVE_CUDA +#include +#endif #include #include #include +#include #include #include #include @@ -557,11 +563,13 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { return true; } - void - send_recv_am(size_t size, ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST) + void send_recv_am(size_t size, + ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST, + ucs_sys_device_t sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN) { /* Prepare receiver data handler */ mem_buffer recv_buf(size, mem_type); + set_buffer_sys_dev(recv_buf, sys_dev); struct ctx_t { mem_buffer *buf; bool received; @@ -611,6 +619,7 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { /* Send data */ mem_buffer buf(size, mem_type); + set_buffer_sys_dev(buf, sys_dev); ucp_request_param_t param = {}; auto sptr = ucp_am_send_nbx(sender().ep(), AM_ID, NULL, 0ul, buf.ptr(), buf.size(), ¶m); @@ -622,6 +631,17 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { EXPECT_TRUE(ctx.received); } + static void + set_buffer_sys_dev(const mem_buffer &buffer, ucs_sys_device_t sys_dev) + { + if (sys_dev == UCS_SYS_DEVICE_ID_UNKNOWN) { + return; + } + + ucs_memtype_cache_update(buffer.ptr(), buffer.size(), buffer.mem_type(), + sys_dev, UCS_MEM_FLAG_REGISTRABLE); + } + void send_recv_am_range(size_t msg_start, size_t msg_end, size_t msg_step, ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST) { @@ -1347,13 +1367,16 @@ UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_gpu, rcx_gpu, class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { public: - test_ucp_proto_mock_mtype_sys_dev() + test_ucp_proto_mock_mtype_sys_dev() : + m_user_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN), + m_transfer_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN) { mock_transport("rc_mlx5"); } virtual void init() override { + setup_fake_sibling_topology(); auto iface_attr_func = [](uct_iface_attr_t &iface_attr) { iface_attr.cap.am.max_short = 2000; iface_attr.bandwidth.shared = 28e9; @@ -1361,41 +1384,26 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { iface_attr.latency.m = 1e-9; }; - add_mock_iface("mock_0:1", iface_attr_func); - add_mock_iface("mock_1:1", iface_attr_func); + add_mock_iface_on_sys_device("mock", m_transfer_sys_dev, + iface_attr_func); test_ucp_proto_mock::init(); - - s_user_sys_dev = get_mock_sys_dev_by_name("mock_0:1"); - s_frag_sys_dev = get_mock_sys_dev_by_name("mock_1:1"); - EXPECT_UCS_OK(ucs_topo_sys_device_set_class( - s_user_sys_dev, UCS_TOPO_DEVICE_CLASS_ACC)); - EXPECT_UCS_OK(ucs_topo_sys_device_set_class( - s_frag_sys_dev, UCS_TOPO_DEVICE_CLASS_ACC)); - - set_frag_sys_dev(sender()); - set_frag_sys_dev(receiver()); } virtual void cleanup() override { test_ucp_proto_mock::cleanup(); - - if (s_user_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { - EXPECT_UCS_OK(ucs_topo_sys_device_set_class( - s_user_sys_dev, UCS_TOPO_DEVICE_CLASS_UNKNOWN)); - } - if (s_frag_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { + if (m_user_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { EXPECT_UCS_OK(ucs_topo_sys_device_set_class( - s_frag_sys_dev, UCS_TOPO_DEVICE_CLASS_UNKNOWN)); + m_user_sys_dev, UCS_TOPO_DEVICE_CLASS_UNKNOWN)); } - s_frag_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - s_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + m_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + m_transfer_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; } protected: - void check_mtype_keeps_user_sys_dev(ucp_operation_id_t op_id, - const char *proto_name) + void + check_mtype_uses_host_frag(ucp_operation_id_t op_id, const char *proto_name) { static constexpr size_t msg_size = UCS_KBYTE; uint8_t remote = 0; @@ -1416,19 +1424,11 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { const ucp_proto_select_elem_t *select_elem; const ucp_proto_threshold_elem_t *threshold; ucp_proto_query_attr_t attr; - unsigned user_ordinal, frag_ordinal; - ASSERT_NE(s_user_sys_dev, s_frag_sys_dev); - user_ordinal = ucs_topo_sys_device_get_bdf_class_ordinal( - s_user_sys_dev); - frag_ordinal = ucs_topo_sys_device_get_bdf_class_ordinal( - s_frag_sys_dev); - ASSERT_NE(UCS_SYS_DEVICE_ORDINAL_INVALID, user_ordinal); - ASSERT_NE(UCS_SYS_DEVICE_ORDINAL_INVALID, frag_ordinal); - ASSERT_NE(user_ordinal % 2, frag_ordinal % 2); + ASSERT_FALSE(ucs_topo_is_reachable(m_transfer_sys_dev, m_user_sys_dev)); mem_info.type = UCS_MEMORY_TYPE_CUDA_MANAGED; - mem_info.sys_dev = s_user_sys_dev; + mem_info.sys_dev = m_user_sys_dev; mem_info.flags = UCS_MEM_FLAG_REGISTRABLE; ucp_proto_select_param_init(&select_param, op_id, 0, 0, UCP_DATATYPE_CONTIG, &mem_info, 1); @@ -1442,57 +1442,125 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, msg_size); ASSERT_STREQ(proto_name, threshold->proto_config.proto->name); - EXPECT_EQ(s_user_sys_dev, - threshold->proto_config.select_param.sys_dev); + EXPECT_EQ(m_user_sys_dev, threshold->proto_config.select_param.sys_dev); if (op_id == UCP_OP_ID_RNDV_RECV) { const auto *rpriv = static_cast( threshold->proto_config.priv); - EXPECT_EQ(s_frag_sys_dev, rpriv->frag_sys_dev); + EXPECT_EQ(UCS_MEMORY_TYPE_HOST, rpriv->frag_mem_type); + EXPECT_TRUE(ucs_topo_is_reachable(m_transfer_sys_dev, + rpriv->frag_sys_dev)); } ucp_proto_config_query(worker, &threshold->proto_config, msg_size, &attr); - /* Keep the user device as the equal-lane spreading key. The fragment - * device is modeled separately through reg_mem_info. */ - EXPECT_STREQ((user_ordinal % 2) ? "rc_mlx5/mock_1:1" : - "rc_mlx5/mock_0:1", - attr.config); + EXPECT_STREQ("rc_mlx5/mock", attr.config); + + send_recv_am(64 * UCS_KBYTE, UCS_MEMORY_TYPE_CUDA_MANAGED, + m_user_sys_dev); } private: - static void set_frag_sys_dev(entity &e) - { - ucp_memory_info_t mem_info; - ucp_md_index_t md_index; + void setup_fake_sibling_topology() + { + static constexpr uintptr_t fake_sibling_value = 0x11685001; + static constexpr uintptr_t fake_dma_value = 0x11685002; + static constexpr uintptr_t fake_user_value = 0; + ucs_sys_device_t cuda_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + ucs_sys_device_t dma_sys_dev, pci_sys_dev, sibling_sys_dev; + ucs_sys_bus_id_t cuda_bus_id, pci_bus_id, transfer_bus_id; + struct dirent *entry; + DIR *dir; + + if (!mem_buffer::is_mem_type_supported(UCS_MEMORY_TYPE_CUDA_MANAGED)) { + UCS_TEST_SKIP_R("CUDA managed memory is unavailable"); + } - ASSERT_UCS_OK(ucp_mm_get_alloc_md_index(e.ucph(), UCS_MEMORY_TYPE_HOST, - UCS_SYS_DEVICE_ID_UNKNOWN, - &md_index, &mem_info)); - e.ucph()->alloc_md[UCS_MEMORY_TYPE_HOST].sys_dev = s_frag_sys_dev; - } +#if HAVE_CUDA + mem_buffer::set_device_context(); + cuda_sys_dev = uct_cuda_get_sys_dev(0); +#else + UCS_TEST_SKIP_R("CUDA support is unavailable"); +#endif + ASSERT_NE(UCS_SYS_DEVICE_ID_UNKNOWN, cuda_sys_dev); + ASSERT_UCS_OK(ucs_topo_get_device_bus_id(cuda_sys_dev, &cuda_bus_id)); + + pci_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + dir = opendir("/sys/bus/pci/devices"); + ASSERT_NE(nullptr, dir); + while ((entry = readdir(dir)) != nullptr) { + if (ucs_topo_find_device_by_bdf_name(entry->d_name, &pci_sys_dev) == + UCS_OK) { + ASSERT_UCS_OK( + ucs_topo_get_device_bus_id(pci_sys_dev, &pci_bus_id)); + if (ucs_topo_get_bus_id_bit_repr(&pci_bus_id) != + ucs_topo_get_bus_id_bit_repr(&cuda_bus_id)) { + break; + } + pci_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + } + } + closedir(dir); + if (pci_sys_dev == UCS_SYS_DEVICE_ID_UNKNOWN) { + UCS_TEST_SKIP_R("no PCI device for fake sibling topology"); + } - static ucs_sys_device_t s_frag_sys_dev; - static ucs_sys_device_t s_user_sys_dev; + ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( + &pci_bus_id, fake_sibling_value, &sibling_sys_dev)); + ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( + &pci_bus_id, fake_dma_value, &dma_sys_dev)); + ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( + &pci_bus_id, fake_user_value, &m_user_sys_dev)); + + transfer_bus_id.domain = 0xffff; + transfer_bus_id.bus = 0xff; + transfer_bus_id.slot = 0xfe; + transfer_bus_id.function = 0; + ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id(&transfer_bus_id, + &m_transfer_sys_dev)); + + ASSERT_UCS_OK(ucs_topo_sys_device_set_name(sibling_sys_dev, + "fake_sibling_nic", 10)); + ASSERT_UCS_OK(ucs_topo_sys_device_set_name(dma_sys_dev, + "fake_sibling_dma", 10)); + ASSERT_UCS_OK(ucs_topo_sys_device_set_name(m_user_sys_dev, + "fake_user_acc", 10)); + ASSERT_UCS_OK(ucs_topo_sys_device_set_name(m_transfer_sys_dev, + "fake_transfer_nic", 10)); + ASSERT_UCS_OK(ucs_topo_sys_device_set_class(m_user_sys_dev, + UCS_TOPO_DEVICE_CLASS_ACC)); + + /* The fake user accelerator is paired with sibling_sys_dev. The mock + * transfer NIC is deliberately a different auxiliary-path device, so + * it cannot access the accelerator directly but can access a host + * staging fragment. Register the fake device last so it replaces any + * earlier match on the same real PCI bridge. */ + ASSERT_UCS_OK(ucs_topo_sys_device_enable_aux_path(m_user_sys_dev)); + ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(sibling_sys_dev, + dma_sys_dev)); + ASSERT_TRUE(ucs_topo_is_sibling(sibling_sys_dev, m_user_sys_dev)); + + ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(m_transfer_sys_dev, + m_transfer_sys_dev)); + ASSERT_FALSE(ucs_topo_is_reachable(m_transfer_sys_dev, m_user_sys_dev)); + } + + ucs_sys_device_t m_user_sys_dev; + ucs_sys_device_t m_transfer_sys_dev; }; -ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_frag_sys_dev = - UCS_SYS_DEVICE_ID_UNKNOWN; -ucs_sys_device_t test_ucp_proto_mock_mtype_sys_dev::s_user_sys_dev = - UCS_SYS_DEVICE_ID_UNKNOWN; - -UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_keeps_user_sys_dev, +UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_host_frag_non_sibling, "RNDV_SCHEME=get_ppln", "RNDV_THRESH=1", "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") { - check_mtype_keeps_user_sys_dev(UCP_OP_ID_RNDV_RECV, "rndv/get/mtype"); + check_mtype_uses_host_frag(UCP_OP_ID_RNDV_RECV, "rndv/get/mtype"); } -UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_keeps_user_sys_dev, +UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_host_frag_non_sibling, "RNDV_SCHEME=put_ppln", "RNDV_THRESH=1", "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K", "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") { - check_mtype_keeps_user_sys_dev(UCP_OP_ID_RNDV_SEND, "rndv/put/mtype"); + check_mtype_uses_host_frag(UCP_OP_ID_RNDV_SEND, "rndv/put/mtype"); } UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_mtype_sys_dev, rcx_gpu, From 80e54567ba1267ac85c8011f4c52e4b28710f7d5 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Wed, 2 Sep 2026 13:42:46 +0300 Subject: [PATCH 17/27] UCP/TEST: Use real devices for mtype reachability --- test/gtest/ucp/test_ucp_proto_mock.cc | 300 +++++++++++++------------- 1 file changed, 146 insertions(+), 154 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 4fb196a0c81..bca6331ca4c 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -6,16 +6,11 @@ #include "ucp_test.h" -#include - extern "C" { #include #include #include #include -#if HAVE_CUDA -#include -#endif #include #include #include @@ -52,15 +47,17 @@ class mock_iface { m_mock.cleanup(); } - void add_mock_iface(const std::string &dev_name = "mock", - iface_attr_func_t cb = - [](uct_iface_attr_t &iface_attr) {}, - perf_attr_func_t perf_cb = default_perf_mock, - ucs_sys_device_t sys_device = UCS_SYS_DEVICE_ID_UNKNOWN) + void add_mock_iface( + const std::string &dev_name = "mock", + iface_attr_func_t cb = [](uct_iface_attr_t &iface_attr) {}, + perf_attr_func_t perf_cb = default_perf_mock, + ucs_sys_device_t sys_device = UCS_SYS_DEVICE_ID_UNKNOWN, + bool use_real_sys_device = false) { - m_iface_attrs_funcs[dev_name] = std::move(cb); - m_perf_attrs_funcs[dev_name] = std::move(perf_cb); - m_sys_devices[dev_name] = sys_device; + m_iface_attrs_funcs[dev_name] = std::move(cb); + m_perf_attrs_funcs[dev_name] = std::move(perf_cb); + m_sys_devices[dev_name] = sys_device; + m_use_real_sys_device[dev_name] = use_real_sys_device; } void add_mock_iface_on_sys_device( @@ -72,6 +69,15 @@ class mock_iface { sys_device); } + void add_mock_iface_on_real_device( + const std::string &dev_name, + iface_attr_func_t cb = [](uct_iface_attr_t &iface_attr) {}, + perf_attr_func_t perf_cb = default_perf_mock) + { + add_mock_iface(dev_name, std::move(cb), std::move(perf_cb), + UCS_SYS_DEVICE_ID_UNKNOWN, true); + } + /* Return the sys_dev assigned to a mock device during topology * registration in query_devices_mock(). */ ucs_sys_device_t get_mock_sys_dev_by_name(const std::string &dev_name) const @@ -79,6 +85,32 @@ class mock_iface { return m_sys_devs_by_name.at(dev_name); } + const std::vector &real_sys_devices() const + { + return m_real_sys_devices; + } + + /* Retarget a mock resource after context creation and before connect(). */ + void set_mock_sys_dev(ucp_context_h context, const std::string &dev_name, + ucs_sys_device_t sys_dev) + { + unsigned count = 0; + + for (ucp_rsc_index_t rsc_index = 0; rsc_index < context->num_tls; + ++rsc_index) { + uct_tl_resource_desc_t *tl_rsc = &context->tl_rscs[rsc_index].tl_rsc; + + if ((dev_name == tl_rsc->dev_name) && + (m_tl->name == std::string(tl_rsc->tl_name))) { + tl_rsc->sys_device = sys_dev; + ++count; + } + } + + ASSERT_GT(count, 0); + m_sys_devs_by_name.at(dev_name) = sys_dev; + } + void mock_transport(const std::string &tl_name) { uct_component_h component; @@ -146,6 +178,17 @@ class mock_iface { return UCS_OK; } + for (unsigned i = 0; i < *num_tl_devices_p; ++i) { + ucs_sys_device_t sys_dev = (*tl_devices_p)[i].sys_device; + + if ((sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) && + (std::find(m_self->m_real_sys_devices.begin(), + m_self->m_real_sys_devices.end(), + sys_dev) == m_self->m_real_sys_devices.end())) { + m_self->m_real_sys_devices.push_back(sys_dev); + } + } + /* Instantiate mock devices only for the first available device */ const char *first_dev_name = (*tl_devices_p)[0].name; if (m_self->m_real_dev_name.empty()) { @@ -160,9 +203,9 @@ class mock_iface { * The number of real devices (and their names) do not match the mocked * ones. In order to pretend that all the mocked devices are supported, * we remember the first real device name, and then substitute the - * response with the mocked devices names. Each mocked device is - * assigned a distinct sys_device: either the one explicitly requested - * via add_mock_iface(), or a freshly registered synthetic topology + * response with the mocked devices names. Each mock is assigned either + * the first real device's sys_device, one explicitly requested via + * add_mock_iface(), or a freshly registered synthetic topology * device (so that mocked distances can be set) when none was requested. * The resulting sys_device is recorded per name for later lookup. Later * on the iface_open_mock will use the real device name (same for all @@ -179,7 +222,9 @@ class mock_iface { mock_devices[dev_count].type = (*tl_devices_p)[0].type; ucs_sys_device_t sys_dev = m_self->m_sys_devices[it.first]; - if (sys_dev == UCS_SYS_DEVICE_ID_UNKNOWN) { + if (m_self->m_use_real_sys_device[it.first]) { + sys_dev = (*tl_devices_p)[0].sys_device; + } else if (sys_dev == UCS_SYS_DEVICE_ID_UNKNOWN) { ucs_sys_bus_id_t bus_id = { .domain = 0xffff, .bus = 0xff, @@ -295,7 +340,9 @@ class mock_iface { std::map m_iface_attrs_funcs; std::map m_perf_attrs_funcs; std::map m_sys_devices; + std::map m_use_real_sys_device; std::map m_sys_devs_by_name; + std::vector m_real_sys_devices; std::string m_real_dev_name; uct_md_h m_real_md; }; @@ -888,70 +935,6 @@ UCS_TEST_P(test_ucp_proto_mock_rcx, rma_put_2_lanes, }, key, 0); } -UCS_TEST_P(test_ucp_proto_mock_rcx, rndv_remote_sys_dev_namespace, - "IB_NUM_PATHS?=1", "RNDV_SCHEME=put_zcopy") -{ - constexpr size_t select_size = UCS_MBYTE; - uint8_t remote = 0; - auto memh = mem_map(receiver(), &remote, sizeof(remote)); - auto rkey_packed = rkey_pack(receiver(), memh); - auto rkey = rkey_unpack(sender().ep(), rkey_packed); - ucp_worker_h worker = sender().worker(); - ucp_worker_cfg_index_t ep_cfg_index = ep_config_index(sender()); - const ucs_sys_device_t remote_sys_dev = get_mock_sys_dev_by_name( - "mock_0:1"); - const ucp_rkey_config_t *base_rkey_config = - &ucs_array_elem(&worker->rkey_config, rkey->cfg_index); - const ucp_ep_config_t *ep_config = ucp_worker_ep_config(worker, - ep_cfg_index); - ucp_rkey_config_key_t rkey_config_key = base_rkey_config->key; - ucs_sys_dev_distance_t lanes_distance[UCP_MAX_LANES]; - ucp_worker_cfg_index_t rkey_cfg_index; - ucp_rkey_config_t *rkey_config; - ucp_proto_select_param_t select_param; - ucp_memory_info_t mem_info; - const ucp_proto_select_elem_t *select_elem; - const ucp_proto_threshold_elem_t *threshold; - const ucp_proto_rndv_ctrl_priv_t *rpriv; - ucp_lane_index_t lane; - - ASSERT_EQ(UCS_SYS_DEVICE_ID_UNKNOWN, rkey_config_key.sys_dev); - ASSERT_NE(UCS_SYS_DEVICE_ID_UNKNOWN, remote_sys_dev); - - for (lane = 0; lane < ep_config->key.num_lanes; ++lane) { - lanes_distance[lane] = base_rkey_config->lanes_distance[lane]; - } - - /* Simulate an rkey received from a peer whose process-local sys_dev value - * happens to name a device in this process as well. */ - rkey_config_key.sys_dev = remote_sys_dev; - ASSERT_UCS_OK(ucp_worker_rkey_config_get(worker, &rkey_config_key, - lanes_distance, &rkey_cfg_index)); - - mem_info.type = UCS_MEMORY_TYPE_HOST; - mem_info.sys_dev = get_mock_sys_dev_by_name("mock_1:1"); - mem_info.flags = UCS_MEM_FLAG_REGISTRABLE; - ucp_proto_select_param_init(&select_param, UCP_OP_ID_RNDV_RECV, 0, 0, - UCP_DATATYPE_CONTIG, &mem_info, 1); - - rkey_config = &ucs_array_elem(&worker->rkey_config, rkey_cfg_index); - select_elem = ucp_proto_select_lookup_slow(worker, - &rkey_config->proto_select, 1, - ep_cfg_index, rkey_cfg_index, - &select_param); - ASSERT_NE(nullptr, select_elem); - - threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, - select_size); - ASSERT_STREQ("rndv/rtr", threshold->proto_config.proto->name); - rpriv = static_cast( - threshold->proto_config.priv); - - EXPECT_EQ(remote_sys_dev, rkey_config->key.sys_dev); - EXPECT_EQ(UCS_SYS_DEVICE_ID_UNKNOWN, - rpriv->remote_proto_config.select_param.sys_dev); -} - UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_rcx, rcx, "rc_x") class test_ucp_proto_mock_rcx2 : public test_ucp_proto_mock { @@ -1369,14 +1352,14 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { public: test_ucp_proto_mock_mtype_sys_dev() : m_user_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN), - m_transfer_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN) + m_transfer_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN), + m_sibling_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN) { mock_transport("rc_mlx5"); } virtual void init() override { - setup_fake_sibling_topology(); auto iface_attr_func = [](uct_iface_attr_t &iface_attr) { iface_attr.cap.am.max_short = 2000; iface_attr.bandwidth.shared = 28e9; @@ -1384,21 +1367,31 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { iface_attr.latency.m = 1e-9; }; - add_mock_iface_on_sys_device("mock", m_transfer_sys_dev, - iface_attr_func); + add_mock_iface_on_real_device("mock", iface_attr_func); test_ucp_proto_mock::init(); } + virtual void post_ucp_init() override + { + setup_fake_sibling_topology(); + } + virtual void cleanup() override { - test_ucp_proto_mock::cleanup(); - if (m_user_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { - EXPECT_UCS_OK(ucs_topo_sys_device_set_class( - m_user_sys_dev, UCS_TOPO_DEVICE_CLASS_UNKNOWN)); + /* Leave no synthetic device in the DEV role. In particular, CUDA + * discovery in a later test must not scan these aliases. */ + if (m_transfer_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { + EXPECT_UCS_OK( + ucs_topo_sys_device_enable_aux_path(m_transfer_sys_dev)); } - + if (m_sibling_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { + EXPECT_UCS_OK( + ucs_topo_sys_device_enable_aux_path(m_sibling_sys_dev)); + } + test_ucp_proto_mock::cleanup(); m_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; m_transfer_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + m_sibling_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; } protected: @@ -1456,97 +1449,96 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { &attr); EXPECT_STREQ("rc_mlx5/mock", attr.config); - send_recv_am(64 * UCS_KBYTE, UCS_MEMORY_TYPE_CUDA_MANAGED, - m_user_sys_dev); + send_recv_am(64 * UCS_KBYTE, UCS_MEMORY_TYPE_CUDA_MANAGED); } private: void setup_fake_sibling_topology() { - static constexpr uintptr_t fake_sibling_value = 0x11685001; - static constexpr uintptr_t fake_dma_value = 0x11685002; - static constexpr uintptr_t fake_user_value = 0; - ucs_sys_device_t cuda_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - ucs_sys_device_t dma_sys_dev, pci_sys_dev, sibling_sys_dev; - ucs_sys_bus_id_t cuda_bus_id, pci_bus_id, transfer_bus_id; - struct dirent *entry; - DIR *dir; + static constexpr uintptr_t fake_sibling_value = 0x11685001; + static constexpr uintptr_t fake_dma_value = 0x11685002; + static constexpr uintptr_t fake_user_value = 0x11685003; + static constexpr uintptr_t fake_transfer_value = 0x11685004; + ucs_sys_device_t dma_sys_dev, transfer_sys_dev; + ucs_sys_bus_id_t cuda_bus_id, transfer_bus_id; + ucp_memory_info_t cuda_mem_info; if (!mem_buffer::is_mem_type_supported(UCS_MEMORY_TYPE_CUDA_MANAGED)) { UCS_TEST_SKIP_R("CUDA managed memory is unavailable"); } -#if HAVE_CUDA - mem_buffer::set_device_context(); - cuda_sys_dev = uct_cuda_get_sys_dev(0); -#else - UCS_TEST_SKIP_R("CUDA support is unavailable"); -#endif - ASSERT_NE(UCS_SYS_DEVICE_ID_UNKNOWN, cuda_sys_dev); - ASSERT_UCS_OK(ucs_topo_get_device_bus_id(cuda_sys_dev, &cuda_bus_id)); - - pci_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - dir = opendir("/sys/bus/pci/devices"); - ASSERT_NE(nullptr, dir); - while ((entry = readdir(dir)) != nullptr) { - if (ucs_topo_find_device_by_bdf_name(entry->d_name, &pci_sys_dev) == - UCS_OK) { - ASSERT_UCS_OK( - ucs_topo_get_device_bus_id(pci_sys_dev, &pci_bus_id)); - if (ucs_topo_get_bus_id_bit_repr(&pci_bus_id) != - ucs_topo_get_bus_id_bit_repr(&cuda_bus_id)) { - break; - } - pci_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - } - } - closedir(dir); - if (pci_sys_dev == UCS_SYS_DEVICE_ID_UNKNOWN) { - UCS_TEST_SKIP_R("no PCI device for fake sibling topology"); - } + /* CUDA managed memory is host-preferred on some systems and therefore + * has no device identity. Use a real CUDA allocation to discover the + * GPU BDF, then create a distinct alias so the test does not modify the + * topology state of the real GPU. */ + mem_buffer cuda_buffer(1, UCS_MEMORY_TYPE_CUDA); + ucp_memory_detect(sender().ucph(), cuda_buffer.ptr(), + cuda_buffer.size(), &cuda_mem_info); + ASSERT_EQ(UCS_MEMORY_TYPE_CUDA, cuda_mem_info.type); + ASSERT_NE(UCS_SYS_DEVICE_ID_UNKNOWN, cuda_mem_info.sys_dev); + ASSERT_UCS_OK(ucs_topo_get_device_bus_id(cuda_mem_info.sys_dev, + &cuda_bus_id)); ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( - &pci_bus_id, fake_sibling_value, &sibling_sys_dev)); + &cuda_bus_id, fake_sibling_value, &m_sibling_sys_dev)); ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( - &pci_bus_id, fake_dma_value, &dma_sys_dev)); + &cuda_bus_id, fake_dma_value, &dma_sys_dev)); ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( - &pci_bus_id, fake_user_value, &m_user_sys_dev)); - - transfer_bus_id.domain = 0xffff; - transfer_bus_id.bus = 0xff; - transfer_bus_id.slot = 0xfe; - transfer_bus_id.function = 0; - ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id(&transfer_bus_id, - &m_transfer_sys_dev)); - - ASSERT_UCS_OK(ucs_topo_sys_device_set_name(sibling_sys_dev, + &cuda_bus_id, fake_user_value, &m_user_sys_dev)); + ASSERT_UCS_OK(ucs_topo_sys_device_set_name(m_sibling_sys_dev, "fake_sibling_nic", 10)); ASSERT_UCS_OK(ucs_topo_sys_device_set_name(dma_sys_dev, "fake_sibling_dma", 10)); ASSERT_UCS_OK(ucs_topo_sys_device_set_name(m_user_sys_dev, "fake_user_acc", 10)); - ASSERT_UCS_OK(ucs_topo_sys_device_set_name(m_transfer_sys_dev, - "fake_transfer_nic", 10)); - ASSERT_UCS_OK(ucs_topo_sys_device_set_class(m_user_sys_dev, - UCS_TOPO_DEVICE_CLASS_ACC)); - - /* The fake user accelerator is paired with sibling_sys_dev. The mock - * transfer NIC is deliberately a different auxiliary-path device, so - * it cannot access the accelerator directly but can access a host - * staging fragment. Register the fake device last so it replaces any - * earlier match on the same real PCI bridge. */ ASSERT_UCS_OK(ucs_topo_sys_device_enable_aux_path(m_user_sys_dev)); - ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(sibling_sys_dev, + ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(m_sibling_sys_dev, dma_sys_dev)); - ASSERT_TRUE(ucs_topo_is_sibling(sibling_sys_dev, m_user_sys_dev)); + ASSERT_TRUE(ucs_topo_is_sibling(m_sibling_sys_dev, m_user_sys_dev)); + + /* Use an alias of a real transport device as the non-sibling mock NIC. + * This keeps all topology entries resolvable through sysfs. */ + for (ucs_sys_device_t sys_dev : real_sys_devices()) { + ASSERT_UCS_OK( + ucs_topo_get_device_bus_id(sys_dev, &transfer_bus_id)); + ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( + &transfer_bus_id, fake_transfer_value, &transfer_sys_dev)); + ASSERT_UCS_OK(ucs_topo_sys_device_set_name(transfer_sys_dev, + "fake_transfer_nic", + 10)); + m_transfer_sys_dev = transfer_sys_dev; + ASSERT_UCS_OK( + ucs_topo_sys_device_set_sys_dev_aux(transfer_sys_dev, + transfer_sys_dev)); + + if (!ucs_topo_is_reachable(transfer_sys_dev, m_user_sys_dev)) { + break; + } + + /* This candidate shares the user's PCIe bridge. Demote it before + * trying another BDF, then restore the intended sibling. */ + ASSERT_UCS_OK( + ucs_topo_sys_device_enable_aux_path(transfer_sys_dev)); + m_transfer_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(m_sibling_sys_dev, + dma_sys_dev)); + } + + if (m_transfer_sys_dev == UCS_SYS_DEVICE_ID_UNKNOWN) { + UCS_TEST_SKIP_R("no real NIC BDF outside the fake sibling path"); + } + + for (ucs::ptr_vector::const_iterator iter = entities().begin(); + iter != entities().end(); ++iter) { + set_mock_sys_dev((*iter)->ucph(), "mock", m_transfer_sys_dev); + } - ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(m_transfer_sys_dev, - m_transfer_sys_dev)); ASSERT_FALSE(ucs_topo_is_reachable(m_transfer_sys_dev, m_user_sys_dev)); } ucs_sys_device_t m_user_sys_dev; ucs_sys_device_t m_transfer_sys_dev; + ucs_sys_device_t m_sibling_sys_dev; }; UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_host_frag_non_sibling, From 6ddfebcfef24675ab23c429799830e59fe9edb73 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Wed, 2 Sep 2026 17:36:06 +0300 Subject: [PATCH 18/27] UCP/TEST: Align mock device members --- test/gtest/ucp/test_ucp_proto_mock.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index bca6331ca4c..4f5a0b2278e 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -340,9 +340,9 @@ class mock_iface { std::map m_iface_attrs_funcs; std::map m_perf_attrs_funcs; std::map m_sys_devices; - std::map m_use_real_sys_device; + std::map m_use_real_sys_device; std::map m_sys_devs_by_name; - std::vector m_real_sys_devices; + std::vector m_real_sys_devices; std::string m_real_dev_name; uct_md_h m_real_md; }; From 77a30b06853e2784c01a84c48944e72227101fa6 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Wed, 2 Sep 2026 19:52:16 +0300 Subject: [PATCH 19/27] UCP/TEST: Restore topology after mtype mock tests --- test/gtest/ucp/test_ucp_proto_mock.cc | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 4f5a0b2278e..022cb543948 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -1351,6 +1351,7 @@ UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_gpu, rcx_gpu, class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { public: test_ucp_proto_mock_mtype_sys_dev() : + m_topo_state(nullptr), m_user_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN), m_transfer_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN), m_sibling_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN) @@ -1367,6 +1368,9 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { iface_attr.latency.m = 1e-9; }; + m_topo_state = ucs_topo_extract_state(); + ASSERT_NE(nullptr, m_topo_state); + add_mock_iface_on_real_device("mock", iface_attr_func); test_ucp_proto_mock::init(); } @@ -1378,17 +1382,13 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { virtual void cleanup() override { - /* Leave no synthetic device in the DEV role. In particular, CUDA - * discovery in a later test must not scan these aliases. */ - if (m_transfer_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { - EXPECT_UCS_OK( - ucs_topo_sys_device_enable_aux_path(m_transfer_sys_dev)); - } - if (m_sibling_sys_dev != UCS_SYS_DEVICE_ID_UNKNOWN) { - EXPECT_UCS_OK( - ucs_topo_sys_device_enable_aux_path(m_sibling_sys_dev)); - } test_ucp_proto_mock::cleanup(); + if (m_topo_state != nullptr) { + ucs_topo_restore_state(m_topo_state); + m_topo_state = nullptr; + ucs_sys_topo_reset_provider(); + } + m_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; m_transfer_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; m_sibling_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; @@ -1536,9 +1536,10 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { ASSERT_FALSE(ucs_topo_is_reachable(m_transfer_sys_dev, m_user_sys_dev)); } - ucs_sys_device_t m_user_sys_dev; - ucs_sys_device_t m_transfer_sys_dev; - ucs_sys_device_t m_sibling_sys_dev; + ucs_global_state_t *m_topo_state; + ucs_sys_device_t m_user_sys_dev; + ucs_sys_device_t m_transfer_sys_dev; + ucs_sys_device_t m_sibling_sys_dev; }; UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_host_frag_non_sibling, From afbf29a35f9af34b61c6e20ed204665734527ce4 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 3 Sep 2026 11:07:03 +0300 Subject: [PATCH 20/27] UCP/TEST: Restore topology when mtype init skips --- test/gtest/ucp/test_ucp_proto_mock.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 022cb543948..5fe7270798b 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -1368,11 +1368,20 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { iface_attr.latency.m = 1e-9; }; + if (!mem_buffer::is_mem_type_supported(UCS_MEMORY_TYPE_CUDA_MANAGED)) { + UCS_TEST_SKIP_R("CUDA managed memory is unavailable"); + } + m_topo_state = ucs_topo_extract_state(); ASSERT_NE(nullptr, m_topo_state); - add_mock_iface_on_real_device("mock", iface_attr_func); - test_ucp_proto_mock::init(); + try { + add_mock_iface_on_real_device("mock", iface_attr_func); + test_ucp_proto_mock::init(); + } catch (...) { + cleanup(); + throw; + } } virtual void post_ucp_init() override @@ -1463,10 +1472,6 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { ucs_sys_bus_id_t cuda_bus_id, transfer_bus_id; ucp_memory_info_t cuda_mem_info; - if (!mem_buffer::is_mem_type_supported(UCS_MEMORY_TYPE_CUDA_MANAGED)) { - UCS_TEST_SKIP_R("CUDA managed memory is unavailable"); - } - /* CUDA managed memory is host-preferred on some systems and therefore * has no device identity. Use a real CUDA allocation to discover the * GPU BDF, then create a distinct alias so the test does not modify the From 2bcee46c6b4ddf6f007ea394cdea6f99dc3f5c71 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 3 Sep 2026 12:23:41 +0300 Subject: [PATCH 21/27] UCP/TEST: Address mtype sysdev review comments --- test/gtest/ucp/test_ucp_proto_mock.cc | 91 ++++++++++++++++++++------- 1 file changed, 69 insertions(+), 22 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 5fe7270798b..b57e9b9a8c4 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -14,7 +14,6 @@ extern "C" { #include #include #include -#include #include #include #include @@ -610,13 +609,11 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { return true; } - void send_recv_am(size_t size, - ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST, - ucs_sys_device_t sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN) + void + send_recv_am(size_t size, ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST) { /* Prepare receiver data handler */ mem_buffer recv_buf(size, mem_type); - set_buffer_sys_dev(recv_buf, sys_dev); struct ctx_t { mem_buffer *buf; bool received; @@ -666,7 +663,6 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { /* Send data */ mem_buffer buf(size, mem_type); - set_buffer_sys_dev(buf, sys_dev); ucp_request_param_t param = {}; auto sptr = ucp_am_send_nbx(sender().ep(), AM_ID, NULL, 0ul, buf.ptr(), buf.size(), ¶m); @@ -678,17 +674,6 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface { EXPECT_TRUE(ctx.received); } - static void - set_buffer_sys_dev(const mem_buffer &buffer, ucs_sys_device_t sys_dev) - { - if (sys_dev == UCS_SYS_DEVICE_ID_UNKNOWN) { - return; - } - - ucs_memtype_cache_update(buffer.ptr(), buffer.size(), buffer.mem_type(), - sys_dev, UCS_MEM_FLAG_REGISTRABLE); - } - void send_recv_am_range(size_t msg_start, size_t msg_end, size_t msg_step, ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST) { @@ -1351,9 +1336,9 @@ UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_gpu, rcx_gpu, class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { public: test_ucp_proto_mock_mtype_sys_dev() : - m_topo_state(nullptr), m_user_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN), m_transfer_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN), + m_topo_state(nullptr), m_sibling_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN) { mock_transport("rc_mlx5"); @@ -1457,10 +1442,11 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { ucp_proto_config_query(worker, &threshold->proto_config, msg_size, &attr); EXPECT_STREQ("rc_mlx5/mock", attr.config); - - send_recv_am(64 * UCS_KBYTE, UCS_MEMORY_TYPE_CUDA_MANAGED); } + ucs_sys_device_t m_user_sys_dev; + ucs_sys_device_t m_transfer_sys_dev; + private: void setup_fake_sibling_topology() { @@ -1542,8 +1528,6 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { } ucs_global_state_t *m_topo_state; - ucs_sys_device_t m_user_sys_dev; - ucs_sys_device_t m_transfer_sys_dev; ucs_sys_device_t m_sibling_sys_dev; }; @@ -1561,6 +1545,69 @@ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_host_frag_non_sibling, check_mtype_uses_host_frag(UCP_OP_ID_RNDV_SEND, "rndv/put/mtype"); } +/* A wire-packed rkey sys_dev belongs to the peer's topology namespace. Model + * a numeric collision with our unreachable fake GPU alias and verify that the + * nested remote selection does not reuse it as a local device. */ +UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, + put_zcopy_remote_sys_dev_namespace, "RNDV_SCHEME=put_zcopy", + "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") +{ + static constexpr size_t select_size = UCS_MBYTE; + uint8_t remote = 0; + ucp_worker_h worker = sender().worker(); + + auto memh = mem_map(receiver(), &remote, sizeof(remote)); + auto rkey_packed = rkey_pack(receiver(), memh); + auto rkey = rkey_unpack(sender().ep(), rkey_packed); + + ucp_worker_cfg_index_t ep_cfg_index = ep_config_index(sender()); + const ucp_rkey_config_t *base_rkey_config = + &ucs_array_elem(&worker->rkey_config, rkey->cfg_index); + const ucp_ep_config_t *ep_config = ucp_worker_ep_config(worker, + ep_cfg_index); + ucp_rkey_config_key_t rkey_config_key = base_rkey_config->key; + ucs_sys_dev_distance_t lanes_distance[UCP_MAX_LANES]; + ucp_worker_cfg_index_t rkey_cfg_index; + ucp_memory_info_t mem_info; + ucp_proto_select_param_t select_param; + const ucp_proto_select_elem_t *select_elem; + const ucp_proto_threshold_elem_t *threshold; + const ucp_proto_rndv_ctrl_priv_t *rpriv; + + ASSERT_EQ(UCS_SYS_DEVICE_ID_UNKNOWN, rkey_config_key.sys_dev); + memcpy(lanes_distance, base_rkey_config->lanes_distance, + ep_config->key.num_lanes * sizeof(lanes_distance[0])); + + rkey_config_key.sys_dev = m_user_sys_dev; + ASSERT_UCS_OK(ucp_worker_rkey_config_get(worker, &rkey_config_key, + lanes_distance, &rkey_cfg_index)); + + mem_info.type = UCS_MEMORY_TYPE_HOST; + mem_info.sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + mem_info.flags = UCS_MEM_FLAG_REGISTRABLE; + ucp_proto_select_param_init(&select_param, UCP_OP_ID_RNDV_RECV, 0, 0, + UCP_DATATYPE_CONTIG, &mem_info, 1); + + ucp_rkey_config_t *rkey_config = &ucs_array_elem(&worker->rkey_config, + rkey_cfg_index); + select_elem = ucp_proto_select_lookup_slow(worker, + &rkey_config->proto_select, 1, + ep_cfg_index, rkey_cfg_index, + &select_param); + ASSERT_NE(nullptr, select_elem); + + threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, + select_size); + ASSERT_STREQ("rndv/rtr", threshold->proto_config.proto->name); + rpriv = static_cast( + threshold->proto_config.priv); + + EXPECT_EQ(m_user_sys_dev, rkey_config->key.sys_dev); + EXPECT_EQ(UCS_SYS_DEVICE_ID_UNKNOWN, + rpriv->remote_proto_config.select_param.sys_dev); + EXPECT_STREQ("rndv/put/zcopy", rpriv->remote_proto_config.proto->name); +} + UCP_INSTANTIATE_TEST_CASE_TLS(test_ucp_proto_mock_mtype_sys_dev, rcx_gpu, "rc_x,cuda,rocm") From 950be0a62e5b51d418d4a05adeebfaef789fc2a2 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Fri, 4 Sep 2026 09:53:11 +0300 Subject: [PATCH 22/27] UCP/TEST: Clean up remote sysdev regression --- test/gtest/ucp/test_ucp_proto_mock.cc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index b57e9b9a8c4..4a3eb248021 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -1568,6 +1568,7 @@ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, ucp_rkey_config_key_t rkey_config_key = base_rkey_config->key; ucs_sys_dev_distance_t lanes_distance[UCP_MAX_LANES]; ucp_worker_cfg_index_t rkey_cfg_index; + ucp_rkey_config_t *rkey_config; ucp_memory_info_t mem_info; ucp_proto_select_param_t select_param; const ucp_proto_select_elem_t *select_elem; @@ -1588,12 +1589,11 @@ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, ucp_proto_select_param_init(&select_param, UCP_OP_ID_RNDV_RECV, 0, 0, UCP_DATATYPE_CONTIG, &mem_info, 1); - ucp_rkey_config_t *rkey_config = &ucs_array_elem(&worker->rkey_config, - rkey_cfg_index); - select_elem = ucp_proto_select_lookup_slow(worker, - &rkey_config->proto_select, 1, - ep_cfg_index, rkey_cfg_index, - &select_param); + rkey_config = &ucs_array_elem(&worker->rkey_config, rkey_cfg_index); + select_elem = ucp_proto_select_lookup_slow(worker, + &rkey_config->proto_select, 1, + ep_cfg_index, rkey_cfg_index, + &select_param); ASSERT_NE(nullptr, select_elem); threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, @@ -1602,7 +1602,6 @@ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, rpriv = static_cast( threshold->proto_config.priv); - EXPECT_EQ(m_user_sys_dev, rkey_config->key.sys_dev); EXPECT_EQ(UCS_SYS_DEVICE_ID_UNKNOWN, rpriv->remote_proto_config.select_param.sys_dev); EXPECT_STREQ("rndv/put/zcopy", rpriv->remote_proto_config.proto->name); From a869ff3a90a2669b4ee01fb9f8820b0cee4fedcc Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 10 Sep 2026 13:54:57 +0300 Subject: [PATCH 23/27] UCP/PROTO: Clarify RNDV reachability rationale --- src/ucp/proto/proto_common.c | 7 +++---- test/gtest/ucp/test_ucp_peer_failure.cc | 3 +++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ucp/proto/proto_common.c b/src/ucp/proto/proto_common.c index 31d8acfe9a6..a45fb879fac 100644 --- a/src/ucp/proto/proto_common.c +++ b/src/ucp/proto/proto_common.c @@ -727,10 +727,9 @@ ucp_proto_common_find_lanes(const ucp_proto_init_params_t *params, } } - /* Check internal reachability only when the selected buffer is - * registered on this lane. For non-zcopy protocols, either no buffer - * is registered or the lane filter checks the buffer described by - * reg_mem_info. */ + /* Only SEND_ZCOPY registers select_param's buffer on this lane. Other + * protocols either register no buffer or check the buffer described by + * reg_mem_info during their own initialization. */ if (flags & UCP_PROTO_COMMON_INIT_FLAG_SEND_ZCOPY) { lane_sys_dev = context->tl_rscs[rsc_index].tl_rsc.sys_device; if (!ucs_topo_is_reachable(lane_sys_dev, diff --git a/test/gtest/ucp/test_ucp_peer_failure.cc b/test/gtest/ucp/test_ucp_peer_failure.cc index 34617742e00..28d59f315f9 100644 --- a/test/gtest/ucp/test_ucp_peer_failure.cc +++ b/test/gtest/ucp/test_ucp_peer_failure.cc @@ -1013,6 +1013,9 @@ UCS_TEST_P(test_ucp_peer_failure_rndv_put_ppln_abort, rtr_mtype) rndv_progress_failure_test(rndv_mode::put_ppln, true); } +/* Use host staging so this test still selects rndv/put/mtype when CUDA + * fragments are unreachable from the network lane. The user buffer remains + * CUDA. */ UCS_TEST_P(test_ucp_peer_failure_rndv_put_ppln_abort, pipeline, "RNDV_FRAG_MEM_TYPES=host", "RNDV_FRAG_SIZE=host:8K") { From f13cc89738235e62c0241fbcff3334baf9047d4e Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 10 Sep 2026 16:01:15 +0300 Subject: [PATCH 24/27] UCP/PROTO: Consolidate RNDV reachability checks --- src/ucp/proto/proto_common.c | 20 ++----------------- test/gtest/ucp/test_ucp_proto_mock.cc | 28 ++++++++++++--------------- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/ucp/proto/proto_common.c b/src/ucp/proto/proto_common.c index a45fb879fac..4a68a16abe3 100644 --- a/src/ucp/proto/proto_common.c +++ b/src/ucp/proto/proto_common.c @@ -536,9 +536,8 @@ ucp_proto_common_filter_min_frag(const ucp_proto_init_params_t *params, /* The lane's device and the memory that will actually be registered * on it (which may differ from select_param's buffer, e.g. a staging * fragment for mtype protocols) must be topologically reachable. */ - if (!ucs_topo_is_reachable( - context->tl_rscs[rsc_index].tl_rsc.sys_device, - common_params->reg_mem_info.sys_dev)) { + if (!ucs_topo_is_reachable(ucp_proto_common_get_sys_dev(params, lane), + common_params->reg_mem_info.sys_dev)) { ucs_trace("%s: no reachability to reg mem sys_dev=%u", lane_desc, common_params->reg_mem_info.sys_dev); return 0; @@ -604,7 +603,6 @@ ucp_proto_common_find_lanes(const ucp_proto_init_params_t *params, ucp_md_index_t md_index; ucp_lane_map_t lane_map; char lane_desc[64]; - ucs_sys_device_t lane_sys_dev; ucs_status_t status; if (max_lanes == 0) { @@ -727,20 +725,6 @@ ucp_proto_common_find_lanes(const ucp_proto_init_params_t *params, } } - /* Only SEND_ZCOPY registers select_param's buffer on this lane. Other - * protocols either register no buffer or check the buffer described by - * reg_mem_info during their own initialization. */ - if (flags & UCP_PROTO_COMMON_INIT_FLAG_SEND_ZCOPY) { - lane_sys_dev = context->tl_rscs[rsc_index].tl_rsc.sys_device; - if (!ucs_topo_is_reachable(lane_sys_dev, - select_param->sys_dev)) { - ucs_trace("%s: no reachability between lane_sys_dev=%u and " - "sys_dev=%u", - lane_desc, lane_sys_dev, select_param->sys_dev); - continue; - } - } - ucs_trace("%s: added as lane %d", lane_desc, lane); lanes[num_lanes++] = lane; } diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 1cf568de0e9..aba4e874cd1 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -1368,8 +1368,7 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { test_ucp_proto_mock_mtype_sys_dev() : m_user_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN), m_transfer_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN), - m_topo_state(nullptr), - m_sibling_sys_dev(UCS_SYS_DEVICE_ID_UNKNOWN) + m_topo_state(nullptr) { mock_transport("rc_mlx5"); } @@ -1415,7 +1414,6 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { m_user_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; m_transfer_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - m_sibling_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; } protected: @@ -1430,9 +1428,9 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { UCS_TEST_SKIP_R("CUDA managed memory type endpoint is unavailable"); } - auto memh = mem_map(receiver(), &remote, sizeof(remote)); - auto rkey_packed = rkey_pack(receiver(), memh); - auto rkey = rkey_unpack(sender().ep(), rkey_packed); + auto memh = mem_map(receiver(), &remote, sizeof(remote)); + auto rkey_packed = rkey_pack(receiver(), memh); + auto rkey = rkey_unpack(sender().ep(), rkey_packed); ucp_worker_cfg_index_t ep_cfg_index = ep_config_index(sender()); ucp_rkey_config_t *rkey_config = &ucs_array_elem(&worker->rkey_config, rkey->cfg_index); @@ -1461,9 +1459,8 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { ASSERT_STREQ(proto_name, threshold->proto_config.proto->name); EXPECT_EQ(m_user_sys_dev, threshold->proto_config.select_param.sys_dev); if (op_id == UCP_OP_ID_RNDV_RECV) { - const auto *rpriv = - static_cast( - threshold->proto_config.priv); + const auto *rpriv = static_cast( + threshold->proto_config.priv); EXPECT_EQ(UCS_MEMORY_TYPE_HOST, rpriv->frag_mem_type); EXPECT_TRUE(ucs_topo_is_reachable(m_transfer_sys_dev, rpriv->frag_sys_dev)); @@ -1484,7 +1481,7 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { static constexpr uintptr_t fake_dma_value = 0x11685002; static constexpr uintptr_t fake_user_value = 0x11685003; static constexpr uintptr_t fake_transfer_value = 0x11685004; - ucs_sys_device_t dma_sys_dev, transfer_sys_dev; + ucs_sys_device_t dma_sys_dev, sibling_sys_dev, transfer_sys_dev; ucs_sys_bus_id_t cuda_bus_id, transfer_bus_id; ucp_memory_info_t cuda_mem_info; @@ -1501,21 +1498,21 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { &cuda_bus_id)); ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( - &cuda_bus_id, fake_sibling_value, &m_sibling_sys_dev)); + &cuda_bus_id, fake_sibling_value, &sibling_sys_dev)); ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( &cuda_bus_id, fake_dma_value, &dma_sys_dev)); ASSERT_UCS_OK(ucs_topo_find_device_by_bus_id_and_user_value( &cuda_bus_id, fake_user_value, &m_user_sys_dev)); - ASSERT_UCS_OK(ucs_topo_sys_device_set_name(m_sibling_sys_dev, + ASSERT_UCS_OK(ucs_topo_sys_device_set_name(sibling_sys_dev, "fake_sibling_nic", 10)); ASSERT_UCS_OK(ucs_topo_sys_device_set_name(dma_sys_dev, "fake_sibling_dma", 10)); ASSERT_UCS_OK(ucs_topo_sys_device_set_name(m_user_sys_dev, "fake_user_acc", 10)); ASSERT_UCS_OK(ucs_topo_sys_device_enable_aux_path(m_user_sys_dev)); - ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(m_sibling_sys_dev, + ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(sibling_sys_dev, dma_sys_dev)); - ASSERT_TRUE(ucs_topo_is_sibling(m_sibling_sys_dev, m_user_sys_dev)); + ASSERT_TRUE(ucs_topo_is_sibling(sibling_sys_dev, m_user_sys_dev)); /* Use an alias of a real transport device as the non-sibling mock NIC. * This keeps all topology entries resolvable through sysfs. */ @@ -1541,7 +1538,7 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { ASSERT_UCS_OK( ucs_topo_sys_device_enable_aux_path(transfer_sys_dev)); m_transfer_sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; - ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(m_sibling_sys_dev, + ASSERT_UCS_OK(ucs_topo_sys_device_set_sys_dev_aux(sibling_sys_dev, dma_sys_dev)); } @@ -1558,7 +1555,6 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { } ucs_global_state_t *m_topo_state; - ucs_sys_device_t m_sibling_sys_dev; }; UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, get_host_frag_non_sibling, From eda461fb47c22886b6980af5d22a63b40bc7cf20 Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 10 Sep 2026 19:12:09 +0300 Subject: [PATCH 25/27] UCP/PROTO: Clarify RNDV topology diagnostics --- src/ucp/proto/proto_common.c | 8 ++++++-- src/ucp/rndv/proto_rndv.c | 11 ++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ucp/proto/proto_common.c b/src/ucp/proto/proto_common.c index 4a68a16abe3..0f5d88587bc 100644 --- a/src/ucp/proto/proto_common.c +++ b/src/ucp/proto/proto_common.c @@ -496,6 +496,7 @@ ucp_proto_common_filter_min_frag(const ucp_proto_init_params_t *params, ucp_md_index_t md_index = context->tl_rscs[rsc_index].md_index; const uct_md_attr_v2_t *md_attr = &context->tl_mds[md_index].attr; const uct_iface_attr_t *iface_attr; + ucs_sys_device_t lane_sys_dev; size_t max_iov, tl_min_frag, tl_max_frag; /* Check memory registration capabilities for zero-copy case */ @@ -536,9 +537,12 @@ ucp_proto_common_filter_min_frag(const ucp_proto_init_params_t *params, /* The lane's device and the memory that will actually be registered * on it (which may differ from select_param's buffer, e.g. a staging * fragment for mtype protocols) must be topologically reachable. */ - if (!ucs_topo_is_reachable(ucp_proto_common_get_sys_dev(params, lane), + lane_sys_dev = ucp_proto_common_get_sys_dev(params, lane); + if (!ucs_topo_is_reachable(lane_sys_dev, common_params->reg_mem_info.sys_dev)) { - ucs_trace("%s: no reachability to reg mem sys_dev=%u", lane_desc, + ucs_trace("%s: no reachability between lane_sys_dev=%u and " + "reg_mem_sys_dev=%u", + lane_desc, lane_sys_dev, common_params->reg_mem_info.sys_dev); return 0; } diff --git a/src/ucp/rndv/proto_rndv.c b/src/ucp/rndv/proto_rndv.c index f6711cad8ae..6585821db7c 100644 --- a/src/ucp/rndv/proto_rndv.c +++ b/src/ucp/rndv/proto_rndv.c @@ -369,11 +369,12 @@ static ucp_proto_select_param_t ucp_proto_rndv_remote_select_param_init( &mem_info, select_param->sg_count); } else { /* If we know the remote buffer parameters, these are actually the local - * parameters for the remote protocol. The rkey system device belongs - * to the remote topology namespace, so it cannot represent a local - * device while estimating the protocol that the peer will select. - * The peer will use its actual local system device when selecting the - * protocol. + * parameters for the remote protocol. A wire rkey system device belongs + * to the peer topology namespace. A nested synthetic key may instead + * contain a local device, but the key does not identify its namespace. + * Treat it as unknown rather than risk interpreting a peer-local numeric + * ID in the current topology. Memory type is namespace-independent and + * remains valid for estimation. */ mem_info.sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; mem_info.type = init_params->rkey_config_key->mem_type; From 656978eb87ec4384f6ec8de6851957cc94d73deb Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 10 Sep 2026 19:48:40 +0300 Subject: [PATCH 26/27] UCP/TEST: Strengthen RNDV fragment device check --- test/gtest/ucp/test_ucp_proto_mock.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index aba4e874cd1..9292d7d2c3e 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -1457,13 +1457,11 @@ class test_ucp_proto_mock_mtype_sys_dev : public test_ucp_proto_mock { threshold = ucp_proto_thresholds_search_slow(select_elem->thresholds, msg_size); ASSERT_STREQ(proto_name, threshold->proto_config.proto->name); - EXPECT_EQ(m_user_sys_dev, threshold->proto_config.select_param.sys_dev); if (op_id == UCP_OP_ID_RNDV_RECV) { const auto *rpriv = static_cast( threshold->proto_config.priv); EXPECT_EQ(UCS_MEMORY_TYPE_HOST, rpriv->frag_mem_type); - EXPECT_TRUE(ucs_topo_is_reachable(m_transfer_sys_dev, - rpriv->frag_sys_dev)); + EXPECT_EQ(UCS_SYS_DEVICE_ID_UNKNOWN, rpriv->frag_sys_dev); } ucp_proto_config_query(worker, &threshold->proto_config, msg_size, From b4c14103934a2f94ff95799c2ea00f87e99154cb Mon Sep 17 00:00:00 2001 From: jeffnvidia Date: Thu, 10 Sep 2026 20:31:36 +0300 Subject: [PATCH 27/27] UCP/PROTO: Preserve topology estimate for remote RNDV --- src/ucp/rndv/proto_rndv.c | 14 +++++--------- test/gtest/ucp/test_ucp_proto_mock.cc | 6 +++--- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/ucp/rndv/proto_rndv.c b/src/ucp/rndv/proto_rndv.c index 6585821db7c..4a2a1abdc9f 100644 --- a/src/ucp/rndv/proto_rndv.c +++ b/src/ucp/rndv/proto_rndv.c @@ -368,16 +368,12 @@ static ucp_proto_select_param_t ucp_proto_rndv_remote_select_param_init( select_param->dt_class, &mem_info, select_param->sg_count); } else { - /* If we know the remote buffer parameters, these are actually the local - * parameters for the remote protocol. A wire rkey system device belongs - * to the peer topology namespace. A nested synthetic key may instead - * contain a local device, but the key does not identify its namespace. - * Treat it as unknown rather than risk interpreting a peer-local numeric - * ID in the current topology. Memory type is namespace-independent and - * remains valid for estimation. - */ - mem_info.sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; mem_info.type = init_params->rkey_config_key->mem_type; + /* If the local and remote memory types match, use the local system + * device as a namespace-safe proxy for remote protocol estimation. */ + mem_info.sys_dev = (mem_info.type == select_param->mem_type) ? + select_param->sys_dev : + UCS_SYS_DEVICE_ID_UNKNOWN; mem_info.flags = ucp_proto_rndv_rkey_mem_flags_estimate(init_params); ucp_proto_select_param_init(&remote_select_param, params->remote_op_id, op_attr_mask, params->remote_op_flags, diff --git a/test/gtest/ucp/test_ucp_proto_mock.cc b/test/gtest/ucp/test_ucp_proto_mock.cc index 9292d7d2c3e..11dab08a3d6 100644 --- a/test/gtest/ucp/test_ucp_proto_mock.cc +++ b/test/gtest/ucp/test_ucp_proto_mock.cc @@ -1571,7 +1571,7 @@ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_host_frag_non_sibling, /* A wire-packed rkey sys_dev belongs to the peer's topology namespace. Model * a numeric collision with our unreachable fake GPU alias and verify that the - * nested remote selection does not reuse it as a local device. */ + * nested remote selection uses the namespace-safe local device instead. */ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, put_zcopy_remote_sys_dev_namespace, "RNDV_SCHEME=put_zcopy", "IB_NUM_PATHS?=1", "MAX_RNDV_LANES=1") @@ -1608,7 +1608,7 @@ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, lanes_distance, &rkey_cfg_index)); mem_info.type = UCS_MEMORY_TYPE_HOST; - mem_info.sys_dev = UCS_SYS_DEVICE_ID_UNKNOWN; + mem_info.sys_dev = m_transfer_sys_dev; mem_info.flags = UCS_MEM_FLAG_REGISTRABLE; ucp_proto_select_param_init(&select_param, UCP_OP_ID_RNDV_RECV, 0, 0, UCP_DATATYPE_CONTIG, &mem_info, 1); @@ -1626,7 +1626,7 @@ UCS_TEST_P(test_ucp_proto_mock_mtype_sys_dev, rpriv = static_cast( threshold->proto_config.priv); - EXPECT_EQ(UCS_SYS_DEVICE_ID_UNKNOWN, + EXPECT_EQ(m_transfer_sys_dev, rpriv->remote_proto_config.select_param.sys_dev); EXPECT_STREQ("rndv/put/zcopy", rpriv->remote_proto_config.proto->name); }