一、为何要有NAPI:中断风暴与性能的权衡
早期Linux网络栈采用纯中断模式——每收到一个数据包,网卡触发硬中断,CPU暂停当前工作进入中断处理例程。千兆网络下82字节小包线速约1.488Mpps,万兆下则高达14.88Mpps。这意味着每秒百万级中断上下文切换,CPU将全部时间消耗在模式切换和数据拷贝上,甚至引发中断风暴(IRQ Storm)——系统完全失去响应,watchdog超时重启。
NAPI(New API)的核心思想是中断 轮询混合模式:
- 第一个数据包到达触发硬中断
- 中断处理中关闭后续中断,将设备加入轮询列表并调度软中断
- 软中断上下文中批量调用驱动的poll()函数收割数据包
- 所有数据包处理完毕,重新开启中断
这种设计将每包一次中断的O(N)中断开销降低为每轮一次中断的O(1)中断开销,极大提升了高吞吐场景的CPU效率。
二、NAPI核心数据结构
2.1 struct napi_struct
每个NAPI实例对应一个napi_struct,这是驱动注册轮询机制的最小单元:
struct napi_struct {
struct list_head poll_list; // 全局轮询链表节点
unsigned long state; // NAPI状态机:SCHED/POLL_DISABLED/...
int weight; // 每次poll()最大收包配额(默认64)
int (*poll)(struct napi_struct *, int); // 收包回调
unsigned int gro_count; // GRO合并计数
#ifdef CONFIG_NET_RX_BUSY_POLL
unsigned long busy_poll_end; // busy polling截止时刻
#endif
};
weight是最关键的调优参数。默认值64意味着每次poll()调用最多处理64个数据包(对应64次DMA描述符处理)。Intel ice驱动在高性能场景配置为128~256。
2.2 状态机与生命周期
NAPI运行在一个精巧的状态机上:
[硬件中断到达]
↓
napi_schedule() // 触发软中断,状态设为NAPI_STATE_SCHED
↓
[softirq NET_RX_SOFTIRQ]
net_rx_action() // 从poll_list中取出napi_struct
↓
调用poll(budget) // budget = dev-

发表评论 取消回复