When unplugging a device, at one point the device will be destroyed via object_unparent(). This will, one the one hand, unrealize the removed device hierarchy, and on the other hand, destroy/free the device hierarchy. When chaining hotplug handlers, we want to overwrite a bus hotplug handler by the machine hotplug handler, to be able to perform some part of the plug/unplug and to forward the calls to the bus hotplug handler. For now, the bus hotplug handler would trigger an object_unparent(), not allowing us to perform some unplug action on a device after we forwarded the call to the bus hotplug handler. The device would be gone at that point. machine_unplug_handler(dev) /* eventually do unplug stuff */ bus_unplug_handler(dev) /* dev is gone, we can't do more unplug stuff */ So move the object_unparent() to the original caller of the unplug. For now, keep the unrealize() at the original places of the object_unparent(). For implicitly chained hotplug handlers (e.g. pc code calling acpi hotplug handlers), the object_unparent() has to be done by the outermost caller. So when calling hotplug_handler_unplug() from inside an unplug handler, nothing is to be done. hotplug_handler_unplug(dev) -> calls machine_unplug_handler() machine_unplug_handler(dev) { /* eventually do unplug stuff */ bus_unplug_handler(dev) -> calls unrealize(dev) /* we can do more unplug stuff but device already unrealized */ } object_unparent(dev) In the long run, every unplug action should be factored out of the unrealize() function into the unplug handler (especially for PCI). Then we can get rid of the additonal unrealize() calls and object_unparent() will properly unrealize the device hierarchy after the device has been unplugged. hotplug_handler_unplug(dev) -> calls machine_unplug_handler() machine_unplug_handler(dev) { /* eventually do unplug stuff */ bus_unplug_handler(dev) -> only unplugs, does not unrealize /* we can do more unplug stuff */ } object_unparent(dev) -> will unrealize The original approach was suggested by Igor Mammedov for the PCI part, but I extended it to all hotplug handlers. I consider this one step into the right direction. To summarize: - object_unparent() on synchronous unplugs is done by common code -- "Caller of hotplug_handler_unplug" - object_unparent() on asynchronous unplugs ("unplug requests") has to be done manually -- "Caller of hotplug_handler_unplug" Reviewed-by: Igor Mammedov <imammedo@redhat.com> Acked-by: Cornelia Huck <cohuck@redhat.com> Signed-off-by: David Hildenbrand <david@redhat.com> Message-Id: <20190228122849.4296-2-david@redhat.com> Reviewed-by: Greg Kurz <groug@kaod.org> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
166 lines
4.8 KiB
C
166 lines
4.8 KiB
C
/*
|
|
* css bridge implementation
|
|
*
|
|
* Copyright 2012,2016 IBM Corp.
|
|
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
|
|
* Pierre Morel <pmorel@linux.vnet.ibm.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
|
* your option) any later version. See the COPYING file in the top-level
|
|
* directory.
|
|
*/
|
|
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "hw/hotplug.h"
|
|
#include "hw/sysbus.h"
|
|
#include "qemu/bitops.h"
|
|
#include "hw/s390x/css.h"
|
|
#include "ccw-device.h"
|
|
#include "hw/s390x/css-bridge.h"
|
|
#include "cpu.h"
|
|
|
|
/*
|
|
* Invoke device-specific unplug handler, disable the subchannel
|
|
* (including sending a channel report to the guest) and remove the
|
|
* device from the virtual css bus.
|
|
*/
|
|
static void ccw_device_unplug(HotplugHandler *hotplug_dev,
|
|
DeviceState *dev, Error **errp)
|
|
{
|
|
CcwDevice *ccw_dev = CCW_DEVICE(dev);
|
|
CCWDeviceClass *k = CCW_DEVICE_GET_CLASS(ccw_dev);
|
|
SubchDev *sch = ccw_dev->sch;
|
|
Error *err = NULL;
|
|
|
|
if (k->unplug) {
|
|
k->unplug(hotplug_dev, dev, &err);
|
|
if (err) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We should arrive here only for device_del, since we don't support
|
|
* direct hot(un)plug of channels.
|
|
*/
|
|
assert(sch != NULL);
|
|
/* Subchannel is now disabled and no longer valid. */
|
|
sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA |
|
|
PMCW_FLAGS_MASK_DNV);
|
|
|
|
css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0);
|
|
|
|
object_property_set_bool(OBJECT(dev), false, "realized", NULL);
|
|
}
|
|
|
|
static void virtual_css_bus_reset(BusState *qbus)
|
|
{
|
|
/* This should actually be modelled via the generic css */
|
|
css_reset();
|
|
}
|
|
|
|
static char *virtual_css_bus_get_dev_path(DeviceState *dev)
|
|
{
|
|
CcwDevice *ccw_dev = CCW_DEVICE(dev);
|
|
SubchDev *sch = ccw_dev->sch;
|
|
VirtualCssBridge *bridge =
|
|
VIRTUAL_CSS_BRIDGE(qdev_get_parent_bus(dev)->parent);
|
|
|
|
/*
|
|
* We can't provide a dev path for backward compatibility on
|
|
* older machines, as it is visible in the migration stream.
|
|
*/
|
|
return bridge->css_dev_path ?
|
|
g_strdup_printf("/%02x.%1x.%04x", sch->cssid, sch->ssid, sch->devno) :
|
|
NULL;
|
|
}
|
|
|
|
static void virtual_css_bus_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
BusClass *k = BUS_CLASS(klass);
|
|
|
|
k->reset = virtual_css_bus_reset;
|
|
k->get_dev_path = virtual_css_bus_get_dev_path;
|
|
}
|
|
|
|
static const TypeInfo virtual_css_bus_info = {
|
|
.name = TYPE_VIRTUAL_CSS_BUS,
|
|
.parent = TYPE_BUS,
|
|
.instance_size = sizeof(VirtualCssBus),
|
|
.class_init = virtual_css_bus_class_init,
|
|
};
|
|
|
|
VirtualCssBus *virtual_css_bus_init(void)
|
|
{
|
|
VirtualCssBus *cbus;
|
|
BusState *bus;
|
|
DeviceState *dev;
|
|
|
|
/* Create bridge device */
|
|
dev = qdev_create(NULL, TYPE_VIRTUAL_CSS_BRIDGE);
|
|
object_property_add_child(qdev_get_machine(), TYPE_VIRTUAL_CSS_BRIDGE,
|
|
OBJECT(dev), NULL);
|
|
qdev_init_nofail(dev);
|
|
|
|
/* Create bus on bridge device */
|
|
bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css");
|
|
cbus = VIRTUAL_CSS_BUS(bus);
|
|
|
|
/* Enable hotplugging */
|
|
qbus_set_hotplug_handler(bus, OBJECT(dev), &error_abort);
|
|
|
|
css_register_io_adapters(CSS_IO_ADAPTER_VIRTIO, true, false,
|
|
0, &error_abort);
|
|
|
|
return cbus;
|
|
}
|
|
|
|
/***************** Virtual-css Bus Bridge Device ********************/
|
|
|
|
static Property virtual_css_bridge_properties[] = {
|
|
DEFINE_PROP_BOOL("css_dev_path", VirtualCssBridge, css_dev_path,
|
|
true),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static bool prop_get_true(Object *obj, Error **errp)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void virtual_css_bridge_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
hc->unplug = ccw_device_unplug;
|
|
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
|
dc->props = virtual_css_bridge_properties;
|
|
object_class_property_add_bool(klass, "cssid-unrestricted",
|
|
prop_get_true, NULL, NULL);
|
|
object_class_property_set_description(klass, "cssid-unrestricted",
|
|
"A css device can use any cssid, regardless whether virtual"
|
|
" or not (read only, always true)",
|
|
NULL);
|
|
}
|
|
|
|
static const TypeInfo virtual_css_bridge_info = {
|
|
.name = TYPE_VIRTUAL_CSS_BRIDGE,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(VirtualCssBridge),
|
|
.class_init = virtual_css_bridge_class_init,
|
|
.interfaces = (InterfaceInfo[]) {
|
|
{ TYPE_HOTPLUG_HANDLER },
|
|
{ }
|
|
}
|
|
};
|
|
|
|
static void virtual_css_register(void)
|
|
{
|
|
type_register_static(&virtual_css_bridge_info);
|
|
type_register_static(&virtual_css_bus_info);
|
|
}
|
|
|
|
type_init(virtual_css_register)
|