0%

【虚拟化】Virtio

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。

img

img

virtio-blk 读写流程

总体框架图

阶段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处理函数。

img

阶段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

阶段3 Guest Kernel

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

virtio-net 收发包流程

总体框架图

img

发包流程

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

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

收包流程

img

img

常见问题

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

层次结构

img

如图所示,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

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;

参考资料