| // 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(); | |
| } |