diff --git a/backends/hostmem.c b/backends/hostmem.c index 87b19d2111..04baf479a1 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -88,7 +88,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, value = find_first_bit(backend->host_nodes, MAX_NODES); if (value == MAX_NODES) { - return; + goto ret; } *node = g_malloc0(sizeof(**node)); @@ -106,6 +106,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, node = &(*node)->next; } while (true); +ret: visit_type_uint16List(v, name, &host_nodes, errp); } diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index a0a43fe6b5..7a90c8f82d 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -126,6 +126,7 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, dev = DEVICE(cdev->cpu); hotplug_ctrl = qdev_get_hotplug_handler(dev); hotplug_handler_unplug(hotplug_ctrl, dev, NULL); + object_unparent(OBJECT(dev)); } break; case ACPI_CPU_CMD_OFFSET_WR: diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 921cad2c5e..297812d5f7 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -189,6 +189,7 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, error_free(local_err); break; } + object_unparent(OBJECT(dev)); trace_mhp_acpi_pc_dimm_deleted(mem_st->selector); } break; diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 9429181323..88e4ae1bcd 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -174,6 +174,7 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slo if (!acpi_pcihp_pc_no_hotplug(s, dev)) { hotplug_ctrl = qdev_get_hotplug_handler(qdev); hotplug_handler_unplug(hotplug_ctrl, qdev, &error_abort); + object_unparent(OBJECT(qdev)); } } } @@ -269,7 +270,7 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, diff --git a/hw/core/qdev.c b/hw/core/qdev.c index d59071b8ed..512ce7ca7a 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -59,6 +59,8 @@ static void bus_remove_child(BusState *bus, DeviceState *child) snprintf(name, sizeof(name), "child[%d]", kid->index); QTAILQ_REMOVE(&bus->children, kid, sibling); + bus->num_children--; + /* This gives back ownership of kid->child back to us. */ object_property_del(OBJECT(bus), name, NULL); object_unref(OBJECT(kid->child)); @@ -73,6 +75,7 @@ static void bus_add_child(BusState *bus, DeviceState *child) char name[32]; BusChild *kid = g_malloc0(sizeof(*kid)); + bus->num_children++; kid->index = bus->max_index++; kid->child = child; object_ref(OBJECT(kid->child)); @@ -233,14 +236,20 @@ HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) return NULL; } +HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) +{ + if (dev->parent_bus) { + return dev->parent_bus->hotplug_handler; + } + return NULL; +} + HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) { - HotplugHandler *hotplug_ctrl; + HotplugHandler *hotplug_ctrl = qdev_get_machine_hotplug_handler(dev); - if (dev->parent_bus && dev->parent_bus->hotplug_handler) { - hotplug_ctrl = dev->parent_bus->hotplug_handler; - } else { - hotplug_ctrl = qdev_get_machine_hotplug_handler(dev); + if (hotplug_ctrl == NULL && dev->parent_bus) { + hotplug_ctrl = qdev_get_bus_hotplug_handler(dev); } return hotplug_ctrl; } @@ -286,8 +295,7 @@ void qbus_reset_all_fn(void *opaque) void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - /* just zap it */ - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } /* diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 578f772e7c..42128183e9 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -2154,8 +2154,7 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev, } pc_dimm_unplug(PC_DIMM(dev), MACHINE(pcms)); - object_unparent(OBJECT(dev)); - + object_property_set_bool(OBJECT(dev), false, "realized", NULL); out: error_propagate(errp, local_err); } @@ -2261,7 +2260,7 @@ static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev, found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL); found_cpu->cpu = NULL; - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); /* decrement the number of CPUs */ pcms->boot_cpus--; diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c9fc2fbe19..35451c1e99 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -428,7 +428,8 @@ PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, void pci_root_bus_cleanup(PCIBus *bus) { pci_bus_uninit(bus); - object_unparent(OBJECT(bus)); + /* the caller of the unplug hotplug handler will delete this device */ + object_property_set_bool(OBJECT(bus), false, "realized", NULL); } void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 3618d6ab2e..640f678773 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -450,7 +450,7 @@ void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque) @@ -458,6 +458,7 @@ static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque) HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(dev)); hotplug_handler_unplug(hotplug_ctrl, DEVICE(dev), &error_abort); + object_unparent(OBJECT(dev)); } void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index 52ccdc5ae3..49bbb841bd 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -249,6 +249,7 @@ static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(affected_dev)); hotplug_handler_unplug(hotplug_ctrl, DEVICE(affected_dev), &error_abort); + object_unparent(OBJECT(affected_dev)); } } } @@ -546,7 +547,7 @@ void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev, diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d7850ada7d..9e01226e18 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3595,6 +3595,7 @@ void spapr_lmb_release(DeviceState *dev) * unplug handler chain. This can never fail. */ hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); + object_unparent(OBJECT(dev)); } static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) @@ -3603,7 +3604,7 @@ static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); pc_dimm_unplug(PC_DIMM(dev), MACHINE(hotplug_dev)); - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); spapr_pending_dimm_unplugs_remove(spapr, ds); } @@ -3667,6 +3668,7 @@ void spapr_core_release(DeviceState *dev) /* Call the unplug handler chain. This can never fail. */ hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); + object_unparent(OBJECT(dev)); } static void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) @@ -3689,7 +3691,7 @@ static void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) assert(core_slot); core_slot->cpu = NULL; - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } static @@ -3940,11 +3942,12 @@ void spapr_phb_release(DeviceState *dev) HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); + object_unparent(OBJECT(dev)); } static void spapr_phb_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) { - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } static void spapr_phb_unplug_request(HotplugHandler *hotplug_dev, diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 06a5ffd281..69059c36eb 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1379,6 +1379,7 @@ void spapr_phb_remove_pci_device_cb(DeviceState *dev) HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); + object_unparent(OBJECT(dev)); } static sPAPRDRConnector *spapr_phb_get_pci_func_drc(sPAPRPHBState *phb, @@ -1506,7 +1507,7 @@ static void spapr_pci_unplug(HotplugHandler *plug_handler, * an 'idle' state, as the device cleanup code expects. */ pci_device_reset(PCI_DEVICE(plugged_dev)); - object_unparent(OBJECT(plugged_dev)); + object_property_set_bool(OBJECT(plugged_dev), false, "realized", NULL); } static void spapr_pci_unplug_request(HotplugHandler *plug_handler, diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 7573c40bad..e04d51b191 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -51,7 +51,7 @@ static void ccw_device_unplug(HotplugHandler *hotplug_dev, css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } static void virtual_css_bus_reset(BusState *qbus) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 5998942b4c..2d0a28d544 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -154,14 +154,17 @@ static void s390_pci_perform_unplug(S390PCIBusDevice *pbdev) /* Unplug the PCI device */ if (pbdev->pdev) { - hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(pbdev->pdev)); - hotplug_handler_unplug(hotplug_ctrl, DEVICE(pbdev->pdev), - &error_abort); + DeviceState *pdev = DEVICE(pbdev->pdev); + + hotplug_ctrl = qdev_get_hotplug_handler(pdev); + hotplug_handler_unplug(hotplug_ctrl, pdev, &error_abort); + object_unparent(OBJECT(pdev)); } /* Unplug the zPCI device */ hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(pbdev)); hotplug_handler_unplug(hotplug_ctrl, DEVICE(pbdev), &error_abort); + object_unparent(OBJECT(pbdev)); } void s390_pci_sclp_deconfigure(SCCB *sccb) @@ -994,7 +997,7 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, pbdev->fh, pbdev->fid); bus = pci_get_bus(pci_dev); devfn = pci_dev->devfn; - object_unparent(OBJECT(pci_dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); s390_pci_msix_free(pbdev); s390_pci_iommu_free(s, bus, devfn); @@ -1005,7 +1008,7 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, pbdev->fid = 0; QTAILQ_REMOVE(&s->zpci_devs, pbdev, link); g_hash_table_remove(s->zpci_table, &pbdev->idx); - object_unparent(OBJECT(pbdev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } } diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index e70a4bfa49..17f09aac72 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -206,6 +206,7 @@ struct BusState { HotplugHandler *hotplug_handler; int max_index; bool realized; + int num_children; QTAILQ_HEAD(, BusChild) children; QLIST_ENTRY(BusState) sibling; }; @@ -280,7 +281,19 @@ DeviceState *qdev_try_create(BusState *bus, const char *name); void qdev_init_nofail(DeviceState *dev); void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version); +HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); +/** + * qdev_get_hotplug_handler: Get handler responsible for device wiring + * + * Find HOTPLUG_HANDLER for @dev that provides [pre|un]plug callbacks for it. + * + * Note: in case @dev has a parent bus, it will be returned as handler unless + * machine handler overrides it. + * + * Returns: pointer to object that implements TYPE_HOTPLUG_HANDLER interface + * or NULL if there aren't any. + */ HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); void qdev_unplug(DeviceState *dev, Error **errp); void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, diff --git a/qdev-monitor.c b/qdev-monitor.c index 07147c63bf..d4320986a2 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -414,7 +414,7 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem) static inline bool qbus_is_full(BusState *bus) { BusClass *bus_class = BUS_GET_CLASS(bus); - return bus_class->max_dev && bus->max_index >= bus_class->max_dev; + return bus_class->max_dev && bus->num_children >= bus_class->max_dev; } /* @@ -862,6 +862,7 @@ void qdev_unplug(DeviceState *dev, Error **errp) DeviceClass *dc = DEVICE_GET_CLASS(dev); HotplugHandler *hotplug_ctrl; HotplugHandlerClass *hdc; + Error *local_err = NULL; if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); @@ -890,10 +891,14 @@ void qdev_unplug(DeviceState *dev, Error **errp) * otherwise just remove it synchronously */ hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl); if (hdc->unplug_request) { - hotplug_handler_unplug_request(hotplug_ctrl, dev, errp); + hotplug_handler_unplug_request(hotplug_ctrl, dev, &local_err); } else { - hotplug_handler_unplug(hotplug_ctrl, dev, errp); + hotplug_handler_unplug(hotplug_ctrl, dev, &local_err); + if (!local_err) { + object_unparent(OBJECT(dev)); + } } + error_propagate(errp, local_err); } void qmp_device_del(const char *id, Error **errp)