Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/cn/rdma.md
Original file line number Diff line number Diff line change
@@ -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功能。
Expand Down
18 changes: 18 additions & 0 deletions docs/en/rdma.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
12 changes: 12 additions & 0 deletions example/rdma_performance/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions example/rdma_performance/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint64_t> g_last_time(0);

Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/brpc/acceptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions src/brpc/acceptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 10 additions & 1 deletion src/brpc/channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() &&
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/brpc/channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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.
Expand Down
6 changes: 4 additions & 2 deletions src/brpc/rdma/rdma_endpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<int>(_handshake_version)
<< connector << "rdma_sq_imm_window_size=" << _sq_imm_window_size
Expand Down
36 changes: 36 additions & 0 deletions src/brpc/rdma/rdma_qos.h
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions src/brpc/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/brpc/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions src/brpc/socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Socket>::Create(id, options);
}

Expand All @@ -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);
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/brpc/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -309,6 +310,7 @@ struct SocketOptions {
bool force_ssl{false};
std::shared_ptr<SocketSSLContext> 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<AppConnect> app_connect;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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> _transport;

// Pass from controller, for progressive reading.
Expand Down
Loading
Loading