中断聚合与 moderation:网卡/DMA 收包中断风暴怎么压
更新时间:2026-09-19。前置:中断与 DMA 入门、DMA 描述符环。
本文要回答的问题
- 小包线速时每秒几十万次中断为什么会拖垮系统?
- 中断聚合有哪几种触发方式(计数/时间/自适应)?
- 聚合省下开销但增加延迟,怎么动态平衡?
- 硬件中断状态位的「脉冲/电平、写 1 清 0」如何与聚合配合才不漏中断?
一、问题:中断风暴
每收一个包/DMA 完成一个描述符就进一次中断:上下文切换、缓存冲刷、ISR+软中断开销可能数十~数百周期。1Gbps 全是 64B 小包可达百万 pps,CPU 几乎全耗在进出中断,吞吐反而上不去——这就是 receive interrupt livelock(接收中断活锁)。
二、三种聚合策略
| 策略 | 触发 | 参数 | 取舍 |
|---|---|---|---|
| 计数阈值 | 攒够 N 个事件/包 | rx-frames/coalesce-frames | 固定批量;空闲时延迟高 |
| 时间窗口 | 首个事件后等 T μs 再报 | rx-usecs/coalesce-time | 限制最大等待;低频时仍及时 |
| 自适应 adaptive | 硬件/驱动按流量动态调 N、T | moderation 算法 | 高流量省中断、低流量低延迟;复杂 |
实际硬件通常计数 OR 时间,先到先触发:
verilog
// 伪代码:事件计数与定时器聚合
always @(posedge clk) begin
if (event_pulse) begin
cnt <= cnt + 1'b1;
if (cnt == THRESH-1) begin irq_pulse <= 1'b1; cnt <= 0; timer <= 0; end
else if (cnt == 1) timer <= TIMER_LOAD; // 首个事件启动窗口
end
if (timer != 0) begin
timer <= timer - 1'b1;
if (timer == 1) begin irq_pulse <= 1'b1; cnt <= 0; end // 窗口到,剩余事件也上报
end
end
// 关键:上报后若又有新事件,必须能重新置位(防止窗口期边界漏事件)三、软件配合:NAPI / 轮询兜底
Linux NAPI 的思路:第一次收包中断进入后关闭该队列硬中断,切到轮询(poll budget,一次处理一批描述符),包处理完或预算用尽再重开中断。这等价于「高负载退化成轮询、低负载保留中断」,与硬件 moderation 叠加把中断率压到可控。NVMe 聚合中断、USB 批量完成中断同理。
四、中断位语义(容易漏/重)
- 脉冲(edge/pulse):硬件发一拍。若多个事件聚合成一个脉冲,软件必须在一次 ISR 里把「自上次以来」所有完成的描述符/包处理干净(看所有权位/环指针),否则只剩一个边沿却有多批数据。
- 电平(level):只要有未处理事件就保持有效。更安全,但 ISR 退出前必须清掉所有源,否则立刻重入。
- 写 1 清 0(W1C)状态位:读改写整个寄存器会误清并发到来的其他位——必须按位写 1 或用硬件提供的 mask;清除与新事件到达存在竞态时要保证「清完再读一次确认」。
- 使能/聚合寄存器热更新:改阈值时正处于窗口中,需明确是否复位定时器/计数,避免一次超长延迟。
五、参数与延迟的权衡
bash
N/T 越大 → 中断/秒↓ CPU 效率↑ 吞吐↑,但尾延迟↑、ping↑
N/T 越小 → 延迟低,但高 pps 下 CPU 软中断占比高- 存储/吞吐基准(iperf、大块 IO):调大 N/T。
- 交易/实时/语音:调小甚至关闭(rx-usecs=0)。
- 自适应:用队列深度/包间隔估计,空闲给小窗口、拥塞给大窗口。
六、常见的坑
- 聚合后 ISR 只处理一个包:一个中断代表一批,处理不干净就丢后续包。
- 阈值设太大且无超时:零星流量时数据一直攒着,应用感知卡顿。
- 关停硬中断后忘记重开:NAPI/轮询退出路径漏 re-enable,链路「停收」。
- W1C 读改写误清:并发事件位被一次写操作带掉,中断丢失。
- 清中断与新事件竞态:清完未复查,刚好到达的完成位没人再触发中断;清→读→必要时软补一次。
- 定时器只在首事件装载但不重置:连续事件下窗口逻辑错,聚合失效或延迟无界。
- 计数器位宽/溢出:极端突发计数溢出回绕,阈值比较错乱。
- 多核/多队列只聚合一族:RSS 多队列各自 moderation,参数不一致造成抖动。
- 把电平中断当脉冲:退出 ISR 没清完立即重入风暴。
- 只压 RX 不管 TX 完成:发送完成中断同样风暴,TX moderation 也要配。
- 自适应算法误判微突发:短暂空窗立刻降到零延迟,紧接着大突发又风暴,需迟滞(hysteresis)。
相关与延伸
完成事件来自描述符环,见 DMA 描述符环、AXI4-Stream 与 DMA;中断基础见 中断与 DMA 入门(RISC-V PLIC/CLINT、ARM GIC/NVIC 等具体中断控制器可在后续专题展开)。
总结
中断聚合用「攒够 N 个事件或首个事件后等 T 微窗,先到先上报」把高 pps 的每包中断压成每批一次,硬件做计数/定时器、软件用 NAPI 式「进中断即关硬中断转轮询、处理完再开」兜底,并在脉冲/电平/W1C 语义上保证一次中断处理完一批、清完再复查;N/T 越大吞吐越高但尾延迟越大,故按流量自适应——ISR 只处理一个、无超时饿死、重开中断遗漏和 W1C 误清是四个高发坑。