vring
Virtqueue 的具体实现是用的 vring, 下面是 qemu 中的 vring 的数据结构, linux 的 virtio 驱动中也会用到这些结构
1
2
3
4
5
6
| struct vring {
unsigned int num;
vring_desc_t *desc;
vring_avail_t *avail;
vring_used_t *used;
};
|
vring 中有 num 编号, 三个子结构 desc, avail,used

vring 结构 [1]vring 描述符链表
desc 为 vring 描述符链表, 每一项描述符指向一片内存(GPA), 类型有输入和输出, 内存管理由驱动负责, flags 用于描述属性, next 指向链表的下一项.
1
2
3
4
5
6
7
8
9
10
11
| /* 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;
};
|
vring_avail
vring_avail 是一个环形队列, 主体在 ring 数组中, idx 为队列的头指针, 由 virtio 驱动维护; 而尾指针 last_avail_idx 则由 device 维护, 驱动向 avail 队列中添加项目, 增加头指针索引号, 设备从其中读取数据, 增加尾指针索引号.
1
2
3
4
5
| struct vring_avail {
__virtio16 flags;
__virtio16 idx;
__virtio16 ring[];
};
|
vring_used
vring_used 也是一个环形队列, 维护设备用完的项目, 设备用完数据后, 将描述符 ID 放到 vring_used 中, 并增加 idx, 原理与 vring_avail 类似.
1
2
3
4
5
6
7
8
9
10
11
12
13
| /* uint32_t 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;
};
struct vring_used {
__virtio16 flags;
__virtio16 idx;
vring_used_elem_t ring[];
};
|
驱动(Driver)分配好内存(scatterlist), 通过 virtqueue_add 添加进描述符项目到 vring_avail, 由设备(Device)用完后放到 vring_used 中.
综上, Virtqueue 通过一个 vring_desc 内存资源池 , 两个环形队列 vring_avail 和 vring_used, 实现了驱动与设备之间的数据交互.
源码版本
引用
[1]: Linux 虚拟化 KVM-Qemu 分析(十一)之 virtqueue