从 virtio 网卡收包段错误问题出发反思个人问题分析的过程
问题描述
kvm arm 虚拟机中 dpdk 业务程序使用 virtio 网卡收包时触发段错误,断在如下位置:
#0 0x00000000004f2f9c in virtio_recv_pkts_vec ()
.....................................................
#5 0x00000000004e14d4 in rte_eal_mp_remote_launch ()
#6 0x000000000044ac64 in main ()
问题必现!
分析过程
确认版本信息
cpu 架构:arm
dpdk 版本:dpdk-16.11
dpdk 网卡驱动:virtio pmd 驱动
确认环境信息
cpu 信息:
processor : 0
model name : ARMv8 CPU
bogomips : 3600.00
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
flags : fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 2
cpu 为 ARMv8 处理器。
virtio 网卡 lspci -nvv 信息:
00:03.0 0200: 1af4:1000Subsystem: 1af4:0001Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-Latency: 0Interrupt: pin A routed to IRQ 42Region 0: I/O ports at 6040 [size=32]Region 1: Memory at 10ae5000 (32-bit, non-prefetchable) [size=4K]Region 4: Memory at 8000a00000 (64-bit, prefetchable) [size=16K]Expansion ROM at 10a40000 [disabled] [size=256K]Capabilities: [98] MSI-X: Enable+ Count=3 Masked-Vector table: BAR=1 offset=00000000PBA: BAR=1 offset=00000800Capabilities: [84] Vendor Specific Information: VirtIO: <unknown>BAR=0 offset=00000000 size=00000000Capabilities: [70] Vendor Specific Information: VirtIO: NotifyBAR=4 offset=00003000 size=00001000 multiplier=00000004Capabilities: [60] Vendor Specific Information: VirtIO: DeviceCfgBAR=4 offset=00002000 size=00001000Capabilities: [50] Vendor Specific Information: VirtIO: ISRBAR=4 offset=00001000 size=00001000Capabilities: [40] Vendor Specific Information: VirtIO: CommonCfgBAR=4 offset=00000000 size=00001000Kernel driver in use: igb_uio
从 lspci -nvv 的信息能够确定此网卡为 virtio modern 类型。
段错误相关信息
使用 gdb 运行程序,出现段错误后反汇编得到如下信息:
(gdb) disass
Dump of assembler code for function virtio_recv_pkts_vec:
...........................................................0x00000000004f2f90 <+336>: sub v7.8h, v7.8h, v0.8h0x00000000004f2f94 <+340>: tbl v1.16b, {v1.16b}, v5.16b0x00000000004f2f98 <+344>: stur q19, [x19,#-16]
=> 0x00000000004f2f9c <+348>: str q18, [x2,#32]0x00000000004f2fa0 <+352>: sub v1.8h, v1.8h, v0.8h
段错误出在 str 指令存储 q18 寄存器的值到 x2 + 32 指向的内存区域时,表明 x2 + 32 这个内存区域不可访问。
virtio_recv_pkts_vec
这个符号在 librte_pmd_virtio.a 中,编译 -O3 -g 版本的 librte_pmd_virtio.a 并使用 objdump -S -d 反汇编,找到如下代码:
__extension__ static __inline void __attribute__ ((__always_inline__))
vst1q_u64 (uint64_t *a, uint64x2_t b)
{__builtin_aarch64_st1v2di ((__builtin_aarch64_simd_di *) a,158: 3c9f0273 stur q19, [x19,#-16]15c: 3d800852 str q18, [x2,#32]
}
能够确定是在调用 vst1q_u64
函数的时候触发了段错误,阅读 virtio_recv_pkts_vec
函数的源码,发现有多处调用,没有找到具体是哪一处调用触发。
dpdk 示例程序对照测试
既然问题【必现】且问题出现在【驱动侧】,那使用相同版本的 l2fwd 测试,应该也能够复现问题。如果 l2fwd 能够复现问题则编译一个 -O3 -g 版本进一步定位,这样就不用依赖产品的业务程序。
使用 l2fwd 测试发现收发正常,同时使用 perf 观测到 l2fwd 使用的是 virtio_recv_pkts
收包函数。
代码侧分析
在 dpdk 程序调用 rte_eth_tx_queue_setup 配置 virtio 接口队列的时候,会调用如下代码判断是否能够开启 vec 收包函数:
#if defined RTE_ARCH_X86if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE3))use_simple_rxtx = 1;
#elif defined RTE_ARCH_ARM64 || defined CONFIG_RTE_ARCH_ARMif (rte_cpu_get_flag_enabled(RTE_CPUFLAG_NEON))use_simple_rxtx = 1;
#endif/* Use simple rx/tx func if single segment and no offloads */if (use_simple_rxtx &&(tx_conf->txq_flags & VIRTIO_SIMPLE_FLAGS) == VIRTIO_SIMPLE_FLAGS &&!vtpci_with_feature(hw, VIRTIO_NET_F_MRG_RXBUF)) {PMD_INIT_LOG(INFO, "Using simple rx/tx path");dev->tx_pkt_burst = virtio_xmit_pkts_simple;dev->rx_pkt_burst = virtio_recv_pkts_vec;hw->use_simple_rxtx = use_simple_rxtx;}
根据上文使用 l2fwd 的对照实验,我怀疑 dpdk 业务程序不应该使用 vec 向量收包函数,arm 架构上的 vec 向量收包函数依赖 neon 指令,我需要确定 neon 指令是否支持。
于是我百度了一下,发现了如下链接:怎么查看 cpu 是否有 neon 指令 。链接里面指出通过查看 /proc/cpuinfo 文件的内容就能够确定,示例信息中 Features 中有 neon 字符表示支持。
按照这个描述我确定虚拟机 cpu 不支持 neon 指令,此时 use_simple_rxtx 为 0,最终 virtio_recv_pkts_vec 的配置【不会生效】,而产品业务程序却段错误断在 virtio_recv_pkts_vec 函数中,表明它使用的的确是这个 vec 收包函数,这里就存在问题!
初步分析结论
根据上文的分析,我判断产品的 dpdk 业务程序根本不应该使用 vec 收包函数,于是得出如下怀疑点:
- 版本信息错误
- dpdk 程序编译问题
找产品同学确认了上面的信息,没有找到疑点,有种大跌眼镜的感觉!
进一步分析结论
既然分析的结论与现实情况严重不符合又需要解决问题,只能编译一个带 dpdk 调试信息的产品业务程序来定位。
首先修改 dpdk 配置文件,开启如下调试信息:
232 CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_INIT=y
235 CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DRIVER=y
然后使用 -O3 -g 编译相同版本代码的 dpdk 库并重新编译产品 dpdk 业务程序。使用新的程序调试发现确实使用了 virtio_recv_pkts_vec
收包函数。
程序启动的打印也证明如下代码确实执行了:
#elif defined RTE_ARCH_ARM64 || defined CONFIG_RTE_ARCH_ARMif (rte_cpu_get_flag_enabled(RTE_CPUFLAG_NEON))use_simple_rxtx = 1;
#endif/* Use simple rx/tx func if single segment and no offloads */if (use_simple_rxtx &&(tx_conf->txq_flags & VIRTIO_SIMPLE_FLAGS) == VIRTIO_SIMPLE_FLAGS &&!vtpci_with_feature(hw, VIRTIO_NET_F_MRG_RXBUF)) {PMD_INIT_LOG(INFO, "Using simple rx/tx path");dev->tx_pkt_burst = virtio_xmit_pkts_simple;dev->rx_pkt_burst = virtio_recv_pkts_vec;hw->use_simple_rxtx = use_simple_rxtx;}
定位到这里已经推翻了我之前的分析结论,事实表明此款 Armv8 处理器支持 neon 指令。
进一步阅读代码,我发现之前的分析中存在如下两个问题:
- cat /proc/cpuinfo 中查看到没有 neon 并不代表处理器不支持 neon 指令,dpdk 实际是访问 /proc/self/auxv 文件来确定处理器支持的指令特性的,当前 armv8 cpu 支持 neon 指令集
- l2fwd 与 dpdk 业务程序使用不同的收包函数,变化点不在于 neon 指令是否支持,而是上层配置的 tx_conf 中的 txq_flags 标志不一致
真正的问题是什么?
继续调试发现断在如下位置:
(gdb) bt
165 vst1q_u64((void *)&rx_pkts[1]->rx_descriptor_fields1,
166 pkt_mb[1]);
................................
问题原因为 rx_pkts[1] 指向的 mbuf 地址为空,访问这个空地址触发了段错误。进一步追问这个 mbuf 地址是从哪里来?它实际是通过如下代码从 sw_ring 中加载的。
141 mbp[0] = vld1q_u64((uint64_t *)(sw_ring + 0));
142 desc[0] = vld1q_u64((uint64_t *)(rused + 0));
143 vst1q_u64((uint64_t *)&rx_pkts[0], mbp[0]);
打印 sw_ring 中的多个 mbuf 地址发现都为 NULL,记录如下:
(gdb) print vq->sw_ring[0]
$26 = (struct rte_mbuf *) 0x0
(gdb) print vq->sw_ring[1]
$27 = (struct rte_mbuf *) 0x0
(gdb) print vq->sw_ring[2]
$28 = (struct rte_mbuf *) 0x0
进一步追问为什么 sw_ring 中的 mbuf 为 NULL?
根据过去阅读 intel 网卡驱动的经验,这个 sw_ring 一般是在为每个收包队列申请描述符的阶段赋值的,此后收包函数在将描述符的包向上层返回时会申请新的 mbuf 并更新相关的 sw_ring。
继续阅读代码确认 sw_ring 在 virtio_dev_rx_queue_setup 函数的如下代码上被配置:
/* Enqueue allocated buffers */if (hw->use_simple_rxtx)error = virtqueue_enqueue_recv_refill_simple(vq, m);elseerror = virtqueue_enqueue_recv_refill(vq, m);
这时候分析得出如下结论:
- virtqueue_enqueue_recv_refill_simple 函数负责填充 mbuf 地址到 sw_ring 上
- 确定 hw->use_simple_rxtx 在 virtio_dev_tx_queue_setup 中被重新赋值为 1
- 确定 l2fwd 与产品 dpdk 业务程序中均先执行 rx_queue_setup 再执行 tx_queue_setup
第三点与第二点因素导致 rx_queue_setup 中【不能判断】到 hw->use_simple_rxtx 为 1,则未填充 sw_ring,收包函数访问 sw_ring 中为 NULL 的 mbuf 时就会出现段错误。
这意味着这版的 virtio 驱动,需要先 tx_queue_setup 再 rx_queue_setup 才不会出问题,这一依赖明显不合理!
问题解决方案
搜索 dpdk git log,找到如下 commit 信息:
commit efc83a1e7fc319876835738871bf968e7ed5c935
Author: Olivier Matz <olivier.matz@6wind.com>
Date: Thu Sep 7 14:13:43 2017 +0200net/virtio: fix queue setup consistencyIn rx/tx queue setup functions, some code is executed only ifuse_simple_rxtx == 1. The value of this variable can change depending onthe offload flags or sse support. If Rx queue setup is called before Txqueue setup, it can result in an invalid configuration:- dev_configure is called: use_simple_rxtx is initialized to 0- rx queue setup is called: queues are initialized without simple pathsupport- tx queue setup is called: use_simple_rxtx switch to 1, and simpleRx/Tx handlers are selectedFix this by postponing a part of Rx/Tx queue initialization indev_start(), as it was the case in the initial implementation.Fixes: 48cec290a3d2 ("net/virtio: move queue configure code to proper place")Cc: stable@dpdk.orgSigned-off-by: Olivier Matz <olivier.matz@6wind.com>Acked-by: Yuanhan Liu <yliu@fridaylinux.org>
参考上述修改打 patch 即可解决问题!
反思
这个问题的分析过程有些波折,事后反思下发现确实存在一些值得思考的问题,主要问题列举如下。
1. 未批判性看待网上搜索到的信息
在想到这个问题的时候,我使用 starpage 搜索了一下,找到了如下链接:
How to check the existence of NEON on arm?
2.7.2. Run-time NEON unit detection
相关的描述信息如下:
As the /proc/cpuinfo output is text based, it is often preferred to look at the auxiliary vector
/proc/self/auxv. This contains the kernel hwcap in a binary format. The /proc/self/auxv file can
be easily searched for the AT_HWCAP record, to check for the HWCAP_NEON bit (4096).
我当时并没有直接搜索,而是看了下 dpdk 解析 cpuflag 的代码,发现它是通过解析 /proc/self/auxv
文件来确定 arm cpu 支持的特殊指令,而不是通过访问 /proc/cpuinfo。其实这个思路是正确的,代码是第一手的资料,网上搜索的信息已经是好多手的资料了,其可信度已经大打折扣,对于这些信息应该批判性看待,不应该盲目的相信。
2. 当事实与分析不一致的时候,优先质疑了事实而不是分析过程
现阶段我解决问题的一般过程是这样的:
- 在在线笔记中创建一个新的问题定位页面
- 写下问题描述、环境信息、然后开始边收集信息边记录边分析问题
- 分析不下去的时候,增加一个提问标题,写下当前的疑点
- 寻找确定当前疑点的数据、证据
- 重新审视问题与提出的疑点,循环这一过程,直至解决问题
随着上面这一过程的推广,我的问题解决能力得到了很大的提高,逐渐从具体的问题走向寻找自己对问题的认知存在的问题,往往当我将问题描述清楚,将一些提问项目明确结论时问题同时也得到了解决,这让我对自己的分析过程充满了信心,当事实与分析不一致时就容易出现否定事实的情况。
然而事实胜于雄辩,在工作上还是尽可能做的更客观一些,缺乏了客观就很容易被打脸。
3. 对比实验中忽略了关键的变化量,导致得出错误的结论
在这个问题中使用 l2fwd 做对比实验却没有找到真正的变化量。对于 l2fwd 与 dpdk 业务程序而言,处理器是否支持 neon 的检测是使用同一套 dpdk 代码做的,变化点不在这里,变化点实际在两个程序的接口配置中,忽略了这个变量,却误将 cpu 是否支持 neon 指令作为变量,进而得出了错误的结论。
这块还需要继续改进!
从 virtio 网卡收包段错误问题出发反思个人问题分析的过程相关推荐
- Linux网络协议栈:网卡收包分析
Table of Contents 网卡收包 一,框架 二,初始化 三,驱动收包 四,内核处理 参考文章 推荐阅读 网卡收包 内核网络模块如何初始化? 内核如何通过网卡驱动收发数据包? 驱动收到的数据 ...
- DPDK 网卡收包流程
Table of Contents 1.Linux网络收发包流程 1.1 网卡与liuux驱动交互 1.2 linux驱动与内核协议栈交互 题外1: 中断处理逻辑 题外2:中断的弊端 2.linux ...
- 网卡收包流程分析(一)
由于本人工作内容主要集中于kernel的网络子系统,刚接触这个模块,于是想梳理一下网卡驱动的收包过程,以下内容为个人理解,如有不对,希望大家能够多多指正,相互成长~ 后续会持续更新有关kernel网络 ...
- 代码学习-Linux内核网卡收包过程(NAPI)
本文通过学习RealTek8169/8168/8101网卡的驱动代码(drivers/net/r8169.c).梳理一下Linux下网卡的收包过程. 在下水平相当有限,有不当之处,还请大家斧正^_^ ...
- 32位网卡驱动 2008_DPDK之网卡收包流程
1.导读 一个网络报文从网卡接收到被应用处理,中间主要需要经历两个阶段: 阶段一:网卡通过其DMA硬件将收到的报文写入到收包队列中(入队) 阶段二:应用从收包队列中读取报文(出队) 下面以ixgbe网 ...
- 网卡收包基础: 中断-轮询-ring buffer-DMA-NAPI
参考链接: NAPL模式 NAPL简介 硬中断和软中断 中断与轮询的区别一 ring buffer 一. 中断 从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号 ...
- 服务器网卡收包性能测试
之前写过不少跟网络相关的 benchmark,比如: * <网络质量评估> * <10G(82599EB) 网卡测试优化(总)> 上面的更多的是放在带宽使用率上,即如何尽可能的 ...
- linux 网卡只收到包不发包,【干货分享】Linux虚拟机网卡只能收包不能发包?
[干货分享]Linux虚拟机网卡只能收包不能发包?: U1 d; M2 ~ ]7 Q: J5 M- v# J3 @ * v; Y P1 Q$ ]: I' T8 z在ovs场景主机与同主机上的虚拟机 ...
- centos 7 局域网丢包排查_ethtool原理介绍和解决网卡丢包排查思路
前言 之前记录过处理因为LVS网卡流量负载过高导致软中断发生丢包的问题,RPS和RFS网卡多队列性能调优实践,对一般人来说压力不大的情况下其实碰见的概率并不高.这次想分享的话题是比较常见服务器网卡丢包 ...
- ethtool 原理介绍和解决网卡丢包排查思路(附ethtool源码下载)
Table of Contents 1. 了解接收数据包的流程 将网卡收到的数据包转移到主机内存(NIC 与驱动交互) 通知系统内核处理(驱动与 Linux 内核交互) 2. ifconfig 解释 ...
最新文章
- POJ 1691 Painting A Board
- dw html5怎么美化,DW CS5/CS6代码格式化、美化插件 Dreamweaver代码格式化美化插件
- R.java文件介绍
- Python 爬虫面试题 102 道
- 【动态规划】POJ-2229
- 饿了么618数据:休闲娱乐业增超200% 医美消费者翻倍
- 超燃!奇安信首度对外公开内部网络攻防演习纪实片
- HDLbits day2 一位全加器逻辑表达式原理 FPGA关于仿真
- ubuntu20.04合并拆分pdf
- ubuntu 通过ssh链接ARM板 及 IMX6使用调试串口通信
- 复合函数高阶求导公式_复合函数求导公式大全 大学复合函数求导法则
- vscode插件Todo Tree配置
- spilt()分割字符串返回列表
- The Preliminary Contest for ICPC Asia Shanghai 2019 B. Light bulbs(卡了线段树空间的思维题)
- 代码随想录算法训练营第二十二天
- MAC系统下 U盘\硬盘速度测试工具BlackMagic Disk Speed Test
- 网易我的世界手机版服务器文件,网易我的世界手机版怎么导入地图_手机版地图导入教程...
- 研究生博士生都喜欢逛哪些网站?
- 电子产品可靠性测试公司/报告/费用/机构
- Linux圆角窗口,在deepin 20中出现electron窗口圆角处有不透明黑色的处理