V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
huangya
V2EX  ›  问与答

网卡驱动中当 cpu 正在运行中断处理函数的时候来了另外一个包怎么处理

  •  
  •   huangya · 2022-12-08 11:58:36 +08:00 · 1029 次点击
    这是一个创建于 502 天前的主题,其中的信息可能已经有所发展或是发生改变。
    1.只考虑接收包的场景
    2.只考虑简单的不带 DMA 功能的网卡
    3.网卡带一个输入缓冲队列

    假设刚开始缓冲队列是空的。下面是我的理解:
    当第一个包进入到输入缓冲队列的时候,网卡产生一个中断。CPU 进入中断处理函数。首先第一条指令就是屏蔽中断(原子操作,一条汇编指令)。然后处理包,主要的处理动作是把它 copy 到内存中。处理完后关中断(也是原子操作,一条汇编指令)

    假设在处理过程中,来了第二个包,因为屏蔽了中断,包是不是只会放到缓冲队列?第一个包处理完后,怎么处理第二个中断丢失的包呢?我看有些驱动的处理函数在每个包处理完后,似乎都会检查一下网卡缓冲队列中是否还有包,但这样检查是原子操作吗?不会放过任何包吗?假设在检查之后,关中断之前来了一个包呢?
    17 条回复    2022-12-13 18:39:50 +08:00
    ampedee
        1
    ampedee  
       2022-12-08 12:06:54 +08:00 via iPhone
    一直检查也没问题,限定一次中断处理能执行的时间片长度,执行完了就退出等下一次调度
    kokutou
        2
    kokutou  
       2022-12-08 12:07:53 +08:00
    丢包嘛,无响应嘛

    电脑死机了,你 ping 电脑 ip 也是不通的啊。
    huangya
        3
    huangya  
    OP
       2022-12-08 12:09:07 +08:00
    @ampedee
    感谢回复,但我更关心的其实是“但这样检查是原子操作吗?不会放过任何包吗?假设在检查之后,关中断之前来了一个包呢?”
    huangya
        4
    huangya  
    OP
       2022-12-08 12:16:00 +08:00
    @kokutou
    我只是觉得已经到缓冲队列的包也算“丢包”比较可惜,当然这种情况应该是非常少。
    654656413245
        5
    654656413245  
       2022-12-08 12:56:37 +08:00
    1. Linux 中断分上半部和底半部,上半部执行很快耗时很短,也只有上半部处理期间会短暂屏蔽网卡外部中断
    2. 网卡自身有缓冲,真的是容量耗尽的时候,直接丢包
    3. 基本没有不带 DMA 的网卡了; DMA 完成后才发出硬中断,CPU 开始处理,而不是发出硬中断后才开始 DMA
    4. 驱动处理函数都是 poll 流程,是软中断(底半部)执行
    leonshaw
        6
    leonshaw  
       2022-12-08 13:56:47 +08:00
    先开中断再处理,而不是等处理完了再开
    sujin190
        7
    sujin190  
       2022-12-08 14:06:15 +08:00
    @huangya #4 一般来说,正常做这个中断程序干完屏蔽中断之后,都是循环读取直到缓冲区读取完成,然后解除屏蔽中断才结束的,正常情况下应该都会把检查队列是否空和清除中断屏蔽设计成一个指令才对
    huangya
        8
    huangya  
    OP
       2022-12-08 14:21:05 +08:00
    @654656413245
    >3. 基本没有不带 DMA 的网卡了; DMA 完成后才发出硬中断,CPU 开始处理,而不是发出硬中断后才开始 DMA
    这个我知道。我是在学习网卡驱动,所以找的是最简单的例子。linux 内核里面的 3com 的 3c509 网卡就是不带 DMA 的。学习东西的时候,我喜欢从简单的硬件入手。现在的网卡除了带最基础的 DMA 功能,还带各种高级功能,各种 offload ,多队列,中断合并啥的。但是这样会让驱动复杂很多,新手会比较吃力。

    回到我这个问题(我这个人有点钻牛角尖哈,也 @sujin190 ),我只是想知道对于这种网卡,有没有理论上的可能性就是这个包已经到了缓冲队列,但还是最终得不到处理或者及时处理的情况?比如过了很久,第三个包过来了,产生了一个中断,第二个包虽然从队列中顺便捞上来了,但已经被协议栈认为超时了,所以就丢弃。有没有这种情况产生呢?
    huangya
        9
    huangya  
    OP
       2022-12-08 14:27:00 +08:00
    @sujin190
    >正常情况下应该都会把检查队列是否空和清除中断屏蔽设计成一个指令才对
    不是一个指令。看了一下例子,检查队列会调一个 ioread16 函数(这个函数可能是封装了一个汇编指令),清除中断是另外一个函数.所以最起码是两条指令。
    huangya
        10
    huangya  
    OP
       2022-12-08 14:30:20 +08:00
    @leonshaw 没太懂你的意思。你说的是中断的下半部分吧( linux 内核把中断分成上半部分和下半部分).下半部分是包已经送到协议栈了,此时中断已经开了。
    sujin190
        11
    sujin190  
       2022-12-08 14:38:26 +08:00
    @huangya #9 如果是这样,那么中断产生条件可能是队列非空,或者你看的这个可能是系统软中断,网卡处理数据正常都是硬中断触发软中断,读取解析数据的过程一般都会在软中断中完成,并不会直接在网卡硬件中断中直接开始处理数据
    leonshaw
        12
    leonshaw  
       2022-12-08 14:42:21 +08:00
    @huangya 参考 linux 内核 handle_edge_irq 里的实现。第一个中断进来时,并不关中断;当第一个中断处理过程中又进来,才关中断同时记录 pending 状态;处理中断前开中断、清状态;循环直到没有 pending 为止。上半部分这样处理好了,下半部分自然也不会有问题,不管检查队列是在哪部分实现的。
    huangya
        13
    huangya  
    OP
       2022-12-08 15:09:23 +08:00
    @sujin190
    @leonshaw
    两位,我看的是这个 https://elixir.bootlin.com/linux/latest/source/drivers/net/ethernet/3com/3c509.c#L907
    el3_rx()被中断处理函数 el3_interrupt()调用. 在第 770 行,el3_interrupt 调用了 spin_lock(),spin_lock 会关中断。然后再第 783 行调用 el3_rx()函数。el3_rx ()使用 while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) (第 915 行)看是否在队列中有新的包。第 941 行的 insl 函数我的理解是从网卡缓冲队列中取包到内存中( skb )。第 946 行 neif_rx 会把 skb 放到协议栈中的队列中,后续会交给软中断或者下半部分处理。
    leonshaw
        14
    leonshaw  
       2022-12-08 15:35:56 +08:00
    @huangya 理解错了,你说的是 CPU 的中断屏蔽。在关的时候产生的中断会在打开后处理,不会丢。
    huangya
        15
    huangya  
    OP
       2022-12-08 15:50:28 +08:00
    @leonshaw 真的不会丢吗?老哥有什么稍微权威点或者大牛的资料或者书籍吗?
    654656413245
        16
    654656413245  
       2022-12-08 16:30:13 +08:00
    CPU 关闭了中断后,网卡中断仍然会送到中断控制器,只是暂时不发给 CPU
    等 CPU 打开中断了,中断控制器会将 pending irq 发给 CPU
    huangya
        17
    huangya  
    OP
       2022-12-13 18:39:50 +08:00
    @654656413245 这几天我还在看 driver 的代码,也在看一些计算机组成原理的书籍。
    >等 CPU 打开中断了,中断控制器会将 pending irq 发给 CPU
    如果是这样,看我在 13 楼的回复,在 CPU 处理第一个中断的时候,有个 while 循环在 check 有没有新包,那会不会出现 pending irq 发给 CPU 的时候,又进入了中断处理函数,但是收了空包,因为已经在上一次中断处理中完成了收包?我看的组成原理书籍都似乎没讲 pending irq 的情况。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4528 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 10:09 · PVG 18:09 · LAX 03:09 · JFK 06:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.