在上期,我们提到,子虚将SPDK的轮询机制,与JFZ女士的日本游记中的画面进行了联系,虽然觉得自己在隐秘地开车,但还是留下了证据。
为了让方老师不把自己开车的证据说出去,子虚主动提出,捐献一篇SPDK的学习心得,这就是SPDK在虚拟化中的应用——vhost加速。
我们知道,Linux下,KVM虚拟机的IO设备,实际上有三种工作方式:
那么,我们有没有办法通过SPDK来加速虚拟机对NVMe SSD的访问呢?
这就涉及到今天的主题——SPDK vhost guest.
我们刚才提到,在KVM虚拟出的虚拟机中,挂载的云存储卷的设备名一般为vd*, 如vda, vdb, vdc... 而挂载在本地的硬盘设备名一般为sda, sdb... 这就是因为,QEMU为虚拟机提供了backend的虚拟化存储设备驱动接口,而在GuestOS操作系统中内置了这些虚拟化设备的Frontend驱动,这种方案叫做VirtIO。
VirtIO对于VM的GuestOS,虚拟出了一个PCI设备,当然也支持PCI-SIG的规范,如配置空间(PCI寄存器)和中断等功能。VM的GuestOS操作VirtIO的块设备时,实际上其驱动会将SCSI命令字写入一个队列,并向这个虚拟的PCI设备写入队列ID。由于写入PCI设备的配置空间属于特权指令,每次这种写入行为都会触发VM_EXIT。可想而知地,这样的实现成为了性能提升的卡点。
以读写NVMe为例:
显然,在虚拟化系统中,步骤2/6/8都会产生VM_EXIT,也就是造成了性能提升的卡点。
SPDK vhost,就是通过基于SPDK实现virtio。SPDK在后端通过轮询机制,读取Frontend驱动向队列中写入的SCSi命令字,并且利用零拷贝技术消除VirtIO的性能卡点。
对于读写NVMe控制器的PCIe的空间造成的VM_Exit,SPDK运用了一个巧妙的技巧:利用NVMe 1.3中的shadow doorbell机制,也就是让虚拟机并不真的向NVMe的PCIe配置空间寄存器中写入doorbell触发NVMe控制器操作,而是模拟一个软件控制器,向软件控制器写入doorbell并不是特权指令,也不会产生VM_EXIT。
在上图中可见,Virtio-SCSI驱动不产生PCIe的MSI-X中断,改为虚拟化侧的SPDK vhost对队列进行轮询。进一步地,Virtio-SCSI驱动还可以用unix domain socket机制,来代替doorbell来通知QEMU。
这样,KVM/QEMU就巧妙地消除了VM_EXIT和内存拷贝的开销,让NVMe的高IO尽量少消耗宝贵的CPU指令周期,让单VM的IO突破百万的上限。
但是,我们发现,这样的机制只能让虚拟机访问本地的NVMe盘。对于分布式块存储的场景,有没有好的办法让虚拟机访问远端云盘也取得同样的加速效果呢?
请看下回分解。