简介
vring_virtqueue是virtio架构的前后端数据共享机制,虚机内在初始化virtio设备时,申请的一段内存空间,然后写BAR空间,触发vm-exit,QEMU便可以获取到GPA,将这个地址传递给后端,就实现了前后端map同一段内存,同时按照virtio定义好的规则,读取内容。
VRING
其中最主要的结构是vring。VRing由三部分组成:Descriptor table、Avail Ring、Used Ring
1. Descriptor table
上图中蓝色部分,存放Guest Driver提供的buffer的指针,每个条目指向一个Guest Driver分配的收发数据buffer。注意,VRing中buffer空间的分配永远由Guest Driver负责,Guest Driver发数据时,还需要向buffer填写数据,Guest Driver收数据时,分配buffer空间后通知Host向buffer中填写数据。
表中每一项定义如下:
/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc { /* Address (guest-physical). */ __virtio64 addr; /* Length. */ __virtio32 len; /* The flags as indicated above. */ __virtio16 flags; /* We chain unused descriptors via this, too */ __virtio16 next; }; |
2.Avail Ring
存放Decriptor Table索引,指向Descriptor Table中的一个entry。当Guest Driver向Vring中添加buffer时,可以一次添加一个或多个buffer,所有buffer组成一个Descriptor chain,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 Rring是Guest维护,提供给Host用
struct vring_avail { __virtio16 flags; __virtio16 idx; __virtio16 ring[]; }; |
ring[]数组存放的是IO请求在desc table中表头位置,每增加一个IO请求,idx++,指向下一个可用位置
3. Used Ring
存放Decriptor Table索引。当Host根据Avail Ring中提供的信息从VRing中取出buffer,处理完之后,更新Used Ring,把这一次处理的Descriptor chain头部的地址放到Used Ring中。Host每取一次buffer,就将Used Ring的idx加1,以表示自己工作在Used Ring中的哪个位置。Used Ring是Host维护,提供给Guest用
struct vring_used { __virtio16 flags; __virtio16 idx; struct vring_used_elem ring[]; }; /* u32 is used here for ids for padding reasons. */ struct vring_used_elem { /* Index of start of used descriptor chain. */ __virtio32 id; /* Total length of the descriptor chain which was used (written to) */ __virtio32 len; }; |