RDMA CM UDP 通信完整指南

RDMA CM UDP 通信完整指南

  1. 📋 项目概述

这是一个使用 RDMA CM (Connection Manager) 和 UDP 协议进行通信的完整示例,包含服务端和客户端实现。

核心特性

  • ✅ 使用 RDMA CM 简化地址解析和路由查找
  • ✅ 基于 UD (Unreliable Datagram) QP 类型
  • ✅ 支持 InfiniBand 和 RoCE 自动切换
  • ✅ 事件驱动的完成处理 (服务端)
  • ✅ 轮询模式的完成处理 (客户端)
    与普通 UDP 的区别
    特性普通 UDPRDMA CM UDP传输方式内核协议栈硬件卸载延迟微秒级亚微秒级CPU 使用率较高极低零拷贝否是内核旁路否是

🔧 编译

Makefile

CC = gcc

CFLAGS = -Wall -O2 -g

LDFLAGS = -lrdmacm -libverbs

all: rdma_udp_server rdma_udp_client

rdma_udp_server: server.c

(CC) (CFLAGS) -o rdma_udp_server server.c $(LDFLAGS)

rdma_udp_client: client.c

(CC) (CFLAGS) -o rdma_udp_client client.c $(LDFLAGS)

clean:

rm -f rdma_udp_server rdma_udp_client

.PHONY: all clean

手动编译

编译服务端

gcc -o rdma_udp_server server.c -lrdmacm -libverbs -Wall -O2

编译客户端

gcc -o rdma_udp_client client.c -lrdmacm -libverbs -Wall -O2

依赖检查

检查库是否安装

pkg-config --libs rdmacm ibverbs

如果未安装

sudo apt-get install libibverbs-dev librdmacm-dev # Ubuntu/Debian

sudo yum install rdma-core-devel # CentOS/RHEL


🚀 运行

  1. 启动服务端
    ./rdma_udp_server

预期输出:

=== RDMA CM UDP Server ===

Server bound to port 12345

Resources created successfully

QP Number: 0x1f

Using GRH (RoCE mode) for reply

Server ready, waiting for UDP messages...

Press Ctrl+C to stop

  1. 启动客户端

基本用法

./rdma_udp_client 192.168.1.100

发送自定义消息

./rdma_udp_client 192.168.1.100 "Hello RDMA World"

预期输出:

=== RDMA CM UDP Client ===

Resolving address 192.168.1.100:12345...

Address resolved

Route resolved

Resources created successfully

Local QP Number: 0x20

Using GRH addressing (RoCE mode)

Address Handle created

Remote QPN set to: 0x20

Sending message: Hello RDMA World

Message sent: Hello RDMA World

Waiting for reply (timeout: 5000ms)...

=== Received reply ===

From QPN: 0x1f
Message: Server reply: Echo 'Hello RDMA World'

Communication completed successfully!

  1. 服务端输出 (收到消息后)
    Waiting for completion events...

=== Received UDP message ===

From QPN: 0x20
From LID: 0x0
Message: Hello RDMA World

Using GRH (RoCE mode) for reply

Reply sent: Server reply: Echo 'Hello RDMA World'


🔍 代码核心要点

  1. RDMA_PS_UDP 协议类型
    // 创建 UDP 类型的 RDMA CM ID
    rdma_create_id(ec, &id, NULL, RDMA_PS_UDP);

与 RDMA_PS_TCP 的区别:

  • RDMA_PS_TCP: 用于 RC (Reliable Connected) QP
  • RDMA_PS_UDP: 用于 UD (Unreliable Datagram) QP
  1. 服务端:事件驱动模型
    // 创建完成通道
    comp_chan = ibv_create_comp_channel(verbs);

// 创建 CQ 并关联到通道

cq = ibv_create_cq(verbs, 16, NULL, comp_chan, 0);

// 请求事件通知

ibv_req_notify_cq(cq, 0);

// 等待事件

ibv_get_cq_event(comp_chan, &ev_cq, &ev_ctx);

// 确认事件

ibv_ack_cq_events(ev_cq, 1);

优势: CPU 效率高,不需要忙等

  1. UD 特有:GRH 空间处理

#define GRH_SIZE 40 // Global Routing Header

// 分配缓冲区时预留 GRH 空间

buffer = malloc(BUFFER_SIZE + GRH_SIZE);

// 发送时跳过 GRH

sge.addr = (uintptr_t)(buffer + GRH_SIZE);

// 接收时数据从 GRH 后开始

printf("Message: %s\n", buffer + GRH_SIZE);

  1. 从接收完成中提取远端地址
    // 提取源地址信息
    ah_attr.dlid = wc.slid; // 源 LID
    uint32_t src_qpn = wc.src_qp; // 源 QPN

// 检查是否有 GRH (RoCE)

if (wc.wc_flags & IBV_WC_GRH) {

struct ibv_grh *grh = (struct ibv_grh *)buffer;

ah_attr.grh.dgid = grh->sgid; // 源 GID

}

  1. UD 发送必须指定 AH 和远端 QPN
    wr.wr.ud.ah = ah; // Address Handle
    wr.wr.ud.remote_qpn = remote_qpn; // 远端 QP 号
    wr.wr.ud.remote_qkey = 0x11111111; // Q_Key

⚠️ 重要注意事项

  1. QPN 交换问题
    当前实现的简化处理:
    // 客户端代码中的简化 (需要改进)
    ctx.remote_qpn = ctx.id->qp->qp_num; // 使用自己的 QPN (错误)

生产环境的正确做法:

方案A: 使用额外的 TCP 连接交换

// 服务端:通过 TCP 发送 QPN

int tcp_sock = accept(...);

uint32_t my_qpn = qp->qp_num;

send(tcp_sock, &my_qpn, sizeof(my_qpn), 0);

// 客户端:通过 TCP 接收 QPN

int tcp_sock = connect(...);

uint32_t server_qpn;

recv(tcp_sock, &server_qpn, sizeof(server_qpn), 0);

ctx.remote_qpn = server_qpn;

方案B: 使用固定 QPN

// 服务端和客户端约定固定的 QPN

#define SERVER_QPN 0x100

#define CLIENT_QPN 0x200

方案C: 使用多播组 (UD 特性)

// 加入多播组

rdma_join_multicast(id, multicast_addr, NULL);

  1. Q_Key 匹配
    // 发送端和接收端必须使用相同的 Q_Key
    #define QKEY 0x11111111

// QP 初始化时设置

modify_qp_to_init(..., qkey=QKEY);

// 发送时指定

wr.wr.ud.remote_qkey = QKEY;

  1. 无连接语义
    UD 模式的特点:
  • ❌ 不保证数据到达
  • ❌ 不保证顺序
  • ❌ 无流量控制
  • ✅ 低延迟
  • ✅ 支持多播
    适用场景:
  • 实时流媒体
  • 传感器数据采集
  • 金融行情推送
  • 日志收集
  1. RoCE vs InfiniBand 自动适配
    代码已自动处理:
    if (path->dlid == 0) {
    // RoCE: 使用 GID
    ah_attr.is_global = 1;
    } else {
    // InfiniBand: 使用 LID
    ah_attr.is_global = 0;
    }

🐛 常见问题排查

问题1: "Failed to resolve address"

原因: 网络不通或 RDMA 设备未激活

排查:

检查设备

ibv_devices

检查端口状态

ibv_devinfo

测试网络

ping <server_ip>

检查 RDMA 端口

rdma link show

问题2: "Failed to create QP"

原因: 资源不足或参数错误

排查:

查看系统资源限制

ulimit -l

增加内存锁定限制

sudo sh -c "echo '* soft memlock unlimited' >> /etc/security/limits.conf"

sudo sh -c "echo '* hard memlock unlimited' >> /etc/security/limits.conf"

问题3: 接收超时

可能原因:

1.QPN 不匹配

2.Q_Key 不匹配

3.防火墙阻止

4.接收队列未投递

调试:

查看 QP 状态

rdma resource show qp

查看统计信息

rdma statistic show

抓包分析

tcpdump -i <rdma_device> -w capture.pcap

问题4: "Completion error: status=5"

错误码含义:

IBV_WC_LOC_LEN_ERR = 1 // 本地长度错误

IBV_WC_LOC_QP_OP_ERR = 2 // QP 操作错误

IBV_WC_LOC_PROT_ERR = 4 // 保护错误

IBV_WC_RNR_RETRY_EXC = 5 // RNR 重试超限

IBV_WC_WR_FLUSH_ERR = 5 // WR 刷新错误


📊 性能测试

测试延迟

修改客户端代码,添加时间戳

struct timespec start, end;

clock_gettime(CLOCK_MONOTONIC, &start);

send_message(ctx, "ping");

receive_reply(ctx, 5000);

clock_gettime(CLOCK_MONOTONIC, &end);

long latency_ns = (end.tv_sec - start.tv_sec) * 1000000000L +

(end.tv_nsec - start.tv_nsec);

printf("Round-trip latency: %ld ns\n", latency_ns);

测试吞吐量

批量发送

for (int i = 0; i < 10000; i++) {

send_message(ctx, "data");

}

对比标准 UDP

RDMA UDP

./rdma_udp_client 192.168.1.100

标准 UDP (使用 netcat)

echo "test" | nc -u 192.168.1.100 12345

预期性能差异:

  • 延迟:RDMA < 5μs,标准 UDP > 50μs
  • CPU 使用率:RDMA < 10%,标准 UDP > 80%

🚀 进阶改进

  1. 多客户端支持
    服务端需要维护客户端地址表:
    struct client_info {
    uint32_t qpn;
    uint16_t lid;
    union ibv_gid gid;
    struct ibv_ah *ah;
    };

struct client_info clients[MAX_CLIENTS];

  1. 可靠性增强

    添加应用层 ACK:

    struct udp_packet {

    uint32_t seq_num;

    uint32_t ack_num;

    uint8_t flags; // SYN, ACK, FIN

    char data[1024];

    };

  2. 多播支持

    // 加入多播组

    struct sockaddr_in mcast_addr;

    mcast_addr.sin_addr.s_addr = inet_addr("224.0.0.1");

    rdma_join_multicast(id, (struct sockaddr *)&mcast_addr, NULL);

  3. 零拷贝优化

    // 使用 inline 发送小消息

    if (msg_len <= 64) {

    wr.send_flags |= IBV_SEND_INLINE;

    }


📚 参考资料

官方文档

  • rdma_create_id(3)
  • rdma_resolve_addr(3)
  • ibv_post_send(3)
    相关示例
  • rdma-core: libibverbs/examples/ud_pingpong.c
  • Linux Kernel: drivers/infiniband/core/cma.c
    进阶阅读
  • "RDMA Aware Programming User Manual"
  • "InfiniBand Architecture Specification" - Chapter 10

✅ 检查清单

在部署前确认:

  • RDMA 设备已激活
  • 端口状态为 ACTIVE
  • 网络连通性正常
  • QPN 交换机制已实现
  • Q_Key 已正确配置
  • 防火墙规则已配置
  • 错误处理已完善
  • 性能测试已通过

总结: 这是一个完整的 RDMA CM UDP 通信示例,展示了如何使用 RDMA CM 简化 UD 通信。在生产环境中,需要特别注意 QPN 交换机制和错误处理。

祝编程愉快!🎉

相关推荐
米羊1212 小时前
fastjson (3修复)
网络·网络协议·安全
IT阳晨。5 小时前
【STM32】天气预报项目
stm32·单片机·嵌入式硬件
IT阳晨。7 小时前
【STM32】智能台灯项目
stm32·单片机·嵌入式硬件
炸膛坦客8 小时前
Cortex-M3-STM32F1 开发:(三十九)DMA详细介绍(3):相关寄存器、库函数介绍,配置步骤,以及内存到内存和内存到外设的实例
stm32·单片机·嵌入式硬件
几道之旅9 小时前
websocket.WebSocketApp是全双工的吗?
网络·websocket·网络协议
BMS小旭9 小时前
CubeMx-GPIO学习
单片机·学习
春蕾夏荷_72829772511 小时前
Sockets-2.3.9.9 UDP使用实例
c++·udp
西幻凌云11 小时前
TCP 解析:头部格式、三次握手与四次挥手
网络协议·tcp/ip·udp·tcp·三次握手·四次挥手·传输层
清风66666611 小时前
基于单片机的PID调节脉动真空灭菌器上位机远程监控设计
数据库·单片机·毕业设计·nosql·课程设计·期末大作业
7ACE14 小时前
Wireshark TS | 超时重传时间不翻倍
网络协议·tcp/ip·wireshark