接前一篇文章:
上一回讲解了 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中,代码如下:
- static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *dc = DEVICE_CLASS(klass);
- VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
- PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
- k->realize = virtio_balloon_pci_realize;
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
- pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON;
- pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
- pcidev_k->class_id = PCI_CLASS_OTHERS;
- }
device_set_realized函数在hw/core/qdev.c中,代码如下:
- static void device_set_realized(Object *obj, bool value, Error **errp)
- {
- DeviceState *dev = DEVICE(obj);
- DeviceClass *dc = DEVICE_GET_CLASS(dev);
- HotplugHandler *hotplug_ctrl;
- BusState *bus;
- NamedClockList *ncl;
- Error *local_err = NULL;
- bool unattached_parent = false;
- static int unattached_count;
-
- if (dev->hotplugged && !dc->hotpluggable) {
- error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj));
- return;
- }
-
- if (value && !dev->realized) {
- if (!check_only_migratable(obj, errp)) {
- goto fail;
- }
-
- if (!obj->parent) {
- gchar *name = g_strdup_printf("device[%d]", unattached_count++);
-
- object_property_add_child(container_get(qdev_get_machine(),
- "/unattached"),
- name, obj);
- unattached_parent = true;
- g_free(name);
- }
-
- hotplug_ctrl = qdev_get_hotplug_handler(dev);
- if (hotplug_ctrl) {
- hotplug_handler_pre_plug(hotplug_ctrl, dev, &local_err);
- if (local_err != NULL) {
- goto fail;
- }
- }
-
- if (dc->realize) {
- dc->realize(dev, &local_err);
- if (local_err != NULL) {
- goto fail;
- }
- }
-
- DEVICE_LISTENER_CALL(realize, Forward, dev);
-
- /*
- * always free/re-initialize here since the value cannot be cleaned up
- * in device_unrealize due to its usage later on in the unplug path
- */
- g_free(dev->canonical_path);
- dev->canonical_path = object_get_canonical_path(OBJECT(dev));
- QLIST_FOREACH(ncl, &dev->clocks, node) {
- if (ncl->alias) {
- continue;
- } else {
- clock_setup_canonical_path(ncl->clock);
- }
- }
-
- if (qdev_get_vmsd(dev)) {
- if (vmstate_register_with_alias_id(VMSTATE_IF(dev),
- VMSTATE_INSTANCE_ID_ANY,
- qdev_get_vmsd(dev), dev,
- dev->instance_id_alias,
- dev->alias_required_for_version,
- &local_err) < 0) {
- goto post_realize_fail;
- }
- }
-
- /*
- * Clear the reset state, in case the object was previously unrealized
- * with a dirty state.
- */
- resettable_state_clear(&dev->reset);
-
- QLIST_FOREACH(bus, &dev->child_bus, sibling) {
- if (!qbus_realize(bus, errp)) {
- goto child_realize_fail;
- }
- }
- if (dev->hotplugged) {
- /*
- * Reset the device, as well as its subtree which, at this point,
- * should be realized too.
- */
- resettable_assert_reset(OBJECT(dev), RESET_TYPE_COLD);
- resettable_change_parent(OBJECT(dev), OBJECT(dev->parent_bus),
- NULL);
- resettable_release_reset(OBJECT(dev), RESET_TYPE_COLD);
- }
- dev->pending_deleted_event = false;
-
- if (hotplug_ctrl) {
- hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
- if (local_err != NULL) {
- goto child_realize_fail;
- }
- }
-
- qatomic_store_release(&dev->realized, value);
-
- } else if (!value && dev->realized) {
-
- /*
- * Change the value so that any concurrent users are aware
- * that the device is going to be unrealized
- *
- * TODO: change .realized property to enum that states
- * each phase of the device realization/unrealization
- */
-
- qatomic_set(&dev->realized, value);
- /*
- * Ensure that concurrent users see this update prior to
- * any other changes done by unrealize.
- */
- smp_wmb();
-
- QLIST_FOREACH(bus, &dev->child_bus, sibling) {
- qbus_unrealize(bus);
- }
- if (qdev_get_vmsd(dev)) {
- vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
- }
- if (dc->unrealize) {
- dc->unrealize(dev);
- }
- dev->pending_deleted_event = true;
- DEVICE_LISTENER_CALL(unrealize, Reverse, dev);
- }
-
- assert(local_err == NULL);
- return;
-
- child_realize_fail:
- QLIST_FOREACH(bus, &dev->child_bus, sibling) {
- qbus_unrealize(bus);
- }
-
- if (qdev_get_vmsd(dev)) {
- vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
- }
-
- post_realize_fail:
- g_free(dev->canonical_path);
- dev->canonical_path = NULL;
- if (dc->unrealize) {
- dc->unrealize(dev);
- }
-
- fail:
- error_propagate(errp, local_err);
- if (unattached_parent) {
- /*
- * Beware, this doesn't just revert
- * object_property_add_child(), it also runs bus_remove()!
- */
- object_unparent(OBJECT(dev));
- unattached_count--;
- }
- }
设置virtio PCI代理设备的realize属性时,device_set_realized函数中会首先调用DeviceClass->realize函数指针所指向的函数,也就是virtio_pci_dc_realize函数。代码片段如下:
- if (dc->realize) {
- dc->realize(dev, &local_err);
- if (local_err != NULL) {
- goto fail;
- }
- }
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中,代码如下:
- static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp)
- {
- VirtioPCIClass *vpciklass = VIRTIO_PCI_GET_CLASS(qdev);
- VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);
- PCIDevice *pci_dev = &proxy->pci_dev;
-
- if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) &&
- virtio_pci_modern(proxy)) {
- pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
- }
-
- vpciklass->parent_dc_realize(qdev, errp);
- }
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中,代码如下:
- static void pci_qdev_realize(DeviceState *qdev, Error **errp)
- {
- PCIDevice *pci_dev = (PCIDevice *)qdev;
- PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev);
- ObjectClass *klass = OBJECT_CLASS(pc);
- Error *local_err = NULL;
- bool is_default_rom;
- uint16_t class_id;
-
- /*
- * capped by systemd (see: udev-builtin-net_id.c)
- * as it's the only known user honor it to avoid users
- * misconfigure QEMU and then wonder why acpi-index doesn't work
- */
- if (pci_dev->acpi_index > ONBOARD_INDEX_MAX) {
- error_setg(errp, "acpi-index should be less or equal to %u",
- ONBOARD_INDEX_MAX);
- return;
- }
-
- /*
- * make sure that acpi-index is unique across all present PCI devices
- */
- if (pci_dev->acpi_index) {
- GSequence *used_indexes = pci_acpi_index_list();
-
- if (g_sequence_lookup(used_indexes,
- GINT_TO_POINTER(pci_dev->acpi_index),
- g_cmp_uint32, NULL)) {
- error_setg(errp, "a PCI device with acpi-index = %" PRIu32
- " already exist", pci_dev->acpi_index);
- return;
- }
- g_sequence_insert_sorted(used_indexes,
- GINT_TO_POINTER(pci_dev->acpi_index),
- g_cmp_uint32, NULL);
- }
-
- if (pci_dev->romsize != -1 && !is_power_of_2(pci_dev->romsize)) {
- error_setg(errp, "ROM size %u is not a power of two", pci_dev->romsize);
- return;
- }
-
- /* initialize cap_present for pci_is_express() and pci_config_size(),
- * Note that hybrid PCIs are not set automatically and need to manage
- * QEMU_PCI_CAP_EXPRESS manually */
- if (object_class_dynamic_cast(klass, INTERFACE_PCIE_DEVICE) &&
- !object_class_dynamic_cast(klass, INTERFACE_CONVENTIONAL_PCI_DEVICE)) {
- pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
- }
-
- if (object_class_dynamic_cast(klass, INTERFACE_CXL_DEVICE)) {
- pci_dev->cap_present |= QEMU_PCIE_CAP_CXL;
- }
-
- pci_dev = do_pci_register_device(pci_dev,
- object_get_typename(OBJECT(qdev)),
- pci_dev->devfn, errp);
- if (pci_dev == NULL)
- return;
-
- if (pc->realize) {
- pc->realize(pci_dev, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- do_pci_unregister_device(pci_dev);
- return;
- }
- }
-
- /*
- * A PCIe Downstream Port that do not have ARI Forwarding enabled must
- * associate only Device 0 with the device attached to the bus
- * representing the Link from the Port (PCIe base spec rev 4.0 ver 0.3,
- * sec 7.3.1).
- * With ARI, PCI_SLOT() can return non-zero value as the traditional
- * 5-bit Device Number and 3-bit Function Number fields in its associated
- * Routing IDs, Requester IDs and Completer IDs are interpreted as a
- * single 8-bit Function Number. Hence, ignore ARI capable devices.
- */
- if (pci_is_express(pci_dev) &&
- !pcie_find_capability(pci_dev, PCI_EXT_CAP_ID_ARI) &&
- pcie_has_upstream_port(pci_dev) &&
- PCI_SLOT(pci_dev->devfn)) {
- warn_report("PCI: slot %d is not valid for %s,"
- " parent device only allows plugging into slot 0.",
- PCI_SLOT(pci_dev->devfn), pci_dev->name);
- }
-
- if (pci_dev->failover_pair_id) {
- if (!pci_bus_is_express(pci_get_bus(pci_dev))) {
- error_setg(errp, "failover primary device must be on "
- "PCIExpress bus");
- pci_qdev_unrealize(DEVICE(pci_dev));
- return;
- }
- class_id = pci_get_word(pci_dev->config + PCI_CLASS_DEVICE);
- if (class_id != PCI_CLASS_NETWORK_ETHERNET) {
- error_setg(errp, "failover primary device is not an "
- "Ethernet device");
- pci_qdev_unrealize(DEVICE(pci_dev));
- return;
- }
- if ((pci_dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)
- || (PCI_FUNC(pci_dev->devfn) != 0)) {
- error_setg(errp, "failover: primary device must be in its own "
- "PCI slot");
- pci_qdev_unrealize(DEVICE(pci_dev));
- return;
- }
- qdev->allow_unplug_during_migration = true;
- }
-
- /* rom loading */
- is_default_rom = false;
- if (pci_dev->romfile == NULL && pc->romfile != NULL) {
- pci_dev->romfile = g_strdup(pc->romfile);
- is_default_rom = true;
- }
-
- pci_add_option_rom(pci_dev, is_default_rom, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- pci_qdev_unrealize(DEVICE(pci_dev));
- return;
- }
-
- pci_set_power(pci_dev, true);
-
- pci_dev->msi_trigger = pci_msi_trigger;
- }
在pci_qdev_realize函数中会调用PCIDeviceClass的realize函数指针所指向的函数,也就是virtio_pci_realize函数。代码片段如下:
- PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev);
- ……
- if (pc->realize) {
- pc->realize(pci_dev, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- do_pci_unregister_device(pci_dev);
- return;
- }
- }
virtio_pci_realize函数在hw/virtio/virtio-pci.c中,代码如下:
- static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
- {
- VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
- VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);
- bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) &&
- !pci_bus_is_root(pci_get_bus(pci_dev));
-
- if (kvm_enabled() && !kvm_has_many_ioeventfds()) {
- proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
- }
-
- /* fd-based ioevents can't be synchronized in record/replay */
- if (replay_mode != REPLAY_MODE_NONE) {
- proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
- }
-
- /*
- * virtio pci bar layout used by default.
- * subclasses can re-arrange things if needed.
- *
- * region 0 -- virtio legacy io bar
- * region 1 -- msi-x bar
- * region 2 -- virtio modern io bar (off by default)
- * region 4+5 -- virtio modern memory (64bit) bar
- *
- */
- proxy->legacy_io_bar_idx = 0;
- proxy->msix_bar_idx = 1;
- proxy->modern_io_bar_idx = 2;
- proxy->modern_mem_bar_idx = 4;
-
- proxy->common.offset = 0x0;
- proxy->common.size = 0x1000;
- proxy->common.type = VIRTIO_PCI_CAP_COMMON_CFG;
-
- proxy->isr.offset = 0x1000;
- proxy->isr.size = 0x1000;
- proxy->isr.type = VIRTIO_PCI_CAP_ISR_CFG;
-
- proxy->device.offset = 0x2000;
- proxy->device.size = 0x1000;
- proxy->device.type = VIRTIO_PCI_CAP_DEVICE_CFG;
-
- proxy->notify.offset = 0x3000;
- proxy->notify.size = virtio_pci_queue_mem_mult(proxy) * VIRTIO_QUEUE_MAX;
- proxy->notify.type = VIRTIO_PCI_CAP_NOTIFY_CFG;
-
- proxy->notify_pio.offset = 0x0;
- proxy->notify_pio.size = 0x4;
- proxy->notify_pio.type = VIRTIO_PCI_CAP_NOTIFY_CFG;
-
- /* subclasses can enforce modern, so do this unconditionally */
- memory_region_init(&proxy->modern_bar, OBJECT(proxy), "virtio-pci",
- /* PCI BAR regions must be powers of 2 */
- pow2ceil(proxy->notify.offset + proxy->notify.size));
-
- if (proxy->disable_legacy == ON_OFF_AUTO_AUTO) {
- proxy->disable_legacy = pcie_port ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
- }
-
- if (!virtio_pci_modern(proxy) && !virtio_pci_legacy(proxy)) {
- error_setg(errp, "device cannot work as neither modern nor legacy mode"
- " is enabled");
- error_append_hint(errp, "Set either disable-modern or disable-legacy"
- " to off\n");
- return;
- }
-
- if (pcie_port && pci_is_express(pci_dev)) {
- int pos;
- uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE;
-
- pos = pcie_endpoint_cap_init(pci_dev, 0);
- assert(pos > 0);
-
- pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0,
- PCI_PM_SIZEOF, errp);
- if (pos < 0) {
- return;
- }
-
- pci_dev->exp.pm_cap = pos;
-
- /*
- * Indicates that this function complies with revision 1.2 of the
- * PCI Power Management Interface Specification.
- */
- pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3);
-
- if (proxy->flags & VIRTIO_PCI_FLAG_AER) {
- pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset,
- PCI_ERR_SIZEOF, NULL);
- last_pcie_cap_offset += PCI_ERR_SIZEOF;
- }
-
- if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) {
- /* Init error enabling flags */
- pcie_cap_deverr_init(pci_dev);
- }
-
- if (proxy->flags & VIRTIO_PCI_FLAG_INIT_LNKCTL) {
- /* Init Link Control Register */
- pcie_cap_lnkctl_init(pci_dev);
- }
-
- if (proxy->flags & VIRTIO_PCI_FLAG_INIT_PM) {
- /* Init Power Management Control Register */
- pci_set_word(pci_dev->wmask + pos + PCI_PM_CTRL,
- PCI_PM_CTRL_STATE_MASK);
- }
-
- if (proxy->flags & VIRTIO_PCI_FLAG_ATS) {
- pcie_ats_init(pci_dev, last_pcie_cap_offset,
- proxy->flags & VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED);
- last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF;
- }
-
- if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) {
- /* Set Function Level Reset capability bit */
- pcie_cap_flr_init(pci_dev);
- }
- } else {
- /*
- * make future invocations of pci_is_express() return false
- * and pci_config_size() return PCI_CONFIG_SPACE_SIZE.
- */
- pci_dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS;
- }
-
- virtio_pci_bus_new(&proxy->bus, sizeof(proxy->bus), proxy);
- if (k->realize) {
- k->realize(proxy, errp);
- }
- }
在这个函数的最后会调用VirtioPCIClass的realize函数指针所指向的函数,也就是virtio_balloon_pci_realize函数。代码片段如下:
- static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
- {
- VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
- VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);
- ……
-
- if (k->realize) {
- k->realize(proxy, errp);
- }
- }
更多内容请看下回。