/** * Copyright (C) Mellanox Technologies Ltd. 2001-2014. ALL RIGHTS RESERVED. * Copyright (C) UT-Battelle, LLC. 2014. ALL RIGHTS RESERVED. * See file LICENSE for terms. */ #include test_uct_ib::test_uct_ib() : m_e1(NULL), m_e2(NULL) { } void test_uct_ib::create_connected_entities() { m_e1 = uct_test::create_entity(0); m_e2 = uct_test::create_entity(0); m_entities.push_back(m_e1); m_entities.push_back(m_e2); m_e1->connect(0, *m_e2, 0); m_e2->connect(0, *m_e1, 0); } void test_uct_ib::init() { uct_test::init(); create_connected_entities(); test_uct_ib::m_ib_am_handler_counter = 0; } ucs_status_t test_uct_ib::ib_am_handler(void *arg, void *data, size_t length, unsigned flags) { recv_desc_t *my_desc = (recv_desc_t *) arg; uint64_t *test_ib_hdr = (uint64_t *) data; uint64_t *actual_data = (uint64_t *) test_ib_hdr + 1; unsigned data_length = length - sizeof(test_ib_hdr); my_desc->length = data_length; if (*test_ib_hdr == 0xbeef) { memcpy(my_desc + 1, actual_data , data_length); } ++test_uct_ib::m_ib_am_handler_counter; return UCS_OK; } void test_uct_ib::send_recv_short() { size_t start_am_counter = test_uct_ib::m_ib_am_handler_counter; uint64_t send_data = 0xdeadbeef; uint64_t test_ib_hdr = 0xbeef; recv_desc_t *recv_buffer; ucs_status_t status; check_caps_skip(UCT_IFACE_FLAG_AM_SHORT); recv_buffer = (recv_desc_t *) malloc(sizeof(*recv_buffer) + sizeof(uint64_t)); recv_buffer->length = 0; /* Initialize length to 0 */ /* set a callback for the uct to invoke for receiving the data */ uct_iface_set_am_handler(m_e2->iface(), 0, ib_am_handler, recv_buffer, 0); /* send the data */ status = uct_ep_am_short(m_e1->ep(0), 0, test_ib_hdr, &send_data, sizeof(send_data)); EXPECT_TRUE((status == UCS_OK) || (status == UCS_INPROGRESS)); flush(); wait_for_value(&test_uct_ib::m_ib_am_handler_counter, start_am_counter + 1, true); ASSERT_EQ(sizeof(send_data), recv_buffer->length); EXPECT_EQ(send_data, *(uint64_t*)(recv_buffer+1)); free(recv_buffer); } size_t test_uct_ib::m_ib_am_handler_counter = 0; class test_uct_ib_addr : public test_uct_ib { public: uct_ib_iface_config_t *ib_config() { return ucs_derived_of(m_iface_config, uct_ib_iface_config_t); } void test_address_pack(uint64_t subnet_prefix) { uct_ib_iface_t *iface = ucs_derived_of(m_e1->iface(), uct_ib_iface_t); static const uint16_t lid_in = 0x1ee7; union ibv_gid gid_in, gid_out; uct_ib_address_t *ib_addr; uint16_t lid_out; ib_addr = (uct_ib_address_t*)malloc(uct_ib_iface_address_size(iface)); gid_in.global.subnet_prefix = subnet_prefix; gid_in.global.interface_id = 0xdeadbeef; uct_ib_iface_address_pack(iface, &gid_in, lid_in, ib_addr); uct_ib_address_unpack(ib_addr, &lid_out, &gid_out); if (uct_ib_iface_is_roce(iface)) { EXPECT_TRUE(iface->config.force_global_addr); } else { EXPECT_EQ(lid_in, lid_out); } if (ib_config()->is_global) { EXPECT_EQ(gid_in.global.subnet_prefix, gid_out.global.subnet_prefix); EXPECT_EQ(gid_in.global.interface_id, gid_out.global.interface_id); } free(ib_addr); } void test_fill_ah_attr(uint64_t subnet_prefix) { uct_ib_iface_t *iface = ucs_derived_of(m_e1->iface(), uct_ib_iface_t); static const uint16_t lid = 0x1ee7; union ibv_gid gid; struct ibv_ah_attr ah_attr; ASSERT_EQ(iface->config.force_global_addr, ib_config()->is_global || uct_ib_iface_is_roce(iface)); gid.global.subnet_prefix = subnet_prefix ?: iface->gid.global.subnet_prefix; gid.global.interface_id = 0xdeadbeef; uct_ib_iface_fill_ah_attr_from_gid_lid(iface, lid, &gid, &ah_attr); if (uct_ib_iface_is_roce(iface)) { /* in case of roce, should be global */ EXPECT_TRUE(ah_attr.is_global); } else if (ib_config()->is_global) { /* in case of global address is forced - ah_attr should use GRH */ EXPECT_TRUE(ah_attr.is_global); } else if (iface->gid.global.subnet_prefix == gid.global.subnet_prefix) { /* in case of subnets are same - ah_attr depend from forced/nonforced GRH */ EXPECT_FALSE(ah_attr.is_global); } else if (iface->gid.global.subnet_prefix != gid.global.subnet_prefix) { /* in case of subnets are different - ah_attr should use GRH */ EXPECT_TRUE(ah_attr.is_global); } } }; UCS_TEST_P(test_uct_ib_addr, address_pack) { test_address_pack(UCT_IB_LINK_LOCAL_PREFIX); test_address_pack(UCT_IB_SITE_LOCAL_PREFIX | htobe64(0x7200)); test_address_pack(0xdeadfeedbeefa880ul); } UCS_TEST_P(test_uct_ib_addr, fill_ah_attr) { test_fill_ah_attr(UCT_IB_LINK_LOCAL_PREFIX); test_fill_ah_attr(UCT_IB_SITE_LOCAL_PREFIX | htobe64(0x7200)); test_fill_ah_attr(0xdeadfeedbeefa880ul); test_fill_ah_attr(0l); } UCS_TEST_P(test_uct_ib_addr, address_pack_global, "IB_IS_GLOBAL=y") { test_address_pack(UCT_IB_LINK_LOCAL_PREFIX); test_address_pack(UCT_IB_SITE_LOCAL_PREFIX | htobe64(0x7200)); test_address_pack(0xdeadfeedbeefa880ul); } UCS_TEST_P(test_uct_ib_addr, fill_ah_attr_global, "IB_IS_GLOBAL=y") { test_fill_ah_attr(UCT_IB_LINK_LOCAL_PREFIX); test_fill_ah_attr(UCT_IB_SITE_LOCAL_PREFIX | htobe64(0x7200)); test_fill_ah_attr(0xdeadfeedbeefa880ul); test_fill_ah_attr(0l); } UCT_INSTANTIATE_IB_TEST_CASE(test_uct_ib_addr); test_uct_ib_with_specific_port::test_uct_ib_with_specific_port() { m_ibctx = NULL; m_port = 0; m_dev_name = ""; memset(&m_port_attr, 0, sizeof(m_port_attr)); } void test_uct_ib_with_specific_port::init() { size_t colon_pos = GetParam()->dev_name.find(":"); std::string port_num_str; m_dev_name = GetParam()->dev_name.substr(0, colon_pos); port_num_str = GetParam()->dev_name.substr(colon_pos + 1); /* port number */ if (sscanf(port_num_str.c_str(), "%d", &m_port) != 1) { UCS_TEST_ABORT("Failed to get the port number on device: " << m_dev_name); } std::string abort_reason = "The requested device " + m_dev_name + " wasn't found in the device list."; struct ibv_device **device_list; int i, num_devices; /* get device list */ device_list = ibv_get_device_list(&num_devices); if (device_list == NULL) { abort_reason = "Failed to get the device list."; num_devices = 0; } /* search for the given device in the device list */ for (i = 0; i < num_devices; ++i) { if (strcmp(device_list[i]->name, m_dev_name.c_str())) { continue; } /* found this dev_name on the host - open it */ m_ibctx = ibv_open_device(device_list[i]); if (m_ibctx == NULL) { abort_reason = "Failed to open the device."; } break; } ibv_free_device_list(device_list); if (m_ibctx == NULL) { UCS_TEST_ABORT(abort_reason); } if (ibv_query_port(m_ibctx, m_port, &m_port_attr) != 0) { UCS_TEST_ABORT("Failed to query port " << m_port << "on device: " << m_dev_name); } try { check_port_attr(); } catch (...) { test_uct_ib_with_specific_port::cleanup(); throw; } } void test_uct_ib_with_specific_port::cleanup() { if (m_ibctx != NULL) { ibv_close_device(m_ibctx); m_ibctx = NULL; } } class test_uct_ib_lmc : public test_uct_ib_with_specific_port { public: void init() { test_uct_ib_with_specific_port::init(); test_uct_ib::init(); } void cleanup() { test_uct_ib::cleanup(); test_uct_ib_with_specific_port::cleanup(); } void check_port_attr() { /* check if a non zero lmc is set on the port */ if (!m_port_attr.lmc) { UCS_TEST_SKIP_R("lmc is set to zero on an IB port"); } } }; UCS_TEST_P(test_uct_ib_lmc, non_default_lmc, "IB_LID_PATH_BITS=1") { send_recv_short(); } UCT_INSTANTIATE_IB_TEST_CASE(test_uct_ib_lmc); class test_uct_ib_gid_idx : public test_uct_ib_with_specific_port { public: void init() { test_uct_ib_with_specific_port::init(); test_uct_ib::init(); } void cleanup() { test_uct_ib::cleanup(); test_uct_ib_with_specific_port::cleanup(); } void check_port_attr() { std::stringstream device_str; device_str << ibv_get_device_name(m_ibctx->device) << ":" << m_port; if (!IBV_PORT_IS_LINK_LAYER_ETHERNET(&m_port_attr)) { UCS_TEST_SKIP_R(device_str.str() + " is not Ethernet"); } union ibv_gid gid; uct_ib_md_config_t *md_config = ucs_derived_of(m_md_config, uct_ib_md_config_t); ucs::handle uct_md; uct_ib_md_t *ib_md; ucs_status_t status; uint8_t gid_index; UCS_TEST_CREATE_HANDLE(uct_md_h, uct_md, uct_ib_md_close, uct_ib_md_open, &uct_ib_component, ibv_get_device_name(m_ibctx->device), m_md_config); ib_md = ucs_derived_of(uct_md, uct_ib_md_t); status = uct_ib_device_select_gid_index(&ib_md->dev, m_port, md_config->ext.gid_index, &gid_index); ASSERT_UCS_OK(status); device_str << " gid index " << static_cast(gid_index); /* check the gid index */ if (ibv_query_gid(m_ibctx, m_port, gid_index, &gid) != 0) { UCS_TEST_ABORT("failed to query " + device_str.str()); } /* check if the gid is valid to use */ if (uct_ib_device_is_gid_raw_empty(gid.raw)) { UCS_TEST_SKIP_R(device_str.str() + " is empty"); } if (!uct_ib_device_test_roce_gid_index(&ib_md->dev, m_port, &gid, gid_index)) { UCS_TEST_SKIP_R("failed to create address handle on " + device_str.str()); } } }; UCS_TEST_P(test_uct_ib_gid_idx, non_default_gid_idx, "GID_INDEX=1") { send_recv_short(); } UCT_INSTANTIATE_IB_TEST_CASE(test_uct_ib_gid_idx); class test_uct_ib_utils : public ucs::test { }; UCS_TEST_F(test_uct_ib_utils, sec_to_qp_time) { double avg; uint8_t qp_val; // 0 sec qp_val = uct_ib_to_qp_fabric_time(0); EXPECT_EQ(1, qp_val); // the average time defined for the [0, 1st element] qp_val = uct_ib_to_qp_fabric_time(4.096 * pow(2, 0) / UCS_USEC_PER_SEC); EXPECT_EQ(1, qp_val); // the time defined for the 1st element qp_val = uct_ib_to_qp_fabric_time(4.096 * pow(2, 1) / UCS_USEC_PER_SEC); EXPECT_EQ(1, qp_val); for (uint8_t index = 2; index <= UCT_IB_FABRIC_TIME_MAX; index++) { uint8_t prev_index = index - 1; // the time defined for the (i)th element qp_val = uct_ib_to_qp_fabric_time(4.096 * pow(2, index) / UCS_USEC_PER_SEC); EXPECT_EQ(index % UCT_IB_FABRIC_TIME_MAX, qp_val); // avg = (the average time defined for the [(i - 1)th element, (i)th element]) avg = (4.096 * pow(2, prev_index) + 4.096 * pow(2, index)) * 0.5; qp_val = uct_ib_to_qp_fabric_time(avg / UCS_USEC_PER_SEC); EXPECT_EQ(index % UCT_IB_FABRIC_TIME_MAX, qp_val); // the average time defined for the [(i - 1)th element, avg] qp_val = uct_ib_to_qp_fabric_time((4.096 * pow(2, prev_index) + avg) * 0.5 / UCS_USEC_PER_SEC); EXPECT_EQ(prev_index, qp_val); // the average time defined for the [avg, (i)th element] qp_val = uct_ib_to_qp_fabric_time((avg + 4.096 * pow(2, index)) * 0.5 / UCS_USEC_PER_SEC); EXPECT_EQ(index % UCT_IB_FABRIC_TIME_MAX, qp_val); } } UCS_TEST_F(test_uct_ib_utils, sec_to_rnr_time) { double avg; uint8_t rnr_val; // 0 sec rnr_val = uct_ib_to_rnr_fabric_time(0); EXPECT_EQ(1, rnr_val); // the average time defined for the [0, 1st element] avg = uct_ib_qp_rnr_time_ms[1] * 0.5; rnr_val = uct_ib_to_rnr_fabric_time(avg / UCS_MSEC_PER_SEC); EXPECT_EQ(1, rnr_val); for (uint8_t index = 1; index < UCT_IB_FABRIC_TIME_MAX; index++) { uint8_t next_index = (index + 1) % UCT_IB_FABRIC_TIME_MAX; // the time defined for the (i)th element rnr_val = uct_ib_to_rnr_fabric_time(uct_ib_qp_rnr_time_ms[index] / UCS_MSEC_PER_SEC); EXPECT_EQ(index, rnr_val); // avg = (the average time defined for the [(i)th element, (i + 1)th element]) avg = (uct_ib_qp_rnr_time_ms[index] + uct_ib_qp_rnr_time_ms[next_index]) * 0.5; rnr_val = uct_ib_to_rnr_fabric_time(avg / UCS_MSEC_PER_SEC); EXPECT_EQ(next_index, rnr_val); // the average time defined for the [(i)th element, avg] rnr_val = uct_ib_to_rnr_fabric_time((uct_ib_qp_rnr_time_ms[index] + avg) * 0.5 / UCS_MSEC_PER_SEC); EXPECT_EQ(index, rnr_val); // the average time defined for the [avg, (i + 1)th element] rnr_val = uct_ib_to_rnr_fabric_time((avg + uct_ib_qp_rnr_time_ms[next_index]) * 0.5 / UCS_MSEC_PER_SEC); EXPECT_EQ(next_index, rnr_val); } // the time defined for the biggest value rnr_val = uct_ib_to_rnr_fabric_time(uct_ib_qp_rnr_time_ms[0] / UCS_MSEC_PER_SEC); EXPECT_EQ(0, rnr_val); // 1 sec rnr_val = uct_ib_to_rnr_fabric_time(1.); EXPECT_EQ(0, rnr_val); } class test_uct_event_ib : public test_uct_ib { public: test_uct_event_ib() { length = 8; wakeup_fd.revents = 0; wakeup_fd.events = POLLIN; wakeup_fd.fd = 0; test_ib_hdr = 0xbeef; m_buf1 = NULL; m_buf2 = NULL; } void init() { ucs_status_t status; test_uct_ib::init(); try { check_caps_skip(UCT_IFACE_FLAG_PUT_SHORT | UCT_IFACE_FLAG_CB_SYNC | UCT_IFACE_FLAG_EVENT_SEND_COMP | UCT_IFACE_FLAG_EVENT_RECV); } catch (...) { test_uct_ib::cleanup(); throw; } /* create receiver wakeup */ status = uct_iface_event_fd_get(m_e1->iface(), &wakeup_fd.fd); ASSERT_EQ(status, UCS_OK); EXPECT_EQ(0, poll(&wakeup_fd, 1, 0)); m_buf1 = new mapped_buffer(length, 0x1, *m_e1); m_buf2 = new mapped_buffer(length, 0x2, *m_e2); /* set a callback for the uct to invoke for receiving the data */ uct_iface_set_am_handler(m_e1->iface(), 0, ib_am_handler, m_buf1->ptr(), 0); test_uct_event_ib::bcopy_pack_count = 0; } static size_t pack_cb(void *dest, void *arg) { const mapped_buffer *buf = (const mapped_buffer *)arg; memcpy(dest, buf->ptr(), buf->length()); ++test_uct_event_ib::bcopy_pack_count; return buf->length(); } /* Use put_bcopy here to provide send_cq entry */ void send_msg_e1_e2(size_t count = 1) { for (size_t i = 0; i < count; ++i) { ssize_t status = uct_ep_put_bcopy(m_e1->ep(0), pack_cb, (void *)m_buf1, m_buf2->addr(), m_buf2->rkey()); if (status < 0) { ASSERT_UCS_OK((ucs_status_t)status); } } } void send_msg_e2_e1(size_t count = 1) { for (size_t i = 0; i < count; ++i) { ucs_status_t status = uct_ep_am_short(m_e2->ep(0), 0, test_ib_hdr, m_buf2->ptr(), m_buf2->length()); ASSERT_UCS_OK(status); } } void check_send_cq(uct_iface_t *iface, size_t val) { uct_ib_iface_t *ib_iface = ucs_derived_of(iface, uct_ib_iface_t); struct ibv_cq *send_cq = ib_iface->cq[UCT_IB_DIR_TX]; if (val != send_cq->comp_events_completed) { uint32_t completed_evt = send_cq->comp_events_completed; /* need this call to acknowledge the completion to prevent iface dtor hung*/ ibv_ack_cq_events(ib_iface->cq[UCT_IB_DIR_TX], 1); UCS_TEST_ABORT("send_cq->comp_events_completed have to be 1 but the value " << completed_evt); } } void check_recv_cq(uct_iface_t *iface, size_t val) { uct_ib_iface_t *ib_iface = ucs_derived_of(iface, uct_ib_iface_t); struct ibv_cq *recv_cq = ib_iface->cq[UCT_IB_DIR_RX]; if (val != recv_cq->comp_events_completed) { uint32_t completed_evt = recv_cq->comp_events_completed; /* need this call to acknowledge the completion to prevent iface dtor hung*/ ibv_ack_cq_events(ib_iface->cq[UCT_IB_DIR_RX], 1); UCS_TEST_ABORT("recv_cq->comp_events_completed have to be 1 but the value " << completed_evt); } } void cleanup() { delete(m_buf1); delete(m_buf2); test_uct_ib::cleanup(); } protected: static const unsigned EVENTS = UCT_EVENT_RECV | UCT_EVENT_SEND_COMP; struct pollfd wakeup_fd; size_t length; uint64_t test_ib_hdr; mapped_buffer *m_buf1, *m_buf2; static size_t bcopy_pack_count; }; size_t test_uct_event_ib::bcopy_pack_count = 0; UCS_TEST_P(test_uct_event_ib, tx_cq) { ucs_status_t status; status = uct_iface_event_arm(m_e1->iface(), EVENTS); ASSERT_EQ(status, UCS_OK); /* check initial state of the fd and [send|recv]_cq */ EXPECT_EQ(0, poll(&wakeup_fd, 1, 0)); check_send_cq(m_e1->iface(), 0); check_recv_cq(m_e1->iface(), 0); /* send the data */ send_msg_e1_e2(); /* make sure the file descriptor is signaled once */ ASSERT_EQ(1, poll(&wakeup_fd, 1, 1000*ucs::test_time_multiplier())); status = uct_iface_event_arm(m_e1->iface(), EVENTS); ASSERT_EQ(status, UCS_ERR_BUSY); /* make sure [send|recv]_cq handled properly */ check_send_cq(m_e1->iface(), 1); check_recv_cq(m_e1->iface(), 0); m_e1->flush(); } UCS_TEST_P(test_uct_event_ib, txrx_cq) { const size_t msg_count = 1; ucs_status_t status; status = uct_iface_event_arm(m_e1->iface(), EVENTS); ASSERT_EQ(UCS_OK, status); /* check initial state of the fd and [send|recv]_cq */ EXPECT_EQ(0, poll(&wakeup_fd, 1, 0)); check_send_cq(m_e1->iface(), 0); check_recv_cq(m_e1->iface(), 0); /* send the data */ send_msg_e1_e2(msg_count); send_msg_e2_e1(msg_count); twait(150); /* Let completion to be generated */ /* Make sure all messages delivered */ while ((test_uct_ib::m_ib_am_handler_counter < msg_count) || (test_uct_event_ib::bcopy_pack_count < msg_count)) { progress(); } /* make sure the file descriptor is signaled */ ASSERT_EQ(1, poll(&wakeup_fd, 1, 1000*ucs::test_time_multiplier())); /* Acknowledge all the requests */ short_progress_loop(); status = uct_iface_event_arm(m_e1->iface(), EVENTS); ASSERT_EQ(UCS_ERR_BUSY, status); /* make sure [send|recv]_cq handled properly */ check_send_cq(m_e1->iface(), 1); check_recv_cq(m_e1->iface(), 1); m_e1->flush(); m_e2->flush(); } UCT_INSTANTIATE_IB_TEST_CASE(test_uct_event_ib);