The qdev_unplug() function contains a g_assert(hotplug_ctrl) statement,
so QEMU crashes when the user tries to device_add + device_del a device
that does not have a corresponding hotplug controller. This could be
provoked for a couple of devices in the past (see commit 4c93950659487c7ad
or 84ebd3e8c7d4fe955 for example). So devices clearly need a hotplug
controller when they should be usable with device_add.
The code in qdev_device_add() already checks whether the bus has a proper
hotplug controller, but for devices that do not have a corresponding bus,
there is no appropriate check available yet. In that case we should check
whether the machine itself provides a suitable hotplug controller and
refuse to plug the device if none is available.
Signed-off-by: Thomas Huth <thuth@redhat.com>
---
v2:
- Do the check earlier in qdev_device_add
- Use common new err_dev_del handler in case of failure
hw/core/qdev.c | 28 ++++++++++++++++++++--------
include/hw/qdev-core.h | 1 +
qdev-monitor.c | 5 +++++
3 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 606ab53..a953ec9 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -253,19 +253,31 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
dev->alias_required_for_version = required_for_version;
}
+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);
+ }
+ }
+
+ return NULL;
+}
+
HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
{
- HotplugHandler *hotplug_ctrl = NULL;
+ HotplugHandler *hotplug_ctrl;
if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
hotplug_ctrl = dev->parent_bus->hotplug_handler;
- } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) {
- MachineState *machine = MACHINE(qdev_get_machine());
- MachineClass *mc = MACHINE_GET_CLASS(machine);
-
- if (mc->get_hotplug_handler) {
- hotplug_ctrl = mc->get_hotplug_handler(machine, dev);
- }
+ } else {
+ hotplug_ctrl = qdev_get_machine_hotplug_handler(dev);
}
return hotplug_ctrl;
}
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 0891461..5aa536d 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -285,6 +285,7 @@ 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_machine_hotplug_handler(DeviceState *dev);
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 cb2b109..f93455f 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -614,6 +614,11 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
if (bus) {
qdev_set_parent_bus(dev, bus);
+ } else if (qdev_hotplug && !qdev_get_machine_hotplug_handler(dev)) {
+ /* No bus, no machine hotplug handler --> device is not hotpluggable */
+ error_setg(&err, "Device '%s' can not be hotplugged on this machine",
+ driver);
+ goto err_del_dev;
}
qdev_set_id(dev, qemu_opts_id(opts));
--
1.8.3.1
On Thu, 5 Oct 2017 14:32:18 +0200
Thomas Huth <thuth@redhat.com> wrote:
> The qdev_unplug() function contains a g_assert(hotplug_ctrl) statement,
> so QEMU crashes when the user tries to device_add + device_del a device
> that does not have a corresponding hotplug controller. This could be
> provoked for a couple of devices in the past (see commit 4c93950659487c7ad
> or 84ebd3e8c7d4fe955 for example). So devices clearly need a hotplug
> controller when they should be usable with device_add.
> The code in qdev_device_add() already checks whether the bus has a proper
> hotplug controller, but for devices that do not have a corresponding bus,
> there is no appropriate check available yet. In that case we should check
> whether the machine itself provides a suitable hotplug controller and
> refuse to plug the device if none is available.
>
> Signed-off-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
> ---
> v2:
> - Do the check earlier in qdev_device_add
> - Use common new err_dev_del handler in case of failure
>
> hw/core/qdev.c | 28 ++++++++++++++++++++--------
> include/hw/qdev-core.h | 1 +
> qdev-monitor.c | 5 +++++
> 3 files changed, 26 insertions(+), 8 deletions(-)
>
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index 606ab53..a953ec9 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -253,19 +253,31 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
> dev->alias_required_for_version = required_for_version;
> }
>
> +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);
> + }
> + }
> +
> + return NULL;
> +}
> +
> HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
> {
> - HotplugHandler *hotplug_ctrl = NULL;
> + HotplugHandler *hotplug_ctrl;
>
> if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
> hotplug_ctrl = dev->parent_bus->hotplug_handler;
> - } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) {
> - MachineState *machine = MACHINE(qdev_get_machine());
> - MachineClass *mc = MACHINE_GET_CLASS(machine);
> -
> - if (mc->get_hotplug_handler) {
> - hotplug_ctrl = mc->get_hotplug_handler(machine, dev);
> - }
> + } else {
> + hotplug_ctrl = qdev_get_machine_hotplug_handler(dev);
> }
> return hotplug_ctrl;
> }
> diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
> index 0891461..5aa536d 100644
> --- a/include/hw/qdev-core.h
> +++ b/include/hw/qdev-core.h
> @@ -285,6 +285,7 @@ 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_machine_hotplug_handler(DeviceState *dev);
> 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 cb2b109..f93455f 100644
> --- a/qdev-monitor.c
> +++ b/qdev-monitor.c
> @@ -614,6 +614,11 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
>
> if (bus) {
> qdev_set_parent_bus(dev, bus);
> + } else if (qdev_hotplug && !qdev_get_machine_hotplug_handler(dev)) {
> + /* No bus, no machine hotplug handler --> device is not hotpluggable */
> + error_setg(&err, "Device '%s' can not be hotplugged on this machine",
> + driver);
> + goto err_del_dev;
> }
>
> qdev_set_id(dev, qemu_opts_id(opts));
© 2016 - 2026 Red Hat, Inc.