背景知识
从网上搜virtio很容易搜到相关入门资料
比如,全全虚拟化和半虚拟化概念
比如,virtio是半虚拟化需要在虚拟机内装驱动(也叫前端)
后端在qemu中
前端和后端通过ring通信
1.guestOS层面
virtio的设备首先是个pci设备
虚拟机的PCI设备整个的是qemu模拟出来的(具体设备模拟在后面qemu部分分析)
在pci上虚拟出一条virtio-pci总线
这样guestos启动的时候根据总线从根向下扫描,会扫描到virtio_pci设备
也就是说,加载virtio-pci总线驱动
这里假设guesos为linux4.9.13
在../drivers/virtio/virtio_pci_common.c中的virtio_pci_probe会被调用
virtio_pci_probe
–>virtio_pci_modern_probe
这里主要注册了opst给设备,后续设备根据情况会调用到对应方法
–>register_virtio_device
这里讲设备总线设置为virtio_bus
当扫描到virtio_bus时候,会通过virtio_dev_probe加载设备
同时通过drv->probe(dev)加载设备的驱动
至此linu设备驱动模型中的设备,驱动,总线全部到位
下面依假设设备为blk,继续分析
–>virtblk_probe
内核中琐碎的工作通过worke的概念呈现的,这里初始化配置变更的钩子
一般情况下是指的磁盘容量变化
INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
init_vq–>vdev->config->find_vqs–>vp_modern_find_vqs–>vp_find_vqs–>vp_try_to_find_vqs–>vp_setup_vq–>setup_vq–>vring_create_virtqueue–>vring_alloc_queue–>virt_to_phys
注意virt_to_phys有gva获取到gha,也就是to_vvq(vq)->queue_dma_addr是前后端交互的基石
将磁盘操作的ops设置给vblk
vblk->tag_set.ops = &virtio_mq_ops;
virtio_queue_rq根据磁盘操作的请求类型,将请求转换为virtblk_req
建立scatter映射;
__virtblk_add_req–>virtqueue_add_sgs–>virtqueue_add将操作添加到队列中;
根据需要virtqueue_notify通知后端,单个或者多个buffer已经添加到queue中
2.qemu层面
设备创建
qemu后端模拟出virtio_pci设备,virtio_pci对应的驱动在guestos中
drive_init_func
–>drive_new
-drive file=/home/raw/centos7.img,format=raw,if=none,id=drive-virtio-disk0
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1
device_init_func
—->qdev_device_add
——>qdev_get_device_class
——>object_property_set_bool(OBJECT(dev), true, “realized”, &err);
——>virtio_blk_pci_info
——>virtio_blk_pci_realize
——>virtio_device_realize–>virtio_bus_device_plugged–>device_plugged–>virtio_pci_device_plugged–>virtio_pci_config_ops
——>virtio_blk_device_realize
——>virtio_add_queue(vdev, 128, virtio_blk_handle_output)
数据传输
virtio前后端交互,通过virtio_queu实现
virtio_queu实现了ring_buffer,用于保存前端驱动和后端处理程序执行的信息
上面示例化的时候有注册ops
virtio_pci_config_write
–>virtio_ioport_write
–>VIRTIO_PCI_QUEUE_PFN
–>virtio_queue_set_addr(这里获取到给guestos的queue信息)
–>VIRTIO_PCI_QUEUE_NOTIFY
–>virtio_queue_notify
–>virtio_queue_notify_vq
–>handle_output
–>virtio_blk_handle_output
–>virtio_blk_get_request–>virtqueue_pop–>vring_desc_addr–>virtio_ldq_phys(根据给gpa转化为gva)
–>virtio_blk_handle_request–>virtio_blk_submit_multireq–>submit_requests–>blk_aio_writev–>bdrv_aio_writev–>bdrv_co_aio_rw_vector–>bdrv_co_do_rw–>bdrv_co_do_writev–>bdrv_co_do_pwritev–>bdrv_aligned_pwritev(到异步IO)