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

123 篇文章 36 订阅 ¥49.90 ¥99.00
本文详细分析了QEMU中virtio设备的virtio_vring_create函数,特别是针对vring_create_virtqueue_packed()和vring_create_virtqueue_split()两个函数进行了深入探讨。文章首先回顾了setup_vq函数的步骤,接着重点讲解了virtio_device、virtio_device_id和virtio_config_ops等关键结构体,为理解virtio设备的工作原理提供了基础。
摘要由CSDN通过智能技术生成

接前一篇文章:

本文内容参考:

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

特此致谢!

上回书讲到了setup_vq函数的第4步:调用vring_create_virtqueue函数实际生成virtqueue。为了便于理解了加深印象,再次贴出该函数源码,在 Linux 内核源码/drivers/virtio/virtio_ring.c中,代码如下:

  1. struct virtqueue *vring_create_virtqueue(
  2. unsigned int index,
  3. unsigned int num,
  4. unsigned int vring_align,
  5. struct virtio_device *vdev,
  6. bool weak_barriers,
  7. bool may_reduce_num,
  8. bool context,
  9. bool (*notify)(struct virtqueue *),
  10. void (*callback)(struct virtqueue *),
  11. const char *name)
  12. {
  13. if (virtio_has_feature(vdev, VIRTIO_F_RING_PACKED))
  14. return vring_create_virtqueue_packed(index, num, vring_align,
  15. vdev, weak_barriers, may_reduce_num,
  16. context, notify, callback, name, vdev->dev.parent);
  17. return vring_create_virtqueue_split(index, num, vring_align,
  18. vdev, weak_barriers, may_reduce_num,
  19. context, notify, callback, name, vdev->dev.parent);
  20. }
  21. EXPORT_SYMBOL_GPL(vring_create_virtqueue);

引出来两个函数:vring_create_virtqueue_packed()和vring_create_virtqueue_split()。上一回只是宏观介绍了两个函数的作用,本回分别对于两个函数的具体内容进行详细解析。

一个一个来看,先来看传统的vring_create_virtqueue_split()。再次贴出其源码,在Linux内核源码/drivers/virtio/virtio_ring.c中,代码如下:

  1. static struct virtqueue *vring_create_virtqueue_split(
  2. unsigned int index,
  3. unsigned int num,
  4. unsigned int vring_align,
  5. struct virtio_device *vdev,
  6. bool weak_barriers,
  7. bool may_reduce_num,
  8. bool context,
  9. bool (*notify)(struct virtqueue *),
  10. void (*callback)(struct virtqueue *),
  11. const char *name,
  12. struct device *dma_dev)
  13. {
  14. struct vring_virtqueue_split vring_split = {};
  15. struct virtqueue *vq;
  16. int err;
  17. err = vring_alloc_queue_split(&vring_split, vdev, num, vring_align,
  18. may_reduce_num, dma_dev);
  19. if (err)
  20. return NULL;
  21. vq = __vring_new_virtqueue(index, &vring_split, vdev, weak_barriers,
  22. context, notify, callback, name, dma_dev);
  23. if (!vq) {
  24. vring_free_split(&vring_split, vdev, dma_dev);
  25. return NULL;
  26. }
  27. to_vvq(vq)->we_own_ring = true;
  28. return vq;
  29. }

在讲解具体的函数功能之前,先得对于函数中用到的各结构进行详细就说明,这样才能更好地理解函数代码和功能。

(1)virtio_device结构

struct virtio_device前文提到过,其定义在Linux内核源码/include/linux/virtio.h中,代码如下:

  1. /**
  2. * struct virtio_device - representation of a device using virtio
  3. * @index: unique position on the virtio bus
  4. * @failed: saved value for VIRTIO_CONFIG_S_FAILED bit (for restore)
  5. * @config_enabled: configuration change reporting enabled
  6. * @config_change_pending: configuration change reported while disabled
  7. * @config_lock: protects configuration change reporting
  8. * @vqs_list_lock: protects @vqs.
  9. * @dev: underlying device.
  10. * @id: the device type identification (used to match it with a driver).
  11. * @config: the configuration ops for this device.
  12. * @vringh_config: configuration ops for host vrings.
  13. * @vqs: the list of virtqueues for this device.
  14. * @features: the features supported by both driver and device.
  15. * @priv: private pointer for the driver's use.
  16. */
  17. struct virtio_device {
  18. int index;
  19. bool failed;
  20. bool config_enabled;
  21. bool config_change_pending;
  22. spinlock_t config_lock;
  23. spinlock_t vqs_list_lock;
  24. struct device dev;
  25. struct virtio_device_id id;
  26. const struct virtio_config_ops *config;
  27. const struct vringh_config_ops *vringh_config;
  28. struct list_head vqs;
  29. u64 features;
  30. void *priv;
  31. };

struct virtio_device是Linux内核中用于表示virtio设备的数据结构,其包含了与virtio设备相关的各种信息和操作函数。如下图中间部分所示:

struct virtio_device各成员详细描述如下:

  • int index:virtio总线(bus)上的唯一位置。
  • bool failed:VIRTIO_CONFIG_S_FAILED位的保存值(用于恢复)。
  • bool config_enabled:是否使能配置(信息)变化报告。
  • bool config_change_pending:当禁用时,是否报告配置(信息)变化。
  • spinlock_t config_lock:保护配置更改报告的自旋锁。
  • spinlock_t vqs_list_lock:保护vqs(成员)的自旋锁。
  • struct device dev:底层设备。即Linux设备模型中的struct device结构体,用于表示virtio设备在内核中的抽象。
  • struct virtio_device_id id:设备类型标识(用于将其与驱动程序相匹配)。
  • const struct virtio_config_ops *config:此设备的配置操作。
  • const struct vringh_config_ops *vringh_config:宿主机vrings的配置操作。
  • struct list_head vqs:此设备的virtqueues列表。
  • u64 features:驱动程序和设备都支持的功能。
  • void *priv:供驱动程序使用的专用指针。

(2)virtio_device_id结构

struct virtio_device_id的定义在Linux内核源码/include/linux/mod_devicetable.h中,代码如下:

  1. struct virtio_device_id {
  2. __u32 device;
  3. __u32 vendor;
  4. };

此结构体很简单也很好理解,只有两个成员:

  • device:设备ID。
  • vendor:厂商(制造商)ID。

其中的device成员标识了当前virtio_device的用途。在Linux内核源码/include/uapi/linux/virtio_ids.h中定义了各种类型:

  1. #define VIRTIO_ID_NET 1 /* virtio net */
  2. #define VIRTIO_ID_BLOCK 2 /* virtio block */
  3. #define VIRTIO_ID_CONSOLE 3 /* virtio console */
  4. #define VIRTIO_ID_RNG 4 /* virtio rng */
  5. #define VIRTIO_ID_BALLOON 5 /* virtio balloon */
  6. #define VIRTIO_ID_IOMEM 6 /* virtio ioMemory */
  7. #define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */
  8. #define VIRTIO_ID_SCSI 8 /* virtio scsi */
  9. #define VIRTIO_ID_9P 9 /* 9p virtio console */
  10. #define VIRTIO_ID_MAC80211_WLAN 10 /* virtio WLAN MAC */
  11. #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
  12. #define VIRTIO_ID_CAIF 12 /* Virtio caif */
  13. #define VIRTIO_ID_MEMORY_BALLOON 13 /* virtio memory balloon */
  14. #define VIRTIO_ID_GPU 16 /* virtio GPU */
  15. #define VIRTIO_ID_CLOCK 17 /* virtio clock/timer */
  16. #define VIRTIO_ID_INPUT 18 /* virtio input */
  17. #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */
  18. #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */
  19. #define VIRTIO_ID_SIGNAL_DIST 21 /* virtio signal distribution device */
  20. #define VIRTIO_ID_PSTORE 22 /* virtio pstore device */
  21. #define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
  22. #define VIRTIO_ID_MEM 24 /* virtio mem */
  23. #define VIRTIO_ID_SOUND 25 /* virtio sound */
  24. #define VIRTIO_ID_FS 26 /* virtio filesystem */
  25. #define VIRTIO_ID_PMEM 27 /* virtio pmem */
  26. #define VIRTIO_ID_RPMB 28 /* virtio rpmb */
  27. #define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */
  28. #define VIRTIO_ID_VIDEO_ENCODER 30 /* virtio video encoder */
  29. #define VIRTIO_ID_VIDEO_DECODER 31 /* virtio video decoder */
  30. #define VIRTIO_ID_SCMI 32 /* virtio SCMI */
  31. #define VIRTIO_ID_NITRO_SEC_MOD 33 /* virtio nitro secure module*/
  32. #define VIRTIO_ID_I2C_ADAPTER 34 /* virtio i2c adapter */
  33. #define VIRTIO_ID_WATCHDOG 35 /* virtio watchdog */
  34. #define VIRTIO_ID_CAN 36 /* virtio can */
  35. #define VIRTIO_ID_DMABUF 37 /* virtio dmabuf */
  36. #define VIRTIO_ID_PARAM_SERV 38 /* virtio parameter server */
  37. #define VIRTIO_ID_AUDIO_POLICY 39 /* virtio audio policy */
  38. #define VIRTIO_ID_BT 40 /* virtio bluetooth */
  39. #define VIRTIO_ID_GPIO 41 /* virtio gpio */

我们当前示例中的balloon设备就是其中的一种:

#define VIRTIO_ID_BALLOON		5 /* virtio balloon */

(3)virtio_config_ops结构

struct virtio_config_ops前文书也讲过,包含了配置(一个)virtio设备的操作。其定义在Linunx内核源码/include/linux/virtio_config.h中,代码如下:

  1. /**
  2. * struct virtio_config_ops - operations for configuring a virtio device
  3. * Note: Do not assume that a transport implements all of the operations
  4. * getting/setting a value as a simple read/write! Generally speaking,
  5. * any of @get/@set, @get_status/@set_status, or @get_features/
  6. * @finalize_features are NOT safe to be called from an atomic
  7. * context.
  8. * @get: read the value of a configuration field
  9. * vdev: the virtio_device
  10. * offset: the offset of the configuration field
  11. * buf: the buffer to write the field value into.
  12. * len: the length of the buffer
  13. * @set: write the value of a configuration field
  14. * vdev: the virtio_device
  15. * offset: the offset of the configuration field
  16. * buf: the buffer to read the field value from.
  17. * len: the length of the buffer
  18. * @generation: config generation counter (optional)
  19. * vdev: the virtio_device
  20. * Returns the config generation counter
  21. * @get_status: read the status byte
  22. * vdev: the virtio_device
  23. * Returns the status byte
  24. * @set_status: write the status byte
  25. * vdev: the virtio_device
  26. * status: the new status byte
  27. * @reset: reset the device
  28. * vdev: the virtio device
  29. * After this, status and feature negotiation must be done again
  30. * Device must not be reset from its vq/config callbacks, or in
  31. * parallel with being added/removed.
  32. * @find_vqs: find virtqueues and instantiate them.
  33. * vdev: the virtio_device
  34. * nvqs: the number of virtqueues to find
  35. * vqs: on success, includes new virtqueues
  36. * callbacks: array of callbacks, for each virtqueue
  37. * include a NULL entry for vqs that do not need a callback
  38. * names: array of virtqueue names (mainly for debugging)
  39. * include a NULL entry for vqs unused by driver
  40. * Returns 0 on success or error status
  41. * @del_vqs: free virtqueues found by find_vqs().
  42. * @synchronize_cbs: synchronize with the virtqueue callbacks (optional)
  43. * The function guarantees that all memory operations on the
  44. * queue before it are visible to the vring_interrupt() that is
  45. * called after it.
  46. * vdev: the virtio_device
  47. * @get_features: get the array of feature bits for this device.
  48. * vdev: the virtio_device
  49. * Returns the first 64 feature bits (all we currently need).
  50. * @finalize_features: confirm what device features we'll be using.
  51. * vdev: the virtio_device
  52. * This sends the driver feature bits to the device: it can change
  53. * the dev->feature bits if it wants.
  54. * Note that despite the name this can be called any number of
  55. * times.
  56. * Returns 0 on success or error status
  57. * @bus_name: return the bus name associated with the device (optional)
  58. * vdev: the virtio_device
  59. * This returns a pointer to the bus name a la pci_name from which
  60. * the caller can then copy.
  61. * @set_vq_affinity: set the affinity for a virtqueue (optional).
  62. * @get_vq_affinity: get the affinity for a virtqueue (optional).
  63. * @get_shm_region: get a shared memory region based on the index.
  64. * @disable_vq_and_reset: reset a queue individually (optional).
  65. * vq: the virtqueue
  66. * Returns 0 on success or error status
  67. * disable_vq_and_reset will guarantee that the callbacks are disabled and
  68. * synchronized.
  69. * Except for the callback, the caller should guarantee that the vring is
  70. * not accessed by any functions of virtqueue.
  71. * @enable_vq_after_reset: enable a reset queue
  72. * vq: the virtqueue
  73. * Returns 0 on success or error status
  74. * If disable_vq_and_reset is set, then enable_vq_after_reset must also be
  75. * set.
  76. */
  77. struct virtio_config_ops {
  78. void (*get)(struct virtio_device *vdev, unsigned offset,
  79. void *buf, unsigned len);
  80. void (*set)(struct virtio_device *vdev, unsigned offset,
  81. const void *buf, unsigned len);
  82. u32 (*generation)(struct virtio_device *vdev);
  83. u8 (*get_status)(struct virtio_device *vdev);
  84. void (*set_status)(struct virtio_device *vdev, u8 status);
  85. void (*reset)(struct virtio_device *vdev);
  86. int (*find_vqs)(struct virtio_device *, unsigned nvqs,
  87. struct virtqueue *vqs[], vq_callback_t *callbacks[],
  88. const char * const names[], const bool *ctx,
  89. struct irq_affinity *desc);
  90. void (*del_vqs)(struct virtio_device *);
  91. void (*synchronize_cbs)(struct virtio_device *);
  92. u64 (*get_features)(struct virtio_device *vdev);
  93. int (*finalize_features)(struct virtio_device *vdev);
  94. const char *(*bus_name)(struct virtio_device *vdev);
  95. int (*set_vq_affinity)(struct virtqueue *vq,
  96. const struct cpumask *cpu_mask);
  97. const struct cpumask *(*get_vq_affinity)(struct virtio_device *vdev,
  98. int index);
  99. bool (*get_shm_region)(struct virtio_device *vdev,
  100. struct virtio_shm_region *region, u8 id);
  101. int (*disable_vq_and_reset)(struct virtqueue *vq);
  102. int (*enable_vq_after_reset)(struct virtqueue *vq);
  103. };

struct virtio_config_ops的各成员详细描述如下:

  • void (*get)(struct virtio_device *vdev, unsigned offset, void *buf, unsigned len)

回调函数(指针),读取配置字段的值。

参数:

vdev:virtio_device。

offset:配置字段的偏移量。

buf:将字段值写入的缓冲区。

len:缓冲区的长度。

  • void (*set)(struct virtio_device *vdev, unsigned offset, const void *buf, unsigned len)

回调函数(指针),写入配置字段。

参数:

vdev:virtio_device。

offset:配置字段的偏移量。

buf:从中读取字段值的缓冲区。

len:缓冲区的长度。

  • u32 (*generation)(struct virtio_device *vdev)

回调函数(指针),配置生成计数器(可选)。

参数:

vdev:virtio_device。

  • u8 (*get_status)(struct virtio_device *vdev)

回调函数(指针),读取状态字节。

参数:

vdev:virtio_device。

返回状态字节。

  • void (*set_status)(struct virtio_device *vdev, u8 status)

回调函数(指针),设置状态字节。

参数:

vdev:virtio_device。

status:新的状态字节。

  • void (*reset)(struct virtio_device *vdev)

回调函数(指针),重置设备。

参数:

vdev:virtio_device。

重置设备之后,必须再次进行状态和功能协商。设备不能从其vq/config回调中重置,也不能与添加/删除并行重置。

  • int (*find_vqs)(struct virtio_device *, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], const bool *ctx, struct irq_affinity *desc)

回调函数(指针),找到virtqueues并实例化它们。

参数:

vdev:virtio_device。

nvqs:要查找的virtqueues的数量。

vqs:调用成功后,包括新的virtqueues。

callbacks:回调数组,每个virtqueue都包括一个,对于不需要回调的vqs,则置为NULL。

names:virtqueue名称数组(主要用于调试),包括驱动程序未使用的vqs的NULL条目。

  • void (*del_vqs)(struct virtio_device *)

回调函数(指针),释放find_vqs()找到的空闲virtqueues。

参数:

vdev:virtio_device。

  • void (*synchronize_cbs)(struct virtio_device *)

回调函数(指针),与virtqueue回调同步(可选)。

该函数保证在其之前的队列上的所有内存操作,对其后调用的vring_interrupt()可见。

参数:

vdev:virtio_device。

  • u64 (*get_features)(struct virtio_device *vdev)

回调函数(指针),获取该设备的特征位数组。

参数:

vdev:virtio_device。

返回前64个特征位(我们当前所需的全部)。

  • int (*finalize_features)(struct virtio_device *vdev)

回调函数(指针),确认将使用哪些设备特性。

参数:

vdev:virtio_device。

这会将驱动程序特性(特征)位发送到设备:如果需要,它可以更改dev->feature位。

注意,尽管叫这个名称,但它可以被调用任意次数。

成功返回0,失败返回错误状态。

  • const char *(*bus_name)(struct virtio_device *vdev)

回调函数(指针),返回与设备关联的总线名称(可选)

参数:

vdev:virtio_device。

这将返回一个指向总线名称,一个仿pci_name的指针,然后调用方可以从该名称进行复制。

  • int (*set_vq_affinity)(struct virtqueue *vq, const struct cpumask *cpu_mask)

回调函数(指针),设置virtqueue的亲和性(可选)。即设置指定的虚拟队列(Virtual Queue)与特定的CPU核心之间的亲和性(affinity)。

参数:

vq:virtqueue。

cpu_mask:CPU掩码。

  • const struct cpumask *(*get_vq_affinity)(struct virtio_device *vdev, int index);

回调函数(指针),获取virtqueue的亲和性(可选)。即获取指定的虚拟队列(Virtual Queue)与特定的CPU核心之间的亲和性(affinity)

参数:

vq:virtqueue。

index:队列的索引值。

  • bool (*get_shm_region)(struct virtio_device *vdev, struct virtio_shm_region *region, u8 id)

回调函数(指针),基于索引获取共享内存区域。

参数:

vdev:virtio_device。

region:共享内存区域。

id:索引值。

  • int (*disable_vq_and_reset)(struct virtqueue *vq)

回调函数(指针),单独重置队列(可选)。

参数:

vq:virtqueue。

成功返回0,失败返回错误状态。

disable_vq_and_reset将确保回调被禁用并同步。

除了回调之外,调用者应该保证virtqueue的任何函数都不会访问vring。

  • int (*enable_vq_after_reset)(struct virtqueue *vq)

回调函数(指针),启用重置队列。

参数:

vq:virtqueue。

成功返回0,失败返回错误状态。

如果设置了disable_vq_and_reset,则还必须设置enable_vq_after_reset。

如下图(就是上面那张图)右上角所示:

更多相关结构的介绍请看下回。

举报

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