Vhost 回顧
Linux中的vhost驅動程序提供了內核virtio設備仿真。 通常,QEMU用戶空間進程模擬guest的I / O訪問。 Vhost將virtio仿真代碼放到內核中,使QEMU用戶空間脫離。 這允許設備仿真代碼直接調用到內核子系統,而不是從用戶空間執行系統調用。
vhost-net驅動程序模擬主機內核中的virtio-net網卡。 Vhost-net是最早的vhost設備,也是主流Linux中唯一可用的設備。 也已經開發了實驗vhost-blk和vhost-scsi裝置。
在Linux 3.0中,vhost代碼存放在drivers / vhost /中。 所有設備使用的通用代碼在drivers / vhost / vhost.c中。 這包括virtio vring訪問功能,所有virtio設備需要為了與客戶進行通信。 vhost-net代碼存放在drivers / vhost / net.c中。
Vhost 驅動模型
vhost-net驅動程序在主機上創建一個/ dev / vhost-net字符設備。 此字符設備作為配置vhost-net實例的接口。
當使用-netdev tap啟動QEMU時,vhost = on將打開/ dev / vhost-net並使用幾個ioctl調用初始化vhost-net實例。 這些必須將QEMU進程與vhost-net實例關聯,准備virtio功能協商,並將guest虛擬機物理內存映射傳遞到vhost-net驅動程序。
在初始化期間,vhost驅動程序創建一個名為vhost- $ pid的內核線程,其中$ pid是QEMU進程pid。 這個線程被稱為“vhost工作線程”。 工作線程的任務是處理I / O事件並執行設備仿真。

內核Virtio仿真
Vhost不會模擬一個完整的virtio PCI適配器。 相反,它只將自己限制為virtqueue操作。 QEMU仍然用於執行virtio特性協商和實時遷移。 這意味着vhost驅動程序不是完整的virtio設備實現,它依賴於用戶空間來處理控制平面,而數據平面在內核中完成。
vhost工作線程等待virtqueue kick,然后處理放在virtqueue上的緩沖區。 在vhost-net這意味着從tx virtque的數據包並且通過tap文件描述符發送它們。
文件描述符輪詢也由vhost工作線程完成。 在vhost-net中,當數據包進入tap文件描述符時,工作線程被喚醒,並將其放置到rx virtqueue中,以便客戶端可以接收它們。
Vhost作為用戶空間接口
vhost架構的一個令人驚訝的方面是它不以任何方式綁定到KVM。 Vhost是一個用戶空間接口,不依賴於KVM內核模塊。這意味着其他用戶空間代碼(如libpcap)在理論上可以使用vhost設備,如果他們發現它們方便的高性能I / O接口。
當客戶端kick主機,因為它已經將緩沖區放在一個virtqueue,需要一種方式來通知vhost工作線程有工作要做。由於vhost不依賴於KVM內核模塊,它們不能直接通信。相反,vhost實例使用vhost工作線程監視活動的eventfd文件描述符進行設置。 KVM內核模塊具有稱為ioeventfd的功能,用於獲取eventfd並將其掛接到特定的guest虛擬機I / O出口。 QEMU用戶空間注冊一個ioeventfd用於VIRTIO_PCI_QUEUE_NOTIFY硬件寄存器訪問,它能kick virtqueue。這是當guest虛擬機kick virtqueue,vhost工作線程被KVM內核模塊通知的方式。
在從vhost工作線程返回到中斷客戶端時,使用了類似的方法。 Vhost需要一個“call”文件描述符,寫這個文件描述符是為了去kick guest。 KVM內核模塊有一個稱為irqfd的功能,它允許eventfd觸發客戶機中斷。 QEMU用戶空間注冊一個irqfd為virtio PCI設備中斷並將其交給vhost實例。這是vhost工作線程如何中斷客戶端。
因此,vhost實例只知道guest虛擬機內存映射,kick eventfd和call eventfd。
More detail
drivers / vhost / vhost.c - 常見的vhost驅動程序代碼
drivers / vhost / net.c - vhost-net驅動
virt / kvm / eventfd.c - ioeventfd和irqfd
QEMU用戶空間代碼顯示如何初始化vhost實例:
hw / vhost.c - 常見的vhost初始化代碼
hw / vhost_net.c - vhost-net初始化
