QEMU源码全解析 —— virtio(29)

123 篇文章 36 订阅 ¥49.90 ¥99.00
本文详细解析了QEMU源码中的virtio结构,重点介绍了virtqueue及其相关结构体,包括struct virtqueue、struct vring_desc、struct vring_avail、struct vring_used等,阐述了它们在数据传输中的作用和功能。
摘要由CSDN通过智能技术生成

接前一篇文章:

本文内容参考:

https://note.youdao.com/ynoteshare/index.html?id=f247acce8c21eb4ca4a7e37403f065f9&type=note&_time=1633000040495

特此致谢!

上回书说到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中,代码如下:

  1. /**
  2. * struct virtqueue - a queue to register buffers for sending or receiving.
  3. * @list: the chain of virtqueues for this device
  4. * @callback: the function to call when buffers are consumed (can be NULL).
  5. * @name: the name of this virtqueue (mainly for debugging)
  6. * @vdev: the virtio device this queue was created for.
  7. * @priv: a pointer for the virtqueue implementation to use.
  8. * @index: the zero-based ordinal number for this queue.
  9. * @num_free: number of elements we expect to be able to fit.
  10. * @num_max: the maximum number of elements supported by the device.
  11. * @reset: vq is in reset state or not.
  12. *
  13. * A note on @num_free: with indirect buffers, each buffer needs one
  14. * element in the queue, otherwise a buffer will need one element per
  15. * sg element.
  16. */
  17. struct virtqueue {
  18. struct list_head list;
  19. void (*callback)(struct virtqueue *vq);
  20. const char *name;
  21. struct virtio_device *vdev;
  22. unsigned int index;
  23. unsigned int num_free;
  24. unsigned int num_max;
  25. bool reset;
  26. void *priv;
  27. };

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中,代码如下:

  1. struct vring {
  2. unsigned int num;
  3. vring_desc_t *desc;
  4. vring_avail_t *avail;
  5. vring_used_t *used;
  6. };

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)中定义(就在上边),如下:

  1. typedef struct vring_desc __attribute__((aligned(VRING_DESC_ALIGN_SIZE)))
  2. vring_desc_t;

struct vring_desc的定义也在Linux内核源码//include/uapi/linux/virtio_ring.h中,代码如下:

  1. /**
  2. * struct vring_desc - Virtio ring descriptors,
  3. * 16 bytes long. These can chain together via @next.
  4. *
  5. * @addr: buffer address (guest-physical)
  6. * @len: buffer length
  7. * @flags: descriptor flags
  8. * @next: index of the next descriptor in the chain,
  9. * if the VRING_DESC_F_NEXT flag is set. We chain unused
  10. * descriptors via this, too.
  11. */
  12. struct vring_desc {
  13. __virtio64 addr;
  14. __virtio32 len;
  15. __virtio16 flags;
  16. __virtio16 next;
  17. };

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中,如下:

  1. /* This marks a buffer as continuing via the next field. */
  2. #define VRING_DESC_F_NEXT 1
  3. /* This marks a buffer as write-only (otherwise read-only). */
  4. #define VRING_DESC_F_WRITE 2
  5. /* This means the buffer contains a list of buffer descriptors. */
  6. #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)中定义(就在上边),如下:

  1. typedef struct vring_avail __attribute__((aligned(VRING_AVAIL_ALIGN_SIZE)))
  2. vring_avail_t;

struct vring_avail的定义也在Linux内核源码/include/uapi/linux/virtio_ring.h中,代码如下:

  1. struct vring_avail {
  2. __virtio16 flags;
  3. __virtio16 idx;
  4. __virtio16 ring[];
  5. };

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)中定义(就在上边),如下:

  1. typedef struct vring_used __attribute__((aligned(VRING_USED_ALIGN_SIZE)))
  2. vring_used_t;

struct vring_used的定义也在Linux内核源码/include/uapi/linux/virtio_ring.h中,代码如下:

  1. struct vring_used {
  2. __virtio16 flags;
  3. __virtio16 idx;
  4. vring_used_elem_t ring[];
  5. };

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)中定义,如下:

  1. typedef struct vring_used_elem __attribute__((aligned(VRING_USED_ALIGN_SIZE)))
  2. vring_used_elem_t;

struct vring_used_elem的定义也在Linux内核源码/include/uapi/linux/virtio_ring.h中,代码如下:

  1. /* u32 is used here for ids for padding reasons. */
  2. struct vring_used_elem {
  3. /* Index of start of used descriptor chain. */
  4. __virtio32 id;
  5. /* Total length of the descriptor chain which was used (written to) */
  6. __virtio32 len;
  7. };

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相关的数据结构就交代清楚了。

欲知后事如何,且看下回分解。

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他