查看原文
其他

如何识别并解决复杂的dcache问题

OPPO TECH OPPO数智技术 2021-10-05

1. 背景

这个是在centos7.6的环境上复现的,但该问题其实在很多内核版本上都有,如何做好对linux一些缓存的监控和控制,一直是云计算方向的热点,但这些热点属于细分场景,很难合入到linux主基线,随着ebpf的逐渐稳定,对通用linux内核的编程,观测,可能会有新的收获。本文将分享我们是怎么排查并解决这个问题的。2. 故障现象oppo云内核团队发现集群的snmpd的cpu消耗冲高,snmpd几乎长时间占用一个核,perf发现热点如下:几乎都消耗在内核态 __d_lookup的调用中,然后strace看到的消耗为:进一步手工操作,发现进入ipv6的路径很慢:可以看到,进入ipv6的路径的时间消耗远远大于ipv4的路径。3. 故障现象分析我们需要看一下,为什么perf的热点显示为__d_lookup中proc_sys_compare消耗较多,它的流程是怎么样的。proc_sys_compare只有一个调用路径,那就是d_compare回调,从调用链看:集群同物理条件的机器,snmp流程应该一样,所以很自然就怀疑,是不是hlist_bl_for_each_entry_rcu循环次数过多,导致了parent->d_op->d_compare不停地比较冲突链,进入ipv6的时候,是否比较次数很多,因为遍历list的过程中肯定会遇到了比较多的cache miss,当遍历了太多的链表元素,则有可能触发这种情况,下面需要验证下:kprobe的结果如下:从冲突链的长度看,确实进入了dcache的hash表中里面一条比较长的冲突链,该链的dentry个数为799259个,而且都指向ipv6这个dentry。了解dcache原理的同学肯定知道,位于冲突链中的元素肯定hash值是一样的,而dcache的hash值是用的parent的dentry加上那么的hash值形成最终的hash值:表面上看,高版本的内核的dentry->dname.hash值的计算变化了,其实是hash存放在dentry->d_name.hash的时候,已经加了helper,具体可以参考如下补丁:问题分析到这里,有两个疑问如下:1. 冲突链虽然长,那也可能我们的dentry在冲突链前面啊不一定每次都比较到那么远;2. proc下的dentry,按道理都是常见和固定的文件名,为什么会这么长的冲突链呢?要解决这两个疑问,有必要,对冲突链里面的dentry进一步分析。我们根据上面kprobe打印的hash头,可以进一步分析其中的dentry如下:由于链表非常长,我们把对应的分析打印到文件,发现所有的这条冲突链中所有的dentry都是属于同一个super_block,也就是 0xffff89db7fd3c800,0xffff89db7fd3c800 是 proc 文件系统,他为什么会创建这么多ipv6的dentry呢?继续使用命令看一下dentry对应的d_inode的情况:我们发现,这些同名的,d_name.name均为 ipv6 的dentry,他的inode是不一样的,说明这些proc下的文件不存在硬链接,所以这个是正常的。我们继续分析ipv6路径的形成。/proc/sys/net/ipv6路径的形成,简单地说分为了如下几个步骤:有了这些基础,接下来,我们盯着最后一个,ipv6的创建流程。ipv6_sysctl_net_init 函数ipv6_sysctl_register-->register_pernet_subsys(&ipv6_sysctl_net_ops)-->register_pernet_operations-->__register_pernet_operations-->ops_init-->ipv6_sysctl_net_init常见的调用栈如下:在dcache中,我们/proc/sys/下的各个net_namespace中的dentry都是一起hash的,那怎么保证一个net_namespace内的dentry隔离呢?我们来看对应的__register_sysctl_table函数:具体代码不展开,每个sys下的dentry通过 ctl_table_set 来区分是否可见然后在查找的时候,比较如下:由以上代码可以看出,当前去查找的进程,如果它归属的net_ns的set和dentry 中归属的set不一致,则会返回失败,而snmpd归属的set其实是init_net的sysctls,而经过查看冲突链中的各个前面绝大多数dentry的sysctls,都不是归属于init_net的,所以前面都比较失败。那么,为什么归属于init_net的/proc/sys/net的这个dentry会在冲突链的末尾呢?那个是因为下面的代码导致的:已经知道了snmp对冲突链表比较需要遍历到很后的位置的原因,接下来,需要弄明白,为什么会有这么多dentry。根据打点,我们发现了,如果docker不停地创建pause容器并销毁,这些net下的ipv6的dentry就会累积,累积的原因,一个是dentry在没有触发内存紧张的情况下,不会自动销毁,能缓存则缓存,另一个则是我们没有对冲突链的长度进行限制。那么问题又来了,为什么ipv4的dentry就没有累积呢?既然ipv6和ipv4的父parent都是一样的,那么查看一下这个父parent有多少个子dentry呢?159万个子目录,去掉前面冲突链较长的799259个,还有差不多79万个,那既然进入ipv4路径很快,说明在net目录下,应该还有其他的dentry有很多子dentry,会不会是一个共性问题?然后查看集群其他机器,也发现类型现象,截取的打印如下:可以看到,ffffbd9d429a7498有着和ffffbd9d5a7a6cc0几乎一样长度的冲突链。先分析ipv6 链,core链的分析其实是一样的,挑取冲突链的数据分析如下:可以看到,ipv6的dentry路径为ipv6/conf/all/disable_ipv6,和probe看到的一样,针对 d_flags ,分析如下:我们看到,disable_ipv6的引用计数为0,但是它是有 DCACHE_LRU_LIST 标志的,根据如下函数:到此,说明它是可以释放的,由于是线上业务,我们不敢使用 echo 2 >/proc/sys/vm/drop_caches然后编写一个模块去释放,模块的主代码如下,参考 shrink_slab:就发现确实两条冲突链都被释放了。比如某个节点在释放前:单独释放后:上面可以看出两个细节:1. 释放前,hlist也是在增长的,释放后,hlist还是在增长。2. 释放后,net的dentry变了,所以hashlist的位置变化了。综上所述,我们遍历热点慢,是因为snmpd所要查找init_net的ctl_table_set和dcache中的其他dentry 归属的 ctl_table_set 不一致导致,而链表的长度则是因为有人在销毁net_namespace的时候,还在访问ipv6/conf/all/disable_ipv6 以及core/somaxconn 导致的,这两个dentry 都被放在了归属的super_block的 s_dentry_lru 上。最后一个疑问,是什么调用访问了这些dentry呢?触发的机制如下:可以看到,其实就是 dockerd和runc 触发了这个问题,k8调用docker不停创建pause容器,cni的网络参数填写不对,导致创建的net_namespace 很快被销毁。虽然销毁时调用了unregister_net_sysctl_table,但同时 runc 和exe 访问了该net_namespace下的两个dentry,导致这两个dentry被缓存在了 super_block的 s_dentry_lru链表上。再因为整体内存比较充足,所以一直会增长。注意到对应的路径就是:ipv6/conf/all/disable_ipv6以及 core/somaxconn,ipv4路径下的dentry因为没有当时在访问的,所以ctl_table能够当时就清理掉。而倒霉的snmpd因为一直要访问对应的链,cpu就冲高了,使用手工 drop_caches 之后,立刻恢复,注意,线上的机器不能使用drop_caches ,这个会导致sys 冲高,影响一些时延敏感型的业务。4. 故障复现1. 内存空余的情况下,没有触发slab的内存回收,k8调用docker创建不同net_namespace的pause容器,但因为cni的参数不对,会立刻销毁刚创建的net_namespace,如果你在dmesg中频繁地看到如下日志:则有必要关注一下 dentry的缓存情况。5. 故障规避或解决可能的解决方案是:1. 通过rcu的方式,读取 dentry_hashtable 的各个冲突链,大于一定程度,抛出告警。2. 通过一个proc参数,设置缓存的dentry的个数。3. 全局可以关注 /proc/sys/fs/dentry-state。4. 局部的,可以针对super_block,读取s_nr_dentry_unused,超过一定数量,则告警,示例代码可以参考shrink_slab函数的实现。5. 注意与 negative-dentry-limit 的区别。6. 内核中使用hash桶的地方很多,我们该怎么监控hash桶冲突链的长度呢?做成模块扫描,或者找地方保存一个链表长度。

作者简介

Anqing   OPPO高级后端工程师

目前在oppo混合云负责linux内核及容器,虚拟机等虚拟化方面的工作。

推荐阅读

|统一预估引擎的设计与实现

|oCPX简介——广告界的“无人驾驶”技术


本文版权属OPPO公司所有,如需转载请在后台留言联系。
: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存