Virtio 是一个应用广泛的半虚拟化解决方案,是半虚拟化 Hypervisor 的一组通用 I/O 设备的抽象。它提供了一套上层应用与各 Hypervisor 虚拟化设备(KVM、Xen、VMware 等)之间的通信框架和编程接口,减少了跨平台所带来的兼容性问题。客户机需要知道自己运行在虚拟化环境中,进而根据 Virtio 标准和 Hypervisor 协作,从而提高 I/O 性能。

virtio 的架构

半虚拟化方式需要借助 virtio 实现,在 GuestOS 中需要安装前端驱动(块设备驱动、网络设备驱动、PCI 设备驱动等),QEMU 中集中调用后端驱动,两者之间通信通过 virtio-ring 实现。这种方案无需频繁切换上下文,减少了内存拷贝次数,I/O 效率较高,目前是公有云虚拟机选择的主流方案。

Virtio 分为了前端驱动后端驱动,virtio 本质上是子机和母机间的一套基于共享内存的通信框架

  • 前端驱动:Frontend Driver,是位于客户机内核中的驱动程序模块,如virtio_blkvirtio_net
  • 后端驱动:Backend Driver,在宿主机用户空间QEMU 中实现

virtio-vring

virtio-ring 分成三个部分:

  • Desc table:描述符表, 目的是存 scatterlist 指向数据的地址和长度
    • 有 1024 项条目, 存放数据的 GPA 和长度, 每项数据长度不超过 4k
    • 描述符表的添加和删除只能由 guest 完成
  • Available Ring:由 Guest 修改, 指向 Desc table 的头部
  • Used Ring:由 Host 修改

Vring 特性:

  • 只有 host 将请求处理完毕,在 used ring 中对请求做了应答,guest 才能将先前添加的描述符表项删除,比一般的环形队列多了应答的机制。
  • guest 将请求添加后通过写 ioport 引起 VMExit 来通知 host,host 处理完请求后通过向 guest 注入中断来通知 guest。

TODO

virtio-blk 读写流程

总体框架图

TODO

阶段 1 Guest Kernel

  • do_virtblk_request 从调度器取出合并后的 IO request,调用 do_req() 开始对 request 进行处理。
  • 由于单个 request 可能包含多个 BIO,调用 blk_rq_map_sg() 将 request 中所有的 biovec 转换为 scatterlist(sg 条目数等于 biovec 的数目),增加状态 sg 条目。
  • 调用 virtqueue_add_buf() 将 scatterlist 写入到 vring 的描述符表,更新 available ring 中的条目指向刚刚写入的描述符表的头。
  • do_virtblk_request 会不停的取 IO request,转换为 scatterlist 并写入到 vring,直到 vring 满。vring 满或将调度器的 IO request 取完后,调用 virtqueue_kick()。
  • virtqueue_kick() 会写 IO 端口,引起缺页,缺页导致 VMExit,退出到 QEMU,进入到 QEMU 中的 IO 处理函数。

阶段 2 QEMU Host

  • VMExit 的退出经过下面的路径,virtio_ioport_write()->virtio_queue_notify()->virtio_queue_notify_vq()->handle_output()进入到 IO 处理函数:virtio_blk_handle_output()
  • QEMU 的后端驱动从 Available Ring 取得 I/O 请求,经过 I/O 合并后,异步提交给 QEMU 的通用块层
    • 调用 virtqueue_pop() 从 vring 中取得 guest 写入的 IO 请求,通过 virtqueue_map_sg() 将 guest 的物理地址转换为 QEMU 的虚拟地址,转换后,QEMU 就可以直接访问子机的内存
    • 将 vring 从取得的 IO 请求转换为 VirtIOBlockReq,由 virtio_blk_handle_request()进行处理,对应写操作,如果磁盘块连续,最多可以对 32 个写请求进行合并,合并后再提交到 QEMU 的通用块层;对于读请求,考虑到实时性,不会合并,直接提交。
  • 对每个 IO 请求,QEMU 都会启动一个协程进行异步执行,执行完毕后,通过 virtqueue_push() 更新 vring 的 used ring 的条目指向刚刚执行完毕的 request 在描述符表的头。
  • 调用 virtio_notify() 向 guest 注入中断,通知 guest 请求已经执行完毕。

img
img

阶段 3 Guest Kernel

  • guest 在 virtio-blk 的中断处理函数中,读取 used ring 取得已经执行完毕的请求
  • 调用 virtqueue_get_buf() 释放 request占用的 buffer,更新 available ring,整个 IO 流程完毕

virtio-net 收发包流程

总体框架图

TODO

发包流程

发包流程跟磁盘 IO 读写流程类似,不同的是:

  • QEMU 将包写入到 tap 设备后无需通过中断通知 guest,这个跟物理网卡的行为也是一致的

收包流程

TODO

常见问题

Q:virtio-pci 驱动与 virtio-net,virtio-blk 等 virtio 驱动的关系?

A:virtio-pci 是 PCI 驱动,而 virtio-net,virtio-blk 是各种 virtio 设备驱动。PCI 有完整的规范,QEMU 模拟 PCI 设备是非常容易的,因此 virtio 框架采取先模拟一个 PCI 设备,在 virtio-pci 驱动中根据 virtio 设备的类型(blk,net,serial 等)创建一个 virtio 设备,再在子机中用内核的统一设备模型将 virtio 设备管理起来,实现只需要一个 virtio-pci 驱动就可以扩展出各种 virtio 设备的目的。

Q:为什么 virtio-blk 只需要一个 virtqueue(vring),而 virtio-net 需要两个 virtqueue(vring)?

A:对 virtio-blk 而言,无论是读还是写,请求都是从 guest 发起的,提交请求时 guest 填充描述符表,请求执行完毕后 guest 将先前填充的描述符表删除,描述符表的填充和删除只会由 guest 发起,因此,一个 vring 就足够了。网络发包对 vring 的操作与磁盘几乎相同,唯一不同的是,发包成功后 qemu 无需注入中断通知 guest。但是,对收包而言,差异就非常大,必须要求 guest 随时提供足够的描述符表以便在包到达时能够将包拷贝到描述符表对应的内存 buffer 中,如果收发使用同一个 vring,对 vring 的操作会有竞争,增加了复杂性。

Q:网络收包时,host 是否会填充描述符表?

A:不会,描述符表的填充和删除都是由 guest 完成的,guest 只会更新 available ring,host 只会更新 used ring,host 只能从描述符表获取 guest 写入的地址,qemu 根据方向决定是将数据拷贝出来还是向里面拷贝数据,如:对读 IO,将磁盘读到的数据拷贝到 guest buffer;对写 IO,将 guest buffer 的内容写入到磁盘;对发包,将 guest buffer 的数据写入到 tap;对收包,将 tap 读取的包内容拷贝到 guest buffer。

源码阅读

版本要求

Kernel >= 2.6.25的内核都支持 virtio。由于 virtio 的后端处理程序是在位于用户空间中的 QEMU 中实现的,所以宿主机只需要比较新的内核即可,不需要特别编译 virtio 相关驱动。而客户机需要有特定 virtio 驱动程序的支持,以便客户机处理 I/O 操作请求时调用前端驱动。

客户机内核中关于 virtio 的部分配置如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 需要启用的选项
CONFIG_VIRTIO_PCI=m
CONFIG_VIRTIO_BALLOON=m
CONFIG_VIRTIO_BLK=m
CONFIG_VIRTIO_NET=m
CONFIG_VIRTIO=m
CONFIG_VIRTIO_RING=y

# 其他相关的选项
CONFIG_VIRTIO_VSOCKETS=m
CONFIG_VIRTIO_VSOCKETS_COMMON=m
CONFIG_SCSI_VIRTIO=m
CONFIG_VIRTIO_CONSOLE=m
CONFIG_HW_RANDOM_VIRTIO=m
CONFIG_DRM_VIRTIO_GPU=m
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_INPUT=m
# CONFIG_VIRTIO_MMIO is not set

# 在子机中查看 VIRTIO 相关内核模块
> lsmod | grep virtio
virtio_balloon         18015  0
virtio_net             28063  0
virtio_blk             18166  2
virtio_pci             22934  0
virtio_ring            22746  4 virtio_blk,virtio_net,virtio_pci,virtio_balloon
virtio                 14959  4 virtio_blk,virtio_net,virtio_pci,virtio_balloon

层次结构

如图所示,virtio 大致分为三个层次:前端驱动(位于客户机)、后端驱动(位于 QEMU)以及中间的传输层

每一个 virtio 设备(块设备、网卡等)在系统层面看来,都是一个 PCI 设备。这些设备之间有共性部分,也有差异部分。

共性部分

  1. 都需要挂接相应的 buffer 队列操作 virtqueue_ops
  2. 都需要申请若干个 buffer 队列,当执行 I/O 输出时,需要向队列写入数据
  3. 都需要执行 pci_iomap 将设备配置寄存器区间映射到内存区间
  4. 都需要设置中断处理
  5. 等中断来了,都需要从队列读出数据,并通知客户机系统,数据已入队

差异部分

  • 与设备相关联的系统、业务、队列中写入的数据含义各不相同
  • 例如,网卡在内核中是一个net_device,与协议栈系统关联起来。同时,向队列中写入什么数据、数据的含义如何,各个设备也不相同。队列中来了什么数据,是什么含义,如何处理,各个设备也不相同

如果每个 virtio 设备都完整的实现自己的功能,就会造成不必要的代码冗余。针对这个问题,virtio 又设计了virtio_pci模块,以处理所有 virtio 设备的共性部分。这样一来所有的 virtio 设备在系统看来都是一个 PCI 设备,其设备驱动都是virtio_pci

但是,virtio_pci并不能完整的驱动任何一个设备。因此,virtio_pci在调用probe()接管每一个设备时,会根据其virtio_device_id来识别出具体是哪一种设备,然后相应的向内核注册一个 virtio 类型的设备

在注册设备之前,virtio_pci驱动已经为该设备做了许多共性操作,同时还为该设备提供了各种操作的适配接口,这些都通过virtio_config_ops来适配。

前端代码层次结构

相关源码文件

Kernel 3.10.0中关于 virito 的重要源码文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
drivers/block/virtio_blk.c
drivers/char/hw_random/virtio_rng.c
drivers/char/virtio_console.c
drivers/net/virtio_net.c
drivers/scsi/virtio_scsi.c
drivers/virtio/virtio_balloon.c
drivers/virtio/virtio_mmio.c
drivers/virtio/virtio_pci.c
drivers/virtio/virtio_ring.c
drivers/virtio/virtio.c

include/linux/virtio_caif.h
include/linux/virtio_config.h
include/linux/virtio_console.h
include/linux/virtio_mmio.h
include/linux/virtio_ring.h
include/linux/virtio_scsi.h
include/linux/virtio.h
include/linux/vring.h

include/uapi/linux/virtio_9p.h
include/uapi/linux/virtio_balloon.h
include/uapi/linux/virtio_blk.h
include/uapi/linux/virtio_config.h
include/uapi/linux/virtio_console.h
include/uapi/linux/virtio_ids.h
include/uapi/linux/virtio_net.h
include/uapi/linux/virtio_pci.h
include/uapi/linux/virtio_ring.h
include/uapi/linux/virtio_rng.h

tools/virtio/linux/virtio_config.h
tools/virtio/linux/virtio_ring.h
tools/virtio/linux/virtio.h
tools/virtio/linux/vring.h
tools/virtio/vitrio_test.c
tools/virtio/vringh_test.c

linux-3.10
 ├─ drivers
 |   ├─ block
 |   |   └─ virtio_blk.c
 |   |
 |   ├─ char
 |   |   ├─ hw_random
 |   |   |   └─ virtio_rng.c
 |   |   |
 |   |   └─ virtio_console.c
 |   |
 |   ├─ net
 |   |   └─ virtio_net.c
 |   |
 |   ├─ scsi
 |   |   └─ virtio_scsi.c
 |   |
 |   └─ virtio
 |       ├─ virtio_balloon.c
 |       ├─ virtio_mmio.c
 |       ├─ virtio_pci.c
 |       ├─ virtio_ring.c
 |       └─ virtio.c
 |
 ├─ include
 |   ├─ linux
 |   |   ├─ virtio_caif.h
 |   |   ├─ virtio_config.h
 |   |   ├─ virtio_console.h
 |   |   ├─ virtio_mmio.h
 |   |   ├─ virtio_ring.h
 |   |   ├─ virtio_scsi.h
 |   |   ├─ virtio.h
 |   |   └─ vring.h
 |   |
 |   └─ uapi
 |       └─ linux
 |           ├─ virtio_9p.h
 |           ├─ virtio_balloon.h
 |           ├─ virtio_blk.h
 |           ├─ virtio_config.h
 |           ├─ virtio_console.h
 |           ├─ virtio_ids.h
 |           ├─ virtio_net.h
 |           ├─ virtio_pci.h
 |           ├─ virtio_ring.h
 |           └─ virtio_rng.h
 |
 └─ tools
     └─ virtio
         ├─ linux
         |   ├─ virtio_config.h
         |   ├─ virtio_ring.h
         |   ├─ virtio.h
         |   └─ vring.h
         |
         ├─ virtio_test.c
         └─ vringh_test.c

类结构层次

virtio 前端驱动客户机内核中,virtio 的类层次结构如下图所示:

img
img

virtio_driver

最顶级的是virtio_driver,在客户机 OS 中表示前端驱动程序,在include/linux/virtio.h中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * virtio_driver - operations for a virtio I/O driver
 * @driver: underlying device driver (populate name and owner).
 * @id_table: the ids serviced by this driver.
 * @feature_table: an array of feature numbers supported by this driver.
 * @feature_table_size: number of entries in the feature table array.
 * @probe: the function to call when a device is found.  Returns 0 or -errno.
 * @remove: the function to call when a device is removed.
 * @config_changed: optional function to call when the device configuration
 *    changes; may be called in interrupt context.
 */
struct virtio_driver {
    struct device_driver driver;
    const struct virtio_device_id *id_table;
    const unsigned int *feature_table;
    unsigned int feature_table_size;
    int (*probe)(struct virtio_device *dev);
    void (*scan)(struct virtio_device *dev);
    void (*remove)(struct virtio_device *dev);
    void (*config_changed)(struct virtio_device *dev);
#ifdef CONFIG_PM
    int (*freeze)(struct virtio_device *dev);
    int (*restore)(struct virtio_device *dev);
#endif
};

virtio_device_id

每个 virtio 设备都有其对应的virtio_device_id,该结构体在include/linux/mod_devicetable.h中定义:

1
2
3
4
5
struct virtio_device_id {
    __u32 device;
    __u32 vendor;
};
#define VIRTIO_DEV_ANY_ID    0xffffffff

virtio_device

与驱动程序匹配的设备由virtio_device封装,它表示在客户机 OS 中的设备,在include/linux/virtio.h中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * virtio_device - representation of a device using virtio
 * @index: unique position on the virtio bus
 * @dev: underlying device.
 * @id: the device type identification (used to match it with a driver).
 * @config: the configuration ops for this device.
 * @vringh_config: configuration ops for host vrings.
 * @vqs: the list of virtqueues for this device.
 * @features: the features supported by both driver and device.
 * @priv: private pointer for the driver's use.
 */
struct virtio_device {
    int index;
    struct device dev;
    struct virtio_device_id id;
    const struct virtio_config_ops *config;
    const struct vringh_config_ops *vringh_config;
    struct list_head vqs;
    /* Note that this is a Linux set_bit-style bitmap. */
    unsigned long features[1];
    void *priv;
};

virtio_config_ops

每一个virtio_device都有一个virtio_config_ops类型的指针*config,它定义了配置 virtio 设备的操作,该结构体在include/linux/virtio_config.h中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 * virtio_config_ops - operations for configuring a virtio device
 * @get: read the value of a configuration field
 * @set: write the value of a configuration field
 * @get_status: read the status byte
 * @set_status: write the status byte
 * @reset: reset the device
 * @find_vqs: find virtqueues and instantiate them.
 * @del_vqs: free virtqueues found by find_vqs().
 * @get_features: get the array of feature bits for this device.
 * @finalize_features: confirm what device features we'll be using.
 * @bus_name: return the bus name associated with the device
 * @set_vq_affinity: set the affinity for a virtqueue.
 */
struct virtio_config_ops {
    void (*get)(struct virtio_device *vdev, unsigned offset,
            void *buf, unsigned len);
    void (*set)(struct virtio_device *vdev, unsigned offset,
            const void *buf, unsigned len);
    u8 (*get_status)(struct virtio_device *vdev);
    void (*set_status)(struct virtio_device *vdev, u8 status);
    void (*reset)(struct virtio_device *vdev);
    int (*find_vqs)(struct virtio_device *, unsigned nvqs,
            struct virtqueue *vqs[],
            vq_callback_t *callbacks[],
            const char *names[]);
    void (*del_vqs)(struct virtio_device *);
    u32 (*get_features)(struct virtio_device *vdev);
    void (*finalize_features)(struct virtio_device *vdev);
    const char *(*bus_name)(struct virtio_device *vdev);
    int (*set_vq_affinity)(struct virtqueue *vq, int cpu);
};

virtqueue

每一个virtqueue包含了对应的virtio_device以及对应的队列操作回调函数,它在include/linux/virtio.h中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * 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.
 *
 * 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;
    void *priv;
};

相关数据结构

前端 Kernel

virtio_driver

include/linux/virtio.h中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * virtio_driver - operations for a virtio I/O driver
 * @driver: underlying device driver (populate name and owner).
 * @id_table: the ids serviced by this driver.
 * @feature_table: an array of feature numbers supported by this driver.
 * @feature_table_size: number of entries in the feature table array.
 * @probe: the function to call when a device is found.  Returns 0 or -errno.
 * @remove: the function to call when a device is removed.
 * @config_changed: optional function to call when the device configuration
 *    changes; may be called in interrupt context.
 */
struct virtio_driver {
    struct device_driver driver;
    const struct virtio_device_id *id_table;
    const unsigned int *feature_table;
    unsigned int feature_table_size;
    int (*probe)(struct virtio_device *dev);
    void (*scan)(struct virtio_device *dev);
    void (*remove)(struct virtio_device *dev);
    void (*config_changed)(struct virtio_device *dev);
#ifdef CONFIG_PM
    int (*freeze)(struct virtio_device *dev);
    int (*restore)(struct virtio_device *dev);
#endif
};

这里的virtio_device_id有两个字段:

1
2
3
4
5
struct virtio_device_id {
    __u32 device;
    __u32 vendor;
};
#define VIRTIO_DEV_ANY_ID    0xffffffff

virtio_device

include/linux/virtio.h中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * virtio_device - representation of a device using virtio
 * @index: unique position on the virtio bus
 * @dev: underlying device.
 * @id: the device type identification (used to match it with a driver).
 * @config: the configuration ops for this device.
 * @vringh_config: configuration ops for host vrings.
 * @vqs: the list of virtqueues for this device.
 * @features: the features supported by both driver and device.
 * @priv: private pointer for the driver's use.
 */
struct virtio_device {
    int index;
    struct device dev;
    struct virtio_device_id id;
    const struct virtio_config_ops *config;
    const struct vringh_config_ops *vringh_config;
    struct list_head vqs;
    /* Note that this is a Linux set_bit-style bitmap. */
    unsigned long features[1];
    void *priv;
};

virtio_config_ops

include/linux/virtio_config.h中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
 * virtio_config_ops - operations for configuring a virtio device
 * @get: read the value of a configuration field
 *    vdev: the virtio_device
 *    offset: the offset of the configuration field
 *    buf: the buffer to write the field value into.
 *    len: the length of the buffer
 * @set: write the value of a configuration field
 *    vdev: the virtio_device
 *    offset: the offset of the configuration field
 *    buf: the buffer to read the field value from.
 *    len: the length of the buffer
 * @get_status: read the status byte
 *    vdev: the virtio_device
 *    Returns the status byte
 * @set_status: write the status byte
 *    vdev: the virtio_device
 *    status: the new status byte
 * @reset: reset the device
 *    vdev: the virtio device
 *    After this, status and feature negotiation must be done again
 *    Device must not be reset from its vq/config callbacks, or in
 *    parallel with being added/removed.
 * @find_vqs: find virtqueues and instantiate them.
 *    vdev: the virtio_device
 *    nvqs: the number of virtqueues to find
 *    vqs: on success, includes new virtqueues
 *    callbacks: array of callbacks, for each virtqueue
 *        include a NULL entry for vqs that do not need a callback
 *    names: array of virtqueue names (mainly for debugging)
 *        include a NULL entry for vqs unused by driver
 *    Returns 0 on success or error status
 * @del_vqs: free virtqueues found by find_vqs().
 * @get_features: get the array of feature bits for this device.
 *    vdev: the virtio_device
 *    Returns the first 32 feature bits (all we currently need).
 * @finalize_features: confirm what device features we'll be using.
 *    vdev: the virtio_device
 *    This gives the final feature bits for the device: it can change
 *    the dev->feature bits if it wants.
 * @bus_name: return the bus name associated with the device
 *    vdev: the virtio_device
 *      This returns a pointer to the bus name a la pci_name from which
 *      the caller can then copy.
 * @set_vq_affinity: set the affinity for a virtqueue.
 */
typedef void vq_callback_t(struct virtqueue *);
struct virtio_config_ops {
    void (*get)(struct virtio_device *vdev, unsigned offset,
            void *buf, unsigned len);
    void (*set)(struct virtio_device *vdev, unsigned offset,
            const void *buf, unsigned len);
    u8 (*get_status)(struct virtio_device *vdev);
    void (*set_status)(struct virtio_device *vdev, u8 status);
    void (*reset)(struct virtio_device *vdev);
    int (*find_vqs)(struct virtio_device *, unsigned nvqs,
            struct virtqueue *vqs[],
            vq_callback_t *callbacks[],
            const char *names[]);
    void (*del_vqs)(struct virtio_device *);
    u32 (*get_features)(struct virtio_device *vdev);
    void (*finalize_features)(struct virtio_device *vdev);
    const char *(*bus_name)(struct virtio_device *vdev);
    int (*set_vq_affinity)(struct virtqueue *vq, int cpu);
};

virtqueue

include/linux/virtio.h中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * 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.
 *
 * 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;
    void *priv;
};

后端 QEMU

VirtQueue

hw/virtio/virtio.c中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct VirtQueue
{
    VRing vring;

    /* Next head to pop */
    uint16_t last_avail_idx;

    /* Last avail_idx read from VQ. */
    uint16_t shadow_avail_idx;

    uint16_t used_idx;

    /* Last used index value we have sjjignalled on */
    uint16_t signalled_used;

    /* Last used index value we have signalled on */
    bool signalled_used_valid;

    /* Notification enabled? */
    bool notification;

    uint16_t queue_index;

    int inuse;

    uint16_t vector;
    void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq);
    void (*handle_aio_output)(VirtIODevice *vdev, VirtQueue *vq);
    VirtIODevice *vdev;
    EventNotifier guest_notifier;
    EventNotifier host_notifier;
    QLIST_ENTRY(VirtQueue) node;
};

VRing

hw/virtio/virtio.c中定义:

1
2
3
4
5
6
7
8
9
typedef struct VRing
{
    unsigned int num;
    unsigned int num_default;
    unsigned int align;
    hwaddr desc;
    hwaddr avail;
    hwaddr used;
} VRing;

参考资料