RDMA的ibv_post_send() 函数

本文详细介绍了InfiniBand技术中数据传输的两种主要类型:IBV_WR_SEND和IBV_WR_RDMA_WRITE。前者将本地内存缓冲区的内容发送到远程队列,后者则直接写入远程内存空间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数原型为

int ibv_post_send(struct ibv_qp *qp, struct ibv_send_wr *wr,
                  struct ibv_send_wr **bad_wr);

其中struct ibv_send_wr结构体的定义为:

struct ibv_send_wr {
    uint64_t        wr_id;
    struct ibv_send_wr     *next;
    struct ibv_sge           *sg_list;
    int            num_sge;
    enum ibv_wr_opcode    opcode;
    int            send_flags;
    uint32_t        imm_data;
    union {
        struct {
            uint64_t    remote_addr;
            uint32_t    rkey;
        } rdma;
        struct {
            uint64_t    remote_addr;
            uint64_t    compare_add;
            uint64_t    swap;
            uint32_t    rkey;
        } atomic;
        struct {
            struct ibv_ah  *ah;
            uint32_t    remote_qpn;
            uint32_t    remote_qkey;
        } ud;
    } wr;
};

在ibv_send_wr 结构体中opcode参数决定了数据传输类型,比如说:

IBV_WR_SEND——在这种类型中,The content of the local memory buffers specified in sg_list is being sent to the remote QP. The sender doesn’t know where the data will be written in the remote node. A Receive Request will be consumed from the head of remote QP's Receive Queue and sent data will be written to the memory buffers which are specified in that Receive Request.当前内存buffer中在sg_list中的内容会被发送给远方的QP。发送方并不会知道数据会写到远方节点的何处。接收方要post_recv,并且接收到的数据要放到指定的地址中。

IBV_WR_RDMA_WRITE——在这种类型中,The content of the local memory buffers specified in sg_list is being sent and written to a contiguous block of memory range in the remote QP's virtual space. This doesn't necessarily means that the remote memory is physically contiguous. No Receive Request will be consumed in the remote QP.本地内存buffer中sg_list中的内容会被发送和写到远方节点的QP的虚拟空间中的一段连续内存块中——这并不意味着远方的内存在物理上也是连续的。并且remote QP也不需要post_recv。

转载于:https://www.cnblogs.com/winifred-tang94/p/5844171.html

<think>我们正在讨论RDMA(远程直接内存访问)编程中的ibv_qp_init_attr结构体。根据引用[3]中提到的“创建QP。用户传入PD和一组属性(包括RQ和SQ绑定到的CQ、QP绑定的SRQ、QP的能力、QP类型等)”,我们可以知道ibv_qp_init_attr结构体是在创建队列对(QP)时用来初始化QP属性的。在RDMA中,队列对(QP)是通信的基本单位,每个QP由发送队列(SQ)和接收队列(RQ)组成。创建QP时需要指定一系列属性,这些属性通过ibv_qp_init_attr结构体传递。下面我们来详细介绍ibv_qp_init_attr结构体的定义和用途。###ibv_qp_init_attr结构体定义根据InfiniBand架构规范(IBSpec)和libibverbs库的文档,`ibv_qp_init_attr`结构体通常包含以下字段(具体字段可能因版本略有不同,但核心字段如下):```cstructibv_qp_init_attr{void*qp_context;/*用户自定义的上下文,可关联到QP*/structibv_cq*send_cq;/*发送完成队列(CQ),用于发送操作的完成事件*/structibv_cq*recv_cq;/*接收完成队列(CQ),用于接收操作的完成事件*/structibv_srq*srq;/*共享接收队列(SRQ),如果使用共享RQ,则指向SRQ,否则为NULL*/structibv_qp_capcap;/*QP的能力,包括发送和接收队列的大小等*/enumibv_qp_typeqp_type;/*QP类型,如IBV_QPT_RC(可靠连接)、IBV_QPT_UC(不可靠连接)、IBV_QPT_UD(不可靠数据报)等*/intsq_sig_all;/*信号标志:如果为1,则每个发送请求都产生一个完成事件;如果为0,则只有设置了IBV_SEND_SIGNALED的请求才产生完成事件*/};```其中,`ibv_qp_cap`结构体定义了QP的队列容量,其定义如下:```cstructibv_qp_cap{uint32_tmax_send_wr;/*发送队列的最大工作请求(WR)数量*/uint32_tmax_recv_wr;/*接收队列的最大工作请求(WR)数量*/uint32_tmax_send_sge;/*每个发送WR的最大分散/聚集元素(SGE)数量*/uint32_tmax_recv_sge;/*每个接收WR的最大分散/聚集元素(SGE)数量*/uint32_tmax_inline_data;/*内联数据的最大大小(字节),如果支持内联发送,则发送小消息时可以直接在WR中携带数据而不需要内存注册*/};```###字段详细说明1.**qp_context**:用户自定义的指针,可以在创建QP时设置,通常用于在异步事件处理中识别QP。例如,当QP发生异步事件(如错误)时,这个指针会被传递到事件处理函数中。2.**send_cq**和**recv_cq**:分别指向发送完成队列和接收完成队列。完成队列(CQ)用于报告工作请求(WR)的完成状态。注意,发送和接收可以共享同一个CQ,也可以使用不同的CQ。3.**srq**:指向共享接收队列(SRQ)的指针。如果使用SRQ,则接收队列由多个QP共享,而不是每个QP有自己的专用接收队列。如果不使用SRQ,则设置为NULL,此时QP使用自己的专用接收队列。4.**cap**:QP的能力结构体,用于设置队列的大小和每个WR可以处理的SGE数量等。这些值不能超过设备支持的最大值(可通过`ibv_query_device`获取设备能力)。需要注意的是,实际分配的队列大小可能小于请求的值(由于资源限制),创建后可以通过`ibv_query_qp`来查询实际分配的大小。5.**qp_type**:指定QP的类型,常见的类型有:-`IBV_QPT_RC`:可靠连接(ReliableConnection),提供可靠、有序的传输。-`IBV_QPT_UC`:不可靠连接(UnreliableConnection),提供不可靠、有序的传输(消息可能丢失,但不乱序)。-`IBV_QPT_UD`:不可靠数据报(UnreliableDatagram),提供不可靠、无序的传输(消息可能丢失和乱序),支持组播。-`IBV_QPT_RAW_PACKET`:原始数据包(需要特殊权限)。-其他类型(如`IBV_QPT_XRC`)等。6.**sq_sig_all**:控制发送请求的完成通知行为:-如果设置为1,那么每次调用`ibv_post_send`提交的发送请求(WR)都会在完成时产生一个完成事件(CQE),无论该WR是否设置了`IBV_SEND_SIGNALED`标志。-如果设置为0,那么只有发送请求中明确设置了`IBV_SEND_SIGNALED`标志的WR才会产生完成事件。###用途`ibv_qp_init_attr`结构体在创建QP时使用,作为`ibv_create_qp`函数的参数。例如:```cstructibv_qp*ibv_create_qp(structibv_pd*pd,structibv_qp_init_attr*qp_init_attr);```其中,`pd`是保护域(ProtectionDomain),`qp_init_attr`就是上述的初始化属性结构体。###使用示例以下是一个创建QP的示例代码片段:```cstructibv_qp_init_attrqp_init_attr={.qp_context=NULL,.send_cq=send_cq,//指向之前创建的发送CQ.recv_cq=recv_cq,//指向之前创建的接收CQ.srq=NULL,//不使用SRQ.cap={.max_send_wr=100,//发送队列最多100个WR.max_recv_wr=100,//接收队列最多100个WR.max_send_sge=1,//每个发送WR最多1个SGE.max_recv_sge=1,//每个接收WR最多1个SGE.max_inline_data=0//不使用内联数据},.qp_type=IBV_QPT_RC,//可靠连接.sq_sig_all=0//仅当WR设置了IBV_SEND_SIGNALED时才产生完成事件};structibv_qp*qp=ibv_create_qp(pd,&qp_init_attr);if(!qp){//错误处理}```###注意事项-在创建QP时,`cap`中的值不能超过设备支持的最大值。可以通过`ibv_query_device`获取设备的能力(`structibv_device_attr`中的相关字段)。-创建QP后,QP的状态为`RESET`,需要经过一系列状态转换(修改QP属性)才能进入`RTS`(ReadytoSend)状态,才能进行通信。-如果使用内联发送(inlinesend),需要设置`cap.max_inline_data`,并且在发送请求中设置`IBV_SEND_INLINE`标志(如引用[2]所示)。注意,内联发送的数据大小不能超过`max_inline_data`。###引用说明在创建QP时,我们使用了保护域(PD)和完成队列(CQ),这些都需要提前创建。同时,引用[2]中提到了内联发送(`IBV_SEND_INLINE`),这种发送方式允许将小数据直接放在WR中发送,而不需要内存注册和DMA操作,但需要硬件支持,并且在创建QP时指定`max_inline_data`。引用[1]和[2]展示了如何发送工作请求,其中引用[1]使用了正常的发送操作,而引用[2]使用了内联发送。[^1]:引用自用户提供的RDMA代码片段[^2]:引用自用户提供的RDMA内联发送说明[^3]:引用自用户提供的创建QP说明§§相关问题§§1.如何查询RDMA设备支持的最大QP能力?2.QP状态机转换的步骤是怎样的?3.共享接收队列(SRQ)有什么优势?4.内联发送(inlinesend)的使用场景和限制是什么?</think>### `ibv_qp_init_attr` 结构体详解 在 RDMA(远程直接内存访问)编程中,`ibv_qp_init_attr` 结构体用于初始化**队列对(Queue Pair, QP)** 的属性。它是创建 QP 时通过 `ibv_create_qp()` 函数传递的核心参数[^3]。以下是其定义和关键字段的详细说明: #### 结构体定义(基于 libibverbs 库) ```c struct ibv_qp_init_attr { void *qp_context; // 用户自定义上下文指针 struct ibv_cq *send_cq; // 关联的发送完成队列(CQ) struct ibv_cq *recv_cq; // 关联的接收完成队列(CQ) struct ibv_srq *srq; // 关联的共享接收队列(SRQ),可为NULL struct ibv_qp_cap cap; // QP 能力参数(队列大小等) enum ibv_qp_type qp_type; // QP 类型(RC/UC/UD等) int sq_sig_all; // 发送队列的信号模式控制 }; ``` #### 关键字段解析 1. **`qp_context`** - 用户自定义指针,可用于在异步事件中标识 QP(如通过 `ibv_get_async_event()`)。 2. **`send_cq` 与 `recv_cq`** - 分别指定发送和接收操作的完成队列(CQ)。若共用同一个 CQ,则两者可指向相同的 `ibv_cq` 对象。 3. **`srq`** - 若使用共享接收队列(SRQ),指向已创建的 `ibv_srq` 对象;否则设为 `NULL`。使用 SRQ 可减少接收缓冲区的总内存占用[^3]。 4. **`cap`(能力参数)** ```c struct ibv_qp_cap { uint32_t max_send_wr; // 发送队列最大工作请求数(WR) uint32_t max_recv_wr; // 接收队列最大工作请求数(WR) uint32_t max_send_sge; // 每个发送 WR 的最大分散/聚集元素(SGE) uint32_t max_recv_sge; // 每个接收 WR 的最大 SGE uint32_t max_inline_data; // 内联发送支持的最大数据大小(字节) }; ``` - **示例值**:`{ .max_send_wr=100, .max_recv_wr=200, .max_send_sge=16, ... }` - 注意:实际值受限于设备能力(通过 `ibv_query_device()` 获取)。 5. **`qp_type`(QP 类型)** - 定义通信语义: - `IBV_QPT_RC`:可靠连接(Reliable Connection) - `IBV_QPT_UC`:不可靠连接(Unreliable Connection) - `IBV_QPT_UD`:不可靠数据报(Unreliable Datagram) - `IBV_QPT_RAW_PACKET`:原始以太网包(需特权) - 选择依据:可靠性需求(如 RC 用于关键数据)、有序性、多播支持等。 6. **`sq_sig_all`(信号模式)** - 控制发送请求的完成通知: - **`0`**:仅当 WR 显式设置 `IBV_SEND_SIGNALED` 标志时生成完成事件(CQE)[^2]。 - **`1`**:所有发送 WR 自动生成 CQE(可能影响性能)。 #### 使用示例 ```c struct ibv_qp_init_attr qp_init_attr = { .send_cq = send_cq, // 指向已创建的发送 CQ .recv_cq = recv_cq, // 指向已创建的接收 CQ .srq = NULL, // 不使用 SRQ .cap = { .max_send_wr = 128, .max_recv_wr = 256, .max_send_sge = 8, .max_recv_sge = 8, .max_inline_data = 64 // 支持 ≤64 字节的内联发送 }, .qp_type = IBV_QPT_RC, // 可靠连接 .sq_sig_all = 0 // 按需触发完成事件 }; struct ibv_qp *qp = ibv_create_qp(pd, &qp_init_attr); // pd 为保护域 if (!qp) { /* 错误处理 */ } ``` > 创建后需通过 `ibv_modify_qp()` 将 QP 状态从 `RESET` 转换至 `RTS`(Ready to Send)[^3]。 #### 关键设计考量 1. **内联数据(Inline Data)** - 若 `max_inline_data > 0`,可在发送 WR 中设置 `IBV_SEND_INLINE` 标志,将小数据直接嵌入 WR(无需内存注册),降低延迟[^2]。 - 例如:发送 `buffer_size ≤ max_inline_data` 时使用内联模式。 2. **性能优化** - 避免过度分配 `max_send_wr/max_recv_wr`,以免浪费资源。 - 根据流量模式选择 `sq_sig_all`:低延迟场景建议设为 `0` 以减少 CQE 数量。 3. **资源限制** - 所有参数值必须符合设备支持的上限(通过 `ibv_query_device_ex()` 获取)。 [^1]: RDMA 发送操作示例(非内联模式) [^2]: 内联发送模式的优势与实现 [^3]: QP 创建与状态转换流程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值