@@ -12,15 +12,18 @@ extern "C" {
1212#include < uct/base/uct_iface.h>
1313#include < ucp/proto/proto_debug.h>
1414#include < ucp/proto/proto_select.inl>
15+
16+ #if HAVE_IB
17+ #include < uct/ib/base/ib_md.h>
18+ #endif
1519}
1620
1721class mock_iface {
1822public:
19- /* Can't use std::function due to coverity errors */
20- using iface_attr_func_t = void (*)(uct_iface_attr&);
21- using perf_attr_func_t = void (*)(uct_perf_attr_t &);
23+ using iface_attr_func_t = std::function<void (uct_iface_attr&)>;
24+ using perf_attr_func_t = std::function<void (uct_perf_attr_t &)>;
2225
23- mock_iface () : m_tl(nullptr )
26+ mock_iface () : m_tl(nullptr ), m_real_md( nullptr )
2427 {
2528 ucs_assert (m_self == nullptr );
2629 m_self = this ;
@@ -37,13 +40,12 @@ class mock_iface {
3740 m_mock.cleanup ();
3841 }
3942
40- void add_mock_iface (
41- const std::string &dev_name = " mock" ,
42- iface_attr_func_t cb = [](uct_iface_attr_t &iface_attr) {},
43- perf_attr_func_t perf_cb = cx7_perf_mock)
43+ void add_mock_iface (const std::string &dev_name = " mock" ,
44+ iface_attr_func_t cb = [](uct_iface_attr_t &iface_attr) {},
45+ perf_attr_func_t perf_cb = default_perf_mock)
4446 {
45- m_iface_attrs_funcs[dev_name] = cb ;
46- m_perf_attrs_funcs[dev_name] = perf_cb;
47+ m_iface_attrs_funcs[dev_name] = std::move (cb) ;
48+ m_perf_attrs_funcs[dev_name] = std::move ( perf_cb) ;
4749 }
4850
4951 void mock_transport (const std::string &tl_name)
@@ -68,6 +70,17 @@ class mock_iface {
6870 FAIL () << " Transport " << tl_name << " not found" ;
6971 }
7072
73+ #if HAVE_IB
74+ void ib_event (enum ibv_event_type event_type, uint8_t port_num)
75+ {
76+ uct_ib_async_event_t event = {};
77+ event.event_type = event_type;
78+ event.port_num = port_num;
79+ uct_ib_md_t *md = reinterpret_cast <uct_ib_md_t *>(m_self->m_real_md );
80+ uct_ib_handle_async_event (&md->dev , &event);
81+ }
82+ #endif
83+
7184private:
7285 static ucs_status_t
7386 query_devices_mock (uct_md_h md, uct_tl_device_resource_t **tl_devices_p,
@@ -83,6 +96,7 @@ class mock_iface {
8396 const char *first_dev_name = (*tl_devices_p)[0 ].name ;
8497 if (m_self->m_real_dev_name .empty ()) {
8598 m_self->m_real_dev_name = first_dev_name;
99+ m_self->m_real_md = md;
86100 } else if (m_self->m_real_dev_name != first_dev_name) {
87101 *num_tl_devices_p = 0 ;
88102 return UCS_OK ;
@@ -151,21 +165,42 @@ class mock_iface {
151165 static ucs_status_t perf_mock (uct_iface_h iface, uct_perf_attr_t *perf_attr)
152166 {
153167 uct_base_iface_t *base = ucs_derived_of (iface, uct_base_iface_t );
168+ uct_iface_attr_t iface_attr;
169+ ucs_status_t status;
154170
155171 UCS_MOCK_ORIG_FUNC (m_self->m_mock ,
156172 &base->internal_ops ->iface_estimate_perf , iface,
157173 perf_attr);
158174
175+ if (perf_attr->field_mask & (UCT_PERF_ATTR_FIELD_BANDWIDTH |
176+ UCT_PERF_ATTR_FIELD_PATH_BANDWIDTH )) {
177+ status = iface_query_mock (iface, &iface_attr);
178+ if (status != UCS_OK ) {
179+ return status;
180+ }
181+
182+ if (perf_attr->field_mask & UCT_PERF_ATTR_FIELD_BANDWIDTH ) {
183+ perf_attr->bandwidth = iface_attr.bandwidth ;
184+ }
185+
186+ if (perf_attr->field_mask & UCT_PERF_ATTR_FIELD_PATH_BANDWIDTH ) {
187+ perf_attr->path_bandwidth = iface_attr.bandwidth ;
188+ }
189+ }
190+
159191 std::string &iface_name = m_self->m_iface_names [base];
160192 auto it = m_self->m_perf_attrs_funcs .find (iface_name);
161193 (it->second )(*perf_attr);
162194 return UCS_OK ;
163195 }
164196
165- static void cx7_perf_mock (uct_perf_attr_t & perf_attr)
197+ static void default_perf_mock (uct_perf_attr_t & perf_attr)
166198 {
167- perf_attr.path_bandwidth .shared = 0.95 * perf_attr.bandwidth .shared ;
168- perf_attr.path_bandwidth .dedicated = 0 ;
199+ if (ucs_test_all_flags (perf_attr.field_mask ,
200+ UCT_PERF_ATTR_FIELD_BANDWIDTH |
201+ UCT_PERF_ATTR_FIELD_PATH_BANDWIDTH )) {
202+ perf_attr.path_bandwidth = perf_attr.bandwidth ;
203+ }
169204 }
170205
171206 /* We have to use singleton to mock C functions */
@@ -177,6 +212,7 @@ class mock_iface {
177212 std::map<std::string, iface_attr_func_t > m_iface_attrs_funcs;
178213 std::map<std::string, perf_attr_func_t > m_perf_attrs_funcs;
179214 std::string m_real_dev_name;
215+ uct_md_h m_real_md;
180216};
181217
182218mock_iface *mock_iface::m_self = nullptr ;
@@ -554,8 +590,9 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface {
554590 return {rkey, ucp_rkey_destroy};
555591 }
556592
557- void send_recv_rma_put (size_t size,
558- ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST )
593+ void send_recv_rma (size_t size, ucp_operation_id_t op_id,
594+ unsigned rkey_cfg_index = 1 ,
595+ ucs_memory_type_t mem_type = UCS_MEMORY_TYPE_HOST )
559596 {
560597 mem_buffer recv_buf (size, mem_type);
561598 recv_buf.pattern_fill (1 );
@@ -568,10 +605,27 @@ class test_ucp_proto_mock : public ucp_test, public mock_iface {
568605
569606 ucp_request_param_t req_param;
570607 req_param.op_attr_mask = 0 ;
571- auto sptr = ucp_put_nbx (sender ().ep (), send_buf.ptr (), size,
572- (uint64_t )recv_buf.ptr (), rkey, &req_param);
608+ ucs_status_ptr_t sptr;
609+ if (op_id == UCP_OP_ID_PUT ) {
610+ sptr = ucp_put_nbx (sender ().ep (), send_buf.ptr (), size,
611+ (uint64_t )recv_buf.ptr (), rkey, &req_param);
612+ } else if (op_id == UCP_OP_ID_GET ) {
613+ sptr = ucp_get_nbx (sender ().ep (), send_buf.ptr (), size,
614+ (uint64_t )recv_buf.ptr (), rkey, &req_param);
615+ } else {
616+ sptr = nullptr ;
617+ FAIL () << " Invalid operation ID: " << op_id;
618+ }
619+
573620 EXPECT_EQ (UCS_OK , request_wait (sptr));
574- recv_buf.pattern_check (2 );
621+
622+ if (op_id == UCP_OP_ID_PUT ) {
623+ recv_buf.pattern_check (2 );
624+ } else if (op_id == UCP_OP_ID_GET ) {
625+ send_buf.pattern_check (1 );
626+ }
627+
628+ EXPECT_EQ (rkey->cfg_index , rkey_cfg_index);
575629 }
576630};
577631
@@ -705,7 +759,7 @@ UCS_TEST_P(test_ucp_proto_mock_rcx, rndv_4_paths,
705759UCS_TEST_P (test_ucp_proto_mock_rcx, rma_put_2_lanes,
706760 " IB_NUM_PATHS?=1" , " MAX_RMA_RAILS=2" )
707761{
708- send_recv_rma_put (64 * UCS_KBYTE );
762+ send_recv_rma (64 * UCS_KBYTE , UCP_OP_ID_PUT );
709763
710764 ucp_proto_select_key_t key = any_key ();
711765 key.param .op_id_flags = UCP_OP_ID_PUT ;
@@ -1165,3 +1219,133 @@ UCS_TEST_P(test_ucp_proto_mock_rcx_twins_get_inline_0,
11651219
11661220UCP_INSTANTIATE_TEST_CASE_TLS (test_ucp_proto_mock_rcx_twins_get_inline_0, rcx,
11671221 " rc_x" )
1222+
1223+
1224+ #if HAVE_DECL_IBV_EVENT_PORT_SPEED_CHANGE
1225+
1226+ class test_ucp_proto_mock_rcx_speed_change : public test_ucp_proto_mock {
1227+ public:
1228+ test_ucp_proto_mock_rcx_speed_change ()
1229+ {
1230+ mock_transport (" rc_mlx5" );
1231+ }
1232+
1233+ virtual void init () override
1234+ {
1235+ m_port_speed[" mock_0:1" ] = 28e9 ;
1236+ add_mock_iface (" mock_0:1" , [](uct_iface_attr_t &iface_attr) {
1237+ iface_attr.cap .get .min_zcopy = 0 ;
1238+ iface_attr.bandwidth .shared = 28e9 ;
1239+ iface_attr.latency .c = 600e-9 ;
1240+ iface_attr.latency .m = 1e-9 ;
1241+ }, [this ](uct_perf_attr_t &perf_attr) {
1242+ perf_attr.bandwidth .shared = this ->m_port_speed [" mock_0:1" ];
1243+ perf_attr.path_bandwidth = perf_attr.bandwidth ;
1244+ });
1245+
1246+ m_port_speed[" mock_1:1" ] = 24e9 ;
1247+ add_mock_iface (" mock_1:1" , [](uct_iface_attr_t &iface_attr) {
1248+ iface_attr.cap .get .min_zcopy = 0 ;
1249+ iface_attr.bandwidth .shared = 24e9 ;
1250+ iface_attr.latency .c = 500e-9 ;
1251+ iface_attr.latency .m = 1e-9 ;
1252+ }, [this ](uct_perf_attr_t &perf_attr) {
1253+ perf_attr.bandwidth .shared = this ->m_port_speed [" mock_1:1" ];
1254+ perf_attr.path_bandwidth = perf_attr.bandwidth ;
1255+ });
1256+ test_ucp_proto_mock::init ();
1257+ }
1258+
1259+ void set_port_speed (const std::string &iface_name, double port_speed)
1260+ {
1261+ m_port_speed[iface_name] = port_speed;
1262+
1263+ ib_event (IBV_EVENT_PORT_SPEED_CHANGE , 1 );
1264+ while (progress ());
1265+ }
1266+
1267+ void test_port_speed (std::function<void (unsigned )> send,
1268+ ucp_operation_id_t op_id)
1269+ {
1270+ // One EP & rkey config created during connection establishment
1271+ ucp_worker_h worker = sender ().worker ();
1272+ EXPECT_EQ (worker->rkey_config_count , 1 );
1273+ EXPECT_EQ (worker->ep_config .length , 1 );
1274+
1275+ // New rkey config created during first operation
1276+ send (1 );
1277+ EXPECT_EQ (worker->rkey_config_count , 2 );
1278+ EXPECT_EQ (worker->ep_config .length , 1 );
1279+
1280+ // Existing rkey config is used during second operation
1281+ send (1 );
1282+ EXPECT_EQ (worker->rkey_config_count , 2 );
1283+ EXPECT_EQ (worker->ep_config .length , 1 );
1284+
1285+ ucp_proto_select_key_t key = any_key ();
1286+ key.param .op_id_flags = op_id;
1287+ key.param .op_attr = 0 ;
1288+
1289+ check_rkey_config (sender (), {
1290+ {0 , INF , " zero-copy" , " 47% on rc_mlx5/mock_1:1 and 53% on rc_mlx5/mock_0:1" },
1291+ }, key, 1 );
1292+
1293+ // Reduce port_speed of mock_0:1 by 50%, new EP & rkey configs are created
1294+ set_port_speed (" mock_0:1" , 14e9 );
1295+ send (2 );
1296+ EXPECT_EQ (worker->rkey_config_count , 3 );
1297+ EXPECT_EQ (worker->ep_config .length , 2 );
1298+
1299+ // Slightly change port_speed, so that quantized value remains the same
1300+ // This shouldn't affect EP or rkey config
1301+ set_port_speed (" mock_0:1" , 14.5e9 );
1302+ send (2 );
1303+ EXPECT_EQ (worker->rkey_config_count , 3 );
1304+ EXPECT_EQ (worker->ep_config .length , 2 );
1305+
1306+ check_rkey_config (sender (), {
1307+ {0 , INF , " zero-copy" , " 64% on rc_mlx5/mock_1:1 and 36% on rc_mlx5/mock_0:1" },
1308+ }, key, 2 );
1309+
1310+ // Reduce port_speed of mock_1:1 to be equal with mock_0:1,
1311+ // new EP & rkey configs are created
1312+ set_port_speed (" mock_1:1" , 14e9 );
1313+ send (3 );
1314+ EXPECT_EQ (worker->rkey_config_count , 4 );
1315+ EXPECT_EQ (worker->ep_config .length , 3 );
1316+
1317+ check_rkey_config (sender (), {
1318+ {0 , INF , " zero-copy" , " 50% on rc_mlx5/mock_1:1 and 50% on rc_mlx5/mock_0:1" },
1319+ }, key, 3 );
1320+
1321+ // Reset port_speeds to initial values, should switch to initial configs
1322+ set_port_speed (" mock_0:1" , 28e9 );
1323+ set_port_speed (" mock_1:1" , 24e9 );
1324+ send (1 );
1325+ EXPECT_EQ (worker->rkey_config_count , 4 );
1326+ EXPECT_EQ (worker->ep_config .length , 3 );
1327+ }
1328+
1329+ private:
1330+ std::map<std::string, double > m_port_speed;
1331+ };
1332+
1333+ UCS_TEST_P (test_ucp_proto_mock_rcx_speed_change, rma_put,
1334+ " IB_NUM_PATHS?=1" , " MAX_RMA_RAILS=2" , " ZCOPY_THRESH=0" )
1335+ {
1336+ test_port_speed ([this ](unsigned rkey_cfg_index) {
1337+ send_recv_rma (64 * UCS_KBYTE , UCP_OP_ID_PUT , rkey_cfg_index);
1338+ }, UCP_OP_ID_PUT );
1339+ }
1340+
1341+ UCS_TEST_P (test_ucp_proto_mock_rcx_speed_change, rma_get,
1342+ " IB_NUM_PATHS?=1" , " MAX_RMA_RAILS=2" , " ZCOPY_THRESH=0" )
1343+ {
1344+ test_port_speed ([this ](unsigned rkey_cfg_index) {
1345+ send_recv_rma (64 * UCS_KBYTE , UCP_OP_ID_GET , rkey_cfg_index);
1346+ }, UCP_OP_ID_GET );
1347+ }
1348+
1349+ UCP_INSTANTIATE_TEST_CASE_TLS (test_ucp_proto_mock_rcx_speed_change, rcx, " rc_x" )
1350+
1351+ #endif // HAVE_DECL_IBV_EVENT_PORT_SPEED_CHANGE
0 commit comments