一、中文注释
这个函数是用来处理 Infiniband 设备在传输完成时的回调。该回调负责释放发送队列中的缓冲区并更新网络设备统计信息。
cpp
static void ipoib_ib_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
{
// 通过net_device结构体获取私有数据结构
struct ipoib_dev_priv *priv = ipoib_priv(dev);
// 获取工作请求ID,这个ID在发送时被赋予,用于标识对应的缓冲区
unsigned int wr_id = wc->wr_id;
struct ipoib_tx_buf *tx_req;
// 调试信息,输出完成的工作请求的ID和状态
ipoib_dbg_data(priv, "send completion: id %d, status: %d\n",
wr_id, wc->status);
// 检查wr_id是否有效
if (unlikely(wr_id >= priv->sendq_size)) {
// 如果不是,打印警告信息
ipoib_warn(priv, "send completion event with wrid %d (> %d)\n",
wr_id, priv->sendq_size);
return;
}
// 使用工作请求ID来获取发送队列中的缓冲区
tx_req = &priv->tx_ring[wr_id];
// 如果不是内联传输,就需要去除DMA映射
if (!tx_req->is_inline)
ipoib_dma_unmap_tx(priv, tx_req);
// 更新网络设备的发送包统计信息
++dev->stats.tx_packets;
dev->stats.tx_bytes += tx_req->skb->len;
// 释放socket缓冲区
dev_kfree_skb_any(tx_req->skb);
tx_req->skb = NULL;
// 更新发送尾部指针和未完成的发送计数
++priv->tx_tail;
atomic_dec(&priv->tx_outstanding);
// 如果网络队列停止了,并且未完成的发送数少于发送队列大小的一半,同时设备标记为"管理上行"则重新唤起网络队列
if (unlikely(netif_queue_stopped(dev) &&
(atomic_read(&priv->tx_outstanding) <= priv->sendq_size >> 1) &&
test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags)))
netif_wake_queue(dev);
// 如果完成状态不是成功且不是"刷新错误",那么会对QP状态进行验证和修复
if (wc->status != IB_WC_SUCCESS &&
wc->status != IB_WC_WR_FLUSH_ERR) {
// 打印错误警告信息
ipoib_warn(priv,
"failed send event (status=%d, wrid=%d vend_err %#x)\n",
wc->status, wr_id, wc->vendor_err);
// 为QP验证工作分配内存
struct ipoib_qp_state_validate *qp_work;
qp_work = kzalloc(sizeof(*qp_work), GFP_ATOMIC);
// 如果内存分配失败就直接返回
if (!qp_work)
return;
// 初始化工作队列项并将它放到工作队列中去
INIT_WORK(&qp_work->work, ipoib_qp_state_validate_work);
qp_work->priv = priv;
queue_work(priv->wq, &qp_work->work);
}
}
该函数的作用是处理InfiniBand网络设备在发送数据完成后的清理工作。它检查完成状态,释放资源并更新发送队列状态,若发送失败则可能会尝试修复QP(Queue Pair)状态。
二、中文讲解
这个函数 ipoib_ib_handle_tx_wc
是处理IP-over-Infiniband(IPoIB)传输完成工作请求(Work Completion,WC)的函数,主要用于处理在IPoIB网络设备上完成发送操作的情况。这个函数是Linux内核中IPoIB模块的一部分,用来处理InfiniBand协议栈发送操作的完成事件。
下面将逐条中文解释该函数的逻辑:
cpp
// 函数声明,接受一个网络设备和一个工作完成结构作为参数
static void ipoib_ib_handle_tx_wc(struct net_device *dev, struct ib_wc *wc) {
// 从网络设备结构获取IPoIB设备私有数据
struct ipoib_dev_priv *priv = ipoib_priv(dev);
// 获取工作请求ID
unsigned int wr_id = wc->wr_id;
struct ipoib_tx_buf *tx_req;
// 输出调试信息,包括工作请求ID和状态
ipoib_dbg_data(priv, "send completion: id %d, status: %d\n", wr_id, wc->status);
// 检查工作请求ID是否有效,如果不在发送队列范围内,打印警告并返回
if (unlikely(wr_id >= priv->sendq_size)) {
ipoib_warn(priv, "send completion event with wrid %d (> %d)\n", wr_id, priv->sendq_size);
return;
}
// 获取对应工作请求ID的发送缓冲区
tx_req = &priv->tx_ring[wr_id];
// 如果发送请求并非内联的(数据不是和描述符一起发送的),则撤销DMA映射
if (!tx_req->is_inline)
ipoib_dma_unmap_tx(priv, tx_req);
// 更新网络设备统计信息:增加发送的数据包和字节计数
++dev->stats.tx_packets;
dev->stats.tx_bytes += tx_req->skb->len;
// 释放对应的skb(socket缓冲区),并将指针置空
dev_kfree_skb_any(tx_req->skb);
tx_req->skb = NULL;
// 增加发送队列尾部索引,表示处理了一个发送请求
++priv->tx_tail;
// 原子减少正在处理的发送请求计数
atomic_dec(&priv->tx_outstanding);
// 如果网络队列停止,且未完成的发送请求数量小于发送队列大小的一半
// 并且网络接口状态为上,那么重新启动网络队列
if (unlikely(netif_queue_stopped(dev) &&
(atomic_read(&priv->tx_outstanding) <= priv->sendq_size >> 1) &&
test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags)))
netif_wake_queue(dev);
// 如果工作完成状态非成功,并且不是因为刷新错误
if (wc->status != IB_WC_SUCCESS && wc->status != IB_WC_WR_FLUSH_ERR) {
struct ipoib_qp_state_validate *qp_work;
// 打印警告,发送请求失败
ipoib_warn(priv, "failed send event (status=%d, wrid=%d vend_err %#x)\n",
wc->status, wr_id, wc->vendor_err);
// 为验证队列对状态分配工作并初始化
qp_work = kzalloc(sizeof(*qp_work), GFP_ATOMIC);
if (!qp_work)
return;
INIT_WORK(&qp_work->work, ipoib_qp_state_validate_work);
qp_work->priv = priv;
// 将验证工作加入到工作队列中
queue_work(priv->wq, &qp_work->work);
}
}
从代码可以看出,这个函数的主要作用是处理InfiniBand设备发送缓冲区完成工作事件,释放资源,更新统计数据,以及在发送操作失败时进行错误处理。