virtio 学习随笔 —— 四、virtio设备发现以及枚举
四、virtio设备发现以及枚举
Linux Kernel: v2.6.15
virtio总线
virtio总线是一个虚拟总线,物理上并不存在,该总线为virtio设备提供统一的管理、探测、注册以及匹配的抽象层,所有的virtio设备都会通过这个总线进行设备匹配与驱动绑定。
virtio bus + virtio device的抽象是为了统一设备模型、简化设备驱动的编写而设计的。这是因为每种设备都有自己的寄存器定义、初始化流程和DMA控制逻辑等,不同设备之间的差异巨大, 因此驱动程序千差万别,维护成本高,缺乏可复用性。virtio通过定义一套统一的通信机制和驱动框架简化这个设备控制逻辑。
virtio bus是一个统一的“虚拟总线抽象”,无论底层设备是接入到PCI总线还是其他总线,都可以通过实现virtio标准挂载到virtio bus。
【上面的内容太抽象,如果不理解可以直接看下面】
【系统总线层 - 平台相关总线】
+----------------+ +----------------+ +----------------+
| PCI bus | | MMIO bus | | CCW bus |
+-------+--------+ +--------+-------+ +--------+-------+
| | |
v v v
【Transport 层 - virtio 传输层驱动】
+---------------+ +---------------+ +----------------+
| virtio-pci | | virtio-mmio | | virtio-ccw |
+-------+-------+ +-------+-------+ +--------+--------+
| | |
| 创建并注册 | virtio_device |
+----------+----------+---------+----------+
| |
v v
【virtio 总线层 - 抽象统一的 virtio bus】
+----------------------------+
| virtio bus |
+-------------+--------------+
|
匹配 virtio_device 和 virtio_driver
|
v
【virtio 驱动层 - 功能驱动】
+-------------------+ +-------------------+ +-------------------+
| virtio_blk | | virtio_net | | virtio_console |
+-------------------+ +-------------------+ +-------------------+
以通过PCI总线连接的virtio-net设备注册与驱动绑定流程为例
- PCI 总线初始化:系统启动时,PCI 子系统会枚举总线上的所有设备,并根据 Vendor/Device ID 识别出 Virtio 设备
- virtio-pci 驱动探测:当 PCI 总线枚举到 Virtio 设备并与 virtio-pci 驱动的 id_table 匹配时,会调用驱动的回调函数
virtio_pci_probe
。在该函数中,通过register_virtio_device()
将 Virtio 设备注册到 virtio 总线 - 设备与驱动的匹配与绑定:
register_virtio_device()
内部调用device_add()
,触发 virtio 总线的设备枚举机制。总线会查找所有已注册的 virtio 驱动(如 virtio-net),如果驱动的 id_table 能与设备匹配,则调用总线的probe
回调(即virtio_dev_probe
),完成 Virtio 特性协商和设备初始化
virtio-pci
和virtio-mmio
驱动作为桥梁,负责将底层硬件平台(如 PCI 或 MMIO 设备)包装成统一的virtio_device
,并接入 virtio 总线(virtio_bus
)。这样,上层的 virtio 驱动(如 virtio-net、virtio-blk 等)就可以通过统一的接口与不同类型的底层设备进行交互,实现虚拟化设备的标准化管理和驱动绑定。
Virtio-PCI设备枚举及驱动绑定全流程
/* 1. PCI总线枚举阶段 */
pci_scan_bus() // 扫描PCI总线
→ pci_scan_slot()
→ pci_scan_device()
→ pci_device_add()
→ device_register(&dev->dev) // 注册PCI设备
/* 2. PCI设备与驱动匹配 */
virtio_pci_driver.probe = virtio_pci_probe // drivers/virtio/virtio_pci_common.c:487
→ when PCI device matches:
vp_dev = kzalloc(sizeof(*vp_dev)) // 分配virtio_pci_device
pci_set_drvdata(pci_dev, vp_dev)
/* 3. 注册virtio设备 */
register_virtio_device(&vp_dev->vdev)
→ device_initialize(&dev->dev) // 初始化设备
→ device_add(&dev->dev) // 加入设备模型
→ bus_add_device(dev) // 添加到virtio总线
→ bus_probe_device(dev) // 触发探测
→ device_initial_probe(dev)
→ __device_attach(dev, false)
→ bus_for_each_drv(dev->bus, __device_attach_driver)
→ for_each_driver_on_bus:
/* 4. 驱动匹配检查 */
driver_match_device(drv, dev) // virtio_dev_match()
if matched:
driver_probe_device(drv, dev)
→ really_probe(dev, drv)
→ dev->bus->probe(dev) // virtio_dev_probe()
/* 5. 调用具体驱动probe */
drv->probe(dev) // virtnet_probe() for virtio-net
/* 6. 网络设备初始化 */
virtnet_probe(struct virtio_device *vdev) // drivers/net/virtio_net.c