【C语言】linux内核ipoib模块 - ipoib_send

发布时间:2024年01月19日

一、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;
    else
        priv->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硬件提交发送任务等。如果传输过程中遇到错误,它会统一处理错误情况,包括记录错误,释放资源,及时唤醒网络队列等。

文章来源:https://blog.csdn.net/eidolon_foot/article/details/135576225
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。