【RDMA】RDMA编程:事件通知机制

本文介绍了RDMA的事件通知机制,包括RDMA_CM API的连接建立,verbs API的数据传输,以及如何通过IO复用技术实现事件通知。RDMA_CM fd关注连接事件,verbs fd关注CQ事件。文章还探讨了rpoll在rsocket中的实现,提供了一种类似socket接口的rdma调用方式。
摘要由CSDN通过智能技术生成

RDMA通过kernel-bypass和协议栈offload两大核心技术,实现了远高于传统TCP/IP的网络通信性能。尽管RDMA的性能要远好于TCP/IP,但目前RDMA的实际落地业务场景却寥寥无几,这其中制约RDMA技术大规模上线应用的主要原因有两点:

  • 主流互联网公司普遍选择RoCE(RDMA over Converged Ethernet)作为RDMA部署方案,而RoCE本质上是RDMA over UDP,在网络上无法保证不丢包。因此RoCE部署方案需要额外的拥塞控制机制来保证底层的无损网络,如PFC、ECN等,这给大规模的上线部署带来挑战。而且目前各大厂商对硬件拥塞控制的支持均还不完善,存在兼容性问题。
  • RDMA提供了完全不同于socket的编程接口,因此要想使用RDMA,需要对现有应用进行改造。而RDMA原生编程API(verbs/RDMA_CM)比较复杂,需要对RDMA技术有深入理解才能做好开发,学习成本较高。

为了降低应用程序的改造成本,决定研发一个RDMA通信库,该通信库直接基于ibvebrs和RDMA_CM,避免对其他第三方库的调用。

本文主要对rdma编程的事件通知机制进行归纳总结。

传统socket编程中通常采用IO复用技术(select、poll、epoll等)来实现事件通知机制,那么对于rdma是否可以同样基于IO复用技术来实现事件通知机制?答案是完全可以。

1. RDMA_CM API(For Connection)

在rdma编程时,可以直接通过RDMA_CM API来建立RDMA连接。

对rdma_create_id函数进行分析,其主要创建了rdma_cm_id对象,并将其注册到驱动中。

int rdma_create_id(struct rdma_event_channel *channel,
           struct rdma_cm_id **id, void *context,
           enum rdma_port_space ps)
{
    enum ibv_qp_type qp_type = (ps == RDMA_PS_IPOIB || ps == RDMA_PS_UDP) ?
          IBV_QPT_UD : IBV_QPT_RC;
    ret = ucma_init(); //查询获取所有IB设备,存放在cma_dev_array全局数组中;检测是否支持AF_IB协议
    
    struct cma_id_private *id_priv = 
         ucma_alloc_id(channel, context, ps, qp_type); //创建并初始化id_priv对象:若未创建rdma_event_channel,那么调用rdma_create_event_channel创建一个。
    
    CMA_INIT_CMD_RESP(&cmd, sizeof cmd, CREATE_ID, &resp, sizeof resp);
    cmd.uid = (uintptr_t) id_priv;
    cmd.ps = ps;        
    cmd.qp_type = qp_type;

    ret = write(id_priv->id.channel->fd, &cmd, sizeof cmd); //将id_priv相关信息注册到内核驱动中,不做过多分析
    *id = &id_priv->id; //返回rdma_cm_id对象
}

rdma_cm_id数据结构定义如下:

struct rdma_cm_id {
struct ibv_context *verbs;  //ibv_open_device
struct rdma_event_channel *channel; //rdma_create_event_channel创建;For Setup connection
void *context;   //user specified context
struct ibv_qp *qp;  //rdma_create_qp,底层调用的是ibv_create_qp
struct rdma_route route; 
enum rdma_port_space ps;  //RDMA_PS_IPOIB or RDMA_PS_UDP or RDMA_PS_TCP
uint8_t port_num;  //port数目
struct rdma_cm_event *event;  //rdma_cm相关的事件events 
struct ibv_comp_channel *send_cq_channel; //ibv_create_comp_channel创建;For data transfer
struct ibv_cq *send_cq;  //发送CQ,通常和recv_cq是同一个CQ
struct ibv_comp_channel *recv_cq_channel; //ibv_create_comp_channel创建;For data transfer
struct ibv_cq *recv_cq;  //接收CQ,通常和send_cq是同一个CQ
struct ibv_srq *srq; 
struct ibv_pd *pd;  //ibv_open_device
enum ibv_qp_type qp_type; //IBV_QPT_RC or IBV_QPT_UD
};

在创建rdma_cm_id时,如果预先没有创建rdma_event_channel,那么需要调用rdma_create_event_channel函数。

struct rdma_event_channel *rdma_create_event_channel(void)
{
    struct rdma_event_channel *channel;

    if (ucma_init())  //通过static局部变量,保证只做一次初始化
        return NULL;

    channel = malloc(sizeof *channel); //创建rdma_event_channel
    if (!channel)    
        return NULL;

    channel->fd = open("/dev/infiniband/rdma_cm", O_RDWR | O_CLOEXEC); //可以看出rdma_event_channel本质上就是一个fd
    if (channel->fd < 0) { 
        goto err; 
    }    
    return channel;
err:
    free(channel);
    return NULL;
}

rdma_event_channel的定义如下:

struct rdma_event_channel {
    int fd;
}

1.1 RDMA_CM原生事件通知实现(in block way)

static int cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event);

ret = rdma_get_cm_event(channel, &event);  //阻塞操作,直到有rdma_cm event发生才返回
if (!ret) {
    ret = cma_handler(event->id, event); //处理事件
    rdma_ack_cm_event(event); //ack event
}

static int cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值