【C语言】linux内核ipoib模块 - 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); // 获取IPoIB设备的私有数据
    struct ipoib_tx_buf *tx_req; // 发送请求结构体
    int hlen, rc; // 分别为头部长度和返回码
    void *phead; // 要发送数据包的头部指针
    unsigned int usable_sge = priv->max_send_sge - !!skb_headlen(skb); // 计算可用的Scatter/Gather Element的数量
    // 检查是否为GSO分段传输的skb
    if (skb_is_gso(skb)) {
        hlen = skb_transport_offset(skb) + tcp_hdrlen(skb); // 计算GSO的头部长度
        phead = skb->data; // 获取skb数据的头部指针
        // 如果skb_pull返回错误,则skb数据区太小
        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); // 释放skb
            return -1;
        }
    // 非GSO传输的处理
    } else {
        // 如果skb的长度过长,则丢弃
        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); // 处理过长的skb
            return -1;
        }
        phead = NULL; // 无需处理头部
        hlen  = 0; // 头部长度为0
    }
    // 检查是否存在太多的skb碎片
    if (skb_shinfo(skb)->nr_frags > usable_sge) {
        // 尝试线性化skb,如果有错误则丢弃
        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); // 释放skb
            return -1;
        }
        // 即使线性化后,碎片数仍然过多则丢弃
        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); // 释放skb
            return -1;
        }
    }
    ipoib_dbg_data(priv,
               "sending packet, length=%d address=%p dqpn=0x%06x\n", // 调试信息:将要发送的数据包信息
               skb->len, address, dqpn);
    // 使用tx_ring队列缓存skb,在确保所有记录和状态一致后调用post_send()
    tx_req = &priv->tx_ring[priv->tx_head & (priv->sendq_size - 1)];
    tx_req->skb = 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 {
        // 处理DMA映射
        if (unlikely(ipoib_dma_map_tx(priv->ca, tx_req))) {
            ++dev->stats.tx_errors; // 统计错误数
            dev_kfree_skb_any(skb); // 释放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;
    // 如果发送队列满了,则暂停网络队列
    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); // 丢弃路由缓存项

    // 如果网络队列停止,则请求在发送完CQ后通知,以便我们可以唤醒它
    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), // 调用post_send函数发送数据包
               address, dqpn, tx_req, phead, hlen);
    if (unlikely(rc)) {
        ipoib_warn(priv, "post_send failed, error %d\n", rc); // 如果post_send出现错误,记录错误信息
        ++dev->stats.tx_errors; // 统计错误数
        if (!tx_req->is_inline)
            ipoib_dma_unmap_tx(priv, tx_req); // 如果不是内联则释放DMA映射
        dev_kfree_skb_any(skb); // 释放skb
        if (netif_queue_stopped(dev)) // 如果网络队列停止,则唤醒
            netif_wake_queue(dev);
        rc = 0; // 将返回码设置为0
    } else {
        netif_trans_update(dev); // 更新最后一个传输的时间戳

        rc = priv->tx_head; // 设置返回码为当前的tx_head
        ++priv->tx_head; // tx_head自增,为下一次发送做准备
        atomic_inc(&priv->tx_outstanding); // 增加未决的发送操作计数
    }
    return rc; // 返回操作结果,成功发送则返回tx_head,失败返回0
}

这个函数是一个Linux内核的网络设备发送函数,用于IP over InfiniBand (IPoIB) 模块。它负责准备并发送用户数据包(skb),并操作网络设备统计信息和发送队列。当不能正确发送或者处理数据包时,该函数将适当地释放资源,并通过相应的返回码和内部统计来标识错误的发生。

二、中文讲解

这段代码是一个InfiniBand网络设备的发送函数,用于在IP over InfiniBand(IPoIB)网络中发送数据。以下是对该函数功能的中文讲解:

int ipoib_send(struct net_device *dev, struct sk_buff *skb, struct ib_ah *address, u32 dqpn)

函数`ipoib_send`有四个参数:
- dev表示要发送数据的网络设备。
- skb是一个指向socket缓冲区(数据包)的指针。
- address是远程端点的InfiniBand地址句柄。
- dqpn是目的端口号。
函数内部的变量说明:
- priv是设备的私有数据结构。
- tx_req表示用于发送的缓冲区。
- hlen是头部的长度,表示在何处开始线性化处理(如果是GSO(大尺寸传输优化)数据包)。
- rc是函数返回码。
- phead是指向数据头部的指针。
- usable_sge是扣除头部数据后剩余可用的分散/聚集元素(Scatter/Gather Elements)数量。
代码流程梳理:
1. 首先判断是不是GSO数据包,如果是,则记录下头部信息并处理skb缓冲区。如果skb太小无法处理,则丢弃并返回。
2. 如果不是GSO数据包,但skb长度超出最大传输单元(MTU),也会丢弃数据包并返回。
3. 检查数据包中的片段(frags)数量,如果超过可用的SGE数量,则尝试线性化skb。线性化失败或线性化后frags仍然太多,则丢弃数据包。
4. 设置传输请求结构`tx_req`并决定是否以内联方式发送数据。如果数据长度小于阈值且无frags,则设为内联。
5. 如果传输队列(TX ring)快满了,停止继续入队新的数据包。
6. 为数据包的发送作最后准备,包括释放相关资源,比如删除路由相关信息,并对发送队列和完成队列进行相关的操作。
7. 调用`post_send`来实际发送数据包。
8. 根据发送结果进行处理。如果失败,释放资源,并根据队列状态恢复传输队列或者记录错误。
这段代码的关键点在于管理数据包缓冲区,并且确保数据可以被有效的发送到InfiniBand网络。涉及到内存管理,异步通信,以及在发送之前后处理网络设备状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/408891.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安装 WSL 报错 Error code: Wsl/WININET_E_NAME_NOT_RESOLVED 问题解决

问题描述 在执行 wsl --install 安装Windows子系统Linux WSL (Windows Subsystem for Linux) 时报错&#xff1a; 无法从“https://raw.githubusercontent.com/microsoft/WSL/master/distributions/DistributionInfo.json”中提取列表分发。无法解析服务器的名称或地址 Error…

如何在本地电脑部署HadSky论坛并发布至公网可远程访问【内网穿透】

文章目录 前言1. 网站搭建1.1 网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3 Cpolar稳定隧道&#xff08;本地设置&#xff09;2.4 公网访问测试 总结 前言 经过多年的基础…

2000-2022年上市公司全要素生产率测算数据合计(原始数据+计算代码+结果)(LP法+OLS法+GMM法+固定效应法)

2000-2022年上市公司全要素生产率测算数据合计&#xff08;原始数据计算代码结果&#xff09;&#xff08;LP法OLS法GMM法固定效应法&#xff09; 1、时间&#xff1a;2000-2022年 2、范围&#xff1a;上市公司 3、指标&#xff1a;证券代码、证券简称、统计截止日期、固定资…

怎么自学python,大概要多久?python多久上手?

无限时长~~~~技术不断在更新&#xff0c;你的自学不也需要一直进行吗&#xff1f; 但如果是问&#xff1a;自学多长时间可以入门&#xff1f;或者可以找到工作&#xff1f;那我可以告诉你答案。 从零基础开始自学Python&#xff0c;依照每个人理解能力的不同&#xff0c;大致…

免费的WP模板网站推荐

免费wordpress模板下载 高端大气上档次的免费wordpress主题&#xff0c;首页大图全屏显示经典风格的wordpress主题。 https://www.wpniu.com/themes/289.html wordpress免费企业主题 深蓝色经典实用的wordpress网站模板&#xff0c;用wordpress免费企业主题搭建网站。 http…

Linux进程【补充】

文章目录 进程概念task_struct 进程创建forkvfork写时拷贝 进程状态僵尸进程孤儿进程守护进程 进程地址空间是什么为什么怎么做 进程概念 进程是一个程序的执行实例或者是担当系统资源分配的实体。当一个程序运行时&#xff0c;被从硬盘加载到内存中&#xff0c;操作系统为每个…

python 线程笔记一 (概念+示例代码)

1. 线程的概念 线程&#xff0c;可简单理解为是程序执行的一条分支&#xff0c;也是程序执行流的最小单元。线程是被系统独立调度和分派的基本单位&#xff0c;线程自己不拥有系统资源&#xff0c;只拥有一点儿在运行中必不可少的资源&#xff0c;但它可与同属于一个进程的其它…

第10届蓝桥杯Scratch图形化编程 选拔赛初级组编程题1:小猫走城堡

准备工作: 导入育最库中的“Castle 3 小猫从坐标点(-165&#xff0c;-93)出发向城堡走去。随着位置的移动&#xff0c;角色大小逐渐变小&#xff0c;最后在城堡前消失。注意1.角色大小在逐渐变化&#xff0c;运行结束再次点击绿旗&#xff0c;程序应还能再次扶行。2.角色应该是…

云原生之API网关Traefik

1. 前言 说到web服务的开源网关&#xff0c;我首先想到的是Nginx&#xff0c;最早使用的就是它&#xff0c;现在都还在使用它。系统上线了Docker Swarm集群之后&#xff0c;不继续使用Nginx直接做Docker服务的网关&#xff0c;是因为Nginx毕竟比Docker Swarm出现的早&#xff0…

YOLOv7基础 | 第2种方式:简化网络结构之yolov7.yaml(由104层简化为30层)

前言:Hello大家好,我是小哥谈。通过下载YOLOv7源码可知,原始的yolov7.yaml文件是拆开写的,比较混乱,也不好理解,并且为后续改进增添了很多困难。基于此种情况,笔者就给大家介绍一种将yolov7.yaml文件简化的方法,将104层简化为30层,并且参数量和计算量和原来是一致的,…

✅鉴权—cookie、session、token、jwt、单点登录

基于 HTTP 的前端鉴权背景cookie 为什么是最方便的存储方案&#xff0c;有哪些操作 cookie 的方式session 方案是如何实现的&#xff0c;存在哪些问题token 是如何实现的&#xff0c;如何进行编码和防篡改&#xff1f;jwt 是做什么的&#xff1f;refresh token 的实现和意义ses…

pythonJax小记(五):python: 使用Jax深度图像(正交投影和透视投影之间的转换)(持续更新,评论区可以补充)

python: 使用Jax深度图像&#xff08;正交投影和透视投影之间的转换&#xff09; 前言问题描述1. 透视投影2. 正交投影 直接上代码解释1. compute_projection_parameters 函数a. 参数解释b. 函数计算 2. ortho_to_persp 函数a. 计算投影参数&#xff1a;b. 生成像素坐标网格&am…

protobuf简单使用(二)

介绍 上一节中&#xff0c;我们介绍了protobuf&#xff0c;简单来说&#xff0c;它是一种消息数据格式&#xff0c;其作用类似于json&#xff0c;但是比json的使用效率要高。 除此以外&#xff0c;我们介绍了protobuf的简单使用&#xff0c;也就是如何可以像使用json一样&…

AI游戏初创公司“奇酷网络”,获得500万元天使投资

发布 | 大力财经 2024年新春伊始的2月25日&#xff0c;国内首家“AI游戏”应用公司“奇酷网络”正式对外宣布&#xff1a;以3,000万元人民币的估值&#xff0c;成功获得500万元人民币的融资&#xff1b;资金来源于一名百度高层和一位知名的天使投资人。 据悉&#xff0c;“奇…

进程 2月24日学习笔记

1.进程: 程序&#xff1a;存放在外存中的一段数据组成的文件 进程&#xff1a;是一个程序动态执行的过程,包括进程的创建、进程的调度、进程的消亡 2.进程相关命令: 1.top 动态查看当前系统中的所有进程信息&#xff08;根据CPU占用率排序&#xff09; PID:唯一识…

namecheap域名如何购买?通过支付宝(详细图文教程)

引言 在完成博客搭建的时候&#xff0c;我们可能需要一个好的域名&#xff0c;自己看起来才会舒服点&#xff0c;同时也可以通过google或者百度等方式搜索到自己博客。 经过实验发现&#xff0c;一个好的后缀名会增强google和百度的搜索seo&#xff0c;增加自己博客的流量。 …

平头哥IP核C906的JTAG调试器DIY教程(一)

背景 最近买了一块基于平头哥C906核&#xff0c;SOC为全志的D1s&#xff0c;的核心板&#xff0c;手工焊接在白嫖的底板上&#xff08;此处感谢百问网老师的友情支持&#xff0c;手动狗头&#xff09;。在焊接完成后&#xff0c;进行点亮跑程序的时候&#xff0c;发现没有优雅…

C++ //练习 8.13 重写本节的电话号码程序,从一个命名文件而非cin读取数据。

C Primer&#xff08;第5版&#xff09; 练习 8.13 练习 8.13 重写本节的电话号码程序&#xff0c;从一个命名文件而非cin读取数据。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /***************************************…

关于设备连接有人云的使用及modbus rtu协议,服务器端TCP调试设置

有人云调试 调试过程问题1. 关于modbus rtu协议,实质上有三种modbus基本原理modbus 格式2. 关于modbus crc16通信校验3. 关于在ubuntu阿里云服务器端,监听网络数据之调试mNetAssist4. 使用有人FAE传给的设置软件问题???之前的一个项目,再拿出来回顾下。 调试过程 先 要在有…

web安全学习笔记【16】——信息打点(6)

信息打点-语言框架&开发组件&FastJson&Shiro&Log4j&SpringBoot等[1] #知识点&#xff1a; 1、业务资产-应用类型分类 2、Web单域名获取-接口查询 3、Web子域名获取-解析枚举 4、Web架构资产-平台指纹识别 ------------------------------------ 1、开源-C…