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

88 篇文章 19 订阅
本文继续深入解析QEMU中virtio设备的实现,重点介绍了virtio balloon PCI代理设备的初始化过程,涉及virtio_balloon_pci_class_init、device_set_realized、virtio_pci_dc_realize、pci_qdev_realize和virtio_pci_realize等关键函数的调用链。文章通过代码分析,详细阐述了设备初始化的流程。
摘要由CSDN通过智能技术生成

接前一篇文章:

上一回讲解了 virtio balloon相关类所涉及的realize函数,如下表所示:

realize函数 parent_dc_realize函数
DeviceClass virtio_pci_dc_realize
PCIDeviceClass virtio_pci_realize
VirtioPCIClass virtio_balloon_pci_realize pci_qdev_realize

本回继续接着讲。

再来回顾一下,virtio balloon PCI 代理设备类型的初始化函数virtio_balloon_pci_class_init(),在hw/virtio/virtio-balloon-pci.c中,代码如下:

  1. static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data)
  2. {
  3. DeviceClass *dc = DEVICE_CLASS(klass);
  4. VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
  5. PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
  6. k->realize = virtio_balloon_pci_realize;
  7. set_bit(DEVICE_CATEGORY_MISC, dc->categories);
  8. pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
  9. pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON;
  10. pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
  11. pcidev_k->class_id = PCI_CLASS_OTHERS;
  12. }

device_set_realized函数在hw/core/qdev.c中,代码如下:

  1. static void device_set_realized(Object *obj, bool value, Error **errp)
  2. {
  3. DeviceState *dev = DEVICE(obj);
  4. DeviceClass *dc = DEVICE_GET_CLASS(dev);
  5. HotplugHandler *hotplug_ctrl;
  6. BusState *bus;
  7. NamedClockList *ncl;
  8. Error *local_err = NULL;
  9. bool unattached_parent = false;
  10. static int unattached_count;
  11. if (dev->hotplugged && !dc->hotpluggable) {
  12. error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj));
  13. return;
  14. }
  15. if (value && !dev->realized) {
  16. if (!check_only_migratable(obj, errp)) {
  17. goto fail;
  18. }
  19. if (!obj->parent) {
  20. gchar *name = g_strdup_printf("device[%d]", unattached_count++);
  21. object_property_add_child(container_get(qdev_get_machine(),
  22. "/unattached"),
  23. name, obj);
  24. unattached_parent = true;
  25. g_free(name);
  26. }
  27. hotplug_ctrl = qdev_get_hotplug_handler(dev);
  28. if (hotplug_ctrl) {
  29. hotplug_handler_pre_plug(hotplug_ctrl, dev, &local_err);
  30. if (local_err != NULL) {
  31. goto fail;
  32. }
  33. }
  34. if (dc->realize) {
  35. dc->realize(dev, &local_err);
  36. if (local_err != NULL) {
  37. goto fail;
  38. }
  39. }
  40. DEVICE_LISTENER_CALL(realize, Forward, dev);
  41. /*
  42. * always free/re-initialize here since the value cannot be cleaned up
  43. * in device_unrealize due to its usage later on in the unplug path
  44. */
  45. g_free(dev->canonical_path);
  46. dev->canonical_path = object_get_canonical_path(OBJECT(dev));
  47. QLIST_FOREACH(ncl, &dev->clocks, node) {
  48. if (ncl->alias) {
  49. continue;
  50. } else {
  51. clock_setup_canonical_path(ncl->clock);
  52. }
  53. }
  54. if (qdev_get_vmsd(dev)) {
  55. if (vmstate_register_with_alias_id(VMSTATE_IF(dev),
  56. VMSTATE_INSTANCE_ID_ANY,
  57. qdev_get_vmsd(dev), dev,
  58. dev->instance_id_alias,
  59. dev->alias_required_for_version,
  60. &local_err) < 0) {
  61. goto post_realize_fail;
  62. }
  63. }
  64. /*
  65. * Clear the reset state, in case the object was previously unrealized
  66. * with a dirty state.
  67. */
  68. resettable_state_clear(&dev->reset);
  69. QLIST_FOREACH(bus, &dev->child_bus, sibling) {
  70. if (!qbus_realize(bus, errp)) {
  71. goto child_realize_fail;
  72. }
  73. }
  74. if (dev->hotplugged) {
  75. /*
  76. * Reset the device, as well as its subtree which, at this point,
  77. * should be realized too.
  78. */
  79. resettable_assert_reset(OBJECT(dev), RESET_TYPE_COLD);
  80. resettable_change_parent(OBJECT(dev), OBJECT(dev->parent_bus),
  81. NULL);
  82. resettable_release_reset(OBJECT(dev), RESET_TYPE_COLD);
  83. }
  84. dev->pending_deleted_event = false;
  85. if (hotplug_ctrl) {
  86. hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
  87. if (local_err != NULL) {
  88. goto child_realize_fail;
  89. }
  90. }
  91. qatomic_store_release(&dev->realized, value);
  92. } else if (!value && dev->realized) {
  93. /*
  94. * Change the value so that any concurrent users are aware
  95. * that the device is going to be unrealized
  96. *
  97. * TODO: change .realized property to enum that states
  98. * each phase of the device realization/unrealization
  99. */
  100. qatomic_set(&dev->realized, value);
  101. /*
  102. * Ensure that concurrent users see this update prior to
  103. * any other changes done by unrealize.
  104. */
  105. smp_wmb();
  106. QLIST_FOREACH(bus, &dev->child_bus, sibling) {
  107. qbus_unrealize(bus);
  108. }
  109. if (qdev_get_vmsd(dev)) {
  110. vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
  111. }
  112. if (dc->unrealize) {
  113. dc->unrealize(dev);
  114. }
  115. dev->pending_deleted_event = true;
  116. DEVICE_LISTENER_CALL(unrealize, Reverse, dev);
  117. }
  118. assert(local_err == NULL);
  119. return;
  120. child_realize_fail:
  121. QLIST_FOREACH(bus, &dev->child_bus, sibling) {
  122. qbus_unrealize(bus);
  123. }
  124. if (qdev_get_vmsd(dev)) {
  125. vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
  126. }
  127. post_realize_fail:
  128. g_free(dev->canonical_path);
  129. dev->canonical_path = NULL;
  130. if (dc->unrealize) {
  131. dc->unrealize(dev);
  132. }
  133. fail:
  134. error_propagate(errp, local_err);
  135. if (unattached_parent) {
  136. /*
  137. * Beware, this doesn't just revert
  138. * object_property_add_child(), it also runs bus_remove()!
  139. */
  140. object_unparent(OBJECT(dev));
  141. unattached_count--;
  142. }
  143. }

设置virtio PCI代理设备的realize属性时,device_set_realized函数中会首先调用DeviceClass->realize函数指针所指向的函数,也就是virtio_pci_dc_realize函数。代码片段如下:

  1. if (dc->realize) {
  2. dc->realize(dev, &local_err);
  3. if (local_err != NULL) {
  4. goto fail;
  5. }
  6. }
realize函数 parent_dc_realize函数
DeviceClass virtio_pci_dc_realize
PCIDeviceClass virtio_pci_realize
VirtioPCIClass virtio_balloon_pci_realize pci_qdev_realize

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

  1. static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp)
  2. {
  3. VirtioPCIClass *vpciklass = VIRTIO_PCI_GET_CLASS(qdev);
  4. VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);
  5. PCIDevice *pci_dev = &proxy->pci_dev;
  6. if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) &&
  7. virtio_pci_modern(proxy)) {
  8. pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
  9. }
  10. vpciklass->parent_dc_realize(qdev, errp);
  11. }

virtio_pci_dc_realize函数中会调用VirtioPCIClass->parent_dc_realize,也就是pci_qdev_realize函数。

这里再复习强调一下,原本是:

DeviceClass pci_qdev_realize
PCIDeviceClass pci_default_realize

经过VirtioPCIClass类的 初始化 函数virtio_pci_class_init后,是:

realize函数 parent_dc_realize函数
DeviceClass virtio_pci_dc_realize
PCIDeviceClass virtio_pci_realize
VirtioPCIClass virtio_balloon_pci_realize pci_qdev_realize

pci_qdev_realize函数在hw/pci/pci.c中,代码如下:

  1. static void pci_qdev_realize(DeviceState *qdev, Error **errp)
  2. {
  3. PCIDevice *pci_dev = (PCIDevice *)qdev;
  4. PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev);
  5. ObjectClass *klass = OBJECT_CLASS(pc);
  6. Error *local_err = NULL;
  7. bool is_default_rom;
  8. uint16_t class_id;
  9. /*
  10. * capped by systemd (see: udev-builtin-net_id.c)
  11. * as it's the only known user honor it to avoid users
  12. * misconfigure QEMU and then wonder why acpi-index doesn't work
  13. */
  14. if (pci_dev->acpi_index > ONBOARD_INDEX_MAX) {
  15. error_setg(errp, "acpi-index should be less or equal to %u",
  16. ONBOARD_INDEX_MAX);
  17. return;
  18. }
  19. /*
  20. * make sure that acpi-index is unique across all present PCI devices
  21. */
  22. if (pci_dev->acpi_index) {
  23. GSequence *used_indexes = pci_acpi_index_list();
  24. if (g_sequence_lookup(used_indexes,
  25. GINT_TO_POINTER(pci_dev->acpi_index),
  26. g_cmp_uint32, NULL)) {
  27. error_setg(errp, "a PCI device with acpi-index = %" PRIu32
  28. " already exist", pci_dev->acpi_index);
  29. return;
  30. }
  31. g_sequence_insert_sorted(used_indexes,
  32. GINT_TO_POINTER(pci_dev->acpi_index),
  33. g_cmp_uint32, NULL);
  34. }
  35. if (pci_dev->romsize != -1 && !is_power_of_2(pci_dev->romsize)) {
  36. error_setg(errp, "ROM size %u is not a power of two", pci_dev->romsize);
  37. return;
  38. }
  39. /* initialize cap_present for pci_is_express() and pci_config_size(),
  40. * Note that hybrid PCIs are not set automatically and need to manage
  41. * QEMU_PCI_CAP_EXPRESS manually */
  42. if (object_class_dynamic_cast(klass, INTERFACE_PCIE_DEVICE) &&
  43. !object_class_dynamic_cast(klass, INTERFACE_CONVENTIONAL_PCI_DEVICE)) {
  44. pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
  45. }
  46. if (object_class_dynamic_cast(klass, INTERFACE_CXL_DEVICE)) {
  47. pci_dev->cap_present |= QEMU_PCIE_CAP_CXL;
  48. }
  49. pci_dev = do_pci_register_device(pci_dev,
  50. object_get_typename(OBJECT(qdev)),
  51. pci_dev->devfn, errp);
  52. if (pci_dev == NULL)
  53. return;
  54. if (pc->realize) {
  55. pc->realize(pci_dev, &local_err);
  56. if (local_err) {
  57. error_propagate(errp, local_err);
  58. do_pci_unregister_device(pci_dev);
  59. return;
  60. }
  61. }
  62. /*
  63. * A PCIe Downstream Port that do not have ARI Forwarding enabled must
  64. * associate only Device 0 with the device attached to the bus
  65. * representing the Link from the Port (PCIe base spec rev 4.0 ver 0.3,
  66. * sec 7.3.1).
  67. * With ARI, PCI_SLOT() can return non-zero value as the traditional
  68. * 5-bit Device Number and 3-bit Function Number fields in its associated
  69. * Routing IDs, Requester IDs and Completer IDs are interpreted as a
  70. * single 8-bit Function Number. Hence, ignore ARI capable devices.
  71. */
  72. if (pci_is_express(pci_dev) &&
  73. !pcie_find_capability(pci_dev, PCI_EXT_CAP_ID_ARI) &&
  74. pcie_has_upstream_port(pci_dev) &&
  75. PCI_SLOT(pci_dev->devfn)) {
  76. warn_report("PCI: slot %d is not valid for %s,"
  77. " parent device only allows plugging into slot 0.",
  78. PCI_SLOT(pci_dev->devfn), pci_dev->name);
  79. }
  80. if (pci_dev->failover_pair_id) {
  81. if (!pci_bus_is_express(pci_get_bus(pci_dev))) {
  82. error_setg(errp, "failover primary device must be on "
  83. "PCIExpress bus");
  84. pci_qdev_unrealize(DEVICE(pci_dev));
  85. return;
  86. }
  87. class_id = pci_get_word(pci_dev->config + PCI_CLASS_DEVICE);
  88. if (class_id != PCI_CLASS_NETWORK_ETHERNET) {
  89. error_setg(errp, "failover primary device is not an "
  90. "Ethernet device");
  91. pci_qdev_unrealize(DEVICE(pci_dev));
  92. return;
  93. }
  94. if ((pci_dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)
  95. || (PCI_FUNC(pci_dev->devfn) != 0)) {
  96. error_setg(errp, "failover: primary device must be in its own "
  97. "PCI slot");
  98. pci_qdev_unrealize(DEVICE(pci_dev));
  99. return;
  100. }
  101. qdev->allow_unplug_during_migration = true;
  102. }
  103. /* rom loading */
  104. is_default_rom = false;
  105. if (pci_dev->romfile == NULL && pc->romfile != NULL) {
  106. pci_dev->romfile = g_strdup(pc->romfile);
  107. is_default_rom = true;
  108. }
  109. pci_add_option_rom(pci_dev, is_default_rom, &local_err);
  110. if (local_err) {
  111. error_propagate(errp, local_err);
  112. pci_qdev_unrealize(DEVICE(pci_dev));
  113. return;
  114. }
  115. pci_set_power(pci_dev, true);
  116. pci_dev->msi_trigger = pci_msi_trigger;
  117. }

在pci_qdev_realize函数中会调用PCIDeviceClass的realize函数指针所指向的函数,也就是virtio_pci_realize函数。代码片段如下:

  1. PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev);
  2. ……
  3. if (pc->realize) {
  4. pc->realize(pci_dev, &local_err);
  5. if (local_err) {
  6. error_propagate(errp, local_err);
  7. do_pci_unregister_device(pci_dev);
  8. return;
  9. }
  10. }

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

  1. static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
  2. {
  3. VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
  4. VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);
  5. bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) &&
  6. !pci_bus_is_root(pci_get_bus(pci_dev));
  7. if (kvm_enabled() && !kvm_has_many_ioeventfds()) {
  8. proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
  9. }
  10. /* fd-based ioevents can't be synchronized in record/replay */
  11. if (replay_mode != REPLAY_MODE_NONE) {
  12. proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
  13. }
  14. /*
  15. * virtio pci bar layout used by default.
  16. * subclasses can re-arrange things if needed.
  17. *
  18. * region 0 -- virtio legacy io bar
  19. * region 1 -- msi-x bar
  20. * region 2 -- virtio modern io bar (off by default)
  21. * region 4+5 -- virtio modern memory (64bit) bar
  22. *
  23. */
  24. proxy->legacy_io_bar_idx = 0;
  25. proxy->msix_bar_idx = 1;
  26. proxy->modern_io_bar_idx = 2;
  27. proxy->modern_mem_bar_idx = 4;
  28. proxy->common.offset = 0x0;
  29. proxy->common.size = 0x1000;
  30. proxy->common.type = VIRTIO_PCI_CAP_COMMON_CFG;
  31. proxy->isr.offset = 0x1000;
  32. proxy->isr.size = 0x1000;
  33. proxy->isr.type = VIRTIO_PCI_CAP_ISR_CFG;
  34. proxy->device.offset = 0x2000;
  35. proxy->device.size = 0x1000;
  36. proxy->device.type = VIRTIO_PCI_CAP_DEVICE_CFG;
  37. proxy->notify.offset = 0x3000;
  38. proxy->notify.size = virtio_pci_queue_mem_mult(proxy) * VIRTIO_QUEUE_MAX;
  39. proxy->notify.type = VIRTIO_PCI_CAP_NOTIFY_CFG;
  40. proxy->notify_pio.offset = 0x0;
  41. proxy->notify_pio.size = 0x4;
  42. proxy->notify_pio.type = VIRTIO_PCI_CAP_NOTIFY_CFG;
  43. /* subclasses can enforce modern, so do this unconditionally */
  44. memory_region_init(&proxy->modern_bar, OBJECT(proxy), "virtio-pci",
  45. /* PCI BAR regions must be powers of 2 */
  46. pow2ceil(proxy->notify.offset + proxy->notify.size));
  47. if (proxy->disable_legacy == ON_OFF_AUTO_AUTO) {
  48. proxy->disable_legacy = pcie_port ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
  49. }
  50. if (!virtio_pci_modern(proxy) && !virtio_pci_legacy(proxy)) {
  51. error_setg(errp, "device cannot work as neither modern nor legacy mode"
  52. " is enabled");
  53. error_append_hint(errp, "Set either disable-modern or disable-legacy"
  54. " to off\n");
  55. return;
  56. }
  57. if (pcie_port && pci_is_express(pci_dev)) {
  58. int pos;
  59. uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE;
  60. pos = pcie_endpoint_cap_init(pci_dev, 0);
  61. assert(pos > 0);
  62. pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0,
  63. PCI_PM_SIZEOF, errp);
  64. if (pos < 0) {
  65. return;
  66. }
  67. pci_dev->exp.pm_cap = pos;
  68. /*
  69. * Indicates that this function complies with revision 1.2 of the
  70. * PCI Power Management Interface Specification.
  71. */
  72. pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3);
  73. if (proxy->flags & VIRTIO_PCI_FLAG_AER) {
  74. pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset,
  75. PCI_ERR_SIZEOF, NULL);
  76. last_pcie_cap_offset += PCI_ERR_SIZEOF;
  77. }
  78. if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) {
  79. /* Init error enabling flags */
  80. pcie_cap_deverr_init(pci_dev);
  81. }
  82. if (proxy->flags & VIRTIO_PCI_FLAG_INIT_LNKCTL) {
  83. /* Init Link Control Register */
  84. pcie_cap_lnkctl_init(pci_dev);
  85. }
  86. if (proxy->flags & VIRTIO_PCI_FLAG_INIT_PM) {
  87. /* Init Power Management Control Register */
  88. pci_set_word(pci_dev->wmask + pos + PCI_PM_CTRL,
  89. PCI_PM_CTRL_STATE_MASK);
  90. }
  91. if (proxy->flags & VIRTIO_PCI_FLAG_ATS) {
  92. pcie_ats_init(pci_dev, last_pcie_cap_offset,
  93. proxy->flags & VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED);
  94. last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF;
  95. }
  96. if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) {
  97. /* Set Function Level Reset capability bit */
  98. pcie_cap_flr_init(pci_dev);
  99. }
  100. } else {
  101. /*
  102. * make future invocations of pci_is_express() return false
  103. * and pci_config_size() return PCI_CONFIG_SPACE_SIZE.
  104. */
  105. pci_dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS;
  106. }
  107. virtio_pci_bus_new(&proxy->bus, sizeof(proxy->bus), proxy);
  108. if (k->realize) {
  109. k->realize(proxy, errp);
  110. }
  111. }

在这个函数的最后会调用VirtioPCIClass的realize函数指针所指向的函数,也就是virtio_balloon_pci_realize函数。代码片段如下:

  1. static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
  2. {
  3. VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
  4. VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);
  5. ……
  6. if (k->realize) {
  7. k->realize(proxy, errp);
  8. }
  9. }

更多内容请看下回。

举报

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