diff --git a/docs/cn/rdma.md b/docs/cn/rdma.md index fd70686bb1..f000b52fb9 100644 --- a/docs/cn/rdma.md +++ b/docs/cn/rdma.md @@ -1,3 +1,37 @@ +# 业务级 QoS + +通过 `ChannelOptions.rdma_qos` 配置请求方向的 RDMA QoS: + +```cpp +brpc::ChannelOptions options; +options.socket_mode = brpc::SOCKET_MODE_RDMA; +options.rdma_qos.service_level = 3; +options.rdma_qos.traffic_class = (26 << 2) | 2; // DSCP=26, ECN=2 +``` + +`service_level` 范围为 0–15,`traffic_class` 为完整的 8 位 GRH/IP Traffic Class,范围为 0–255, +两者默认均为 0。SL 的调度待遇由网络侧 SL→VL 或优先级映射决定。 +RoCE 部署结合网卡及交换机的 PCP/DSCP 映射配置;示例中的 DSCP/ECN 值按部署策略选择。 + +同一地址下,相同 QoS 的 Channel 可共享连接,不同 SL 或 Traffic Class 使用独立连接及 QP。 +配置随单连接、连接池和短连接传递,并在 QP 重建时重新应用。 + +响应方向通过 `ServerOptions.rdma_qos` 配置: + +```cpp +brpc::ServerOptions options; +options.socket_mode = brpc::SOCKET_MODE_RDMA; +options.rdma_qos.service_level = 3; +options.rdma_qos.traffic_class = 106; +``` + +客户端和服务端分别配置各自的发送方向。设置在 Channel 初始化或 Server 启动时生效, +V2/V3 握手沿用现有格式;TCP 传输按原配置运行。 +连接调试信息提供 `rdma_sl` 和 `rdma_traffic_class`。 + +`example/rdma_performance` 的 client/server 支持 `--rdma_sl`、`--rdma_traffic_class`。 +实机验证时检查 QP 属性和实际报文标记,再比较竞争流量下的吞吐与 P99。 + # 编译 由于RDMA对驱动与硬件有要求,目前仅支持在Linux系统编译并运行RDMA功能。 diff --git a/docs/en/rdma.md b/docs/en/rdma.md index 98ac6981bc..cfac9f83f3 100644 --- a/docs/en/rdma.md +++ b/docs/en/rdma.md @@ -1,3 +1,21 @@ +# Business-level QoS + +Set `ChannelOptions.rdma_qos.service_level` (0–15) and +`ChannelOptions.rdma_qos.traffic_class` (0–255) for outbound requests. +Both default to 0. Traffic Class is the full GRH/IP byte, including DSCP and ECN. +Set `ServerOptions.rdma_qos` independently for outbound responses. + +Different SL or Traffic Class values use separate connections and QPs, while equal +settings can share connections. Settings follow pooled and short sockets and are +applied whenever the QP is brought up. V2/V3 handshake formats stay the same. +TCP transports retain their existing settings. + +The fabric defines how SL maps to VLs and scheduling priority. For RoCE, configure +the NIC and switch PCP/DSCP mappings to match the deployment. The performance +client/server accept `--rdma_sl` and `--rdma_traffic_class`; socket diagnostics show +both applied values. Validate QP attributes and packet markings on hardware before +comparing throughput and P99 under competing workloads. + # Build Since RDMA requires driver and hardware support, only the build on linux is verified. diff --git a/example/rdma_performance/client.cpp b/example/rdma_performance/client.cpp index 8c07eca236..365c61697d 100644 --- a/example/rdma_performance/client.cpp +++ b/example/rdma_performance/client.cpp @@ -42,6 +42,8 @@ DEFINE_string(connection_type, "single", "Connection type of the channel"); DEFINE_string(protocol, "baidu_std", "Protocol type."); DEFINE_string(servers, "0.0.0.0:8002+0.0.0.0:8002", "IP Address of servers"); DEFINE_bool(use_rdma, true, "Use RDMA or not"); +DEFINE_int32(rdma_sl, 0, "Outbound RDMA service level [0,15]"); +DEFINE_int32(rdma_traffic_class, 0, "Outbound RDMA traffic class byte [0,255]"); DEFINE_int32(rpc_timeout_ms, 2000, "RPC call timeout"); DEFINE_int32(test_seconds, 20, "Test running time"); DEFINE_int32(test_iterations, 0, "Test iterations"); @@ -103,6 +105,8 @@ class PerformanceTest { int Init() { brpc::ChannelOptions options; options.socket_mode = FLAGS_use_rdma? brpc::SOCKET_MODE_RDMA : brpc::SOCKET_MODE_TCP; + options.rdma_qos.service_level = FLAGS_rdma_sl; + options.rdma_qos.traffic_class = FLAGS_rdma_traffic_class; options.protocol = FLAGS_protocol; options.connection_type = FLAGS_connection_type; options.timeout_ms = FLAGS_rpc_timeout_ms; @@ -274,6 +278,14 @@ void Test(int thread_num, int attachment_size) { int main(int argc, char* argv[]) { GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true); + brpc::RdmaQosOptions qos; + qos.service_level = FLAGS_rdma_sl; + qos.traffic_class = FLAGS_rdma_traffic_class; + if (FLAGS_use_rdma && !qos.IsValid()) { + LOG(ERROR) << "RDMA SL must be in [0,15] and traffic class in [0,255]"; + return 1; + } + // Initialize RDMA environment in advance. if (FLAGS_use_rdma) { brpc::rdma::GlobalRdmaInitializeOrDie(); diff --git a/example/rdma_performance/server.cpp b/example/rdma_performance/server.cpp index 2e93e1eec7..b444b7b02d 100644 --- a/example/rdma_performance/server.cpp +++ b/example/rdma_performance/server.cpp @@ -28,6 +28,8 @@ DEFINE_int32(port, 8002, "TCP Port of this server"); DEFINE_bool(use_rdma, true, "Use RDMA or not"); +DEFINE_int32(rdma_sl, 0, "Outbound RDMA service level [0,15]"); +DEFINE_int32(rdma_traffic_class, 0, "Outbound RDMA traffic class byte [0,255]"); butil::atomic g_last_time(0); @@ -77,6 +79,8 @@ int main(int argc, char* argv[]) { brpc::ServerOptions options; options.socket_mode = FLAGS_use_rdma? brpc::SOCKET_MODE_RDMA : brpc::SOCKET_MODE_TCP; + options.rdma_qos.service_level = FLAGS_rdma_sl; + options.rdma_qos.traffic_class = FLAGS_rdma_traffic_class; if (server.Start(FLAGS_port, &options) != 0) { LOG(ERROR) << "Fail to start EchoServer"; return -1; diff --git a/src/brpc/acceptor.cpp b/src/brpc/acceptor.cpp index 99280037d5..d19d98ee0a 100644 --- a/src/brpc/acceptor.cpp +++ b/src/brpc/acceptor.cpp @@ -338,6 +338,7 @@ void Acceptor::OnNewConnectionsUntilEAGAIN(Socket* acception) { options.force_ssl = am->_force_ssl; options.initial_ssl_ctx = am->_ssl_ctx; options.socket_mode = am->_socket_mode; + options.rdma_qos = am->_rdma_qos; options.bthread_tag = am->_bthread_tag; if (Socket::Create(options, &socket_id) != 0) { am->ReleaseConnectionSlot(); diff --git a/src/brpc/acceptor.h b/src/brpc/acceptor.h index 01a2b26a9d..2485d9f387 100644 --- a/src/brpc/acceptor.h +++ b/src/brpc/acceptor.h @@ -24,6 +24,7 @@ #include "butil/containers/flat_map.h" #include "brpc/input_messenger.h" #include "brpc/socket_mode.h" +#include "brpc/rdma/rdma_qos.h" namespace brpc { @@ -137,6 +138,7 @@ friend class Server; // Choose to use a certain socket: 0 TCP, 1 RDMA SocketMode _socket_mode; + RdmaQosOptions _rdma_qos; // Acceptor belongs to this tag bthread_tag_t _bthread_tag; diff --git a/src/brpc/channel.cpp b/src/brpc/channel.cpp index de0a1bde49..9a7811f8f3 100644 --- a/src/brpc/channel.cpp +++ b/src/brpc/channel.cpp @@ -78,7 +78,8 @@ ChannelSSLOptions* ChannelOptions::mutable_ssl_options() { } static ChannelSignature ComputeChannelSignature(const ChannelOptions& opt) { - if (opt.auth == nullptr && + if (opt.socket_mode == SOCKET_MODE_TCP && + opt.auth == nullptr && !opt.has_ssl_options() && opt.client_host.empty() && opt.device_name.empty() && @@ -139,6 +140,8 @@ static ChannelSignature ComputeChannelSignature(const ChannelOptions& opt) { } if (opt.socket_mode == SOCKET_MODE_RDMA) { buf.append("|rdma"); + buf.append("|sl=").append(std::to_string(opt.rdma_qos.service_level)); + buf.append("|tc=").append(std::to_string(opt.rdma_qos.traffic_class)); } else if (opt.socket_mode == SOCKET_MODE_URMA) { buf.append("|urma"); } @@ -187,6 +190,10 @@ int Channel::InitChannelOptions(const ChannelOptions* options) { if (options) { // Override default options if user provided one. _options = *options; } + if (_options.socket_mode == SOCKET_MODE_RDMA && !_options.rdma_qos.IsValid()) { + LOG(ERROR) << "RDMA SL must be in [0,15] and traffic class in [0,255]"; + return -1; + } const Protocol* protocol = FindProtocol(_options.protocol); if (nullptr == protocol || !protocol->support_client()) { LOG(ERROR) << "Channel does not support the protocol"; @@ -407,6 +414,7 @@ int Channel::InitSingle(const butil::EndPoint& server_addr_and_port, opt.local_side = client_endpoint; opt.initial_ssl_ctx = ssl_ctx; opt.socket_mode = _options.socket_mode; + opt.rdma_qos = _options.rdma_qos; opt.hc_option = _options.hc_option; opt.device_name = _options.device_name; if (SocketMapInsert(SocketMapKey(server_addr_and_port, sig), @@ -453,6 +461,7 @@ int Channel::Init(const char* ns_url, ns_opt.succeed_without_server = _options.succeed_without_server; ns_opt.log_succeed_without_server = _options.log_succeed_without_server; ns_opt.socket_option.socket_mode = _options.socket_mode; + ns_opt.socket_option.rdma_qos = _options.rdma_qos; ns_opt.channel_signature = ComputeChannelSignature(_options); ns_opt.socket_option.hc_option = _options.hc_option; ns_opt.socket_option.local_side = client_endpoint; diff --git a/src/brpc/channel.h b/src/brpc/channel.h index a778a1bd2a..b4bff6092b 100644 --- a/src/brpc/channel.h +++ b/src/brpc/channel.h @@ -38,6 +38,7 @@ #include "brpc/naming_service_filter.h" #include "brpc/health_check_option.h" #include "brpc/socket_mode.h" +#include "brpc/rdma/rdma_qos.h" namespace brpc { @@ -111,6 +112,9 @@ struct ChannelOptions { // Default: SOCKET_MODE_TCP SocketMode socket_mode; + // Outbound RDMA QoS for this Channel. Used when socket_mode is RDMA. + RdmaQosOptions rdma_qos; + // Turn on authentication for this channel if `auth' is not nullptr. // Note `auth' will not be deleted by channel and must remain valid when // the channel is being used. diff --git a/src/brpc/rdma/rdma_endpoint.cpp b/src/brpc/rdma/rdma_endpoint.cpp index 0dc41c9dd1..fe3b25e66f 100644 --- a/src/brpc/rdma/rdma_endpoint.cpp +++ b/src/brpc/rdma/rdma_endpoint.cpp @@ -1453,9 +1453,9 @@ int RdmaEndpoint::BringUpQp(const ParsedHello& remote, bool is_server) { attr.ah_attr.grh.flow_label = 0; attr.ah_attr.grh.sgid_index = GetRdmaGidIndex(); attr.ah_attr.grh.hop_limit = MAX_HOP_LIMIT; - attr.ah_attr.grh.traffic_class = 0; + attr.ah_attr.grh.traffic_class = _socket->rdma_qos().traffic_class; attr.ah_attr.dlid = remote.lid; - attr.ah_attr.sl = 0; + attr.ah_attr.sl = _socket->rdma_qos().service_level; attr.ah_attr.src_path_bits = 0; attr.ah_attr.static_rate = 0; attr.ah_attr.is_global = 1; @@ -1867,6 +1867,8 @@ std::string RdmaEndpoint::GetStateStr() const { void RdmaEndpoint::DebugInfo(std::ostream& os, butil::StringPiece connector) const { os << "rdma_state=ON" + << connector << "rdma_sl=" << _socket->rdma_qos().service_level + << connector << "rdma_traffic_class=" << _socket->rdma_qos().traffic_class << connector << "handshake_state=" << GetStateStr() << connector << "handshake_version=" << static_cast(_handshake_version) << connector << "rdma_sq_imm_window_size=" << _sq_imm_window_size diff --git a/src/brpc/rdma/rdma_qos.h b/src/brpc/rdma/rdma_qos.h new file mode 100644 index 0000000000..e75cdafa61 --- /dev/null +++ b/src/brpc/rdma/rdma_qos.h @@ -0,0 +1,36 @@ +// 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. + +#ifndef BRPC_RDMA_QOS_H +#define BRPC_RDMA_QOS_H + +namespace brpc { + +// Outbound QP attributes. Their scheduling behavior is defined by the fabric. +struct RdmaQosOptions { + int service_level = 0; // SL, range [0, 15]. + int traffic_class = 0; // Full GRH/IP traffic class byte, range [0, 255]. + + bool IsValid() const { + return service_level >= 0 && service_level <= 15 && + traffic_class >= 0 && traffic_class <= 255; + } +}; + +} // namespace brpc + +#endif // BRPC_RDMA_QOS_H diff --git a/src/brpc/server.cpp b/src/brpc/server.cpp index 19d58ac422..96734ae8d0 100644 --- a/src/brpc/server.cpp +++ b/src/brpc/server.cpp @@ -875,6 +875,11 @@ int Server::StartInternal(const butil::EndPoint& endpoint, const ServerOptions default_opt; const ServerOptions& real_opt = opt ? *opt : default_opt; + if (real_opt.socket_mode == SOCKET_MODE_RDMA && !real_opt.rdma_qos.IsValid()) { + LOG(ERROR) << "RDMA SL must be in [0,15] and traffic class in [0,255]"; + return -1; + } + if (!real_opt.h2_settings.IsValid(true/*log_error*/)) { LOG(ERROR) << "Invalid h2_settings"; return -1; @@ -1162,6 +1167,7 @@ int Server::StartInternal(const butil::EndPoint& endpoint, _am->_socket_mode = _options.socket_mode; _am->_bthread_tag = _options.bthread_tag; } + _am->_rdma_qos = _options.rdma_qos; // Set `_status' to RUNNING before accepting connections // to prevent requests being rejected as ELOGOFF _status = RUNNING; diff --git a/src/brpc/server.h b/src/brpc/server.h index 31dc69af2f..769cee4150 100644 --- a/src/brpc/server.h +++ b/src/brpc/server.h @@ -46,6 +46,7 @@ #include "brpc/baidu_master_service.h" #include "brpc/rpc_pb_message_factory.h" #include "brpc/socket_mode.h" +#include "brpc/rdma/rdma_qos.h" namespace brpc { @@ -242,6 +243,9 @@ struct ServerOptions { // Default: SOCKET_MODE_TCP SocketMode socket_mode; + // Outbound RDMA QoS for responses on this server's accepted connections. + RdmaQosOptions rdma_qos; + // [CAUTION] This option is for implementing specialized baidu-std proxies, // most users don't need it. Don't change this option unless you fully // understand the description below. diff --git a/src/brpc/socket.cpp b/src/brpc/socket.cpp index 57cfd2296a..f560320b4b 100644 --- a/src/brpc/socket.cpp +++ b/src/brpc/socket.cpp @@ -716,6 +716,10 @@ void SetSocketBufferOptions(int fd) { // version: from version part of _versioned_nref, must be an EVEN number. // slot: designated by ResourcePool. int Socket::Create(const SocketOptions& options, SocketId* id) { + if (options.socket_mode == SOCKET_MODE_RDMA && !options.rdma_qos.IsValid()) { + errno = EINVAL; + return -1; + } return VersionedRefWithId::Create(id, options); } @@ -731,6 +735,7 @@ int Socket::OnCreated(const SocketOptions& options) { }); // start build the transport _socket_mode = options.socket_mode; + _rdma_qos = options.rdma_qos; _transport = TransportFactory::CreateTransport(options.socket_mode); CHECK(nullptr != _transport); _transport->Init(this, options); @@ -2830,6 +2835,7 @@ int Socket::GetPooledSocket(SocketUniquePtr* pooled_socket) { opt.keytable_pool = _keytable_pool; opt.app_connect = _app_connect; opt.socket_mode = _socket_mode; + opt.rdma_qos = _rdma_qos; socket_pool = new SocketPool(opt); SocketPool* expected = nullptr; if (!main_sp->socket_pool.compare_exchange_strong( @@ -2936,6 +2942,7 @@ int Socket::GetShortSocket(SocketUniquePtr* short_socket) { opt.keytable_pool = _keytable_pool; opt.app_connect = _app_connect; opt.socket_mode = _socket_mode; + opt.rdma_qos = _rdma_qos; if (get_client_side_messenger()->Create(opt, &id) != 0 || Address(id, short_socket) != 0) { return -1; diff --git a/src/brpc/socket.h b/src/brpc/socket.h index da701e9ad7..9cd26674c4 100644 --- a/src/brpc/socket.h +++ b/src/brpc/socket.h @@ -43,6 +43,7 @@ #include "brpc/versioned_ref_with_id.h" #include "brpc/health_check_option.h" #include "brpc/socket_mode.h" +#include "brpc/rdma/rdma_qos.h" #include "brpc/input_messenger_processor.h" // InputMessengerProcessor namespace brpc { @@ -309,6 +310,7 @@ struct SocketOptions { bool force_ssl{false}; std::shared_ptr initial_ssl_ctx; SocketMode socket_mode{SOCKET_MODE_TCP}; + RdmaQosOptions rdma_qos; bthread_keytable_pool_t* keytable_pool{nullptr}; SocketConnection* conn{nullptr}; std::shared_ptr app_connect; @@ -632,6 +634,7 @@ friend class TransportFactory; // The socket mode decided at creation. SocketMode socket_mode() const { return _socket_mode; } + const RdmaQosOptions& rdma_qos() const { return _rdma_qos; } // Get an UNUSED socket connecting to the same place as this socket // from the SocketPool of this socket. @@ -999,6 +1002,7 @@ friend class TransportFactory; // Should use SOCKET_MODE_RDMA or SOCKET_MODE_TCP or Other, default is SOCKET_MODE_TCP Transport SocketMode _socket_mode; + RdmaQosOptions _rdma_qos; std::unique_ptr _transport; // Pass from controller, for progressive reading. diff --git a/test/brpc_rdma_unittest.cpp b/test/brpc_rdma_unittest.cpp index 6ec54ed90b..1d3ac2c934 100644 --- a/test/brpc_rdma_unittest.cpp +++ b/test/brpc_rdma_unittest.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "butil/endpoint.h" #include "butil/fd_guard.h" @@ -47,6 +48,7 @@ #include "brpc/rdma/rdma_handshake_constants.h" #include "brpc/rdma/rdma_handshake.pb.h" #include "brpc/rdma/rdma_helper.h" +#include "brpc/details/load_balancer_with_naming.h" #include "echo.pb.h" static const int PORT = 8713; @@ -3316,6 +3318,147 @@ TEST(RdmaInlineDataTest, should_post_inline) { EXPECT_FALSE(rdma::ShouldPostInline(true, 1, 0)); } +TEST_F(RdmaTest, qos_rejects_invalid_options_before_connecting) { + for (const auto& values : {std::pair(-1, 0), {16, 0}, {0, -1}, {0, 256}}) { + ChannelOptions options; + options.socket_mode = SOCKET_MODE_RDMA; + options.rdma_qos.service_level = values.first; + options.rdma_qos.traffic_class = values.second; + Channel channel; + EXPECT_EQ(-1, channel.Init(g_ep, &options)); + ServerOptions server_options; + server_options.socket_mode = SOCKET_MODE_RDMA; + server_options.rdma_qos = options.rdma_qos; + Server server; + EXPECT_EQ(-1, server.Start(PORT, &server_options)); + SocketOptions socket_options; + socket_options.socket_mode = SOCKET_MODE_RDMA; + socket_options.rdma_qos = options.rdma_qos; + SocketId sid; + EXPECT_EQ(-1, Socket::Create(socket_options, &sid)); + EXPECT_EQ(EINVAL, errno); + } +} + +TEST_F(RdmaTest, qos_isolates_channels_and_preserves_equal_config_sharing) { + ChannelOptions options; + options.socket_mode = SOCKET_MODE_RDMA; + Channel a, b, same, different_tc, default_rdma, tcp, tcp_with_qos; + ASSERT_EQ(0, default_rdma.Init(g_ep, &options)); + ASSERT_EQ(0, tcp.Init(g_ep, nullptr)); + EXPECT_NE(default_rdma._server_id, tcp._server_id); + options.rdma_qos.service_level = 3; + options.rdma_qos.traffic_class = 106; + ASSERT_EQ(0, a.Init(g_ep, &options)); + ASSERT_EQ(0, same.Init(g_ep, &options)); + EXPECT_EQ(a._server_id, same._server_id); + EXPECT_NE(a._server_id, default_rdma._server_id); + options.rdma_qos.service_level = 4; + ASSERT_EQ(0, b.Init(g_ep, &options)); + EXPECT_NE(a._server_id, b._server_id); + options.rdma_qos.service_level = 3; + options.rdma_qos.traffic_class = 104; + ASSERT_EQ(0, different_tc.Init(g_ep, &options)); + EXPECT_NE(a._server_id, different_tc._server_id); + options.socket_mode = SOCKET_MODE_TCP; + ASSERT_EQ(0, tcp_with_qos.Init(g_ep, &options)); + EXPECT_EQ(tcp._server_id, tcp_with_qos._server_id); + SocketUniquePtr socket; + ASSERT_EQ(0, Socket::Address(a._server_id, &socket)); + EXPECT_EQ(3, socket->rdma_qos().service_level); + EXPECT_EQ(106, socket->rdma_qos().traffic_class); +} + +TEST_F(RdmaTest, qos_follows_pooled_short_and_named_connections) { + ChannelOptions options; + options.socket_mode = SOCKET_MODE_RDMA; + options.rdma_qos.service_level = 7; + options.rdma_qos.traffic_class = 255; + Channel direct, named; + ASSERT_EQ(0, direct.Init(g_ep, &options)); + ASSERT_EQ(0, named.Init(_naming_url.c_str(), "rr", &options)); + SocketUniquePtr main_socket; + ASSERT_EQ(0, Socket::Address(direct._server_id, &main_socket)); + SocketUniquePtr pooled, short_socket, selected; + ASSERT_EQ(0, main_socket->GetPooledSocket(&pooled)); + ASSERT_EQ(0, main_socket->GetShortSocket(&short_socket)); + LoadBalancer::SelectIn input{}; + LoadBalancer::SelectOut output(&selected); + ASSERT_EQ(0, named._lb->SelectServer(input, &output)); + for (Socket* s : {pooled.get(), short_socket.get(), selected.get()}) { + EXPECT_EQ(7, s->rdma_qos().service_level); + EXPECT_EQ(255, s->rdma_qos().traffic_class); + } + EXPECT_EQ(main_socket->id(), selected->id()); + EXPECT_EQ(0, pooled->ReturnToPool()); + short_socket->SetFailed(); +} + +TEST_F(RdmaTest, qos_server_options_follow_accept_and_restart) { + for (int sl : {3, 7}) { + ServerOptions options; + options.socket_mode = SOCKET_MODE_RDMA; + options.rdma_qos.service_level = sl; + options.rdma_qos.traffic_class = 106; + ASSERT_EQ(0, _server.Start(PORT, &options)); + butil::fd_guard fd; + ASSERT_NO_FATAL_FAILURE(ConnectToServer(&fd)); + Socket* socket = WaitForServerSocket(); + ASSERT_TRUE(socket != nullptr); + EXPECT_EQ(sl, socket->rdma_qos().service_level); + EXPECT_EQ(106, socket->rdma_qos().traffic_class); + fd.reset(-1); + ASSERT_TRUE(WaitForServerSocketGone()); + StopServer(); + } +} + +static ibv_ah_attr g_qos_rtr_address; +static int g_qos_rtr_calls; +static int CaptureQosModify(ibv_qp*, ibv_qp_attr* attr, ibv_qp_attr_mask mask) { + if (mask & IBV_QP_AV) { + g_qos_rtr_address = attr->ah_attr; + ++g_qos_rtr_calls; + } + return 0; +} + +TEST_F(RdmaTest, qos_applies_to_rtr_on_each_qp_bringup) { + for (const auto& values : {std::pair(15, 255), {0, 0}}) { + SocketOptions options; + options.socket_mode = SOCKET_MODE_RDMA; + options.rdma_qos.service_level = values.first; + options.rdma_qos.traffic_class = values.second; + SocketId sid; + ASSERT_EQ(0, Socket::Create(options, &sid)); + SocketUniquePtr socket; + ASSERT_EQ(0, Socket::Address(sid, &socket)); + auto* ep = RdmaTransportOf(socket)->_rdma_ep; + ibv_qp qp{}; + rdma::RdmaResource resource; + resource.qp = &qp; + ep->_resource = &resource; + ep->_rq_size = 0; + rdma::ParsedHello remote{}; + const bool skip = rdma::g_skip_rdma_init; + const auto modify = rdma::IbvModifyQp; + rdma::g_skip_rdma_init = false; + rdma::IbvModifyQp = CaptureQosModify; + g_qos_rtr_calls = 0; + // Reapply attributes on both client and server bringup. + EXPECT_EQ(0, ep->BringUpQp(remote, false)); + EXPECT_EQ(0, ep->BringUpQp(remote, true)); + rdma::g_skip_rdma_init = skip; + rdma::IbvModifyQp = modify; + EXPECT_EQ(2, g_qos_rtr_calls); + EXPECT_EQ(values.first, g_qos_rtr_address.sl); + EXPECT_EQ(values.second, g_qos_rtr_address.grh.traffic_class); + ep->_resource = nullptr; + resource.qp = nullptr; + socket->SetFailed(); + } +} + #endif // if BRPC_WITH_RDMA int main(int argc, char* argv[]) {