目录
1. 问题描述
VIRTIO_PCI_QUEUE_NOTIFY 是前端驱动进行 KICK 的接口,前端驱动写完报文,就要通过 VIRTIO_PCI_QUEUE_NOTIFY 发送 KICK 消息给qemu以便发送出来,我们以0.95为例,在QEMU中抓取对 VIRTIO_PCI_QUEUE_NOTIFY 的写操作,发现只有一次 VIRTIO_PCI_QUEUE_NOTIFY 的IO写操作,和预期是不符合的
但从前端驱动打印来看,确实每个报文都有kick操作
这些KICK操作都上哪里去了?
2. 问题跟踪及解释
想跟踪这个问题,有一个很好的入口,就是Control Queue,因为驱动加载后的第一步就是通过Control Queue发送使用的queue pair数量,那么我们就可以进行断点抓取,入口是 virtio_net_handle_ctrl
可以看到 virtio的事件处理接口是 virtio_queue_host_notifier_read
virtio_queue_host_notifier_read 是在 virtio_device_start_ioeventfd_impl 中注册的
我们继续跟踪 virtio_bus_set_host_notifier
可以看到是在驱动加载最后一个向 0x18(PCI_STATUS)中写入 0x7 (DRVIER_OK)时候完成 注册的,我们下面来看一下注册的过程
从 virtio_bus_start_ioeventfd 开始看
virtio_device_start_ioeventfd_impl
首先调用 virtio_bus_set_host_notifier ,把 notifier 注册给KVM
virtio_pci_ioeventfd_assign
我们可以看到把 notifier 注册给地址 legacy_mr 的 VIRTIO_PCI_QUEUE_NOTIFY(0x10), 这就是KICK地址端,长度2WORD,16字节,这就和前端驱动对起来了
之后,KVM接收到这段地址MR的退出后,直接就dispatch到notifier上了,就完成了kick通知
我们回到 virtio_device_start_ioeventfd_impl, 可以看到将 host_notifier 注册为 virtio_queue_host_notifier_read
这样,一旦guest发生了写KICK事件,KVM退出后,就会通过MR直接 dispatch 到 virtio_queue_host_notifier_read上进行处理
不再经过QEMU的IO处理,提高了效率。从而qemu的virtio_ioport_write也就接收不到对VIRTIO_PCI_QUEUE_NOTIFY写操作了