| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include <cstring> |
| #include <limits> |
| #include <gtest/gtest.h> |
| #include <gflags/gflags.h> |
| |
| #if BRPC_WITH_URMA |
| #include "butil/atomicops.h" |
| #include "butil/sys_byteorder.h" |
| #include "urma_api.h" |
| #include "brpc/urma/urma_handshake.h" |
| #include "brpc/urma/urma_handshake.pb.h" |
| #include "brpc/urma/urma_helper.h" |
| #include "urma_types.h" |
| |
| using namespace brpc; |
| |
| namespace brpc { |
| namespace urma { |
| |
| DECLARE_int32(urma_client_handshake_version); |
| extern bool g_skip_urma_init; |
| extern butil::atomic<bool> g_urma_available; |
| |
| } // namespace urma |
| } // namespace brpc |
| |
| // --------------------------------------------------------------------------- |
| // v2 binary HelloMessage: serialize + deserialize round-trips. |
| // --------------------------------------------------------------------------- |
| TEST(UrmaHandshakeTest, v2_serialize_deserialize_roundtrip) { |
| urma::v2_wire::HelloMessage m; |
| m.msg_len = urma::v2_wire::HELLO_PACKET_LEN; |
| m.hello_ver = urma::v2_wire::HELLO_V2_VERSION; |
| m.impl_ver = urma::v2_wire::IMPL_V2_VERSION; |
| m.buffer_size = 8192; |
| m.recv_buffer_cnt = 127; |
| m.jetty_id = 0x12345678; |
| for (int i = 0; i < 16; ++i) { |
| m.eid[i] = static_cast<uint8_t>(i + 1); |
| } |
| m.uasid = 0xdeadbeef; |
| m.tp_type = 1; // URMA_CTP |
| for (int i = 0; i < 16; ++i) { |
| m.seg_eid[i] = static_cast<uint8_t>(16 - i); |
| } |
| m.seg_uasid = 0xcafebabe; |
| m.seg_va = 0x1122334455667788ULL; |
| m.seg_len = 1ULL << 20; |
| m.seg_token_id = 0x42424242; |
| |
| uint8_t buf[urma::v2_wire::HELLO_BODY_LEN]; |
| m.Serialize(buf); |
| |
| urma::v2_wire::HelloMessage m2; |
| m2.Deserialize(buf); |
| EXPECT_EQ(m.msg_len, m2.msg_len); |
| EXPECT_EQ(m.hello_ver, m2.hello_ver); |
| EXPECT_EQ(m.impl_ver, m2.impl_ver); |
| EXPECT_EQ(m.buffer_size, m2.buffer_size); |
| EXPECT_EQ(m.recv_buffer_cnt, m2.recv_buffer_cnt); |
| EXPECT_EQ(m.jetty_id, m2.jetty_id); |
| EXPECT_EQ(0, memcmp(m.eid, m2.eid, 16)); |
| EXPECT_EQ(m.uasid, m2.uasid); |
| EXPECT_EQ(m.tp_type, m2.tp_type); |
| EXPECT_EQ(0, memcmp(m.seg_eid, m2.seg_eid, 16)); |
| EXPECT_EQ(m.seg_uasid, m2.seg_uasid); |
| EXPECT_EQ(m.seg_va, m2.seg_va); |
| EXPECT_EQ(m.seg_len, m2.seg_len); |
| EXPECT_EQ(m.seg_token_id, m2.seg_token_id); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // v2 packet on the wire: "URMA" magic + body. |
| // --------------------------------------------------------------------------- |
| TEST(UrmaHandshakeTest, v2_packet_magic_is_urma) { |
| EXPECT_EQ(4u, urma::v2_wire::MAGIC_STR_LEN); |
| char magic[4] = {'U', 'R', 'M', 'A'}; |
| EXPECT_EQ(0, memcmp(magic, "URMA", 4)); |
| EXPECT_EQ(4u + 82u, urma::v2_wire::HELLO_PACKET_LEN); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // v3 protobuf UrmaHello: serialize + parse round-trips. |
| // --------------------------------------------------------------------------- |
| TEST(UrmaHandshakeTest, v3_protobuf_roundtrip) { |
| urma::UrmaHello msg; |
| msg.set_buffer_size(8192); |
| msg.set_recv_buffer_cnt(127); |
| msg.set_jetty_id(0x12345678); |
| uint8_t eid[16]; |
| for (int i = 0; i < 16; ++i) { |
| eid[i] = static_cast<uint8_t>(i + 1); |
| } |
| msg.set_eid(eid, 16); |
| msg.set_uasid(0xdeadbeef); |
| msg.set_tp_type(1); |
| uint8_t seg_eid[16]; |
| for (int i = 0; i < 16; ++i) { |
| seg_eid[i] = static_cast<uint8_t>(16 - i); |
| } |
| msg.set_seg_eid(seg_eid, 16); |
| msg.set_seg_uasid(0xcafebabe); |
| msg.set_seg_va(0x1122334455667788ULL); |
| msg.set_seg_len(1ULL << 20); |
| msg.set_seg_token_id(0x42424242); |
| |
| std::string body; |
| ASSERT_TRUE(msg.SerializeToString(&body)); |
| urma::UrmaHello msg2; |
| ASSERT_TRUE(msg2.ParseFromString(body)); |
| EXPECT_EQ(msg.buffer_size(), msg2.buffer_size()); |
| EXPECT_EQ(msg.recv_buffer_cnt(), msg2.recv_buffer_cnt()); |
| EXPECT_EQ(msg.jetty_id(), msg2.jetty_id()); |
| EXPECT_EQ(16, msg2.eid().size()); |
| EXPECT_EQ(0, memcmp(msg.eid().data(), msg2.eid().data(), 16)); |
| EXPECT_EQ(msg.uasid(), msg2.uasid()); |
| EXPECT_EQ(msg.tp_type(), msg2.tp_type()); |
| EXPECT_EQ(16, msg2.seg_eid().size()); |
| EXPECT_EQ(msg.seg_uasid(), msg2.seg_uasid()); |
| EXPECT_EQ(msg.seg_va(), msg2.seg_va()); |
| EXPECT_EQ(msg.seg_len(), msg2.seg_len()); |
| EXPECT_EQ(msg.seg_token_id(), msg2.seg_token_id()); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // CreateServerHandshakeByMagic dispatches on the magic bytes. |
| // --------------------------------------------------------------------------- |
| TEST(UrmaHandshakeTest, server_handshake_factory_dispatches_on_magic) { |
| // We cannot fully exercise the server handshake without a real socket + |
| // endpoint, but we can verify the factory returns the right protocol |
| // version for each magic, and nullptr for an unknown magic. |
| uint8_t magic_v2[4] = {'U', 'R', 'M', 'A'}; |
| uint8_t magic_v3[4] = {'U', 'R', 'M', '3'}; |
| uint8_t magic_bad[4] = {'P', 'R', 'P', 'C'}; |
| |
| // v2 magic -> protocol version 2 |
| urma::UrmaHandshake* hs2 = |
| urma::CreateServerHandshakeByMagic(nullptr, magic_v2); |
| // Note: the factory dereferences the endpoint only inside SendLocalHello / |
| // ReceiveAndParseRemoteHello; passing nullptr is safe for the version query. |
| // (We delete immediately to avoid touching the endpoint.) |
| if (hs2) { |
| EXPECT_EQ(2, hs2->ProtocolVersion()); |
| delete hs2; |
| } |
| // v3 magic -> protocol version 3 |
| urma::UrmaHandshake* hs3 = |
| urma::CreateServerHandshakeByMagic(nullptr, magic_v3); |
| if (hs3) { |
| EXPECT_EQ(3, hs3->ProtocolVersion()); |
| delete hs3; |
| } |
| // unknown magic -> nullptr (caller falls back to TCP) |
| urma::UrmaHandshake* hsb = |
| urma::CreateServerHandshakeByMagic(nullptr, magic_bad); |
| EXPECT_EQ(nullptr, hsb); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // CreateClientHandshake picks the version from the gflag. |
| // --------------------------------------------------------------------------- |
| TEST(UrmaHandshakeTest, client_handshake_factory_respects_flag) { |
| GFLAGS_NAMESPACE::FlagSaver flag_saver; |
| |
| urma::FLAGS_urma_client_handshake_version = 2; |
| urma::UrmaHandshake* hs2 = urma::CreateClientHandshake(nullptr); |
| if (hs2) { |
| EXPECT_EQ(2, hs2->ProtocolVersion()); |
| delete hs2; |
| } |
| |
| urma::FLAGS_urma_client_handshake_version = 3; |
| urma::UrmaHandshake* hs3 = urma::CreateClientHandshake(nullptr); |
| if (hs3) { |
| EXPECT_EQ(3, hs3->ProtocolVersion()); |
| delete hs3; |
| } |
| } |
| |
| // --------------------------------------------------------------------------- |
| // 4-byte ACK: HELLO_ACK_URMA_OK bit. |
| // --------------------------------------------------------------------------- |
| TEST(UrmaHandshakeTest, ack_bit_is_urma_ok) { |
| // The ACK is a 4-byte big-endian flags word; bit 0 means "I want URMA". |
| // Verify the round-trip: host -> net -> host preserves the bit. |
| uint32_t flags = 0x1; // HELLO_ACK_URMA_OK |
| uint32_t flags_be = butil::HostToNet32(flags); |
| uint32_t flags_back = butil::NetToHost32(flags_be); |
| EXPECT_EQ(flags, flags_back); |
| EXPECT_NE(0u, flags_back & 0x1); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // ParsedHello field layout: covers the flattened segment (seg_* fields). |
| // --------------------------------------------------------------------------- |
| TEST(UrmaHandshakeTest, parsed_hello_segment_fields) { |
| urma::ParsedHello p{}; |
| p.buffer_size = 8192; |
| p.recv_buffer_cnt = 127; |
| p.jetty_id = 42; |
| p.tp_type = 1; |
| p.seg_va = 0x1000; |
| p.seg_len = 0x100000; |
| p.seg_token_id = 7; |
| EXPECT_EQ(8192u, p.buffer_size); |
| EXPECT_EQ(127u, p.recv_buffer_cnt); |
| EXPECT_EQ(42u, p.jetty_id); |
| EXPECT_EQ(1u, p.tp_type); |
| EXPECT_EQ(0x1000u, p.seg_va); |
| EXPECT_EQ(0x100000u, p.seg_len); |
| EXPECT_EQ(7u, p.seg_token_id); |
| } |
| |
| TEST(UrmaHandshakeTest, rejects_invalid_resource_and_window_values) { |
| urma::ParsedHello hello; |
| hello.buffer_size = 8192; |
| hello.recv_buffer_cnt = 127; |
| hello.jetty_id = 1; |
| hello.tp_type = URMA_CTP; |
| hello.seg_va = 0x1000; |
| hello.seg_len = 8192; |
| EXPECT_TRUE(urma::ValidHello(hello)); |
| |
| hello.recv_buffer_cnt = 2; |
| EXPECT_FALSE(urma::ValidHello(hello)); |
| hello.recv_buffer_cnt = 127; |
| hello.seg_len = 0; |
| EXPECT_FALSE(urma::ValidHello(hello)); |
| hello.seg_len = 8192; |
| hello.jetty_id = 0; |
| EXPECT_FALSE(urma::ValidHello(hello)); |
| |
| hello.jetty_id = 1; |
| hello.seg_va = std::numeric_limits<uint64_t>::max() - 7; |
| hello.seg_len = 8; |
| EXPECT_FALSE(urma::ValidHello(hello)); |
| } |
| |
| TEST(UrmaHelperTest, selects_priority_matching_transport_path_type) { |
| urma_device_attr_t attr{}; |
| attr.dev_cap.priority_info[3].tp_type.bs.rtp = 1; |
| attr.dev_cap.priority_info[6].tp_type.bs.ctp = 1; |
| |
| EXPECT_EQ(3, urma::FindUrmaPriorityForTpType(attr, URMA_RTP)); |
| EXPECT_EQ(6, urma::FindUrmaPriorityForTpType(attr, URMA_CTP)); |
| EXPECT_EQ(-1, urma::FindUrmaPriorityForTpType(attr, URMA_UTP)); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // SupportedByUrma: only baidu_std. |
| // --------------------------------------------------------------------------- |
| TEST(UrmaHandshakeTest, supported_by_urma_protocol_allowlist) { |
| EXPECT_TRUE(urma::SupportedByUrma("baidu_std")); |
| EXPECT_FALSE(urma::SupportedByUrma("http")); |
| EXPECT_FALSE(urma::SupportedByUrma("hulu_pbrpc")); |
| EXPECT_FALSE(urma::SupportedByUrma("nshead")); |
| } |
| |
| // --------------------------------------------------------------------------- |
| // URMA mock smoke test: drives urma_init / device enumeration / context / |
| // jetty / post / poll. These tests rely on mock semantics and are skipped |
| // when the test binary links a real liburma provider. |
| // --------------------------------------------------------------------------- |
| class UrmaMockTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| urma::g_skip_urma_init = false; |
| |
| urma_init_attr_t init_attr{}; |
| const urma_status_t status = urma_init(&init_attr); |
| ASSERT_TRUE(status == URMA_SUCCESS || status == URMA_EEXIST); |
| _owns_urma_init = status == URMA_SUCCESS; |
| |
| int num_devices = 0; |
| urma_device_t** devices = urma_get_device_list(&num_devices); |
| ASSERT_NE(nullptr, devices); |
| ASSERT_GT(num_devices, 0); |
| const bool using_mock = |
| strcmp(devices[0]->name, "mock_urma_device") == 0; |
| urma_free_device_list(devices); |
| if (!using_mock) { |
| if (_owns_urma_init) { |
| EXPECT_EQ(URMA_SUCCESS, urma_uninit()); |
| _owns_urma_init = false; |
| } |
| GTEST_SKIP() << "UrmaMockTest requires the URMA link-time mock"; |
| } |
| } |
| |
| void TearDown() override { |
| if (_owns_urma_init) { |
| EXPECT_EQ(URMA_SUCCESS, urma_uninit()); |
| } |
| urma::g_skip_urma_init = true; |
| urma::g_urma_available.store(true, butil::memory_order_relaxed); |
| } |
| |
| private: |
| bool _owns_urma_init{false}; |
| }; |
| |
| TEST_F(UrmaMockTest, init_and_enumerate_device) { |
| urma_init_attr_t init_attr{}; |
| EXPECT_EQ(URMA_EEXIST, urma_init(&init_attr)); |
| |
| int num_devices = 0; |
| urma_device_t** devices = urma_get_device_list(&num_devices); |
| ASSERT_NE(nullptr, devices); |
| ASSERT_GE(num_devices, 1); |
| EXPECT_STREQ("mock_urma_device", devices[0]->name); |
| urma_free_device_list(devices); |
| } |
| |
| TEST_F(UrmaMockTest, create_context_and_query_device) { |
| int num_devices = 0; |
| urma_device_t** devices = urma_get_device_list(&num_devices); |
| ASSERT_NE(nullptr, devices); |
| ASSERT_GE(num_devices, 1); |
| |
| uint32_t eid_cnt = 0; |
| urma_eid_info_t* eids = urma_get_eid_list(devices[0], &eid_cnt); |
| ASSERT_NE(nullptr, eids); |
| ASSERT_GE(eid_cnt, 1u); |
| urma_free_eid_list(eids); |
| |
| urma_context_t* ctx = urma_create_context(devices[0], 0); |
| ASSERT_NE(nullptr, ctx); |
| |
| urma_device_attr_t attr{}; |
| ASSERT_EQ(URMA_SUCCESS, urma_query_device(devices[0], &attr)); |
| EXPECT_GE(attr.dev_cap.max_jfc, 1u); |
| EXPECT_GE(attr.dev_cap.max_jetty, 1u); |
| |
| EXPECT_EQ(URMA_SUCCESS, urma_delete_context(ctx)); |
| urma_free_device_list(devices); |
| } |
| |
| TEST_F(UrmaMockTest, rejects_send_without_target_jetty) { |
| // A SEND without a target jetty is invalid. Keeping the mock strict here |
| // prevents tests from relying on input that a real provider cannot post. |
| int num_devices = 0; |
| urma_device_t** devices = urma_get_device_list(&num_devices); |
| ASSERT_NE(nullptr, devices); |
| ASSERT_GE(num_devices, 1); |
| uint32_t eid_cnt = 0; |
| urma_eid_info_t* eids = urma_get_eid_list(devices[0], &eid_cnt); |
| urma_free_eid_list(eids); |
| urma_context_t* ctx = urma_create_context(devices[0], 0); |
| ASSERT_NE(nullptr, ctx); |
| |
| urma_jfce_t* jfce = urma_create_jfce(ctx); |
| ASSERT_NE(nullptr, jfce); |
| urma_jfc_cfg_t jfc_cfg{}; |
| jfc_cfg.depth = 16; |
| jfc_cfg.jfce = jfce; |
| urma_jfc_t* jfc = urma_create_jfc(ctx, &jfc_cfg); |
| ASSERT_NE(nullptr, jfc); |
| |
| urma_jfr_cfg_t jfr_cfg{}; |
| jfr_cfg.depth = 16; |
| jfr_cfg.trans_mode = URMA_TM_RM; |
| jfr_cfg.max_sge = 1; |
| jfr_cfg.min_rnr_timer = URMA_TYPICAL_MIN_RNR_TIMER; |
| jfr_cfg.jfc = jfc; |
| urma_jfr_t* jfr = urma_create_jfr(ctx, &jfr_cfg); |
| ASSERT_NE(nullptr, jfr); |
| |
| urma_jetty_cfg_t jetty_cfg{}; |
| jetty_cfg.flag.bs.share_jfr = 1; |
| jetty_cfg.jfs_cfg.depth = 16; |
| jetty_cfg.jfs_cfg.trans_mode = URMA_TM_RM; |
| jetty_cfg.jfs_cfg.priority = URMA_MAX_PRIORITY; |
| jetty_cfg.jfs_cfg.max_sge = 1; |
| jetty_cfg.jfs_cfg.rnr_retry = URMA_TYPICAL_RNR_RETRY; |
| jetty_cfg.jfs_cfg.err_timeout = URMA_TYPICAL_ERR_TIMEOUT; |
| jetty_cfg.jfs_cfg.jfc = jfc; |
| jetty_cfg.shared.jfr = jfr; |
| jetty_cfg.shared.jfc = jfc; |
| urma_jetty_t* jetty = urma_create_jetty(ctx, &jetty_cfg); |
| ASSERT_NE(nullptr, jetty); |
| |
| urma_jfs_wr_t wr{}; |
| memset(&wr, 0, sizeof(wr)); |
| wr.opcode = URMA_OPC_SEND; |
| wr.flag.bs.complete_enable = 1; |
| wr.user_ctx = 0xABCD; |
| wr.next = nullptr; |
| urma_jfs_wr_t* bad = nullptr; |
| EXPECT_EQ(URMA_EINVAL, urma_post_jetty_send_wr(jetty, &wr, &bad)); |
| EXPECT_EQ(&wr, bad); |
| |
| urma_cr_t crs[4]; |
| EXPECT_EQ(0, urma_poll_jfc(jfc, 4, crs)); |
| |
| urma_delete_jetty(jetty); |
| urma_delete_jfr(jfr); |
| urma_delete_jfc(jfc); |
| urma_delete_jfce(jfce); |
| urma_delete_context(ctx); |
| urma_free_device_list(devices); |
| } |
| |
| TEST_F(UrmaMockTest, |
| paired_send_is_bidirectional_and_separates_immediate_credit) { |
| int num_devices = 0; |
| urma_device_t** devices = urma_get_device_list(&num_devices); |
| ASSERT_NE(nullptr, devices); |
| ASSERT_GT(num_devices, 0); |
| urma_context_t* ctx = urma_create_context(devices[0], 0); |
| ASSERT_NE(nullptr, ctx); |
| |
| urma_jfce_t* sender_jfce = urma_create_jfce(ctx); |
| urma_jfce_t* receiver_jfce = urma_create_jfce(ctx); |
| ASSERT_NE(nullptr, sender_jfce); |
| ASSERT_NE(nullptr, receiver_jfce); |
| urma_jfc_cfg_t sender_jfc_cfg{}; |
| sender_jfc_cfg.depth = 8; |
| sender_jfc_cfg.jfce = sender_jfce; |
| urma_jfc_t* sender_jfc = urma_create_jfc(ctx, &sender_jfc_cfg); |
| ASSERT_NE(nullptr, sender_jfc); |
| urma_jfc_cfg_t receiver_jfc_cfg{}; |
| receiver_jfc_cfg.depth = 8; |
| receiver_jfc_cfg.jfce = receiver_jfce; |
| urma_jfc_t* receiver_jfc = urma_create_jfc(ctx, &receiver_jfc_cfg); |
| ASSERT_NE(nullptr, receiver_jfc); |
| |
| urma_jfr_cfg_t sender_jfr_cfg{}; |
| sender_jfr_cfg.depth = 4; |
| sender_jfr_cfg.trans_mode = URMA_TM_RM; |
| sender_jfr_cfg.max_sge = 1; |
| sender_jfr_cfg.jfc = sender_jfc; |
| urma_jfr_t* sender_jfr = urma_create_jfr(ctx, &sender_jfr_cfg); |
| ASSERT_NE(nullptr, sender_jfr); |
| urma_jfr_cfg_t receiver_jfr_cfg = sender_jfr_cfg; |
| receiver_jfr_cfg.jfc = receiver_jfc; |
| urma_jfr_t* receiver_jfr = urma_create_jfr(ctx, &receiver_jfr_cfg); |
| ASSERT_NE(nullptr, receiver_jfr); |
| |
| auto create_jetty = [&](urma_jfc_t* jfc, urma_jfr_t* jfr) { |
| urma_jetty_cfg_t cfg{}; |
| cfg.flag.bs.share_jfr = 1; |
| cfg.jfs_cfg.depth = 4; |
| cfg.jfs_cfg.trans_mode = URMA_TM_RM; |
| cfg.jfs_cfg.max_sge = 1; |
| cfg.jfs_cfg.jfc = jfc; |
| cfg.shared.jfr = jfr; |
| cfg.shared.jfc = jfc; |
| return urma_create_jetty(ctx, &cfg); |
| }; |
| urma_jetty_t* sender = create_jetty(sender_jfc, sender_jfr); |
| urma_jetty_t* receiver = create_jetty(receiver_jfc, receiver_jfr); |
| ASSERT_NE(nullptr, sender); |
| ASSERT_NE(nullptr, receiver); |
| |
| urma_rjetty_t remote{}; |
| remote.jetty_id = receiver->jetty_id; |
| remote.trans_mode = URMA_TM_RM; |
| remote.type = URMA_JETTY; |
| remote.tp_type = URMA_CTP; |
| urma_token_t token{}; |
| urma_target_jetty_t* target = |
| urma_import_jetty(ctx, &remote, &token); |
| ASSERT_NE(nullptr, target); |
| |
| char recv_buf[64]{}; |
| urma_sge_t recv_sge{ |
| reinterpret_cast<uint64_t>(recv_buf), sizeof(recv_buf), nullptr, |
| nullptr}; |
| urma_sg_t recv_sg{&recv_sge, 1}; |
| urma_jfr_wr_t recv_wr{recv_sg, 99, nullptr}; |
| char credit_recv_buf[1]{}; |
| urma_sge_t credit_recv_sge{ |
| reinterpret_cast<uint64_t>(credit_recv_buf), |
| sizeof(credit_recv_buf), nullptr, nullptr}; |
| urma_sg_t credit_recv_sg{&credit_recv_sge, 1}; |
| urma_jfr_wr_t credit_recv_wr{credit_recv_sg, 100, nullptr}; |
| recv_wr.next = &credit_recv_wr; |
| urma_jfr_wr_t* bad_recv = nullptr; |
| ASSERT_EQ(URMA_SUCCESS, |
| urma_post_jfr_wr(receiver_jfr, &recv_wr, &bad_recv)); |
| |
| const char payload[] = "urma-payload"; |
| urma_sge_t send_sge{ |
| reinterpret_cast<uint64_t>(payload), sizeof(payload), nullptr, |
| nullptr}; |
| urma_sg_t send_sg{&send_sge, 1}; |
| urma_jfs_wr_t send_wr{}; |
| send_wr.opcode = URMA_OPC_SEND; |
| send_wr.flag.bs.complete_enable = 1; |
| send_wr.tjetty = target; |
| send_wr.user_ctx = 7; |
| send_wr.send.src = send_sg; |
| urma_jfs_wr_t credit_wr{}; |
| credit_wr.opcode = URMA_OPC_SEND_IMM; |
| credit_wr.flag.bs.complete_enable = 1; |
| credit_wr.tjetty = target; |
| credit_wr.user_ctx = 8; |
| credit_wr.send.imm_data = 13; |
| send_wr.next = &credit_wr; |
| urma_jfs_wr_t* bad_send = nullptr; |
| ASSERT_EQ(URMA_SUCCESS, |
| urma_post_jetty_send_wr(sender, &send_wr, &bad_send)); |
| |
| urma_cr_t sender_cr[2]{}; |
| ASSERT_EQ(2, urma_poll_jfc(sender_jfc, 2, sender_cr)); |
| EXPECT_EQ(0, sender_cr[0].flag.bs.s_r); |
| EXPECT_EQ(7u, sender_cr[0].user_ctx); |
| EXPECT_EQ(0, sender_cr[1].flag.bs.s_r); |
| EXPECT_EQ(8u, sender_cr[1].user_ctx); |
| |
| urma_cr_t receiver_cr[2]{}; |
| ASSERT_EQ(2, urma_poll_jfc(receiver_jfc, 2, receiver_cr)); |
| EXPECT_EQ(1, receiver_cr[0].flag.bs.s_r); |
| EXPECT_EQ(URMA_CR_OPC_SEND, receiver_cr[0].opcode); |
| EXPECT_EQ(0u, receiver_cr[0].imm_data); |
| EXPECT_EQ(sizeof(payload), receiver_cr[0].completion_len); |
| EXPECT_EQ(0, memcmp(payload, recv_buf, sizeof(payload))); |
| EXPECT_EQ(1, receiver_cr[1].flag.bs.s_r); |
| EXPECT_EQ(URMA_CR_OPC_SEND_WITH_IMM, receiver_cr[1].opcode); |
| EXPECT_EQ(13u, receiver_cr[1].imm_data); |
| EXPECT_EQ(0u, receiver_cr[1].completion_len); |
| |
| // Exercise the response direction as well. Production posts all receive |
| // WRs through the shared JFR. |
| urma_rjetty_t sender_remote = remote; |
| sender_remote.jetty_id = sender->jetty_id; |
| urma_target_jetty_t* sender_target = |
| urma_import_jetty(ctx, &sender_remote, &token); |
| ASSERT_NE(nullptr, sender_target); |
| char response_buf[64]{}; |
| urma_sge_t response_recv_sge{ |
| reinterpret_cast<uint64_t>(response_buf), sizeof(response_buf), |
| nullptr, nullptr}; |
| urma_sg_t response_recv_sg{&response_recv_sge, 1}; |
| urma_jfr_wr_t response_recv_wr{response_recv_sg, 101, nullptr}; |
| ASSERT_EQ(URMA_SUCCESS, |
| urma_post_jfr_wr(sender_jfr, &response_recv_wr, &bad_recv)); |
| |
| const char response[] = "urma-response"; |
| urma_sge_t response_send_sge{ |
| reinterpret_cast<uint64_t>(response), sizeof(response), nullptr, |
| nullptr}; |
| urma_sg_t response_send_sg{&response_send_sge, 1}; |
| urma_jfs_wr_t response_send_wr{}; |
| response_send_wr.opcode = URMA_OPC_SEND; |
| response_send_wr.flag.bs.complete_enable = 1; |
| response_send_wr.tjetty = sender_target; |
| response_send_wr.user_ctx = 9; |
| response_send_wr.send.src = response_send_sg; |
| ASSERT_EQ(URMA_SUCCESS, |
| urma_post_jetty_send_wr(receiver, &response_send_wr, &bad_send)); |
| |
| urma_cr_t response_send_cr{}; |
| ASSERT_EQ(1, urma_poll_jfc(receiver_jfc, 1, &response_send_cr)); |
| EXPECT_EQ(0, response_send_cr.flag.bs.s_r); |
| EXPECT_EQ(9u, response_send_cr.user_ctx); |
| |
| urma_cr_t response_recv_cr{}; |
| ASSERT_EQ(1, urma_poll_jfc(sender_jfc, 1, &response_recv_cr)); |
| EXPECT_EQ(1, response_recv_cr.flag.bs.s_r); |
| EXPECT_EQ(URMA_CR_OPC_SEND, response_recv_cr.opcode); |
| EXPECT_EQ(sizeof(response), response_recv_cr.completion_len); |
| EXPECT_EQ(0, memcmp(response, response_buf, sizeof(response))); |
| |
| urma_unimport_jetty(sender_target); |
| urma_unimport_jetty(target); |
| urma_delete_jetty(receiver); |
| urma_delete_jetty(sender); |
| urma_delete_jfr(receiver_jfr); |
| urma_delete_jfr(sender_jfr); |
| urma_delete_jfc(receiver_jfc); |
| urma_delete_jfc(sender_jfc); |
| urma_delete_jfce(receiver_jfce); |
| urma_delete_jfce(sender_jfce); |
| urma_delete_context(ctx); |
| urma_free_device_list(devices); |
| } |
| |
| #else // BRPC_WITH_URMA |
| |
| // When URMA is not compiled in, the test file is a no-op so the build stays |
| // clean. The brpc_urma_unittest target still links (against brpc-shared which |
| // provides the empty stubs). |
| |
| #endif // BRPC_WITH_URMA |
| |
| int main(int argc, char** argv) { |
| testing::InitGoogleTest(&argc, argv); |
| gflags::ParseCommandLineFlags(&argc, &argv, true); |
| #if BRPC_WITH_URMA |
| urma::g_skip_urma_init = true; |
| urma::g_urma_available.store(true, butil::memory_order_relaxed); |
| #endif |
| return RUN_ALL_TESTS(); |
| } |