在一次丢包问题排查中,需要了解如下问题:
分析思路是,先可以用动态分析去初步分析,然后通过静态分析走读代码再从代码层面确认。
本文旨在记录分析过程与结论,并同时深挖了ethtool与网卡(本文是以i40e为例,其他网卡实现都有相似之处)在统计指标的内核数据结构之间的实现与关系。
netstat -s命令会看到一些协议栈网络数据的统计,我们通过strace来看看他是读取的哪里的统计:
……
……
……
可以看到,其主要是从/proc/net/snmp和/proc/net/netstat中读取的数据。
这里简单介绍一下两个文件的主要作用。
该文件是snmp协议对协议栈中各协议(ip/icmp/tcp/udp)相关信息的一些基本统计,比如出入路径上的错误计数等,通过查看其中一些统计的异常值,可以对系统潜在的性能问题进行排查。(比如当udp中的InErrors不断增长的时候,可以猜测系统的socket接受队列达到瓶颈)
打印如下:
/proc/net/netstat
对比/proc/net/snmp,netstat给出了tcp和ip协议的一些更详细的统计(TcpExt/IpExt),比如可以通过Syncookies*可以分析出tcp半连接的一些相关状态信息。
而netstat工具给出的信息,也正是将这些信息归档,并做出了更详细的文字描述,这里我们可以在netstat源码中看到映射关系, 比如可以看到上面提到的syncookies*对应的是如下这些文字描述:
nstat
nstat与netstat有很多相似之处,同样用strace来看下:
......
……
可以看到,和netstat一样,其主要是从/proc/net/snmp和/proc/net/netstat中读取的数据。
值得一提的是,nstat的指标基本都是从对应的proc文件的原始字段名,而netstat则是一段文字说明(找映射关系的办法在netstat部分已经给出)。
ifconfig给出的主要是interface的信息,跟协议栈无关,因此猜测和netstat/nstat那套无关,和ethtool是一套,这里同样strace来验证一下:
……
……
可以看到他读的是/proc/net/dev和/proc/net/if_inet6 (这个文件是网卡接口地址)的值,而/proc/net/dev的值是从何而来的,在后面的源码分析中会给出答案。
同样是用strace去跟一下:
……
……
可以看到,这里是直接用ioctl和设备交互获取的,并不是从/proc/net/dev中读出来的。
ioctl(input/output control)是一个专用于设备输入输出操作的系统调用,该调用传入一个跟设备有关的请求码,系统调用的功能完全取决于请求码。
在这里猜测是ethtool尝试获取网卡设备特定硬件上的作为统计信息的buffer,后面会从源码层面验证。
从上述观测结果来看,netstat、nstat其实都是/proc/net/snmp和/proc/net/netstat的数据,而ifconfig和ethtool显然与这两者无关,问题1已经有了答案。
而ifconfig和ethtool给出的信息虽然看着统计的是相似网卡相关数据,但通过动态追踪还没有办法直接实锤(ifconfig是读的/proc/net/dev,而ethtool是直接通过ioctl下放),后面通过对内核代码的分析来给出结论。
此处不详细展开以太网以及一二层架构与ethtool的关系,重点关注一下Linux内核及设备驱动程序对ethtool的支持。
ethtool实现框架,原图来自互联网:
这里MII寄存器相关定义在:include/uapi/linux/mii.h。
MII(Media Independent Interface),多数以太网口用MII去与网卡芯片做一些相关信息的协商,不同的信号脚位有不同的用途,如通知对方将有输入输出、通知是否有拥塞等等,此处不再详细展开,总之可以简单理解为硬件层面提供给驱动获取一些信息的接口。
重点关注驱动中是如何实现ethtool功能的,这里我们能看到,ethtool在内核态提供了一套ethtool_ops函数接口,可供驱动程序去注册:
找到对应statistics的注册函数get_ethtool_stats:
其作为net_device里的成员变量,因此作为任何版本的驱动程序,只需在对应结构里注册自己的函数即可。
这里我们以i40e为例,看看ethtool是如何获取数据的,先找到i40e注册的ethtool_ops,这里是i40e_ethtool_ops,而对应的get_ethtool_stats是i40e_get_ethtool_stats:
……下面还有很多此处不展开
可以看到这里会调用i40e_update_stats(vsi)去更新数据,而从上面的代码片段来看,vsi和入参netdev是关联的,先看下vsi:
看看i40e_update_stats:
这里注意到这个pf其实是对应port相关的数据,此处先不展开,原理和vsi是一样的,重点是了解数据是如何更新的即可,继续跟i40e_update_vsi_stats:
……
这里我们终于见到了想看到的东西,把vsi相关联的net_device里的stats更新成vsi里记录的eth相关的stats,i40e_update_eth_stats是从网卡寄存器里(前面提到的MII)一一读取对应值的接口,通过这种方式实现了eth相关的stats的更新,这里细节就不展开了。
总之到这里我们就知道了net_device里的stats是在这条路径上被更新了。
这里还需要提一下rtnl_link_stats64这个结构体,是在net_device里主要统计数据的结构体,也就是说ethtool读到的还是这个东西:
其实这里从注释里也能找到问题2的一些答案了(rx_dropped, rx_errors相关注释)。
最后通过一张流程图图表明ethtool是如何获取i40e网卡相关统计信息的:
对于proc文件系统此处不详细介绍,直接从创建/proc/net/dev的入口dev_proc_init开始:
这里register_pernet_subsys就是注册一套网络命名空间子系统,里头会注册ops指针,这里也不详细展开,看一下具体注册的dev_proc_ops:
看下dev_proc_net_init:
proc_create_net这里注意到创建“dev”时注册了dev_seq_ops:
这里我们就可以看一下show对应的函数到底是怎么打印信息的了:
看到上面那个seq_puts打出来的是个error header,看一下下面的dev_seq_printf_stats:
这里终于看到我们熟悉的老朋友了,从net_device里拿出来的rtnl_link_stats64,可以看到下面都是熟悉的指标。
打印/proc/net/dev的关系图:
至此,已经可以得出结论/proc/net/dev下看到的数据和ethtool看到的数据都是net_device里的rtnl_link_stats64,可以认为两者是一个东西。具体哪个域代表哪几个指标可以从这里看到(比如drop是rx_dropped+ rx_missed_errors),也可以对应具体驱动去分析源码(比如对于上面的i40e,就可以去走读i40e_update_stats函数)。
回顾到问题本身,我们之前已经验证过ifconfig是从/proc/net/dev里来的,看一下/proc/net/dev:
可以看到其中对每个网卡的信息已经做了归档,ifconfig只是将这些信息的格式进行了美化,这里只要知道errs和drop对应了哪些指标,开头抛出的问题2也就迎刃而解。而这可以通过直接回顾刚才的对应/proc/net/dev的打印接口来解决。
首先来看errs,对应第四列:
这样得知了RX errors就是rx_errors
而drop对应第五列:
这样得知了RX dropped是rx_dropped + rx_missed_errors
netstat、nstat是来自/proc/net/netstat和/proc/net/snmp的数据;
ifconfig是读取/proc/net/dev下的数据,而后者的数据是从设备在内核的数据结构net_device里的结构rtnl_link_stats64中获取的;
ethtool是直接通过ioctl下放的方式从同样的结构(net_device中的rtnl_link_stats64)中获取数据;
因此可以认为ifconfig和ethtool两者看到的网卡相关数据来源是一样的,但是/proc/net/dev进行了一定程度的归档,因此ifconfig中的RX dropped = rx_dropped + rx_missed_errors,RX errors = rx_errors。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。