QEMU模拟的VIRTIO设备同时也是PCIE设备,Guest中VIRTIO PCIE驱动与之匹配,根据设备驱动模型,最终触发probe函数virtio_pci_probe()。该probe函数使能PCIE设备,并注册VIRTIO设备,并与VIRTIO-BLK匹配,触发VIRTIO-BLK驱动probe函数。
这里涉及两个驱动:VIRTIO-PCI驱动和VIRTIO-BLK驱动。
VIRTIO PCI驱动
作为PCIE驱动,其执行过程基本与其他PCIE驱动步骤类似如下:
- 使能PCIE设备pci_enable_device();
- 根据force_legacy决定是传统模式还是modern模式,这里不分析传统模式。对于modern模式,通过virtio_pci_modern_probe()将common/isr/notify/device配置空间进行映射。
- 设置PCIE master,pci_set_master();
-
通过函数register_virtio_device()注册virtio_device;
其中virto_pci_modern_probe()详细过程如下:
VIRTIO-BLK驱动
前面VIRTIO PCIE驱动注册virtio_device,根据设备驱动模式,由于加载virtio_driver,最终触发probe函数virtblk_probe()。Probe函数实际上执行block驱动基本操作如下:
- 通过函数ida_alloc_range()分配block设置index;
- 设置tag_set.ops = &virtio_mq_ops,主要用于IO请求的下发和完成;
- 通过函数blk_mq_alloc_tag_set()分配tag_set;
- 通过函数blk_mq_alloc_disk()分配disk;
- 通过函数blk_queue_max_segments()设置IO请求最大segment;
- 通过函数blk_queue_max_hw_sectors()设置IO请求最大sectors;
- 通过函数set_capacity_and_notify()设置disk的capacity;
- 通过函数device_add_disk()将disk加入到系统中
除了上述BLOCK层驱动基本操作外,它会通过init_vq()初始化vblk的virtqueue,它会分配vqs,并设置vq对应的callback函数virtblk_done(),且通过函数vdev->config->find_vqs()设置中断用于通知数据处理完成。
该过程分配vqs,并为每个vq分配一个中断,用于通知Guest数据处理完成,中断处理函数最终调用vq->callback(),对于VIRTIO-BLK,调用函数virtblk_done()。同时设置vq,包括设置notify函数,用于通知数据下发完成。