mirror of
https://gitlab.com/qemu-project/qemu.git
synced 2026-08-26 22:23:12 -04:00
Commit9940b2cfbc("qdev: New qdev_new(), qdev_realize(), etc.") says "device state 'no QOM parent, but plugged into bus' is dangerous". In such a case, unrealizing the bus will hang in bus_unparent(): while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { DeviceState *dev = kid->child; object_unparent(OBJECT(dev)); } object_unparent() does nothing when its argument has no QOM parent, and the loop spins forever. However, that commit did not completely eliminate such a situation. When the device is not parented, device_set_realized() lets /machine/unattached parent it, but it happens after setting parent bus. Therefore, any failure between the two operations can leave the device in a dangerous state. qdev_realize() at least asserts that the device is not already realized and prevents one realization failure pattern, but it is not comprehensive. Besides, it will trip with a command line like the following: qemu-system-x86_64 -M none -nodefaults -nographic \ -device ipmi-bmc-sim,realized=on Eliminate the dangerous state by ensuring that the device is parented before calling qdev_set_parent_bus(). Also, stop asserting that the device is not already realized in qdev_realize(); it is broken and no longer serves any purpose. Fixes:9940b2cfbc("qdev: New qdev_new(), qdev_realize(), etc.") Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Message-ID: <20260721-qdev-v3-14-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp>
109 lines
2.9 KiB
C
109 lines
2.9 KiB
C
#include "qemu/osdep.h"
|
|
#include "hw/core/qdev-properties.h"
|
|
#include "qom/object.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/visitor.h"
|
|
|
|
|
|
#define TYPE_MY_DEV "my-dev"
|
|
typedef struct MyDev MyDev;
|
|
DECLARE_INSTANCE_CHECKER(MyDev, STATIC_TYPE,
|
|
TYPE_MY_DEV)
|
|
|
|
struct MyDev {
|
|
DeviceState parent_obj;
|
|
|
|
uint32_t prop_u32;
|
|
char *prop_string;
|
|
uint32_t *prop_array_u32;
|
|
uint32_t prop_array_u32_nb;
|
|
};
|
|
|
|
static const Property my_dev_props[] = {
|
|
DEFINE_PROP_UINT32("u32", MyDev, prop_u32, 100),
|
|
DEFINE_PROP_STRING("string", MyDev, prop_string),
|
|
DEFINE_PROP_ARRAY("array-u32", MyDev, prop_array_u32_nb, prop_array_u32,
|
|
qdev_prop_uint32, uint32_t),
|
|
};
|
|
|
|
static void my_dev_class_init(ObjectClass *oc, const void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
|
|
|
dc->realize = NULL;
|
|
device_class_set_props(dc, my_dev_props);
|
|
}
|
|
|
|
static const TypeInfo my_dev_type_info = {
|
|
.name = TYPE_MY_DEV,
|
|
.parent = TYPE_DEVICE,
|
|
.instance_size = sizeof(MyDev),
|
|
.class_init = my_dev_class_init,
|
|
};
|
|
|
|
/*
|
|
* Initialize a fake machine, being prepared for future tests.
|
|
*
|
|
* Realization of anonymous qdev (with no parent object) requires both
|
|
* the machine object and its "unattached" container to be at least present.
|
|
*/
|
|
static void test_init_machine(void)
|
|
{
|
|
/* This is a fake machine - it doesn't need to be a machine object */
|
|
Object *machine = object_property_add_new_container(
|
|
object_get_root(), "machine");
|
|
|
|
/* This container must exist for anonymous qdevs to realize() */
|
|
object_property_add_new_container(machine, "unattached");
|
|
}
|
|
|
|
static void test_qdev_free_properties(void)
|
|
{
|
|
MyDev *mt;
|
|
|
|
mt = STATIC_TYPE(object_new(TYPE_MY_DEV));
|
|
object_set_props(OBJECT(mt), &error_fatal,
|
|
"string", "something",
|
|
"array-u32", "12,13",
|
|
NULL);
|
|
qdev_realize(DEVICE(mt), NULL, &error_fatal);
|
|
|
|
g_assert_cmpuint(mt->prop_u32, ==, 100);
|
|
g_assert_cmpstr(mt->prop_string, ==, "something");
|
|
g_assert_cmpuint(mt->prop_array_u32_nb, ==, 2);
|
|
g_assert_cmpuint(mt->prop_array_u32[0], ==, 12);
|
|
g_assert_cmpuint(mt->prop_array_u32[1], ==, 13);
|
|
|
|
object_unparent(OBJECT(mt));
|
|
object_unref(mt);
|
|
}
|
|
|
|
static void test_qdev_double_realization(void)
|
|
{
|
|
MyDev *mt = STATIC_TYPE(object_new(TYPE_MY_DEV));
|
|
|
|
qdev_realize(DEVICE(mt), NULL, &error_fatal);
|
|
qdev_realize(DEVICE(mt), NULL, &error_fatal);
|
|
object_unparent(OBJECT(mt));
|
|
object_unref(OBJECT(mt));
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
module_call_init(MODULE_INIT_QOM);
|
|
type_register_static(&my_dev_type_info);
|
|
test_init_machine();
|
|
|
|
g_test_add_func("/qdev/free-properties",
|
|
test_qdev_free_properties);
|
|
|
|
g_test_add_func("/qdev/double-realization",
|
|
test_qdev_double_realization);
|
|
|
|
g_test_run();
|
|
|
|
return 0;
|
|
}
|