• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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
    ..........................................................................,....
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    信息收集

    内核版本信息

    3.16.35

    qemu 版本

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

    接口驱动绑定信息

    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
    
    • 1
    • 2
    • 3
    • 4

    网卡正常绑定到 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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
    
    • 1
    • 2

    对照测试

    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 ()
    ...................................................
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    高版本 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    它的功能是写入 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.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    可以确定是在访问 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    而我们使用的 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;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    临时注掉了使用 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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
    
    • 1

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

    参考链接

    virtio_net 与 virtio-pci 驱动关联浅析

  • 相关阅读:
    CodeForces Round #817 (div.4) A~F
    Java进阶之路——从初级程序员到架构师,从小工到专家
    无线传感器网络数据压缩与融合及安全机制的matlab仿真
    Ubuntu18 Opencv3.4.12 viz 3D显示安装、编译、移植
    GEE:Bfast时间序列扰动检测
    20. mediasoup服务器的布署与使用
    SRE 排障利器,接口请求超时试试 httpstat
    Ansible 企业级自动化运维平台开发实战
    数据库中的数据
    在window11上安装sshd
  • 原文地址:https://blog.csdn.net/Longyu_wlz/article/details/128065623