一、ipoib_cm_post_receive_nonsrq_rss函数定义
static int ipoib_cm_post_receive_nonsrq_rss(struct net_device *dev,
struct ipoib_cm_rx *rx, int id)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
struct ipoib_recv_ring *recv_ring = priv->recv_ring + rx->index;
struct ib_sge *sge;
struct ib_recv_wr *wr;
int i, ret;
sge = recv_ring->cm.rx_sge;
wr = &recv_ring->cm.rx_wr;
wr->wr_id = id | IPOIB_OP_CM | IPOIB_OP_RECV;
for (i = 0; i < IPOIB_CM_RX_SG; ++i)
sge[i].addr = rx->rx_ring[id].mapping[i];
ret = ib_post_recv(rx->qp, wr, NULL);
if (unlikely(ret)) {
ipoib_warn(priv, "post recv failed for buf %d (%d)\n", id, ret);
ipoib_cm_dma_unmap_rx(priv, IPOIB_CM_RX_SG - 1,
rx->rx_ring[id].mapping);
dev_kfree_skb_any(rx->rx_ring[id].skb);
rx->rx_ring[id].skb = NULL;
}
return ret;
}
二、解读
解读1
这段代码是Linux内核中的一个函数,用于在使用RDMA(远程直接内存访问)协议的InfiniBand网卡上进行接收数据。具体来说,这段代码用于在RDMA接收队列(rx->qp)上提交一个接收工作请求(wr),以便接收数据。
首先,函数从net_device结构体中获取与RDMA相关的私有数据结构ipoib_dev_priv。然后,它根据接收队列的索引(rx->index)获取接收环的指针(recv_ring),接收环包含了一些用于接收数据的缓冲区描述符。
接下来,函数从接收环中获取RDMA接收工作请求的描述符(wr)和缓冲区的描述符(sge)。wr->wr_id是一个用于标识接收工作请求的唯一值,通过将id与IPOIB_OP_CM和IPOIB_OP_RECV相或运算得到。
然后,函数使用rx->rx_ring[id].mapping数组中的缓冲区地址来初始化sge数组中的每个元素。这些缓冲区是之前分配给接收队列的。
最后,函数调用ib_post_recv函数将接收工作请求提交到接收队列上。如果返回值不为0,则表示提交失败,函数会释放相关资源(解映射DMA内存和释放缓冲区)。
总之,这段代码是一个在RDMA接收队列上提交接收工作请求的函数,用于接收来自RDMA协议的数据。
解读2
这段代码是Linux内核中的一个模块驱动函数,用于处理IP over InfiniBand (IPoIB) 的非Shared Receive Queue (SRQ) 情况下的Connected Mode (CM) 接收路径。函数`ipoib_cm_post_receive_nonsrq_rss`与之前讨论的`ipoib_cm_post_receive_srq_rss` 类似,但是针对的是非SRQ,也即每个Queue Pair (QP) 有自己独立的接收队列。这辟出来的通道允许接收侧的扩展(RSS),用来在多个CPU之间分配网络处理任务。下面来详细解析这段代码:
1. 函数接收三个参数:
- *dev:指向 net_device 结构的指针,它与InfiniBand 网络设备相关联。
- *rx:指向一个 ipoib_cm_rx 结构的指针,该结构包含了与特定CM接收队列相关的信息。
- id:一个用于追踪特定接收缓冲区的标识符。
2. 通过 ipoib_priv(dev) 获取到一个指向IPoIB 私有结构(`ipoib_dev_priv`)的指针。
3. 计算对应接收环(`ipoib_recv_ring`)的地址,它是通过在IPoIB私有结构中的`recv_ring`加上`rx`提供的索引(`index`)得到的。
4. 设置局部指针变量指向Scatter-Gather元素(`ib_sge`)和接收工作请求(`ib_recv_wr`),这两者都从`recv_ring` 结构中获得。
5. 设置接收工作请求(Receive Work Request)的`wr_id`字段,将提供的 id 加上 IPOIB_OP_CM 和 IPOIB_OP_RECV 标志,表明这个操作与Connected Mode的接收动作有关。
6. 一个通过 IPOIB_CM_RX_SG 控制的循环用来设置 ib_sge 数组的地址(`addr`)。这些地址指向的是内存映射,硬件会使用它们在接收到包的时候放入其中。
7. 通过 ib_post_recv() 将接收工作请求提交到对应的Queue Pair (QP)。这个函数通常需要QP,指向工作请求的指针,以及用于存放下一个工作请求指针的指针(在这里是空指针NULL,因为不需要)。
8. 如果调用 ib_post_recv() 失败(返回非零值),通过 ipoib_warn() 记录错误信息。然后调用 ipoib_cm_dma_unmap_rx() 进行DMA解映射处理,并通过调用 dev_kfree_skb_any() 释放关联的套接字缓冲区,并将 skb 字段设置为 NULL。
9. 最后,这个函数返回来自 ib_post_recv() 的状态代码(`ret`),它指示成功(0)或错误码。
这个代码片段依然是源自于一个比较底层的Linux内核驱动程序,涉及到内核开发、InfiniBand架构、设备驱动程序和直接内存访问(DMA)。因此,它处理的网络设备操作和内存管理是由InfiniBand规范和驱动程序的架构决定的
解读3
这段代码是一个Linux内核模块驱动的一部分,特定于InfiniBand(IB)通信中的非SRQ(Subscription/Notification Queue)接收操作。下面是对代码的中文解释:
-
函数定义:
static int ipoib_cm_post_receive_nonsrq_rss(struct net_device *dev, struct ipoib_cm_rx *rx, int id)
:这是一个静态函数,返回类型为int
。它接收三个参数:一个网络设备结构体指针dev
,一个指向ipoib_cm_rx
结构体的指针rx
,和一个整数id
。
-
局部变量:
struct ipoib_dev_priv *priv = ipoib_priv(dev);
:从网络设备结构体中获取私有数据结构体指针。struct ipoib_recv_ring *recv_ring = priv->recv_ring + rx->index;
:根据给定的索引从私有数据结构体的接收环中获取接收环结构体指针。struct ib_sge *sge;
:定义一个指向InfiniBand SGE(Scatter Gather Element)的指针。struct ib_recv_wr *wr;
:定义一个指向InfiniBand接收写请求的指针。int i, ret;
:定义整数变量i和ret。
-
设置写请求:
sge = recv_ring->cm.rx_sge;
:将SGE指针设置为接收环中的SGE。wr = &recv_ring->cm.rx_wr;
:将写请求指针设置为接收环中的写请求。wr->wr_id = id | IPOIB_OP_CM | IPOIB_OP_RECV;
:设置写请求的ID,由id、IPOIB_OP_CM和IPOIB_OP_RECV按位或运算得到。
-
设置SGE:
- 循环遍历私有数据结构体的碎片数量,并设置每个SGE的地址。
-
发送接收请求:
ret = ib_post_recv(rx->qp, wr, NULL);
:使用InfiniBand API发送接收请求。如果返回值不为0,则进入错误处理部分。
-
错误处理:
- 如果返回值ret不为0,则打印警告信息,取消DMA映射,并释放相关缓冲区。最后返回ret。
-
返回值:
- 函数返回ret,即接收请求的返回值。如果成功,则返回0;否则返回非0值。
总体来说,这个函数的主要目的是通过InfiniBand接口发送非SRQ接收请求,并处理可能发生的错误情况。