VirtIO实现原理之数据结构与数据传输演示(3)

132 篇文章 41 订阅 yen49.90 yen99.00

接前一篇文章:VirtIO实现原理之数据结构与数据传输演示(2)

本文内容参考:

VirtIO实现原理——vring数据结构-CSDN博客

VirtIO实现原理——数据传输演示-CSDN博客

特此致谢!

一、数据结构总览

2. 相关数据结构

前文书介绍了《Virtual I/O Device (VIRTIO) Version 1.3》规范的“2.6 Virtqueues”一节中提到的相关数据结构。再来回顾一下上一回的那3个结构:

为了便于理解,这里还是以老版本进行讲解。先来看一下VirtIO数据结构的总览图:

c4718596b05643f7ba9a3c4e890d8e7a.png

(2)可用环(Available Ring)

存放Decriptor Table索引,指向Descriptor Table中的一个entry。当Guest Driver向Vring中添加buffer时,可以一次添加一个或多个buffer,所有buffer组成一个Descriptor chain(就是Descriptor Table),Guest Driver添加buffer成功后,需要将Descriptor chain头部的地址记录到Avail Ring中,让Host端能够知道新的可用的buffer是从VRing的哪个地方开始的。

Host查找Descriptor chain头部地址,需要经过两次索引Buffer Adress = Descriptor Table[Avail Ring[last_avail_idx]],last_avail_idx是Host端记录的Guest上一次增加的buffer在Avail Ring中的位置。Guest Driver每添加一次buffer,就将Avail Ring的idx加1,以表示自己工作在Avail Ring中的哪个位置。Avail Ring是Guest维护,提供给Host用。

可用环(Available Ring)对应的结构为struct vring_avail。struct vring_avail的定义在QEMU源码中有不止一处(qemu-8.1.4版本一共3处),这里以roms/u-boot/include/virtio_ring.h中的为例,代码如下:

struct vring_avail {
	__virtio16 flags;
	__virtio16 idx;
	__virtio16 ring[];
};

Guest通过Avail Ring向Host提供buffer,指示Guest增加的buffer位置和当前工作的位置。

成员说明

  • __virtio16 flags

用于指示Host当它处理完buffer,将Descriptor index写入Used Ring之后,是否通过注入中断通知Guest。如果flags为0,Host每处理完一次buffer就会中断通知Guest,从而触发VMExit,增加开销;如果flags为1,不通知Guest。这是一种比较粗糙的方式,要么不通知,要么通知。还有一种比较优雅的方式,叫做VIRTIO_F_EVENT_IDX特性,它根据前后端的处理速度,来判断是否进行通知。如果该特性开启,那么flags的意义将会改变,Guest必须把flags设置为0,然后通过used_event机制实现通知。

  • __virtio16 idx

指示Guest下一次添加buffer时的在Avail Ring所处的位置,换句话说,idx存放的是ring数组索引,ring[idx]存放的才是下一次添加的buffer头在Descriptor Table的位置。

  • __virtio16 ring[]

存放Descriptor Table索引的环,是一个数组,长度是队列深度加1个,其中最后一个用作Event方式通知机制。VirtIO实现了两级索引(Buffer Adress = Descriptor Table[Avail Ring[last_avail_idx]]):一级索引指向Descriptor Table中的元素,Avail Ring和Used Ring代表的是一级索引,核心就是这里的ring[]数组成员;二级索引指向buffer的物理地址,Descriptor Table是二级索引。

至此,第2个数据结构——可用环(Available Ring)对应的vring_avail结构就讲解完了。下一回继续讲解更多数据结构。