一、ipoib_send函数定义
int ipoib_send(struct net_device *dev, struct sk_buff *skb,struct ib_ah *address, u32 dqpn)
{struct ipoib_dev_priv *priv = ipoib_priv(dev);struct ipoib_tx_buf *tx_req;int hlen, rc;void *phead;unsigned int usable_sge = priv->max_send_sge - !!skb_headlen(skb);if (skb_is_gso(skb)) {hlen = skb_transport_offset(skb) + tcp_hdrlen(skb);phead = skb->data;if (unlikely(!skb_pull(skb, hlen))) {ipoib_warn(priv, "linear data too small\n");++dev->stats.tx_dropped;++dev->stats.tx_errors;dev_kfree_skb_any(skb);return -1;}} else {if (unlikely(skb->len > priv->mcast_mtu + IPOIB_ENCAP_LEN)) {ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n",skb->len, priv->mcast_mtu + IPOIB_ENCAP_LEN);++dev->stats.tx_dropped;++dev->stats.tx_errors;ipoib_cm_skb_too_long(dev, skb, priv->mcast_mtu);return -1;}phead = NULL;hlen = 0;}if (skb_shinfo(skb)->nr_frags > usable_sge) {if (skb_linearize(skb) < 0) {ipoib_warn(priv, "skb could not be linearized\n");++dev->stats.tx_dropped;++dev->stats.tx_errors;dev_kfree_skb_any(skb);return -1;}/* Does skb_linearize return ok without reducing nr_frags? */if (skb_shinfo(skb)->nr_frags > usable_sge) {ipoib_warn(priv, "too many frags after skb linearize\n");++dev->stats.tx_dropped;++dev->stats.tx_errors;dev_kfree_skb_any(skb);return -1;}}ipoib_dbg_data(priv,"sending packet, length=%d address=%p dqpn=0x%06x\n",skb->len, address, dqpn);/** We put the skb into the tx_ring _before_ we call post_send()* because it's entirely possible that the completion handler will* run before we execute anything after the post_send(). That* means we have to make sure everything is properly recorded and* our state is consistent before we call post_send().*/tx_req = &priv->tx_ring[priv->tx_head & (priv->sendq_size - 1)];tx_req->skb = skb;if (skb->len < ipoib_inline_thold &&!skb_shinfo(skb)->nr_frags) {tx_req->is_inline = 1;priv->tx_wr.wr.send_flags |= IB_SEND_INLINE;} else {if (unlikely(ipoib_dma_map_tx(priv->ca, tx_req))) {++dev->stats.tx_errors;dev_kfree_skb_any(skb);return -1;}tx_req->is_inline = 0;priv->tx_wr.wr.send_flags &= ~IB_SEND_INLINE;}if (skb->ip_summed == CHECKSUM_PARTIAL)priv->tx_wr.wr.send_flags |= IB_SEND_IP_CSUM;elsepriv->tx_wr.wr.send_flags &= ~IB_SEND_IP_CSUM;/* increase the tx_head after send success, but use it for queue state */if (atomic_read(&priv->tx_outstanding) == priv->sendq_size - 1) {ipoib_dbg(priv, "TX ring full, stopping kernel net queue\n");netif_stop_queue(dev);}skb_orphan(skb);skb_dst_drop(skb);if (netif_queue_stopped(dev))if (ib_req_notify_cq(priv->send_cq, IB_CQ_NEXT_COMP |IB_CQ_REPORT_MISSED_EVENTS) < 0)ipoib_warn(priv, "request notify on send CQ failed\n");rc = post_send(priv, priv->tx_head & (priv->sendq_size - 1),address, dqpn, tx_req, phead, hlen);if (unlikely(rc)) {ipoib_warn(priv, "post_send failed, error %d\n", rc);++dev->stats.tx_errors;if (!tx_req->is_inline)ipoib_dma_unmap_tx(priv, tx_req);dev_kfree_skb_any(skb);if (netif_queue_stopped(dev))netif_wake_queue(dev);rc = 0;} else {netif_trans_update(dev);rc = priv->tx_head;++priv->tx_head;atomic_inc(&priv->tx_outstanding);}return rc;
}
二、函数解读
是的,这个函数`ipoib_send`是IPoIB驱动中的一个函数,它的责任是从上层网络协议栈接收数据并使用InfiniBand(IB)将其发送到网络中。具体的步骤包括:
1. ipoib_send接收四个参数:`net_device *dev`指向网络设备的指针;`struct sk_buff *skb`是要发送的套接字缓冲区(socket buffer);`struct ib_ah *address`是目标地址句柄(address handle),用于IB通信;`u32 dqpn`是目的QP(Queue Pair)编号。
2. 函数首先通过`ipoib_priv(dev)`获取与网络设备关联的IPoIB设备私有数据结构。
3. 然后这个函数检查传入的`skb`是否是一个GSO包(Generic Segmentation Offload),也就是大型数据包需要分段发送的情况。如果是,它计算传输头的长度,并通过`skb_pull`调整`skb`的数据指针,排除这些头部。
4. 如果`skb`的长度超过了IPoIB的最大传输单位(MTU)加上封装的长度,函数会打印一条警告信息,递增统计计数器,处理`skb`为过长,并返回。
5. 接下来的代码检查是否有足够的scatter/gather元素(分布/聚集元素)去描述`skb`的数据。如果不是,会尝试使`skb`变成一个线性的数据结构;如果线性化失败,函数会记录一个错误,释放`skb`并返回。
6. 在将`skb`发送到IB硬件之前,函数将它放入传输队列`tx_ring`。这个队列是一个环形缓冲区,用来存储即将被发送或正等待完成通知的包。
7. 函数设置`tx_req`结构体中与`skb`相关的字段,并根据条件判定数据是否可以内联发送(内联发送即数据随发送请求一起发送,而非通过DMA读取)。
8. 函数还设置是否启用IP校验和卸载功能。
9. 在发送操作之前,代码检查传输队列是否已满。如果满了,则会停止网络队列。
10. 函数 then calls post_send,该函数负责准备IB发送请求并提交到硬件进行处理。
11. 如果`post_send`返回错误,函数会记录错误,回滚上面的操作,并唤醒网络队列(如果它停止了)。
12. 如果`post_send`成功,函数修正`tx_head`,也就是传输队列头的位置,并增加处理中的传输数量`tx_outstanding`。
13. 最后,函数返回`post_send`的结果或者一个错误码。如果成功,返回的是之前增加的`tx_head`的值。
总体来说,`ipoib_send`负责处理将要发送的数据包,包括准备IB发送请求,处理数据包的线性化和分散/聚集元素,以及向IB硬件提交发送任务等。如果传输过程中遇到错误,它会统一处理错误情况,包括记录错误,释放资源,及时唤醒网络队列等。