接前一篇文章:
本文内容参考:
特此致谢!
上回书说到setup_vq函数的第4步:调用vring_create_virtqueue函数实际生成virtqueue。在开始要详细解析其中的两个核心函数vring_create_virtqueue_packed()和vring_create_virtqueue_split()的时候,笔者觉得应该藉此机会先介绍一下与virtqueue创建流程相关的 结构体 ,之前一直没有整体地对于virtqueue创建流程乃至整个virtio中的结构体进行系统地讲解,在这里、在这个节骨眼上正是好时机。
上一回先介绍了3个结构,分别是:struct virtio_device、struct virtio_device_id和struct virtio_config_ops。接下来继续往下讲解其它重要结构。
(4)virtqueue结构
struct virtqueue的定义在 Linux 内核源码/include/linux/virtio.h中,代码如下:
- /**
- * struct virtqueue - a queue to register buffers for sending or receiving.
- * @list: the chain of virtqueues for this device
- * @callback: the function to call when buffers are consumed (can be NULL).
- * @name: the name of this virtqueue (mainly for debugging)
- * @vdev: the virtio device this queue was created for.
- * @priv: a pointer for the virtqueue implementation to use.
- * @index: the zero-based ordinal number for this queue.
- * @num_free: number of elements we expect to be able to fit.
- * @num_max: the maximum number of elements supported by the device.
- * @reset: vq is in reset state or not.
- *
- * A note on @num_free: with indirect buffers, each buffer needs one
- * element in the queue, otherwise a buffer will need one element per
- * sg element.
- */
- struct virtqueue {
- struct list_head list;
- void (*callback)(struct virtqueue *vq);
- const char *name;
- struct virtio_device *vdev;
- unsigned int index;
- unsigned int num_free;
- unsigned int num_max;
- bool reset;
- void *priv;
- };
struct virtqueue是用于在虚拟化环境中进行数据传输的数据结构。它是在Linux内核中实现的一种机制,用于在宿主机和 虚拟机 之间进行高效的数据传输。
struct virtqueue的各成员详细描述如下:
- struct list_head list
此设备的virtqueues链。加入virtio_device的vqs链表。
- void (*callback)(struct virtqueue *vq)
当缓冲区被消费时要调用的函数(可以为NULL)。virtqueue被触发中断时执行的回调函数。
- const char *name
此virtqueue的名字(主要用于调试)。
- struct virtio_device *vdev
创建此队列的virtio设备,即此virtqueue所属的virtio_device。
- unsigned int index
此队列的从零开始的序数,即virtqueue的索引编号。
- unsigned int num_free
期望能够适应的元素数量。即virtqueue中空闲的descriptor个数。
- unsigned int num_max
设备支持的最大元素数。即virtqueue中最大的descriptor个数。
- bool reset
vq是否处于重置状态。
- void *priv
virtqueue实现所使用的指针。
(5)vring相关结构
struct vring的定义在Linux内核源码/include/uapi/linux/virtio_ring.h中,代码如下:
- struct vring {
- unsigned int num;
-
- vring_desc_t *desc;
-
- vring_avail_t *avail;
-
- vring_used_t *used;
- };
struct vring是用于实现虚拟环的数据结构,常用于虚拟化技术中的设备模拟和设备驱动程序之间的通信。它通常由三个部分组成:描述符表和可用、已用环。
struct vring的各成员详细描述如下:
- unsigned int num
表示virtqueue的大小,也就是这个vring的descriptor(vring_desc)的个数。
- vring_desc_t *desc
描述符表(descriptor table)。
- vring_avail_t *avail
available ring,前端驱动(Guest)生产,后端设备(Host)消费。
- vring_used_t *used
used ring,后端设备(Host)生产,前端驱动(Guest)消费。
struct vring中的desc、avail、used分别表示descriptor table、available ring、used ring的起始地址。
图片引自: VirtIO实现原理——数据传输演示-CSDN博客
(6)vring_desc_t结构(vring_desc结构)
vring_desc_t的定义在同文件(Linux内核源码/include/uapi/linux/virtio_ring.h)中定义(就在上边),如下:
- typedef struct vring_desc __attribute__((aligned(VRING_DESC_ALIGN_SIZE)))
- vring_desc_t;
struct vring_desc的定义也在Linux内核源码//include/uapi/linux/virtio_ring.h中,代码如下:
- /**
- * struct vring_desc - Virtio ring descriptors,
- * 16 bytes long. These can chain together via @next.
- *
- * @addr: buffer address (guest-physical)
- * @len: buffer length
- * @flags: descriptor flags
- * @next: index of the next descriptor in the chain,
- * if the VRING_DESC_F_NEXT flag is set. We chain unused
- * descriptors via this, too.
- */
- struct vring_desc {
- __virtio64 addr;
- __virtio32 len;
- __virtio16 flags;
- __virtio16 next;
- };
struct vring_desc表示virtio环(虚拟环)描述符,是用于描述虚拟环的数据结构,它在虚拟化技术中被广泛使用。虚拟环是一种用于在虚拟机和宿主机之间传递数据的机制。
struct vring_desc的各成员详细描述如下:
- __virtio64 addr
I/O的物理地址(GPA)。
- __virtio32 len
I/O的长度,通常是一个固定值。
- __virtio16 flags
描述符的标志位,用于表示当前描述符的状态或属性。
flags可以是VRING_DESC_F_NEXT,表示这段I/O包括下一个连续的vring_desc;也可以是VRING_DESC_F_WRITE,表示这段I/O是只写的(对设备而言);还可以是VRING_DESC_F_INDIRECT,表示这段I/O由不连续的vring_desc构成。
VRING_DESC_F_NEXT、VRING_DESC_F_WRITE、VRING_DESC_F_INDIRECT的定义在Linux内核源码/include/uapi/linux/virtio_ring.h中,如下:
- /* This marks a buffer as continuing via the next field. */
- #define VRING_DESC_F_NEXT 1
- /* This marks a buffer as write-only (otherwise read-only). */
- #define VRING_DESC_F_WRITE 2
- /* This means the buffer contains a list of buffer descriptors. */
- #define VRING_DESC_F_INDIRECT 4
- __virtio16 next
指向下一个描述符(vring_desc)的索引,用于构建描述符链表。
(7)vring_avail_t结构(vring_avail结构)
vring_avail_t的定义在同文件(Linux内核源码/include/uapi/linux/virtio_ring.h)中定义(就在上边),如下:
- typedef struct vring_avail __attribute__((aligned(VRING_AVAIL_ALIGN_SIZE)))
- vring_avail_t;
struct vring_avail的定义也在Linux内核源码/include/uapi/linux/virtio_ring.h中,代码如下:
- struct vring_avail {
- __virtio16 flags;
- __virtio16 idx;
- __virtio16 ring[];
- };
struct vring_avail是用于描述virtio环(虚拟环)的可用环的数据结构。
struct vring_avail的各成员详细描述如下:
- __virtio16 flags
标志位,用于指示可用环的状态。virtio_avail中,flags通常不用。
- __virtio16 idx
指示下一个可用的描述符的索引。即下一次virtio驱动应该写ring数组的哪一个。
- __virtio16 ring[]
一个数组,用于存储可用的描述符的索引。也就是说,ring是一个索引数组,其大小为virtqueue的大小,其中存放的是vring_desc的索引,表示这次的I/O数据。
仍以上图为例:
(8)vring_used_t 结构(vring_used 结构)
vring_used_t的定义在同文件(Linux内核源码/include/uapi/linux/virtio_ring.h)中定义(就在上边),如下:
- typedef struct vring_used __attribute__((aligned(VRING_USED_ALIGN_SIZE)))
- vring_used_t;
struct vring_used的定义也在Linux内核源码/include/uapi/linux/virtio_ring.h中,代码如下:
- struct vring_used {
- __virtio16 flags;
- __virtio16 idx;
- vring_used_elem_t ring[];
- };
struct vring_used是用于描述virtio环(虚拟环)的已使用的缓冲区的数据结构。它通常在虚拟化技术中用于实现共享内存的数据传输。
struct vring_used的各成员详细描述如下:
- __virtio16 flags
标志位,用于指示已用环的状态。vring_used中,flags也通常不用。
- __virtio16 idx
指示下一次virtio设备应该写ring数组的哪一个。
- vring_used_elem_t ring[]
表示一个索引数组,其大小为virtqueue的大小。ring[]中的项有两部分:第一部分是id,表示所使用descriptor table中的索引;第二部分是len,表示本次virtio设备总共写了多少数据。
(9)vring_used_elem_t结构(vring_used_elem结构)
vring_used_elem_t的定义在同文件(Linux内核源码/include/uapi/linux/virtio_ring.h)中定义,如下:
- typedef struct vring_used_elem __attribute__((aligned(VRING_USED_ALIGN_SIZE)))
- vring_used_elem_t;
struct vring_used_elem的定义也在Linux内核源码/include/uapi/linux/virtio_ring.h中,代码如下:
- /* 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;
- };
struct vring_used_elem是用于描述virtio设备中已使用的缓冲区元素的结构体。在virtio设备中,用于数据传输的缓冲区被分为两个部分:可用环(available ring)和已使用环(used ring)。可用环用于通知设备有新的缓冲区可供使用,而已使用环则用于通知驱动程序设备已经使用了哪些缓冲区。
struct vring_used_elem的各成员详细描述如下(在struct vring_used中已讲过):
- __virtio32 id
表示已使用的descriptor table的索引。
- __virtio32 len
表示已使用的缓冲区的长度。即本次virtio设备总共写了多少数据。
至此,virtio相关的、尤其是virtqueue、vring相关的数据结构就交代清楚了。
欲知后事如何,且看下回分解。