目录

virtqueue vring 的实现

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

https://static-1251996892.file.myqcloud.com/img/markdown/2022/vring-struct.png

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, 实现了驱动与设备之间的数据交互.

源码版本

  • qemu v7.0.0
  • linux 5.15.58

引用

[1]: Linux 虚拟化 KVM-Qemu 分析(十一)之 virtqueue