问题描述

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 收包函数,于是得出如下怀疑点:

  1. 版本信息错误
  2. 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 指令。

进一步阅读代码,我发现之前的分析中存在如下两个问题:

  1. cat /proc/cpuinfo 中查看到没有 neon 并不代表处理器不支持 neon 指令,dpdk 实际是访问 /proc/self/auxv 文件来确定处理器支持的指令特性的,当前 armv8 cpu 支持 neon 指令集
  2. 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);

这时候分析得出如下结论:

  1. virtqueue_enqueue_recv_refill_simple 函数负责填充 mbuf 地址到 sw_ring 上
  2. 确定 hw->use_simple_rxtxvirtio_dev_tx_queue_setup 中被重新赋值为 1
  3. 确定 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. 当事实与分析不一致的时候,优先质疑了事实而不是分析过程

现阶段我解决问题的一般过程是这样的:

  1. 在在线笔记中创建一个新的问题定位页面
  2. 写下问题描述、环境信息、然后开始边收集信息边记录边分析问题
  3. 分析不下去的时候,增加一个提问标题,写下当前的疑点
  4. 寻找确定当前疑点的数据、证据
  5. 重新审视问题与提出的疑点,循环这一过程,直至解决问题

随着上面这一过程的推广,我的问题解决能力得到了很大的提高,逐渐从具体的问题走向寻找自己对问题的认知存在的问题,往往当我将问题描述清楚,将一些提问项目明确结论时问题同时也得到了解决,这让我对自己的分析过程充满了信心,当事实与分析不一致时就容易出现否定事实的情况。

然而事实胜于雄辩,在工作上还是尽可能做的更客观一些,缺乏了客观就很容易被打脸。

3. 对比实验中忽略了关键的变化量,导致得出错误的结论

在这个问题中使用 l2fwd 做对比实验却没有找到真正的变化量。对于 l2fwd 与 dpdk 业务程序而言,处理器是否支持 neon 的检测是使用同一套 dpdk 代码做的,变化点不在这里,变化点实际在两个程序的接口配置中,忽略了这个变量,却误将 cpu 是否支持 neon 指令作为变量,进而得出了错误的结论。

这块还需要继续改进!

从 virtio 网卡收包段错误问题出发反思个人问题分析的过程相关推荐

  1. Linux网络协议栈:网卡收包分析

    Table of Contents 网卡收包 一,框架 二,初始化 三,驱动收包 四,内核处理 参考文章 推荐阅读 网卡收包 内核网络模块如何初始化? 内核如何通过网卡驱动收发数据包? 驱动收到的数据 ...

  2. DPDK 网卡收包流程

    Table of Contents 1.Linux网络收发包流程 1.1 网卡与liuux驱动交互 1.2  linux驱动与内核协议栈交互 题外1: 中断处理逻辑 题外2:中断的弊端 2.linux ...

  3. 网卡收包流程分析(一)

    由于本人工作内容主要集中于kernel的网络子系统,刚接触这个模块,于是想梳理一下网卡驱动的收包过程,以下内容为个人理解,如有不对,希望大家能够多多指正,相互成长~ 后续会持续更新有关kernel网络 ...

  4. 代码学习-Linux内核网卡收包过程(NAPI)

    本文通过学习RealTek8169/8168/8101网卡的驱动代码(drivers/net/r8169.c).梳理一下Linux下网卡的收包过程. 在下水平相当有限,有不当之处,还请大家斧正^_^ ...

  5. 32位网卡驱动 2008_DPDK之网卡收包流程

    1.导读 一个网络报文从网卡接收到被应用处理,中间主要需要经历两个阶段: 阶段一:网卡通过其DMA硬件将收到的报文写入到收包队列中(入队) 阶段二:应用从收包队列中读取报文(出队) 下面以ixgbe网 ...

  6. 网卡收包基础: 中断-轮询-ring buffer-DMA-NAPI

    参考链接: NAPL模式 NAPL简介 硬中断和软中断 中断与轮询的区别一 ring buffer 一. 中断 从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号 ...

  7. 服务器网卡收包性能测试

    之前写过不少跟网络相关的 benchmark,比如: * <网络质量评估> * <10G(82599EB) 网卡测试优化(总)> 上面的更多的是放在带宽使用率上,即如何尽可能的 ...

  8. linux 网卡只收到包不发包,【干货分享】Linux虚拟机网卡只能收包不能发包?

    [干货分享]Linux虚拟机网卡只能收包不能发包?: U1 d; M2 ~  ]7 Q: J5 M- v# J3 @ * v; Y  P1 Q$ ]: I' T8 z在ovs场景主机与同主机上的虚拟机 ...

  9. centos 7 局域网丢包排查_ethtool原理介绍和解决网卡丢包排查思路

    前言 之前记录过处理因为LVS网卡流量负载过高导致软中断发生丢包的问题,RPS和RFS网卡多队列性能调优实践,对一般人来说压力不大的情况下其实碰见的概率并不高.这次想分享的话题是比较常见服务器网卡丢包 ...

  10. ethtool 原理介绍和解决网卡丢包排查思路(附ethtool源码下载)

    Table of Contents 1. 了解接收数据包的流程 将网卡收到的数据包转移到主机内存(NIC 与驱动交互) 通知系统内核处理(驱动与 Linux 内核交互) 2. ifconfig 解释 ...

最新文章

  1. POJ 1691 Painting A Board
  2. dw html5怎么美化,DW CS5/CS6代码格式化、美化插件 Dreamweaver代码格式化美化插件
  3. R.java文件介绍
  4. Python 爬虫面试题 102 道
  5. 【动态规划】POJ-2229
  6. 饿了么618数据:休闲娱乐业增超200% 医美消费者翻倍
  7. 超燃!奇安信首度对外公开内部网络攻防演习纪实片
  8. HDLbits day2 一位全加器逻辑表达式原理 FPGA关于仿真
  9. ubuntu20.04合并拆分pdf
  10. ubuntu 通过ssh链接ARM板 及 IMX6使用调试串口通信
  11. 复合函数高阶求导公式_复合函数求导公式大全 大学复合函数求导法则
  12. vscode插件Todo Tree配置
  13. spilt()分割字符串返回列表
  14. The Preliminary Contest for ICPC Asia Shanghai 2019 B. Light bulbs(卡了线段树空间的思维题)
  15. 代码随想录算法训练营第二十二天
  16. MAC系统下 U盘\硬盘速度测试工具BlackMagic Disk Speed Test
  17. 网易我的世界手机版服务器文件,网易我的世界手机版怎么导入地图_手机版地图导入教程...
  18. 研究生博士生都喜欢逛哪些网站?
  19. 电子产品可靠性测试公司/报告/费用/机构
  20. Linux圆角窗口,在deepin 20中出现electron窗口圆角处有不透明黑色的处理

热门文章

  1. 移动、联通、电信APN
  2. 百度商桥点击咨询自定义按钮
  3. 一只菜鸟的前端实习记录(碎碎念)
  4. IDEA SVN拉下来的项目import project 没反应 解决方案
  5. 多极神经元切片手绘图,神经组织切片手绘图片
  6. 神经元如何将视觉世界映射到人脑?
  7. 论文阅读Underexposed Photo Enhancement using Deep Illumination Estimation
  8. mysql drop语句怎么用_SQL DROP 语句
  9. 从步履蹒跚到举重若轻,阿里基础架构如何扛住全球最猛的流量洪峰?
  10. 教你简单制作视频滚动字幕