dpdk-16.11 virtio 驱动初始化卡住问题定位

问题描述

qemu 5.1.0 版本 kvm 虚拟化环境中使用 virtio 网卡时,dpdk 程序初始化打印了如下信息后卡住:

..............................................
EAL: PCI device 0000:00:04.0 on NUMA socket -1
EAL:   probe driver: 1af4:1000 rte_virtio_pmd
EAL:   PCI memory mapped at 0x400148000000
EAL:   PCI memory mapped at 0x400148001000

gdb 查看到如下堆栈信息:

(gdb) bt
#0  0x00007ffff75faec4 in modern_set_status () from /lib64/libdpdk.so
#1  0x00007ffff75fb4cd in vtpci_reset () from /lib64/libdpdk.so
#2  0x00007ffff75ff534 in virtio_init_device () from /lib64/libdpdk.so
#3  0x00007ffff75ffe86 in eth_virtio_dev_init () from /lib64/libdpdk.so
..........................................................................,....

堆栈信息表明程序是在 modern_set_status 函数中卡住了,具体原因并不清楚。

信息收集

内核版本信息

3.16.35

qemu 版本

[root@localhost ~]#  /usr/bin/qemu-system-x86_64 --version
QEMU emulator version 5.1.0

接口驱动绑定信息

Network devices using DPDK-compatible driver
============================================
0000:00:04.0 'Virtio network device' drv=igb_uio unused=uio_pci_generic
0000:00:05.0 'Virtio network device' drv=igb_uio unused=uio_pci_generic

网卡正常绑定到 igb_uio 驱动上。

接口 lspci 信息

00:04.0 0200: 1af4:1000
        Subsystem: 1af4:0001
        Control: 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: 0
        Interrupt: pin A routed to IRQ 11
        Region 0: I/O ports at c180 [size=32]
        Region 1: Memory at febd2000 (32-bit, non-prefetchable) [size=4K]
        Region 4: Memory at fe000000 (64-bit, prefetchable) [size=16K]
        Expansion ROM at feb40000 [disabled] [size=256K]
        Capabilities: [98] MSI-X: Enable+ Count=3 Masked-
                Vector table: BAR=1 offset=00000000
                PBA: BAR=1 offset=00000800
        Capabilities: [84] Vendor Specific Information: VirtIO: <unknown>
                BAR=0 offset=00000000 size=00000000
        Capabilities: [70] Vendor Specific Information: VirtIO: Notify
                BAR=4 offset=00003000 size=00001000 multiplier=00000004
        Capabilities: [60] Vendor Specific Information: VirtIO: DeviceCfg
                BAR=4 offset=00002000 size=00001000
        Capabilities: [50] Vendor Specific Information: VirtIO: ISR
                BAR=4 offset=00001000 size=00001000
        Capabilities: [40] Vendor Specific Information: VirtIO: CommonCfg
                BAR=4 offset=00000000 size=00001000
        Kernel driver in use: igb_uio

lspci 信息未见明显异常。

程序 pmap 信息

程序映射的 resource 信息如下:

0000600009e00000      4K rw-s- /sys/devices/pci0000:00/0000:00:04.0/resource1
0000600009e01000     16K rw-s- /sys/devices/pci0000:00/0000:00:04.0/resource4

对照测试

dpdk 示例程序 l2fwd 测试

测试结论:
同样卡住,堆栈与产品程序 一致,日志记录如下:

(gdb) bt
#0  0x00000000004f1184 in modern_set_status ()
#1  0x00000000004f178d in vtpci_reset ()
#2  0x00000000004f57f4 in virtio_init_device ()
#3  0x00000000004f6146 in eth_virtio_dev_init ()
...................................................

高版本 l2fwd 程序测试

编译 dpdk-19.11 版本 l2fwd 测试,现象相同。

官方驱动测试

能够正常绑定到 virtio-pci 驱动中,表明问题出在 dpdk 驱动上。

排查内容

1. dmesg 信息

虚拟机 dmesg 信息未见异常,宿主机 dmesg 信息未见异常。

2. 关闭 iommu

关闭后问题仍旧存在

源码分析

modern_set_status 函数源码如下:

static void
modern_set_status(struct virtio_hw *hw, uint8_t status)
{
	io_write8(status, &hw->common_cfg->device_status);
}

它的功能是写入 virtio 网卡寄存器,设定虚拟网卡状态。执行 io_write8 函数写入的时候卡住表明问题出在虚拟网卡 pci resource 空间访问上,确认代码逻辑中此处为第一次访问网卡寄存器,表明映射的虚拟网卡寄存器空间不可访问,怀疑问题可能与 qemu 版本有关。

卡住的现象到底是怎样的?

gdb 查看卡住是的寄存器信息与函数返汇编代码如下:

(gdb) info registers
rax            0x600009e01000   105553281945600
rbx            0x60000501f100   105553200279808
rcx            0x0      0
rdx            0x3      3
rsi            0x0      0
rdi            0x60000501f100   105553200279808
rbp            0x29cb1f0        0x29cb1f0
rsp            0x7fffffffdd48   0x7fffffffdd48
r8             0x1      1
r9             0xfeff7efdff336462       -72199439641254814
r10            0x7fffffffdb20   140737488345888
r11            0x7ffff75fb4c0   140737343632576
r12            0x29cb1f0        43823600
r13            0x60000501f1c0   105553200280000
r14            0x60000501f100   105553200279808
r15            0x74fcd0 7666896
rip            0x7ffff75faec4   0x7ffff75faec4 <modern_set_status+4>
eflags         0x3246   [ PF ZF IF #12 #13 ]
cs             0x33     51
ss             0x2b     43
....................................................................
(gdb) disass
Dump of assembler code for function modern_set_status:
   0x00007ffff75faec0 <+0>:     mov    0x40(%rdi),%rax
=> 0x00007ffff75faec4 <+4>:     mov    %sil,0x14(%rax)
   0x00007ffff75faec8 <+8>:     retq
End of assembler dump.

可以确定是在访问 resource4 virtio bar 空间的时候卡住,仅仅影响当前进程,其它进程仍旧能够正常运行。有这个现象,我比较怀疑是 qemu 本身的问题,但是解释不了为什么官方驱动能够正常工作,还需要继续定位。

思考

内核驱动与 dpdk 驱动访问网卡的 pci resource bar 空间都需要经过地址映射,dpdk 中通过 mmap resource 文件来映射地址,这个操作我判断大概率不会存在差别,毕竟 resource 文件也是内核探测 pci 设备的时候配置的,出问题的概率非常小。

从现象上来看比较像是在用户态无法访问,但是一般来说在虚拟机中访问 virtio resource bar 空间在 qemu 中的行为应当是一致的,不应该出现内核能访问,用户态程序无法访问的问题,毕竟 qemu 并不区分虚拟机中的内核与用户态程序,而且其实内核与用户态程序只是分时执行代码,qemu 侧不应该有这个区别。

阅读内核代码

由于我对内核 virtio-pci、virtio-net 这部分代码的实现并不清楚,既然内核代码能够工作,那需要先研究下内核代码的逻辑,一定是这个过程与 dpdk 驱动的执行过程有区别才造成了不同的结果

阅读 3.16.35 内核的代码,发现它的 virtio-pci 驱动并不支持 virtio modern,只支持 virtio legacy 这种访问方式。

在这种访问方式下,它设置 virtio 网卡 status 寄存器使用如下代码:

static void vp_reset(struct virtio_device *vdev)
{
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        /* 0 status means a reset. */
        iowrite8(0, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
        /* Flush out the status write, and flush in device writes,
         * including MSi-X interrupts, if any. */
        ioread8(vp_dev->ioaddr + VIRTIO_PCI_STATUS);
        /* Flush pending VQ/configuration callbacks. */
        vp_synchronize_vectors(vdev);
}

而我们使用的 dpdk virtio 驱动支持 virtio modern 设备,reset virtio 网卡通过读写 virtio resource bar4 中 CommonCfg 处的空间来进行 virtio 网卡的 reset。

修改 dpdk virtio 驱动,使用 legacy 模式访问 virtio 网卡寄存器

将 vtpci_init 函数修改为如下逻辑:

				#if 0
        if (virtio_read_caps(dev, hw) == 0) {
                PMD_INIT_LOG(INFO, "modern virtio pci detected.");
                hw->vtpci_ops = &modern_ops;
                hw->modern    = 1;
                *dev_flags |= RTE_ETH_DEV_INTR_LSC;
                return 0;
        }
        #endif

        PMD_INIT_LOG(INFO, "trying with legacy virtio pci.");
        if (legacy_virtio_resource_init(dev, hw, dev_flags) < 0) {
                if (dev->kdrv == RTE_KDRV_UNKNOWN) {
                        PMD_INIT_LOG(INFO,
                                "skip kernel managed virtio device.");
                        return 1;
                }
                return -1;
        }

临时注掉了使用 virtio modern pci ops 的逻辑,改为使用 legacy pci ops,重新编译 l2fwd 能够正常初始化。
l2fwd 运行打印了如下与 virtio 驱动相关的信息:

EAL: PCI device 0000:00:04.0 on NUMA socket -1
EAL:   probe driver: 1af4:1000 rte_virtio_pmd
EAL: PCI Port IO found start=0xc180
EAL: PCI device 0000:00:05.0 on NUMA socket -1
EAL:   probe driver: 1af4:1000 rte_virtio_pmd
EAL: PCI Port IO found start=0xc1a0

dpdk 驱动对 virtio modern 设备会先尝试使用 modern pci ops 的方法读写网卡寄存器,当尝试失败后再继续尝试 legacy pci ops,这个顺序并不能调整,如果将 legacy pci ops 尝试放到前面,那一些 virtio modern 网卡就不能使用 modern pci ops 来读写网卡寄存器,性能会下降。

从现象上表明问题出在 qemu 上,可能是某种兼容性问题,可以尝试通过升级 qemu 版本解决。

能否修改 qemu 启动参数指定使用 virtio-legacy 设备?

异常 kvm VM qemu 启动参数中 virtio 网卡相关内容如下:

-device virtio-net-pci,netdev=hostnet1,id=net1,mac=52:54:00:5e:90:1a,bus=pci.0,addr=0x4 -netdev tap,fd=48,id=hostnet2,vhost=on,vhostfd=49 -device virtio-net-pci,netdev=hostnet2,id=net2,mac=52:54:00:af:a8:c9,bus=pci.0,addr=0x5

配置上没有发现有啥异常,网上搜索到可以设置 disable-modern=‘off’ 参数,但是我们的虚拟机配置是通过 virt-manager 生成的,一通搜索与尝试确定 libvirtio 不支持强制设置 virtio 设备为 legacy 模式。

参考链接

virtio_net 与 virtio-pci 驱动关联浅析

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值