aboutsummaryrefslogtreecommitdiff
path: root/hw/core/qdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/core/qdev.c')
-rw-r--r--hw/core/qdev.c900
1 files changed, 354 insertions, 546 deletions
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 046d8f1f76..c68d0f7c51 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -6,7 +6,7 @@
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -26,19 +26,21 @@
this API directly. */
#include "qemu/osdep.h"
-#include "hw/qdev.h"
-#include "sysemu/sysemu.h"
#include "qapi/error.h"
-#include "qapi/qapi-events-misc.h"
+#include "qapi/qapi-events-qdev.h"
+#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
#include "qapi/visitor.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
-#include "hw/hotplug.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
#include "hw/boards.h"
#include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
+#include "migration/vmstate.h"
+#include "trace.h"
-bool qdev_hotplug = false;
static bool qdev_hot_added = false;
bool qdev_hot_removed = false;
@@ -48,6 +50,12 @@ const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
return dc->vmsd;
}
+static void bus_free_bus_child(BusChild *kid)
+{
+ object_unref(OBJECT(kid->child));
+ g_free(kid);
+}
+
static void bus_remove_child(BusState *bus, DeviceState *child)
{
BusChild *kid;
@@ -57,13 +65,16 @@ static void bus_remove_child(BusState *bus, DeviceState *child)
char name[32];
snprintf(name, sizeof(name), "child[%d]", kid->index);
- QTAILQ_REMOVE(&bus->children, kid, sibling);
+ QTAILQ_REMOVE_RCU(&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));
- g_free(kid);
- return;
+ object_property_del(OBJECT(bus), name);
+
+ /* free the bus kid, when it is safe to do so*/
+ call_rcu(kid, bus_free_bus_child, rcu);
+ break;
}
}
}
@@ -73,11 +84,12 @@ 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));
- QTAILQ_INSERT_HEAD(&bus->children, kid, sibling);
+ QTAILQ_INSERT_HEAD_RCU(&bus->children, kid, sibling);
/* This transfers ownership of kid->child to the property. */
snprintf(name, sizeof(name), "child[%d]", kid->index);
@@ -85,80 +97,84 @@ static void bus_add_child(BusState *bus, DeviceState *child)
object_get_typename(OBJECT(child)),
(Object **)&kid->child,
NULL, /* read-only property */
- 0, /* return ownership on prop deletion */
- NULL);
+ 0);
}
-void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
+static bool bus_check_address(BusState *bus, DeviceState *child, Error **errp)
{
- bool replugging = dev->parent_bus != NULL;
+ BusClass *bc = BUS_GET_CLASS(bus);
+ return !bc->check_address || bc->check_address(bus, child, errp);
+}
+
+bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)
+{
+ BusState *old_parent_bus = dev->parent_bus;
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
- if (replugging) {
- /* Keep a reference to the device while it's not plugged into
+ assert(dc->bus_type && object_dynamic_cast(OBJECT(bus), dc->bus_type));
+
+ if (!bus_check_address(bus, dev, errp)) {
+ return false;
+ }
+
+ if (old_parent_bus) {
+ trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)),
+ old_parent_bus, object_get_typename(OBJECT(old_parent_bus)),
+ OBJECT(bus), object_get_typename(OBJECT(bus)));
+ /*
+ * Keep a reference to the device while it's not plugged into
* any bus, to avoid it potentially evaporating when it is
* dereffed in bus_remove_child().
+ * Also keep the ref of the parent bus until the end, so that
+ * we can safely call resettable_change_parent() below.
*/
object_ref(OBJECT(dev));
bus_remove_child(dev->parent_bus, dev);
- object_unref(OBJECT(dev->parent_bus));
}
dev->parent_bus = bus;
object_ref(OBJECT(bus));
bus_add_child(bus, dev);
- if (replugging) {
+ if (dev->realized) {
+ resettable_change_parent(OBJECT(dev), OBJECT(bus),
+ OBJECT(old_parent_bus));
+ }
+ if (old_parent_bus) {
+ object_unref(OBJECT(old_parent_bus));
object_unref(OBJECT(dev));
}
+ return true;
}
-/* Create a new device. This only initializes the device state
- structure and allows properties to be set. The device still needs
- to be realized. See qdev-core.h. */
-DeviceState *qdev_create(BusState *bus, const char *name)
+DeviceState *qdev_new(const char *name)
{
- DeviceState *dev;
-
- dev = qdev_try_create(bus, name);
- if (!dev) {
- if (bus) {
- error_report("Unknown device '%s' for bus '%s'", name,
- object_get_typename(OBJECT(bus)));
+ ObjectClass *oc = object_class_by_name(name);
+#ifdef CONFIG_MODULES
+ if (!oc) {
+ int rv = module_load_qom(name, &error_fatal);
+ if (rv > 0) {
+ oc = object_class_by_name(name);
} else {
- error_report("Unknown device '%s' for default sysbus", name);
+ error_report("could not find a module for type '%s'", name);
+ exit(1);
}
+ }
+#endif
+ if (!oc) {
+ error_report("unknown type '%s'", name);
abort();
}
-
- return dev;
+ return DEVICE(object_new(name));
}
-DeviceState *qdev_try_create(BusState *bus, const char *type)
+DeviceState *qdev_try_new(const char *name)
{
- DeviceState *dev;
-
- if (object_class_by_name(type) == NULL) {
+ if (!module_object_class_by_name(name)) {
return NULL;
}
- dev = DEVICE(object_new(type));
- if (!dev) {
- return NULL;
- }
-
- if (!bus) {
- /* Assert that the device really is a SysBusDevice before
- * we put it onto the sysbus. Non-sysbus devices which aren't
- * being put onto a bus should be created with object_new(TYPE_FOO),
- * not qdev_create(NULL, TYPE_FOO).
- */
- g_assert(object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE));
- bus = sysbus_get_default();
- }
-
- qdev_set_parent_bus(dev, bus);
- object_unref(OBJECT(dev));
- return dev;
+ return DEVICE(object_new(name));
}
-static QTAILQ_HEAD(device_listeners, DeviceListener) device_listeners
+static QTAILQ_HEAD(, DeviceListener) device_listeners
= QTAILQ_HEAD_INITIALIZER(device_listeners);
enum ListenerDirection { Forward, Reverse };
@@ -177,7 +193,7 @@ enum ListenerDirection { Forward, Reverse };
break; \
case Reverse: \
QTAILQ_FOREACH_REVERSE(_listener, &device_listeners, \
- device_listeners, link) { \
+ link) { \
if (_listener->_callback) { \
_listener->_callback(_listener, ##_args); \
} \
@@ -208,318 +224,115 @@ void device_listener_unregister(DeviceListener *listener)
QTAILQ_REMOVE(&device_listeners, listener, link);
}
-void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
- int required_for_version)
+bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp)
{
- assert(!dev->realized);
- dev->instance_id_alias = alias_id;
- dev->alias_required_for_version = required_for_version;
-}
+ ERRP_GUARD();
+ DeviceListener *listener;
-HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev)
-{
- MachineState *machine;
- MachineClass *mc;
- Object *m_obj = qdev_get_machine();
-
- if (object_dynamic_cast(m_obj, TYPE_MACHINE)) {
- machine = MACHINE(m_obj);
- mc = MACHINE_GET_CLASS(machine);
- if (mc->get_hotplug_handler) {
- return mc->get_hotplug_handler(machine, dev);
+ QTAILQ_FOREACH(listener, &device_listeners, link) {
+ if (listener->hide_device) {
+ if (listener->hide_device(listener, opts, from_json, errp)) {
+ return true;
+ } else if (*errp) {
+ return false;
+ }
}
}
- return NULL;
-}
-
-HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
-{
- HotplugHandler *hotplug_ctrl;
-
- 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);
- }
- return hotplug_ctrl;
-}
-
-static int qdev_reset_one(DeviceState *dev, void *opaque)
-{
- device_reset(dev);
-
- return 0;
-}
-
-static int qbus_reset_one(BusState *bus, void *opaque)
-{
- BusClass *bc = BUS_GET_CLASS(bus);
- if (bc->reset) {
- bc->reset(bus);
- }
- return 0;
-}
-
-void qdev_reset_all(DeviceState *dev)
-{
- qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
-}
-
-void qdev_reset_all_fn(void *opaque)
-{
- qdev_reset_all(DEVICE(opaque));
-}
-
-void qbus_reset_all(BusState *bus)
-{
- qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
-}
-
-void qbus_reset_all_fn(void *opaque)
-{
- BusState *bus = opaque;
- qbus_reset_all(bus);
-}
-
-/* can be used as ->unplug() callback for the simple cases */
-void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
-{
- /* just zap it */
- object_unparent(OBJECT(dev));
+ return false;
}
-/*
- * Realize @dev.
- * Device properties should be set before calling this function. IRQs
- * and MMIO regions should be connected/mapped after calling this
- * function.
- * On failure, report an error with error_report() and terminate the
- * program. This is okay during machine creation. Don't use for
- * hotplug, because there callers need to recover from failure.
- * Exception: if you know the device's init() callback can't fail,
- * then qdev_init_nofail() can't fail either, and is therefore usable
- * even then. But relying on the device implementation that way is
- * somewhat unclean, and best avoided.
- */
-void qdev_init_nofail(DeviceState *dev)
+void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
+ int required_for_version)
{
- Error *err = NULL;
-
assert(!dev->realized);
-
- object_ref(OBJECT(dev));
- object_property_set_bool(OBJECT(dev), true, "realized", &err);
- if (err) {
- error_reportf_err(err, "Initialization of device %s failed: ",
- object_get_typename(OBJECT(dev)));
- exit(1);
- }
- object_unref(OBJECT(dev));
-}
-
-void qdev_machine_creation_done(void)
-{
- /*
- * ok, initial machine setup is done, starting from now we can
- * only create hotpluggable devices
- */
- qdev_hotplug = true;
+ dev->instance_id_alias = alias_id;
+ dev->alias_required_for_version = required_for_version;
}
-bool qdev_machine_modified(void)
+void device_cold_reset(DeviceState *dev)
{
- return qdev_hot_added || qdev_hot_removed;
+ resettable_reset(OBJECT(dev), RESET_TYPE_COLD);
}
-BusState *qdev_get_parent_bus(DeviceState *dev)
+bool device_is_in_reset(DeviceState *dev)
{
- return dev->parent_bus;
+ return resettable_is_in_reset(OBJECT(dev));
}
-static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev,
- const char *name)
+static ResettableState *device_get_reset_state(Object *obj)
{
- NamedGPIOList *ngl;
-
- QLIST_FOREACH(ngl, &dev->gpios, node) {
- /* NULL is a valid and matchable name, otherwise do a normal
- * strcmp match.
- */
- if ((!ngl->name && !name) ||
- (name && ngl->name && strcmp(name, ngl->name) == 0)) {
- return ngl;
- }
- }
-
- ngl = g_malloc0(sizeof(*ngl));
- ngl->name = g_strdup(name);
- QLIST_INSERT_HEAD(&dev->gpios, ngl, node);
- return ngl;
+ DeviceState *dev = DEVICE(obj);
+ return &dev->reset;
}
-void qdev_init_gpio_in_named_with_opaque(DeviceState *dev,
- qemu_irq_handler handler,
- void *opaque,
- const char *name, int n)
+static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb,
+ void *opaque, ResetType type)
{
- int i;
- NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
-
- assert(gpio_list->num_out == 0 || !name);
- gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler,
- opaque, n);
-
- if (!name) {
- name = "unnamed-gpio-in";
- }
- for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) {
- gchar *propname = g_strdup_printf("%s[%u]", name, i);
+ DeviceState *dev = DEVICE(obj);
+ BusState *bus;
- object_property_add_child(OBJECT(dev), propname,
- OBJECT(gpio_list->in[i]), &error_abort);
- g_free(propname);
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ cb(OBJECT(bus), opaque, type);
}
-
- gpio_list->num_in += n;
}
-void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
+bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp)
{
- qdev_init_gpio_in_named(dev, handler, NULL, n);
-}
+ assert(!dev->realized && !dev->parent_bus);
-void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
- const char *name, int n)
-{
- int i;
- NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
-
- assert(gpio_list->num_in == 0 || !name);
-
- if (!name) {
- name = "unnamed-gpio-out";
- }
- memset(pins, 0, sizeof(*pins) * n);
- for (i = 0; i < n; ++i) {
- gchar *propname = g_strdup_printf("%s[%u]", name,
- gpio_list->num_out + i);
-
- object_property_add_link(OBJECT(dev), propname, TYPE_IRQ,
- (Object **)&pins[i],
- object_property_allow_set_link,
- OBJ_PROP_LINK_STRONG,
- &error_abort);
- g_free(propname);
+ if (bus) {
+ if (!qdev_set_parent_bus(dev, bus, errp)) {
+ return false;
+ }
+ } else {
+ assert(!DEVICE_GET_CLASS(dev)->bus_type);
}
- gpio_list->num_out += n;
-}
-
-void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
-{
- qdev_init_gpio_out_named(dev, pins, NULL, n);
-}
-
-qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n)
-{
- NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
- assert(n >= 0 && n < gpio_list->num_in);
- return gpio_list->in[n];
+ return object_property_set_bool(OBJECT(dev), "realized", true, errp);
}
-qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
+bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp)
{
- return qdev_get_gpio_in_named(dev, NULL, n);
-}
+ bool ret;
-void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
- qemu_irq pin)
-{
- char *propname = g_strdup_printf("%s[%d]",
- name ? name : "unnamed-gpio-out", n);
- if (pin) {
- /* We need a name for object_property_set_link to work. If the
- * object has a parent, object_property_add_child will come back
- * with an error without doing anything. If it has none, it will
- * never fail. So we can just call it with a NULL Error pointer.
- */
- object_property_add_child(container_get(qdev_get_machine(),
- "/unattached"),
- "non-qdev-gpio[*]", OBJECT(pin), NULL);
- }
- object_property_set_link(OBJECT(dev), OBJECT(pin), propname, &error_abort);
- g_free(propname);
+ ret = qdev_realize(dev, bus, errp);
+ object_unref(OBJECT(dev));
+ return ret;
}
-qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
+void qdev_unrealize(DeviceState *dev)
{
- char *propname = g_strdup_printf("%s[%d]",
- name ? name : "unnamed-gpio-out", n);
-
- qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
- NULL);
-
- return ret;
+ object_property_set_bool(OBJECT(dev), "realized", false, &error_abort);
}
-/* disconnect a GPIO output, returning the disconnected input (if any) */
-
-static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
- const char *name, int n)
+static int qdev_assert_realized_properly_cb(Object *obj, void *opaque)
{
- char *propname = g_strdup_printf("%s[%d]",
- name ? name : "unnamed-gpio-out", n);
+ DeviceState *dev = DEVICE(object_dynamic_cast(obj, TYPE_DEVICE));
+ DeviceClass *dc;
- qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
- NULL);
- if (ret) {
- object_property_set_link(OBJECT(dev), NULL, propname, NULL);
+ if (dev) {
+ dc = DEVICE_GET_CLASS(dev);
+ assert(dev->realized);
+ assert(dev->parent_bus || !dc->bus_type);
}
- g_free(propname);
- return ret;
+ return 0;
}
-qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt,
- const char *name, int n)
+void qdev_assert_realized_properly(void)
{
- qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n);
- qdev_connect_gpio_out_named(dev, name, n, icpt);
- return disconnected;
+ object_child_foreach_recursive(object_get_root(),
+ qdev_assert_realized_properly_cb, NULL);
}
-void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
+bool qdev_machine_modified(void)
{
- qdev_connect_gpio_out_named(dev, NULL, n, pin);
+ return qdev_hot_added || qdev_hot_removed;
}
-void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
- const char *name)
+BusState *qdev_get_parent_bus(const DeviceState *dev)
{
- int i;
- NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name);
-
- for (i = 0; i < ngl->num_in; i++) {
- const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in";
- char *propname = g_strdup_printf("%s[%d]", nm, i);
-
- object_property_add_alias(OBJECT(container), propname,
- OBJECT(dev), propname,
- &error_abort);
- g_free(propname);
- }
- for (i = 0; i < ngl->num_out; i++) {
- const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out";
- char *propname = g_strdup_printf("%s[%d]", nm, i);
-
- object_property_add_alias(OBJECT(container), propname,
- OBJECT(dev), propname,
- &error_abort);
- g_free(propname);
- }
- QLIST_REMOVE(ngl, node);
- QLIST_INSERT_HEAD(&container->gpios, ngl, node);
+ return dev->parent_bus;
}
BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
@@ -579,17 +392,19 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
DeviceState *ret;
BusState *child;
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- DeviceState *dev = kid->child;
+ WITH_RCU_READ_LOCK_GUARD() {
+ QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
- if (dev->id && strcmp(dev->id, id) == 0) {
- return dev;
- }
+ if (dev->id && strcmp(dev->id, id) == 0) {
+ return dev;
+ }
- QLIST_FOREACH(child, &dev->child_bus, sibling) {
- ret = qdev_find_recursive(child, id);
- if (ret) {
- return ret;
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ ret = qdev_find_recursive(child, id);
+ if (ret) {
+ return ret;
+ }
}
}
}
@@ -612,156 +427,24 @@ char *qdev_get_dev_path(DeviceState *dev)
return NULL;
}
-/**
- * Legacy property handling
- */
-
-static void qdev_get_legacy_property(Object *obj, Visitor *v,
- const char *name, void *opaque,
- Error **errp)
+void qdev_add_unplug_blocker(DeviceState *dev, Error *reason)
{
- DeviceState *dev = DEVICE(obj);
- Property *prop = opaque;
-
- char buffer[1024];
- char *ptr = buffer;
-
- prop->info->print(dev, prop, buffer, sizeof(buffer));
- visit_type_str(v, name, &ptr, errp);
+ dev->unplug_blockers = g_slist_prepend(dev->unplug_blockers, reason);
}
-/**
- * qdev_property_add_legacy:
- * @dev: Device to add the property to.
- * @prop: The qdev property definition.
- * @errp: location to store error information.
- *
- * Add a legacy QOM property to @dev for qdev property @prop.
- * On error, store error in @errp.
- *
- * Legacy properties are string versions of QOM properties. The format of
- * the string depends on the property type. Legacy properties are only
- * needed for "info qtree".
- *
- * Do not use this in new code! QOM Properties added through this interface
- * will be given names in the "legacy" namespace.
- */
-static void qdev_property_add_legacy(DeviceState *dev, Property *prop,
- Error **errp)
+void qdev_del_unplug_blocker(DeviceState *dev, Error *reason)
{
- gchar *name;
-
- /* Register pointer properties as legacy properties */
- if (!prop->info->print && prop->info->get) {
- return;
- }
-
- if (prop->info->create) {
- return;
- }
-
- name = g_strdup_printf("legacy-%s", prop->name);
- object_property_add(OBJECT(dev), name, "str",
- prop->info->print ? qdev_get_legacy_property : prop->info->get,
- NULL,
- NULL,
- prop, errp);
-
- g_free(name);
+ dev->unplug_blockers = g_slist_remove(dev->unplug_blockers, reason);
}
-/**
- * qdev_property_add_static:
- * @dev: Device to add the property to.
- * @prop: The qdev property definition.
- * @errp: location to store error information.
- *
- * Add a static QOM property to @dev for qdev property @prop.
- * On error, store error in @errp. Static properties access data in a struct.
- * The type of the QOM property is derived from prop->info.
- */
-void qdev_property_add_static(DeviceState *dev, Property *prop,
- Error **errp)
+bool qdev_unplug_blocked(DeviceState *dev, Error **errp)
{
- Error *local_err = NULL;
- Object *obj = OBJECT(dev);
-
- if (prop->info->create) {
- prop->info->create(obj, prop, &local_err);
- } else {
- /*
- * TODO qdev_prop_ptr does not have getters or setters. It must
- * go now that it can be replaced with links. The test should be
- * removed along with it: all static properties are read/write.
- */
- if (!prop->info->get && !prop->info->set) {
- return;
- }
- object_property_add(obj, prop->name, prop->info->name,
- prop->info->get, prop->info->set,
- prop->info->release,
- prop, &local_err);
- }
-
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- object_property_set_description(obj, prop->name,
- prop->info->description,
- &error_abort);
-
- if (prop->set_default) {
- prop->info->set_default_value(obj, prop);
+ if (dev->unplug_blockers) {
+ error_propagate(errp, error_copy(dev->unplug_blockers->data));
+ return true;
}
-}
-
-/* @qdev_alias_all_properties - Add alias properties to the source object for
- * all qdev properties on the target DeviceState.
- */
-void qdev_alias_all_properties(DeviceState *target, Object *source)
-{
- ObjectClass *class;
- Property *prop;
- class = object_get_class(OBJECT(target));
- do {
- DeviceClass *dc = DEVICE_CLASS(class);
-
- for (prop = dc->props; prop && prop->name; prop++) {
- object_property_add_alias(source, prop->name,
- OBJECT(target), prop->name,
- &error_abort);
- }
- class = object_class_get_parent(class);
- } while (class != object_class_by_name(TYPE_DEVICE));
-}
-
-static int qdev_add_hotpluggable_device(Object *obj, void *opaque)
-{
- GSList **list = opaque;
- DeviceState *dev = (DeviceState *)object_dynamic_cast(OBJECT(obj),
- TYPE_DEVICE);
-
- if (dev == NULL) {
- return 0;
- }
-
- if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) {
- *list = g_slist_append(*list, dev);
- }
-
- return 0;
-}
-
-GSList *qdev_build_hotpluggable_device_list(Object *peripheral)
-{
- GSList *list = NULL;
-
- object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list);
-
- return list;
+ return false;
}
static bool device_get_realized(Object *obj, Error **errp)
@@ -770,12 +453,12 @@ static bool device_get_realized(Object *obj, Error **errp)
return dev->realized;
}
-static bool check_only_migratable(Object *obj, Error **err)
+static bool check_only_migratable(Object *obj, Error **errp)
{
DeviceClass *dc = DEVICE_GET_CLASS(obj);
if (!vmstate_check_only_migratable(dc->vmsd)) {
- error_setg(err, "Device %s is not migratable, but "
+ error_setg(errp, "Device %s is not migratable, but "
"--only-migratable was specified",
object_get_typename(obj));
return false;
@@ -790,6 +473,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
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;
@@ -800,7 +484,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
}
if (value && !dev->realized) {
- if (!check_only_migratable(obj, &local_err)) {
+ if (!check_only_migratable(obj, errp)) {
goto fail;
}
@@ -809,7 +493,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
object_property_add_child(container_get(qdev_get_machine(),
"/unattached"),
- name, obj, &error_abort);
+ name, obj);
unattached_parent = true;
g_free(name);
}
@@ -824,31 +508,31 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
if (dc->realize) {
dc->realize(dev, &local_err);
- }
-
- if (local_err != NULL) {
- goto fail;
+ if (local_err != NULL) {
+ goto fail;
+ }
}
DEVICE_LISTENER_CALL(realize, Forward, dev);
- if (hotplug_ctrl) {
- hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
- }
-
- if (local_err != NULL) {
- goto post_realize_fail;
- }
-
/*
* 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(dev, -1, qdev_get_vmsd(dev), 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) {
@@ -856,66 +540,94 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
}
}
+ /*
+ * 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) {
- object_property_set_bool(OBJECT(bus), true, "realized",
- &local_err);
- if (local_err != NULL) {
+ if (!qbus_realize(bus, errp)) {
goto child_realize_fail;
}
}
if (dev->hotplugged) {
- device_reset(dev);
+ /*
+ * 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_post_plug(hotplug_ctrl, dev);
- }
+ 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) {
- Error **local_errp = NULL;
+
+ /*
+ * 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) {
- local_errp = local_err ? NULL : &local_err;
- object_property_set_bool(OBJECT(bus), false, "realized",
- local_errp);
+ qbus_unrealize(bus);
}
if (qdev_get_vmsd(dev)) {
- vmstate_unregister(dev, qdev_get_vmsd(dev), dev);
+ vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
}
if (dc->unrealize) {
- local_errp = local_err ? NULL : &local_err;
- dc->unrealize(dev, local_errp);
+ dc->unrealize(dev);
}
dev->pending_deleted_event = true;
DEVICE_LISTENER_CALL(unrealize, Reverse, dev);
}
- if (local_err != NULL) {
- goto fail;
- }
-
- dev->realized = value;
+ assert(local_err == NULL);
return;
child_realize_fail:
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
- object_property_set_bool(OBJECT(bus), false, "realized",
- NULL);
+ qbus_unrealize(bus);
}
if (qdev_get_vmsd(dev)) {
- vmstate_unregister(dev, qdev_get_vmsd(dev), 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, NULL);
+ 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--;
}
@@ -930,7 +642,7 @@ static bool device_get_hotpluggable(Object *obj, Error **errp)
qbus_is_hotpluggable(dev->parent_bus));
}
-static bool device_get_hotplugged(Object *obj, Error **err)
+static bool device_get_hotplugged(Object *obj, Error **errp)
{
DeviceState *dev = DEVICE(obj);
@@ -940,42 +652,27 @@ static bool device_get_hotplugged(Object *obj, Error **err)
static void device_initfn(Object *obj)
{
DeviceState *dev = DEVICE(obj);
- ObjectClass *class;
- Property *prop;
- if (qdev_hotplug) {
+ if (phase_check(PHASE_MACHINE_READY)) {
dev->hotplugged = 1;
qdev_hot_added = true;
}
dev->instance_id_alias = -1;
dev->realized = false;
+ dev->allow_unplug_during_migration = false;
- object_property_add_bool(obj, "realized",
- device_get_realized, device_set_realized, NULL);
- object_property_add_bool(obj, "hotpluggable",
- device_get_hotpluggable, NULL, NULL);
- object_property_add_bool(obj, "hotplugged",
- device_get_hotplugged, NULL,
- &error_abort);
-
- class = object_get_class(OBJECT(dev));
- do {
- for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) {
- qdev_property_add_legacy(dev, prop, &error_abort);
- qdev_property_add_static(dev, prop, &error_abort);
- }
- class = object_class_get_parent(class);
- } while (class != object_class_by_name(TYPE_DEVICE));
-
- object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS,
- (Object **)&dev->parent_bus, NULL, 0,
- &error_abort);
QLIST_INIT(&dev->gpios);
+ QLIST_INIT(&dev->clocks);
}
static void device_post_init(Object *obj)
{
+ /*
+ * Note: ordered so that the user's global properties take
+ * precedence.
+ */
+ object_apply_compat_props(obj);
qdev_prop_set_globals(DEVICE(obj));
}
@@ -986,6 +683,8 @@ static void device_finalize(Object *obj)
DeviceState *dev = DEVICE(obj);
+ g_assert(!dev->unplug_blockers);
+
QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) {
QLIST_REMOVE(ngl, node);
qemu_free_irqs(ngl->in, ngl->num_in);
@@ -996,16 +695,19 @@ static void device_finalize(Object *obj)
*/
}
+ qdev_finalize_clocklist(dev);
+
/* Only send event if the device had been completely realized */
if (dev->pending_deleted_event) {
g_assert(dev->canonical_path);
- qapi_event_send_device_deleted(!!dev->id, dev->id, dev->canonical_path);
+ qapi_event_send_device_deleted(dev->id, dev->canonical_path);
g_free(dev->canonical_path);
dev->canonical_path = NULL;
}
- qemu_opts_del(dev->opts);
+ qobject_unref(dev->opts);
+ g_free(dev->id);
}
static void device_class_base_init(ObjectClass *class, void *data)
@@ -1015,7 +717,7 @@ static void device_class_base_init(ObjectClass *class, void *data)
/* We explicitly look up properties in the superclasses,
* so do not propagate them to the subclasses.
*/
- klass->props = NULL;
+ klass->props_ = NULL;
}
static void device_unparent(Object *obj)
@@ -1024,7 +726,7 @@ static void device_unparent(Object *obj)
BusState *bus;
if (dev->realized) {
- object_property_set_bool(obj, false, "realized", NULL);
+ qdev_unrealize(dev);
}
while (dev->num_child_bus) {
bus = QLIST_FIRST(&dev->child_bus);
@@ -1037,9 +739,70 @@ static void device_unparent(Object *obj)
}
}
+static char *
+device_vmstate_if_get_id(VMStateIf *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+
+ return qdev_get_dev_path(dev);
+}
+
+/**
+ * device_phases_reset:
+ * Transition reset method for devices to allow moving
+ * smoothly from legacy reset method to multi-phases
+ */
+static void device_phases_reset(DeviceState *dev)
+{
+ ResettableClass *rc = RESETTABLE_GET_CLASS(dev);
+
+ if (rc->phases.enter) {
+ rc->phases.enter(OBJECT(dev), RESET_TYPE_COLD);
+ }
+ if (rc->phases.hold) {
+ rc->phases.hold(OBJECT(dev));
+ }
+ if (rc->phases.exit) {
+ rc->phases.exit(OBJECT(dev));
+ }
+}
+
+static void device_transitional_reset(Object *obj)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(obj);
+
+ /*
+ * This will call either @device_phases_reset (for multi-phases transitioned
+ * devices) or a device's specific method for not-yet transitioned devices.
+ * In both case, it does not reset children.
+ */
+ if (dc->reset) {
+ dc->reset(DEVICE(obj));
+ }
+}
+
+/**
+ * device_get_transitional_reset:
+ * check if the device's class is ready for multi-phase
+ */
+static ResettableTrFunction device_get_transitional_reset(Object *obj)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(obj);
+ if (dc->reset != device_phases_reset) {
+ /*
+ * dc->reset has been overridden by a subclass,
+ * the device is not ready for multi phase yet.
+ */
+ return device_transitional_reset;
+ }
+ return NULL;
+}
+
static void device_class_init(ObjectClass *class, void *data)
{
DeviceClass *dc = DEVICE_CLASS(class);
+ VMStateIfClass *vc = VMSTATE_IF_CLASS(class);
+ ResettableClass *rc = RESETTABLE_CLASS(class);
class->unparent = device_unparent;
@@ -1051,6 +814,34 @@ static void device_class_init(ObjectClass *class, void *data)
*/
dc->hotpluggable = true;
dc->user_creatable = true;
+ vc->get_id = device_vmstate_if_get_id;
+ rc->get_state = device_get_reset_state;
+ rc->child_foreach = device_reset_child_foreach;
+
+ /*
+ * @device_phases_reset is put as the default reset method below, allowing
+ * to do the multi-phase transition from base classes to leaf classes. It
+ * allows a legacy-reset Device class to extend a multi-phases-reset
+ * Device class for the following reason:
+ * + If a base class B has been moved to multi-phase, then it does not
+ * override this default reset method and may have defined phase methods.
+ * + A child class C (extending class B) which uses
+ * device_class_set_parent_reset() (or similar means) to override the
+ * reset method will still work as expected. @device_phases_reset function
+ * will be registered as the parent reset method and effectively call
+ * parent reset phases.
+ */
+ dc->reset = device_phases_reset;
+ rc->get_transitional_function = device_get_transitional_reset;
+
+ object_class_property_add_bool(class, "realized",
+ device_get_realized, device_set_realized);
+ object_class_property_add_bool(class, "hotpluggable",
+ device_get_hotpluggable, NULL);
+ object_class_property_add_bool(class, "hotplugged",
+ device_get_hotplugged, NULL);
+ object_class_property_add_link(class, "parent_bus", TYPE_BUS,
+ offsetof(DeviceState, parent_bus), NULL, 0);
}
void device_class_set_parent_reset(DeviceClass *dc,
@@ -1077,15 +868,6 @@ void device_class_set_parent_unrealize(DeviceClass *dc,
dc->unrealize = dev_unrealize;
}
-void device_reset(DeviceState *dev)
-{
- DeviceClass *klass = DEVICE_GET_CLASS(dev);
-
- if (klass->reset) {
- klass->reset(dev);
- }
-}
-
Object *qdev_get_machine(void)
{
static Object *dev;
@@ -1097,6 +879,27 @@ Object *qdev_get_machine(void)
return dev;
}
+char *qdev_get_human_name(DeviceState *dev)
+{
+ g_assert(dev != NULL);
+
+ return dev->id ?
+ g_strdup(dev->id) : object_get_canonical_path(OBJECT(dev));
+}
+
+static MachineInitPhase machine_phase;
+
+bool phase_check(MachineInitPhase phase)
+{
+ return machine_phase >= phase;
+}
+
+void phase_advance(MachineInitPhase phase)
+{
+ assert(machine_phase == phase - 1);
+ machine_phase = phase;
+}
+
static const TypeInfo device_type_info = {
.name = TYPE_DEVICE,
.parent = TYPE_OBJECT,
@@ -1108,6 +911,11 @@ static const TypeInfo device_type_info = {
.class_init = device_class_init,
.abstract = true,
.class_size = sizeof(DeviceClass),
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_VMSTATE_IF },
+ { TYPE_RESETTABLE_INTERFACE },
+ { }
+ }
};
static void qdev_register_types(void)