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

88 篇文章 19 订阅
本文继续探讨QEMU源码中的virtio驱动,重点在于virtio驱动的初始化,介绍了virtio_pci_config_ops及其初始化过程。通过分析Linux内核源码,解释了get_status和set_status等关键函数的功能,它们直接操作设备状态并涉及QEMU中的MemoryRegionOps。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析 —— virtio(21)

前几回讲解了 virtio 驱动的加载。本回开始讲解virtio驱动的初始化。

在讲解virtio驱动的初始化之前,先要介绍virtio配置的函数集合变量virtio_pci_config_ops。实际上前文书也有提到,如下图的右上角:

virtio_pci_config_ops的初始化有两处,分别在 Linux 内核源码/drivers/virtio/virtio_pci_legacy.c和Linux内核源码/drivers/virtio/virtio_pci_modern.c中。代码分别如下:

  • legacy
  1. static const struct virtio_config_ops virtio_pci_config_ops = {
  2. .get = vp_get,
  3. .set = vp_set,
  4. .get_status = vp_get_status,
  5. .set_status = vp_set_status,
  6. .reset = vp_reset,
  7. .find_vqs = vp_find_vqs,
  8. .del_vqs = vp_del_vqs,
  9. .synchronize_cbs = vp_synchronize_vectors,
  10. .get_features = vp_get_features,
  11. .finalize_features = vp_finalize_features,
  12. .bus_name = vp_bus_name,
  13. .set_vq_affinity = vp_set_vq_affinity,
  14. .get_vq_affinity = vp_get_vq_affinity,
  15. };
  • modern
  1. static const struct virtio_config_ops virtio_pci_config_ops = {
  2. .get = vp_get,
  3. .set = vp_set,
  4. .generation = vp_generation,
  5. .get_status = vp_get_status,
  6. .set_status = vp_set_status,
  7. .reset = vp_reset,
  8. .find_vqs = vp_modern_find_vqs,
  9. .del_vqs = vp_del_vqs,
  10. .synchronize_cbs = vp_synchronize_vectors,
  11. .get_features = vp_get_features,
  12. .finalize_features = vp_finalize_features,
  13. .bus_name = vp_bus_name,
  14. .set_vq_affinity = vp_set_vq_affinity,
  15. .get_vq_affinity = vp_get_vq_affinity,
  16. .get_shm_region = vp_get_shm_region,
  17. .disable_vq_and_reset = vp_modern_disable_vq_and_reset,
  18. .enable_vq_after_reset = vp_modern_enable_vq_after_reset,
  19. };

在此以Linux内核源码/drivers/virtio/virtio_pci_modern.c中的virtio_pci_config_ops为例进行讲解。

在前文书讲到的virtio_pci_modern_probe函数(Linux内核源码/drivers/virtio/virtio_pci_modern.c)中:

  1. /* the PCI probing function */
  2. int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev)
  3. {
  4. struct virtio_pci_modern_device *mdev = &vp_dev->mdev;
  5. struct pci_dev *pci_dev = vp_dev->pci_dev;
  6. int err;
  7. mdev->pci_dev = pci_dev;
  8. err = vp_modern_probe(mdev);
  9. if (err)
  10. return err;
  11. if (mdev->device)
  12. vp_dev->vdev.config = &virtio_pci_config_ops;
  13. else
  14. vp_dev->vdev.config = &virtio_pci_config_nodev_ops;
  15. vp_dev->config_vector = vp_config_vector;
  16. vp_dev->setup_vq = setup_vq;
  17. vp_dev->del_vq = del_vq;
  18. vp_dev->isr = mdev->isr;
  19. vp_dev->vdev.id = mdev->id;
  20. return 0;
  21. }

virtio_pci_config_ops变量被赋值给了virtio_device结构的config成员。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_config_ops的定义在Linux内核源码/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. };

再回过头来看一下Linux内核源码/drivers/virtio/virtio_pci_modern.c中的virtio_pci_config_ops,对照着上边 struct virtio_config_ops的定义。

  1. static const struct virtio_config_ops virtio_pci_config_ops = {
  2. .get = vp_get,
  3. .set = vp_set,
  4. .generation = vp_generation,
  5. .get_status = vp_get_status,
  6. .set_status = vp_set_status,
  7. .reset = vp_reset,
  8. .find_vqs = vp_modern_find_vqs,
  9. .del_vqs = vp_del_vqs,
  10. .synchronize_cbs = vp_synchronize_vectors,
  11. .get_features = vp_get_features,
  12. .finalize_features = vp_finalize_features,
  13. .bus_name = vp_bus_name,
  14. .set_vq_affinity = vp_set_vq_affinity,
  15. .get_vq_affinity = vp_get_vq_affinity,
  16. .get_shm_region = vp_get_shm_region,
  17. .disable_vq_and_reset = vp_modern_disable_vq_and_reset,
  18. .enable_vq_after_reset = vp_modern_enable_vq_after_reset,
  19. };

virtio_pci_config_ops结构中的成员函数通常是virtio PCI代理设备的IO操作,包括读写virtio PCI代理设备的PIO和MMIO,如get_status和set_status成员对应的vp_get_status函数和vp_set_status函数。分别来看:

  • get_status

根据struct virtio_config_ops中的说明:

@get_status: read the status byte
*    vdev: the virtio_device
*    Returns the status byte

get_status的作用是读取状态字节。有一个参数vdev,代表了virtio device。返回值为读取到的状态字节。

get_status所指向的vp_get_status函数也在Linux内核源码/drivers/virtio/virtio_pci_modern.c中,代码如下:

  1. /* config->{get,set}_status() implementations */
  2. static u8 vp_get_status(struct virtio_device *vdev)
  3. {
  4. struct virtio_pci_device *vp_dev = to_vp_device(vdev);
  5. return vp_modern_get_status(&vp_dev->mdev);
  6. }

vp_modern_get_status函数在Linux内核源码/drivers/virtio/virtio_pci_modern_dev.c中,代码如下:

  1. /*
  2. * vp_modern_get_status - get the device status
  3. * @mdev: the modern virtio-pci device
  4. *
  5. * Returns the status read from device
  6. */
  7. u8 vp_modern_get_status(struct virtio_pci_modern_device *mdev)
  8. {
  9. struct virtio_pci_common_cfg __iomem *cfg = mdev->common;
  10. return vp_ioread8(&cfg->device_status);
  11. }
  12. EXPORT_SYMBOL_GPL(vp_modern_get_status);
  • set_status

@set_status: write the status byte
*    vdev: the virtio_device
*    status: the new status byte

set_status的作用是写入状态字节。有两个参数:vdev代表了virtio device;status为新的要写入的状态字节。

set_status所指向的vp_set_status函数也在Linux内核源码/drivers/virtio/virtio_pci_modern.c中,代码如下:

  1. static void vp_set_status(struct virtio_device *vdev, u8 status)
  2. {
  3. struct virtio_pci_device *vp_dev = to_vp_device(vdev);
  4. /* We should never be setting status to 0. */
  5. BUG_ON(status == 0);
  6. vp_modern_set_status(&vp_dev->mdev, status);
  7. }

vp_modern_set_status函数在Linux内核源码/drivers/virtio/virtio_pci_modern_dev.c中,代码如下:

  1. /*
  2. * vp_modern_set_status - set status to device
  3. * @mdev: the modern virtio-pci device
  4. * @status: the status set to device
  5. */
  6. void vp_modern_set_status(struct virtio_pci_modern_device *mdev,
  7. u8 status)
  8. {
  9. struct virtio_pci_common_cfg __iomem *cfg = mdev->common;
  10. /*
  11. * Per memory-barriers.txt, wmb() is not needed to guarantee
  12. * that the cache coherent memory writes have completed
  13. * before writing to the MMIO region.
  14. */
  15. vp_iowrite8(status, &cfg->device_status);
  16. }
  17. EXPORT_SYMBOL_GPL(vp_modern_set_status);

vp_modern_get_status和vp_modern_set_status函数直接读写vp_dev->mdev->common->device_status。从前文书( QEMU源码全解析 —— virtio(14) )的讲解可知,vp_dev->common对应的是virtio PCI代理设备第四个BAR表示的地址中的一段空间。

vp_dev->mdev->common的类型为struct virtio_pci_common_cfg,该结构的定义在Linux内核源码/include/uapi/linux/virtio_pci.h中,代码如下:

  1. /* Fields in VIRTIO_PCI_CAP_COMMON_CFG: */
  2. struct virtio_pci_common_cfg {
  3. /* About the whole device. */
  4. __le32 device_feature_select; /* read-write */
  5. __le32 device_feature; /* read-only */
  6. __le32 guest_feature_select; /* read-write */
  7. __le32 guest_feature; /* read-write */
  8. __le16 msix_config; /* read-write */
  9. __le16 num_queues; /* read-only */
  10. __u8 device_status; /* read-write */
  11. __u8 config_generation; /* read-only */
  12. /* About a specific virtqueue. */
  13. __le16 queue_select; /* read-write */
  14. __le16 queue_size; /* read-write, power of 2. */
  15. __le16 queue_msix_vector; /* read-write */
  16. __le16 queue_enable; /* read-write */
  17. __le16 queue_notify_off; /* read-only */
  18. __le32 queue_desc_lo; /* read-write */
  19. __le32 queue_desc_hi; /* read-write */
  20. __le32 queue_avail_lo; /* read-write */
  21. __le32 queue_avail_hi; /* read-write */
  22. __le32 queue_used_lo; /* read-write */
  23. __le32 queue_used_hi; /* read-write */
  24. };

struct virtio_pci_common_cfg的每一个成员都表示一个virtio PCI代理设备modern MMIO地址空间中对应的值,读写这写成员都会陷入到 QEMU 中。比如上面的读取或者设置设备状态的device_status成员,其地址从virtio_pci_common_cfg结构开始的偏移20字节处(4+4+4+4+2+2=20),所以读写该地址的时候会陷入到QEMU中,并且地址是virtio设备的common MemoryRegion偏移20字节处。该MemoryRegion对应的回调操作结构是common_ops,类型为MemoryRegionOps。

common_ops在hw/virtio/virtio-pci.c中初始化,代码如下:

  1. static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy,
  2. const char *vdev_name)
  3. {
  4. static const MemoryRegionOps common_ops = {
  5. .read = virtio_pci_common_read,
  6. .write = virtio_pci_common_write,
  7. .impl = {
  8. .min_access_size = 1,
  9. .max_access_size = 4,
  10. },
  11. .endianness = DEVICE_LITTLE_ENDIAN,
  12. };
  13. ……
  14. }

回到struct_pci_config_ops。

  1. static const struct virtio_config_ops virtio_pci_config_ops = {
  2. .get = vp_get,
  3. .set = vp_set,
  4. .generation = vp_generation,
  5. .get_status = vp_get_status,
  6. .set_status = vp_set_status,
  7. .reset = vp_reset,
  8. .find_vqs = vp_modern_find_vqs,
  9. .del_vqs = vp_del_vqs,
  10. .synchronize_cbs = vp_synchronize_vectors,
  11. .get_features = vp_get_features,
  12. .finalize_features = vp_finalize_features,
  13. .bus_name = vp_bus_name,
  14. .set_vq_affinity = vp_set_vq_affinity,
  15. .get_vq_affinity = vp_get_vq_affinity,
  16. .get_shm_region = vp_get_shm_region,
  17. .disable_vq_and_reset = vp_modern_disable_vq_and_reset,
  18. .enable_vq_after_reset = vp_modern_enable_vq_after_reset,
  19. };

virtio_pci_config_ops的各个函数封装了这些I/O操作,不仅是MMO操作,还有PIO操作。virtio设备可以通过此结构中的各个回调函数来驱动设备。

本回就讲到这里。下一回以virtio balloon设备的初始化过程为例,分析virtio设备的初始化过程,即上一回讲到的virtio驱动初始化设备的过程中的“执行设备相关的 初始化操作 ”一步。

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

举报

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