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

123 篇文章 36 订阅 ¥49.90 ¥99.00
本文继续探讨QEMU源码中的virtio设备实现,重点解析virtio_device_realize函数中如何通过virtio_bus_device_plugged将virtio设备插入到virtio总线的过程。这一操作在virtio-bus.c的virtio_bus_device_plugged函数中进行,该函数调用VirtioBusClass的device_plugged回调,实际执行者为virtio_pci_device_plugged函数,定义于virtio-pci.c。后续文章将深入分析virtio_pci_device_plugged的详细工作原理。
摘要由CSDN通过智能技术生成

展开

接前一篇文章:

通过前文书(从 QEMU源码全解析 —— virtio(9) 开始)对整个流程以及各个相关函数的解析,可以看到从 virtio PCI代理设备的具现化到virtio设备的具现化过程。但前述分析还遗漏了一部分,就是virtio设备挂载到virtio总线上的行为。这个过程是在virtio_device_realize函数中通过调用virtio_bus_device_plugged函数完成的。在 QEMU源码全解析 —— virtio(11) 中提到过,有图为证:

d96a462e8eaa79f7c03c4c4871c64b09.png

virtio_bus_device_plugged函数的作用就是将virtio设备插入到virtio总线上去。其源码在hw/virtio/virtio-bus.c中,如下:

  1. /* A VirtIODevice is being plugged */
  2. void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp)
  3. {
  4. DeviceState *qdev = DEVICE(vdev);
  5. BusState *qbus = BUS(qdev_get_parent_bus(qdev));
  6. VirtioBusState *bus = VIRTIO_BUS(qbus);
  7. VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
  8. VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
  9. bool has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM);
  10. bool vdev_has_iommu;
  11. Error *local_err = NULL;
  12. DPRINTF("%s: plug device.\n", qbus->name);
  13. if (klass->pre_plugged != NULL) {
  14. klass->pre_plugged(qbus->parent, &local_err);
  15. if (local_err) {
  16. error_propagate(errp, local_err);
  17. return;
  18. }
  19. }
  20. /* Get the features of the plugged device. */
  21. assert(vdc->get_features != NULL);
  22. vdev->host_features = vdc->get_features(vdev, vdev->host_features,
  23. &local_err);
  24. if (local_err) {
  25. error_propagate(errp, local_err);
  26. return;
  27. }
  28. if (klass->device_plugged != NULL) {
  29. klass->device_plugged(qbus->parent, &local_err);
  30. }
  31. if (local_err) {
  32. error_propagate(errp, local_err);
  33. return;
  34. }
  35. vdev->dma_as = &address_space_memory;
  36. if (has_iommu) {
  37. vdev_has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM);
  38. /*
  39. * Present IOMMU_PLATFORM to the driver iff iommu_plattform=on and
  40. * device operational. If the driver does not accept IOMMU_PLATFORM
  41. * we fail the device.
  42. */
  43. virtio_add_feature(&vdev->host_features, VIRTIO_F_IOMMU_PLATFORM);
  44. if (klass->get_dma_as) {
  45. vdev->dma_as = klass->get_dma_as(qbus->parent);
  46. if (!vdev_has_iommu && vdev->dma_as != &address_space_memory) {
  47. error_setg(errp,
  48. "iommu_platform=true is not supported by the device");
  49. return;
  50. }
  51. }
  52. }
  53. }

virtio_bus_device_plugged函数主要是调用了VirtioBusClass类型的device_plugged回调函数。代码片段如下:

  1. VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
  2. ……
  3. if (klass->device_plugged != NULL) {
  4. klass->device_plugged(qbus->parent, &local_err);
  5. }
  6. if (local_err) {
  7. error_propagate(errp, local_err);
  8. return;
  9. }

device_plugged回调函数也即klass->device_plugged函数指针所指向的函数在virtio_pci_bus_class_init函数中被初始化成了virtio_pci_device_plugged函数。virtio_pci_bus_class_init函数在hw/virtio/virtio- pci .c中,代码如下:

  1. static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
  2. {
  3. BusClass *bus_class = BUS_CLASS(klass);
  4. VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
  5. bus_class->max_dev = 1;
  6. k->notify = virtio_pci_notify;
  7. k->save_config = virtio_pci_save_config;
  8. k->load_config = virtio_pci_load_config;
  9. k->save_queue = virtio_pci_save_queue;
  10. k->load_queue = virtio_pci_load_queue;
  11. k->save_extra_state = virtio_pci_save_extra_state;
  12. k->load_extra_state = virtio_pci_load_extra_state;
  13. k->has_extra_state = virtio_pci_has_extra_state;
  14. k->query_guest_notifiers = virtio_pci_query_guest_notifiers;
  15. k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
  16. k->set_host_notifier_mr = virtio_pci_set_host_notifier_mr;
  17. k->vmstate_change = virtio_pci_vmstate_change;
  18. k->pre_plugged = virtio_pci_pre_plugged;
  19. k->device_plugged = virtio_pci_device_plugged;
  20. k->device_unplugged = virtio_pci_device_unplugged;
  21. k->query_nvectors = virtio_pci_query_nvectors;
  22. k->ioeventfd_enabled = virtio_pci_ioeventfd_enabled;
  23. k->ioeventfd_assign = virtio_pci_ioeventfd_assign;
  24. k->get_dma_as = virtio_pci_get_dma_as;
  25. k->iommu_enabled = virtio_pci_iommu_enabled;
  26. k->queue_enabled = virtio_pci_queue_enabled;
  27. }

virtio_pci_device_plugged函数在hw/virtio/virtio-pci.c中,代码如下:

  1. /* This is called by virtio-bus just after the device is plugged. */
  2. static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
  3. {
  4. VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
  5. VirtioBusState *bus = &proxy->bus;
  6. bool legacy = virtio_pci_legacy(proxy);
  7. bool modern;
  8. bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
  9. uint8_t *config;
  10. uint32_t size;
  11. VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
  12. /*
  13. * Virtio capabilities present without
  14. * VIRTIO_F_VERSION_1 confuses guests
  15. */
  16. if (!proxy->ignore_backend_features &&
  17. !virtio_has_feature(vdev->host_features, VIRTIO_F_VERSION_1)) {
  18. virtio_pci_disable_modern(proxy);
  19. if (!legacy) {
  20. error_setg(errp, "Device doesn't support modern mode, and legacy"
  21. " mode is disabled");
  22. error_append_hint(errp, "Set disable-legacy to off\n");
  23. return;
  24. }
  25. }
  26. modern = virtio_pci_modern(proxy);
  27. config = proxy->pci_dev.config;
  28. if (proxy->class_code) {
  29. pci_config_set_class(config, proxy->class_code);
  30. }
  31. if (legacy) {
  32. if (!virtio_legacy_allowed(vdev)) {
  33. /*
  34. * To avoid migration issues, we allow legacy mode when legacy
  35. * check is disabled in the old machine types (< 5.1).
  36. */
  37. if (virtio_legacy_check_disabled(vdev)) {
  38. warn_report("device is modern-only, but for backward "
  39. "compatibility legacy is allowed");
  40. } else {
  41. error_setg(errp,
  42. "device is modern-only, use disable-legacy=on");
  43. return;
  44. }
  45. }
  46. if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) {
  47. error_setg(errp, "VIRTIO_F_IOMMU_PLATFORM was supported by"
  48. " neither legacy nor transitional device");
  49. return;
  50. }
  51. /*
  52. * Legacy and transitional devices use specific subsystem IDs.
  53. * Note that the subsystem vendor ID (config + PCI_SUBSYSTEM_VENDOR_ID)
  54. * is set to PCI_SUBVENDOR_ID_REDHAT_QUMRANET by default.
  55. */
  56. pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus));
  57. if (proxy->trans_devid) {
  58. pci_config_set_device_id(config, proxy->trans_devid);
  59. }
  60. } else {
  61. /* pure virtio-1.0 */
  62. pci_set_word(config + PCI_VENDOR_ID,
  63. PCI_VENDOR_ID_REDHAT_QUMRANET);
  64. pci_set_word(config + PCI_DEVICE_ID,
  65. PCI_DEVICE_ID_VIRTIO_10_BASE + virtio_bus_get_vdev_id(bus));
  66. pci_config_set_revision(config, 1);
  67. }
  68. config[PCI_INTERRUPT_PIN] = 1;
  69. if (modern) {
  70. struct virtio_pci_cap cap = {
  71. .cap_len = sizeof cap,
  72. };
  73. struct virtio_pci_notify_cap notify = {
  74. .cap.cap_len = sizeof notify,
  75. .notify_off_multiplier =
  76. cpu_to_le32(virtio_pci_queue_mem_mult(proxy)),
  77. };
  78. struct virtio_pci_cfg_cap cfg = {
  79. .cap.cap_len = sizeof cfg,
  80. .cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG,
  81. };
  82. struct virtio_pci_notify_cap notify_pio = {
  83. .cap.cap_len = sizeof notify,
  84. .notify_off_multiplier = cpu_to_le32(0x0),
  85. };
  86. struct virtio_pci_cfg_cap *cfg_mask;
  87. virtio_pci_modern_regions_init(proxy, vdev->name);
  88. virtio_pci_modern_mem_region_map(proxy, &proxy->common, &cap);
  89. virtio_pci_modern_mem_region_map(proxy, &proxy->isr, &cap);
  90. virtio_pci_modern_mem_region_map(proxy, &proxy->device, &cap);
  91. virtio_pci_modern_mem_region_map(proxy, &proxy->notify, &notify.cap);
  92. if (modern_pio) {
  93. memory_region_init(&proxy->io_bar, OBJECT(proxy),
  94. "virtio-pci-io", 0x4);
  95. pci_register_bar(&proxy->pci_dev, proxy->modern_io_bar_idx,
  96. PCI_BASE_ADDRESS_SPACE_IO, &proxy->io_bar);
  97. virtio_pci_modern_io_region_map(proxy, &proxy->notify_pio,
  98. &notify_pio.cap);
  99. }
  100. pci_register_bar(&proxy->pci_dev, proxy->modern_mem_bar_idx,
  101. PCI_BASE_ADDRESS_SPACE_MEMORY |
  102. PCI_BASE_ADDRESS_MEM_PREFETCH |
  103. PCI_BASE_ADDRESS_MEM_TYPE_64,
  104. &proxy->modern_bar);
  105. proxy->config_cap = virtio_pci_add_mem_cap(proxy, &cfg.cap);
  106. cfg_mask = (void *)(proxy->pci_dev.wmask + proxy->config_cap);
  107. pci_set_byte(&cfg_mask->cap.bar, ~0x0);
  108. pci_set_long((uint8_t *)&cfg_mask->cap.offset, ~0x0);
  109. pci_set_long((uint8_t *)&cfg_mask->cap.length, ~0x0);
  110. pci_set_long(cfg_mask->pci_cfg_data, ~0x0);
  111. }
  112. if (proxy->nvectors) {
  113. int err = msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors,
  114. proxy->msix_bar_idx, NULL);
  115. if (err) {
  116. /* Notice when a system that supports MSIx can't initialize it */
  117. if (err != -ENOTSUP) {
  118. warn_report("unable to init msix vectors to %" PRIu32,
  119. proxy->nvectors);
  120. }
  121. proxy->nvectors = 0;
  122. }
  123. }
  124. proxy->pci_dev.config_write = virtio_write_config;
  125. proxy->pci_dev.config_read = virtio_read_config;
  126. if (legacy) {
  127. size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
  128. + virtio_bus_get_vdev_config_len(bus);
  129. size = pow2ceil(size);
  130. memory_region_init_io(&proxy->bar, OBJECT(proxy),
  131. &virtio_pci_config_ops,
  132. proxy, "virtio-pci", size);
  133. pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar_idx,
  134. PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar);
  135. }
  136. }

可以看到,virtio_pci_device_plugged函数比较长,在笔者选用的 qemu -8.1.3版本的源码中有160行左右。

对于virtio_pci_device_plugged函数的详细解析,请看下回。

举报

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