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

88 篇文章 19 订阅
本文深入探讨QEMU中virtio设备的初始化,特别是在设备具现化过程中的工作细节。通过分析`device_set_realized`函数及其调用的设备类`realize`函数,揭示virtio balloon设备在QEMU启动时如何被实例化和激活。文章还介绍了设备类的继承链关系,为理解virtio在QEMU中的实现提供了关键洞察。
摘要由CSDN通过智能技术生成

接前一篇文章:

上一回以 virtio balloon设备为例开始对于virtio设备的初始化过程进行分析,讲解了“virtio_balloon_pci_instance_init() -> virtio_instance_init_common() -> object_initialize_child_with_props() -> object_initialize_child_with_propsv() -> object_initialize()”的过程。并指出从这一过程中可以看出来,vitrtio设备在实例创建过程中并没有做很多事情,大部分的工作都是在设备的具现化过程中做的。本回就来深入解析这一过程。

在讲解virtio balloon设备的具现化之前,先来回顾一下设备具现化所调用的函数。QEMU在main函数中会对所有的“-device”参数进行具现化(前文书讲过,实际是在main函数中的qemu_init函数中进行的),设备的具现化函数都会调用device_set_realized函数,在该函数中会调用设备类的realize函数。相关代码如下:

  • -device选项

与-device选项相对应的代码在softmmu/qdev-monitor.c中,如下所示:

  1. QemuOptsList qemu_device_opts = {
  2. .name = "device",
  3. .implied_opt_name = "driver",
  4. .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head),
  5. .desc = {
  6. /*
  7. * no elements => accept any
  8. * sanity checking will happen later
  9. * when setting device properties
  10. */
  11. { /* end of list */ }
  12. },
  13. };
  • device_set_realized函数

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. }
  • 设备类的realize函数

相关代码片段:

  1. if (dc->realize) {
  2. dc->realize(dev, &local_err);
  3. if (local_err != NULL) {
  4. goto fail;
  5. }
  6. }

设备类的realize函数最开始调用的是DeviceClass的realize函数,这个回调的默认函数是xxx_device_realize()。当然,如果是继承自Device的类,也可以重写这个函数。如PCIDeviceClass类就在其类 初始化 函数pci_device_class_init()中将DeviceClass->realize重写为了pci_qdev_realize。代码在hw/pci/pci.c中,如下所示:

  1. static void pci_device_class_init(ObjectClass *klass, void *data)
  2. {
  3. DeviceClass *k = DEVICE_CLASS(klass);
  4. k->realize = pci_qdev_realize;
  5. k->unrealize = pci_qdev_unrealize;
  6. k->bus_type = TYPE_PCI_BUS;
  7. device_class_set_props(k, pci_props);
  8. }

对于PCIDeviceClass本身来说,其PCIDeviceClass->realize可以设置为pci_default_realize函数(此处只是为了更好地说明问题,新版QEMU中已无此函数)。后边继承PCIDeviceClass的类可以在自己的类初始化函数中设置其自己的realize函数。

virtio设备类的继承链关系为“DeviceClass -> PCIDeviceClass ->VirtioPCIClass”。

下一回将分析在VirtioPCIClass类还没有初始化时的realize函数。

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

举报

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