Skip to content

Commit

Permalink
qdev: Unrealize must not fail
Browse files Browse the repository at this point in the history
Devices may have component devices and buses.

Device realization may fail.  Realization is recursive: a device's
realize() method realizes its components, and device_set_realized()
realizes its buses (which should in turn realize the devices on that
bus, except bus_set_realized() doesn't implement that, yet).

When realization of a component or bus fails, we need to roll back:
unrealize everything we realized so far.  If any of these unrealizes
failed, the device would be left in an inconsistent state.  Must not
happen.

device_set_realized() lets it happen: it ignores errors in the roll
back code starting at label child_realize_fail.

Since realization is recursive, unrealization must be recursive, too.
But how could a partly failed unrealize be rolled back?  We'd have to
re-realize, which can fail.  This design is fundamentally broken.

device_set_realized() does not roll back at all.  Instead, it keeps
unrealizing, ignoring further errors.

It can screw up even for a device with no buses: if the lone
dc->unrealize() fails, it still unregisters vmstate, and calls
listeners' unrealize() callback.

bus_set_realized() does not roll back either.  Instead, it stops
unrealizing.

Fortunately, no unrealize method can fail, as we'll see below.

To fix the design error, drop parameter @errp from all the unrealize
methods.

Any unrealize method that uses @errp now needs an update.  This leads
us to unrealize() methods that can fail.  Merely passing it to another
unrealize method cannot cause failure, though.  Here are the ones that
do other things with @errp:

* virtio_serial_device_unrealize()

  Fails when qbus_set_hotplug_handler() fails, but still does all the
  other work.  On failure, the device would stay realized with its
  resources completely gone.  Oops.  Can't happen, because
  qbus_set_hotplug_handler() can't actually fail here.  Pass
  &error_abort to qbus_set_hotplug_handler() instead.

* hw/ppc/spapr_drc.c's unrealize()

  Fails when object_property_del() fails, but all the other work is
  already done.  On failure, the device would stay realized with its
  vmstate registration gone.  Oops.  Can't happen, because
  object_property_del() can't actually fail here.  Pass &error_abort
  to object_property_del() instead.

* spapr_phb_unrealize()

  Fails and bails out when remove_drcs() fails, but other work is
  already done.  On failure, the device would stay realized with some
  of its resources gone.  Oops.  remove_drcs() fails only when
  chassis_from_bus()'s object_property_get_uint() fails, and it can't
  here.  Pass &error_abort to remove_drcs() instead.

Therefore, no unrealize method can fail before this patch.

device_set_realized()'s recursive unrealization via bus uses
object_property_set_bool().  Can't drop @errp there, so pass
&error_abort.

We similarly unrealize with object_property_set_bool() elsewhere,
always ignoring errors.  Pass &error_abort instead.

Several unrealize methods no longer handle errors from other unrealize
methods: virtio_9p_device_unrealize(),
virtio_input_device_unrealize(), scsi_qdev_unrealize(), ...
Much of the deleted error handling looks wrong anyway.

One unrealize methods no longer ignore such errors:
usb_ehci_pci_exit().

Several realize methods no longer ignore errors when rolling back:
v9fs_device_realize_common(), pci_qdev_unrealize(),
spapr_phb_realize(), usb_qdev_realize(), vfio_ccw_realize(),
virtio_device_realize().

Signed-off-by: Markus Armbruster <[email protected]>
Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Reviewed-by: Paolo Bonzini <[email protected]>
Message-Id: <[email protected]>
  • Loading branch information
Markus Armbruster committed May 15, 2020
1 parent 40c2281 commit b69c3c2
Show file tree
Hide file tree
Showing 93 changed files with 158 additions and 214 deletions.
4 changes: 2 additions & 2 deletions hw/9pfs/9p.c
Original file line number Diff line number Diff line change
Expand Up @@ -4124,13 +4124,13 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t,
rc = 0;
out:
if (rc) {
v9fs_device_unrealize_common(s, NULL);
v9fs_device_unrealize_common(s);
}
v9fs_path_free(&path);
return rc;
}

void v9fs_device_unrealize_common(V9fsState *s, Error **errp)
void v9fs_device_unrealize_common(V9fsState *s)
{
if (s->ops && s->ops->cleanup) {
s->ops->cleanup(&s->ctx);
Expand Down
2 changes: 1 addition & 1 deletion hw/9pfs/9p.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
const char *name, V9fsPath *path);
int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t,
Error **errp);
void v9fs_device_unrealize_common(V9fsState *s, Error **errp);
void v9fs_device_unrealize_common(V9fsState *s);

V9fsPDU *pdu_alloc(V9fsState *s);
void pdu_free(V9fsPDU *pdu);
Expand Down
4 changes: 2 additions & 2 deletions hw/9pfs/virtio-9p-device.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,15 @@ static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output);
}

static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp)
static void virtio_9p_device_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
V9fsVirtioState *v = VIRTIO_9P(dev);
V9fsState *s = &v->state;

virtio_delete_queue(v->vq);
virtio_cleanup(vdev);
v9fs_device_unrealize_common(s, errp);
v9fs_device_unrealize_common(s);
}

/* virtio-9p device */
Expand Down
2 changes: 1 addition & 1 deletion hw/acpi/pcihp.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
{
trace_acpi_pci_unplug(PCI_SLOT(PCI_DEVICE(dev)->devfn),
acpi_pcihp_get_bsel(pci_get_bus(PCI_DEVICE(dev))));
object_property_set_bool(OBJECT(dev), false, "realized", NULL);
object_property_set_bool(OBJECT(dev), false, "realized", &error_abort);
}

void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev,
Expand Down
2 changes: 1 addition & 1 deletion hw/audio/intel-hda.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ static void hda_codec_dev_realize(DeviceState *qdev, Error **errp)
}
}

static void hda_codec_dev_unrealize(DeviceState *qdev, Error **errp)
static void hda_codec_dev_unrealize(DeviceState *qdev)
{
HDACodecDevice *dev = HDA_CODEC_DEVICE(qdev);
HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev);
Expand Down
2 changes: 1 addition & 1 deletion hw/block/pflash_cfi02.c
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ static Property pflash_cfi02_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};

static void pflash_cfi02_unrealize(DeviceState *dev, Error **errp)
static void pflash_cfi02_unrealize(DeviceState *dev)
{
PFlashCFI02 *pfl = PFLASH_CFI02(dev);
timer_del(&pfl->timer);
Expand Down
2 changes: 1 addition & 1 deletion hw/block/vhost-user-blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
vhost_user_cleanup(&s->vhost_user);
}

static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp)
static void vhost_user_blk_device_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(dev);
Expand Down
2 changes: 1 addition & 1 deletion hw/block/virtio-blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -1223,7 +1223,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
conf->conf.lsecs);
}

static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp)
static void virtio_blk_device_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOBlock *s = VIRTIO_BLK(dev);
Expand Down
8 changes: 4 additions & 4 deletions hw/block/xen-block.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ static void xen_block_connect(XenDevice *xendev, Error **errp)
g_free(ring_ref);
}

static void xen_block_unrealize(XenDevice *xendev, Error **errp)
static void xen_block_unrealize(XenDevice *xendev)
{
XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev);
XenBlockDeviceClass *blockdev_class =
Expand All @@ -157,7 +157,7 @@ static void xen_block_unrealize(XenDevice *xendev, Error **errp)
blockdev->dataplane = NULL;

if (blockdev_class->unrealize) {
blockdev_class->unrealize(blockdev, errp);
blockdev_class->unrealize(blockdev);
}
}

Expand Down Expand Up @@ -567,7 +567,7 @@ static const TypeInfo xen_block_type_info = {
.class_init = xen_block_class_init,
};

static void xen_disk_unrealize(XenBlockDevice *blockdev, Error **errp)
static void xen_disk_unrealize(XenBlockDevice *blockdev)
{
trace_xen_disk_unrealize();
}
Expand Down Expand Up @@ -606,7 +606,7 @@ static const TypeInfo xen_disk_type_info = {
.class_init = xen_disk_class_init,
};

static void xen_cdrom_unrealize(XenBlockDevice *blockdev, Error **errp)
static void xen_cdrom_unrealize(XenBlockDevice *blockdev)
{
trace_xen_cdrom_unrealize();
}
Expand Down
2 changes: 1 addition & 1 deletion hw/char/serial-pci-multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static void multi_serial_pci_exit(PCIDevice *dev)

for (i = 0; i < pci->ports; i++) {
s = pci->state + i;
object_property_set_bool(OBJECT(s), false, "realized", NULL);
object_property_set_bool(OBJECT(s), false, "realized", &error_abort);
memory_region_del_subregion(&pci->iobar, &s->io);
g_free(pci->name[i]);
}
Expand Down
2 changes: 1 addition & 1 deletion hw/char/serial-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ static void serial_pci_exit(PCIDevice *dev)
PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
SerialState *s = &pci->state;

object_property_set_bool(OBJECT(s), false, "realized", NULL);
object_property_set_bool(OBJECT(s), false, "realized", &error_abort);
qemu_free_irq(s->irq);
}

Expand Down
2 changes: 1 addition & 1 deletion hw/char/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,7 @@ static void serial_realize(DeviceState *dev, Error **errp)
serial_reset(s);
}

static void serial_unrealize(DeviceState *dev, Error **errp)
static void serial_unrealize(DeviceState *dev)
{
SerialState *s = SERIAL(dev);

Expand Down
2 changes: 1 addition & 1 deletion hw/char/virtio-console.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp)
}
}

static void virtconsole_unrealize(DeviceState *dev, Error **errp)
static void virtconsole_unrealize(DeviceState *dev)
{
VirtConsole *vcon = VIRTIO_CONSOLE(dev);

Expand Down
8 changes: 4 additions & 4 deletions hw/char/virtio-serial-bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ static void virtser_port_device_plug(HotplugHandler *hotplug_dev,
virtio_notify_config(VIRTIO_DEVICE(hotplug_dev));
}

static void virtser_port_device_unrealize(DeviceState *dev, Error **errp)
static void virtser_port_device_unrealize(DeviceState *dev)
{
VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(dev);
Expand All @@ -1022,7 +1022,7 @@ static void virtser_port_device_unrealize(DeviceState *dev, Error **errp)
QTAILQ_REMOVE(&vser->ports, port, next);

if (vsc->unrealize) {
vsc->unrealize(dev, errp);
vsc->unrealize(dev);
}
}

Expand Down Expand Up @@ -1122,7 +1122,7 @@ static const TypeInfo virtio_serial_port_type_info = {
.class_init = virtio_serial_port_class_init,
};

static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp)
static void virtio_serial_device_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOSerial *vser = VIRTIO_SERIAL(dev);
Expand All @@ -1147,7 +1147,7 @@ static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp)
g_free(vser->post_load);
}

qbus_set_hotplug_handler(BUS(&vser->bus), NULL, errp);
qbus_set_hotplug_handler(BUS(&vser->bus), NULL, &error_abort);

virtio_cleanup(vdev);
}
Expand Down
17 changes: 4 additions & 13 deletions hw/core/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,33 +176,24 @@ static void bus_set_realized(Object *obj, bool value, Error **errp)
BusState *bus = BUS(obj);
BusClass *bc = BUS_GET_CLASS(bus);
BusChild *kid;
Error *local_err = NULL;

if (value && !bus->realized) {
if (bc->realize) {
bc->realize(bus, &local_err);
bc->realize(bus, errp);
}

/* TODO: recursive realization */
} else if (!value && bus->realized) {
QTAILQ_FOREACH(kid, &bus->children, sibling) {
DeviceState *dev = kid->child;
object_property_set_bool(OBJECT(dev), false, "realized",
&local_err);
if (local_err != NULL) {
break;
}
&error_abort);
}
if (bc->unrealize && local_err == NULL) {
bc->unrealize(bus, &local_err);
if (bc->unrealize) {
bc->unrealize(bus);
}
}

if (local_err != NULL) {
error_propagate(errp, local_err);
return;
}

bus->realized = value;
}

Expand Down
2 changes: 1 addition & 1 deletion hw/core/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp)
trace_init_vcpu(cpu);
}

static void cpu_common_unrealizefn(DeviceState *dev, Error **errp)
static void cpu_common_unrealizefn(DeviceState *dev)
{
CPUState *cpu = CPU(dev);
/* NOTE: latest generic point before the cpu is fully unrealized */
Expand Down
2 changes: 1 addition & 1 deletion hw/core/generic-loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ static void generic_loader_realize(DeviceState *dev, Error **errp)
}
}

static void generic_loader_unrealize(DeviceState *dev, Error **errp)
static void generic_loader_unrealize(DeviceState *dev)
{
qemu_unregister_reset(generic_loader_reset, dev);
}
Expand Down
17 changes: 6 additions & 11 deletions hw/core/qdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb,
void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
object_property_set_bool(OBJECT(dev), false, "realized", NULL);
object_property_set_bool(OBJECT(dev), false, "realized", &error_abort);
}

/*
Expand Down Expand Up @@ -945,23 +945,18 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
}

} else if (!value && dev->realized) {
/* We want local_err to track only the first error */
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
object_property_set_bool(OBJECT(bus), false, "realized",
local_err ? NULL : &local_err);
&error_abort);
}
if (qdev_get_vmsd(dev)) {
vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
}
if (dc->unrealize) {
dc->unrealize(dev, local_err ? NULL : &local_err);
dc->unrealize(dev);
}
dev->pending_deleted_event = true;
DEVICE_LISTENER_CALL(unrealize, Reverse, dev);

if (local_err != NULL) {
goto fail;
}
}

assert(local_err == NULL);
Expand All @@ -971,7 +966,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
child_realize_fail:
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
object_property_set_bool(OBJECT(bus), false, "realized",
NULL);
&error_abort);
}

if (qdev_get_vmsd(dev)) {
Expand All @@ -982,7 +977,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
g_free(dev->canonical_path);
dev->canonical_path = NULL;
if (dc->unrealize) {
dc->unrealize(dev, NULL);
dc->unrealize(dev);
}

fail:
Expand Down Expand Up @@ -1083,7 +1078,7 @@ static void device_unparent(Object *obj)
BusState *bus;

if (dev->realized) {
object_property_set_bool(obj, false, "realized", NULL);
object_property_set_bool(obj, false, "realized", &error_abort);
}
while (dev->num_child_bus) {
bus = QLIST_FIRST(&dev->child_bus);
Expand Down
2 changes: 1 addition & 1 deletion hw/display/virtio-gpu-base.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ virtio_gpu_base_set_features(VirtIODevice *vdev, uint64_t features)
}

static void
virtio_gpu_base_device_unrealize(DeviceState *qdev, Error **errp)
virtio_gpu_base_device_unrealize(DeviceState *qdev)
{
VirtIOGPUBase *g = VIRTIO_GPU_BASE(qdev);

Expand Down
2 changes: 1 addition & 1 deletion hw/dma/rc4030.c
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ static void rc4030_realize(DeviceState *dev, Error **errp)
address_space_init(&s->dma_as, MEMORY_REGION(&s->dma_mr), "rc4030-dma");
}

static void rc4030_unrealize(DeviceState *dev, Error **errp)
static void rc4030_unrealize(DeviceState *dev)
{
rc4030State *s = RC4030(dev);

Expand Down
2 changes: 1 addition & 1 deletion hw/i386/kvm/apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ static void kvm_apic_realize(DeviceState *dev, Error **errp)
}
}

static void kvm_apic_unrealize(DeviceState *dev, Error **errp)
static void kvm_apic_unrealize(DeviceState *dev)
{
}

Expand Down
4 changes: 2 additions & 2 deletions hw/i386/pc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1385,7 +1385,7 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev,
}

pc_dimm_unplug(PC_DIMM(dev), MACHINE(pcms));
object_property_set_bool(OBJECT(dev), false, "realized", NULL);
object_property_set_bool(OBJECT(dev), false, "realized", &error_abort);
out:
error_propagate(errp, local_err);
}
Expand Down Expand Up @@ -1493,7 +1493,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_property_set_bool(OBJECT(dev), false, "realized", NULL);
object_property_set_bool(OBJECT(dev), false, "realized", &error_abort);

/* decrement the number of CPUs */
x86ms->boot_cpus--;
Expand Down
4 changes: 2 additions & 2 deletions hw/ide/qdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
/* --------------------------------- */

static char *idebus_get_fw_dev_path(DeviceState *dev);
static void idebus_unrealize(BusState *qdev, Error **errp);
static void idebus_unrealize(BusState *qdev);

static Property ide_props[] = {
DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1),
Expand All @@ -51,7 +51,7 @@ static void ide_bus_class_init(ObjectClass *klass, void *data)
k->unrealize = idebus_unrealize;
}

static void idebus_unrealize(BusState *bus, Error **errp)
static void idebus_unrealize(BusState *bus)
{
IDEBus *ibus = IDE_BUS(bus);

Expand Down
2 changes: 1 addition & 1 deletion hw/input/virtio-input-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ static void virtio_input_hid_realize(DeviceState *dev, Error **errp)
}
}

static void virtio_input_hid_unrealize(DeviceState *dev, Error **errp)
static void virtio_input_hid_unrealize(DeviceState *dev)
{
VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev);
qemu_input_handler_unregister(vhid->hs);
Expand Down
2 changes: 1 addition & 1 deletion hw/input/virtio-input-host.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ static void virtio_input_host_realize(DeviceState *dev, Error **errp)
return;
}

static void virtio_input_host_unrealize(DeviceState *dev, Error **errp)
static void virtio_input_host_unrealize(DeviceState *dev)
{
VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev);

Expand Down
Loading

0 comments on commit b69c3c2

Please sign in to comment.