Linux namespace bug导致上不了网问题排查过程
1问题现象
计算节点的namespace出问题了,系统卡住了,导致这台上的所有虚机都上不了网
2分析过程
sudo ip netns list 有报错
TNETLINK answers: Invalid argument
sudo strace ip netns list发现 有
Peer netns reference is invalid
2.1添加ns 卡住——拿不到锁
sudo ip netns add mmm
打印进程调用栈看到卡在copy_net_ns函数:
进一步分析是卡在net_mutex锁的获取那里。可能别的进程拥有了这个锁迟迟不释放,导致程序一致卡住这里。
2.2 Kworker进程cpu 100%
同事xx发现出问题的节点中必有一个kworker cpu为100%
上工具,通过perf性能剖析工具看看热点在哪里?
初看这个函数是协议栈中ip分片功能的函数,感觉和netns没有什么关联啊。一开始也觉得这里没有什么问题,后来排查其他功能也没有收获之后,又回到这里来分析。
2.3 Kprobe调试内核函数调用链
用kprobe工具(不做介绍自行搜索了解),看看到底是谁调用了inet_evict_bucket函数,具体代码见下面这两个文件:
直接make生成ko文件,如果编译有问题请百度
insmod xx.ko,动态加载模块到内核
在dmesg会看到相应的内核调用栈信息,如下:
[Tue Jun 2 16:04:34 2020] CPU: 7 PID: 29371 Comm: kworker/u128:1 Tainted: G O 4.16.13-1.el7.elrepo.x86_64 #1
[Tue Jun 2 16:04:34 2020] Workqueue: netns cleanup_net
[Tue Jun 2 16:04:34 2020] Call Trace:
[Tue Jun 2 16:04:34 2020] dump_stack+0x63/0x88
[Tue Jun 2 16:04:34 2020] ? inet_evict_bucket.isra.8+0x1/0x120
[Tue Jun 2 16:04:34 2020] handler_pre+0x35/0x39 [kprobe_demo]
[Tue Jun 2 16:04:34 2020] kprobe_ftrace_handler+0x90/0xf0
[Tue Jun 2 16:04:34 2020] ftrace_ops_assist_func+0x62/0xf0
[Tue Jun 2 16:04:34 2020] ? inet_evict_bucket.isra.8+0x1/0x120
[Tue Jun 2 16:04:34 2020] ? inet_evict_bucket.isra.8+0x5/0x120
[Tue Jun 2 16:04:34 2020] ? inet_frags_exit_net+0x51/0xa0
[Tue Jun 2 16:04:34 2020] ? inet_evict_bucket.isra.8+0x5/0x120
[Tue Jun 2 16:04:34 2020] ? inet_frags_exit_net+0x51/0xa0
[Tue Jun 2 16:04:34 2020] ? ipv4_frags_exit_net+0x3a/0x40
[Tue Jun 2 16:04:34 2020] ? ops_exit_list.isra.6+0x3b/0x70
[Tue Jun 2 16:04:34 2020] ? cleanup_net+0x212/0x320
[Tue Jun 2 16:04:34 2020] ? process_one_work+0x15f/0x370
[Tue Jun 2 16:04:34 2020] ? worker_thread+0x4d/0x3e0
[Tue Jun 2 16:04:34 2020] ? kthread+0x105/0x140
[Tue Jun 2 16:04:34 2020] ? max_active_store+0x80/0x80
[Tue Jun 2 16:04:34 2020] ? kthread_bind+0x20/0x20
[Tue Jun 2 16:04:34 2020] ? do_syscall_64+0x79/0x1b0
[Tue Jun 2 16:04:34 2020] ? ret_from_fork+0x35/0x40
[Tue Jun 2 16:04:34 2020] <inet_evict_bucket.isra.8> post_handler: p->addr = inet_evict_bucket.isra.8+0x0/0x120, flags = 0x206
2.4 cleanup_net函数——持锁不放
看了cleanup_net函数的代码来解释现象:
因为cleanup_net函数拥有了net_mutex锁,并且在这个锁里做了很耗时的操作(类似死循环),导致锁迟迟等不到释放。而新建的netns是要成功获取到锁以后才可以,现在锁被别人占用了,只能一直等一直等就好像卡死了一样。这就解释了为什么出现问题的节点上,新建netns卡住了。
接下来我们就要搞清楚cleanup net为什么要那么耗时,拿住锁一直不释放。
ops_free_list辗转调用到inet_frags_exit_net 函数 inet_evict_bucket
结合之前的kprobe信息,这里确实有一个死循环
每次239行的if都为真,直接goto evict_again,所以变成的死循环。
1)要么顺序锁的writer一直在操作,所以这里的顺序锁reader一直要retry,但是前面perf看到的调用里面顺序锁并不是热点,所以排除顺序锁的问题。
2)那就怀疑sum_frag_mem_limit(nf)试试?sum_frag_mem_limit(nf)是干什么的?简单来说,就是看看所有分片是不是都处理完,如果是那就返回0,否则返回非0.
本来我们想在出现问题的机器上,通过kprobe去打印nf的值看看是不是不为0.但是由于此时已经死循环了,好像也没有什么办法。
然后在一台虚机上,把内核源码的239行sum_frag_mem_limit(nf)直接用1替代,重编以后。系统启动以后kworker cpu 100%,新建netns为空,坎函数调用栈、perf热点,现象一致,我们更加相信是协议栈分片的问题导致的了。但是要花时间去搞懂分片的原理,这个估计很花时间不太现实。
2.5 协议栈分片重组有问题导致bug
基于上面的分析,接下里我们就是漫长的搜索和查看提交记录,找到确实有这样的bug并且有了patch。
简单来说就是分片重组有问题导致即使分片处理完了sum_frag_mem_limit(nf) 不为0,当del netns的时候inet_frags_exit_net进入死循环。
The underlying issue is that a kworker thread (executing cleanup_net) spins in inet_frags_exit_net, waiting for sum_frag_mem_limit(nf) to become zero, which never happens becacuse it has underflowed to some negative multiple of 64. That kworker thread keeps holding net_mutex and therefore blocks any further
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1765980
https://github.com/torvalds/linux/commit/ebaf39e6032faf77218220707fc3fa22487784e0
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.19.10
4结论
Kernel 4.16 是否有这个bug?存在
解决办法:
直接升级到内核4.19.10以后