这篇文章的背景是有次业务反馈服务丢包的问题,TX给出的分析报告是由于业务请求有微突发,导致网卡接收数据包之后分配内存失败,分配内存失败的原因是因为网卡收到数据包,通过软中断转成SKB,由于转SKB需要通过alloc_pages分配物理内存,但由于微突发(毫秒级别包量很大)的时候,物理内存不足,导致alloc_pages失败,内核抛出trace log。带着疑惑去查看内核代码。本文根据trace log查看对应的网卡收包进入软中断之后的内存分配部分。
另外,内核有个参数min_free_kbytes,这个表示内核维持的最小可用物理内存数,如果可用物理内存低于min_free_kbytes,则内核会通过kswapd进行内存回收动作。但为什么内存分配失败的时候没有触发OOM呢?这里阐述下原因,由于网卡收包转成SKB的时候,使用的是GFP_ATOMIC标识位
意思就是GFP_ATOMIC标识是原子性,不会被阻塞,而且从内核代码看,这个标识位存在的情况下,也不会发起OOM(拉起oom-killer释放内存)。
由于现有监控系统都没有达到毫秒级别,因此从监控系统看不出系统指标有明显异常,但从内核tracelog和源码分析可以得出:出现微突发的时候,机器内存已不足,可用物理内存应该是0。
下面就从内核代码阐述下网卡收包软中断下关于内存分配的逻辑。
例如网卡收包和发包,action分别对应的是net_rx_action和net_tx_action,action在boot的时候注册的。
其中,big_packets、mergeable_rx_bufs分别对应的是TSO和GRO模式,可以通过ethtool–k 网卡名 的方式查看是否开启。具体细节这里不做阐述,感兴趣的可以google研究下。
__alloc_pages_nodemask的核心代码如下:
get_page_from_freelist的核心代码如下:
zone_watermark_fast->__zone_watermark_ok,主要是遍历zone,尝试找到足够的内存空间分配order阶内存页
__alloc_pages_slowpath是慢分配逻辑:
进入慢速分配函数后,先检查所请求的分配阶是否超过了MAX_ORDER。如果指定了GFP_THISNODE标志后,则不能继续进行慢速内存分配,因为该标志指明了内存不能进行回收,因此直接跳到nopage处的代码。
在经历一系列的参数检查之后,该函数通过调用wake_all_kswapd()唤醒每个zone所属node中的kswapd守护进程。这个守护进程负责换出很少使用的页,以提高目前系统可以用的空闲页框。
另外,网络驱动设备的注册是在机器boot的时候通过如下调用链路:
boot->virtnet_probe->init_vqs->virtnet_alloc_queues->netif_napi_add->virtnet_poll
总结一下:
以上就是关于软中断下网卡收包内存分配逻辑,不过没有对涉及到的数据结构做讲解,例如ring_buffer、SKB等,这些内容待后续文章详细阐述。