* [PATCH 0/4] QOM: Singleton interface
@ 2024-10-24 16:56 Peter Xu
2024-10-24 16:56 ` [PATCH 1/4] qom: TYPE_SINGLETON interface Peter Xu
` (4 more replies)
0 siblings, 5 replies; 38+ messages in thread
From: Peter Xu @ 2024-10-24 16:56 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Daniel P . Berrangé, Alex Williamson, Paolo Bonzini,
Peter Maydell
This patchset introduces the singleton interface for QOM.
The singleton interface is as simple as "this class can only create one
instance".
We used to have similar demand when working on all kinds of vIOMMUs,
because in most cases that I am aware of, vIOMMU must be a singleton as
it's closely bound to the machine and also the root PCIe systems. We used
to have a bunch of special code guarding those, for example, X86 has
pc_machine_device_pre_plug_cb() just to detect when vIOMMU is created more
than once.
There is a similar demand raising recently (even if the problem existed
over years) when we were looking at a migration bug, that part of it was
involved with the current_migration global pointer being referenced
anywhere, even after the migration object was finalize()ed. So far without
singleton support, there's no way to reset the variable properly.
Declaring migration object to be a singleton also just makes sense, e.g.,
dynamically creating migration objects on the fly with QMP commands doesn't
sound right..
The idea behind is pretty simple: any object that can only be created once
can now declare the TYPE_SINGLETON interface, then QOM facilities will make
sure it won't be created more than once. For example, qom-list-properties,
device-list-properties, etc., will be smart enough to not try to create
temporary singleton objects now. Meanwhile, we also guard device-add paths
so that it'll fail properly if it's created more than once (and iff it's a
TYPE_DEVICE first).
The 1st patch introduces the QOM interface, while I made both x86-iommu and
migration as the initial users of it, so it's really more about the 1st
patch to discuss first. I didn't yet touch ARM/SMMU as it's also more
complicated; I also didn't yet dig anything else that may apply, but I do
feel like this can apply more than the two attached here. Hopefully the
two use cases can be good enough to start the discussion.
Thanks,
Peter Xu (4):
qom: TYPE_SINGLETON interface
x86/iommu: Make x86-iommu a singleton object
migration: Make migration object a singleton object
migration: Reset current_migration properly
include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
hw/i386/x86-iommu.c | 14 ++++++++++
migration/migration.c | 36 ++++++++++++++++++++++---
qom/object.c | 3 +++
qom/object_interfaces.c | 24 +++++++++++++++++
qom/qom-qmp-cmds.c | 22 ++++++++++++---
system/qdev-monitor.c | 7 +++++
7 files changed, 147 insertions(+), 6 deletions(-)
--
2.45.0
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-24 16:56 [PATCH 0/4] QOM: Singleton interface Peter Xu
@ 2024-10-24 16:56 ` Peter Xu
2024-10-24 20:02 ` Philippe Mathieu-Daudé
` (2 more replies)
2024-10-24 16:56 ` [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object Peter Xu
` (3 subsequent siblings)
4 siblings, 3 replies; 38+ messages in thread
From: Peter Xu @ 2024-10-24 16:56 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Daniel P . Berrangé, Alex Williamson, Paolo Bonzini,
Peter Maydell
Signed-off-by: Peter Xu <peterx@redhat.com>
---
include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
qom/object.c | 3 +++
qom/object_interfaces.c | 24 +++++++++++++++++
qom/qom-qmp-cmds.c | 22 ++++++++++++---
system/qdev-monitor.c | 7 +++++
5 files changed, 100 insertions(+), 3 deletions(-)
diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
index 02b11a7ef0..9b2cc0e554 100644
--- a/include/qom/object_interfaces.h
+++ b/include/qom/object_interfaces.h
@@ -177,4 +177,51 @@ bool user_creatable_del(const char *id, Error **errp);
*/
void user_creatable_cleanup(void);
+#define TYPE_SINGLETON "singleton"
+
+typedef struct SingletonClass SingletonClass;
+DECLARE_CLASS_CHECKERS(SingletonClass, SINGLETON, TYPE_SINGLETON)
+
+/**
+ * SingletonClass:
+ *
+ * @parent_class: the base class
+ * @get_instance: fetch the singleton instance if it is created,
+ * NULL otherwise.
+ *
+ * Singleton class describes the type of object classes that can only
+ * provide one instance for the whole lifecycle of QEMU. It will fail the
+ * operation if one attemps to create more than one instance.
+ *
+ * One can fetch the single object using class's get_instance() callback if
+ * it was created before. This can be useful for operations like QMP
+ * qom-list-properties, where dynamically creating an object might not be
+ * feasible.
+ */
+struct SingletonClass {
+ /* <private> */
+ InterfaceClass parent_class;
+ /* <public> */
+ Object *(*get_instance)(Error **errp);
+};
+
+/**
+ * object_class_is_singleton:
+ *
+ * @class: the class to detect singleton
+ *
+ * Returns: true if it's a singleton class, false otherwise.
+ */
+bool object_class_is_singleton(ObjectClass *class);
+
+/**
+ * singleton_get_instance:
+ *
+ * @class: the class to fetch singleton instance
+ *
+ * Returns: the object* if the class is a singleton class and the singleton
+ * object is created, NULL otherwise.
+ */
+Object *singleton_get_instance(ObjectClass *class);
+
#endif
diff --git a/qom/object.c b/qom/object.c
index 11424cf471..ded299ae1a 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -553,6 +553,9 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type
g_assert(type->abstract == false);
g_assert(size >= type->instance_size);
+ /* Singleton class can only create one object */
+ g_assert(!singleton_get_instance(type->class));
+
memset(obj, 0, type->instance_size);
obj->class = type->class;
object_ref(obj);
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index e0833c8bfe..6766060d0a 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -354,6 +354,23 @@ void user_creatable_cleanup(void)
object_unparent(object_get_objects_root());
}
+bool object_class_is_singleton(ObjectClass *class)
+{
+ return !!object_class_dynamic_cast(class, TYPE_SINGLETON);
+}
+
+Object *singleton_get_instance(ObjectClass *class)
+{
+ SingletonClass *singleton =
+ (SingletonClass *)object_class_dynamic_cast(class, TYPE_SINGLETON);
+
+ if (!singleton) {
+ return NULL;
+ }
+
+ return singleton->get_instance(&error_abort);
+}
+
static void register_types(void)
{
static const TypeInfo uc_interface_info = {
@@ -362,7 +379,14 @@ static void register_types(void)
.class_size = sizeof(UserCreatableClass),
};
+ static const TypeInfo singleton_interface_info = {
+ .name = TYPE_SINGLETON,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(SingletonClass),
+ };
+
type_register_static(&uc_interface_info);
+ type_register_static(&singleton_interface_info);
}
type_init(register_types)
diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
index e91a235347..ecc1cf781c 100644
--- a/qom/qom-qmp-cmds.c
+++ b/qom/qom-qmp-cmds.c
@@ -126,6 +126,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
ObjectProperty *prop;
ObjectPropertyIterator iter;
ObjectPropertyInfoList *prop_list = NULL;
+ bool create;
klass = module_object_class_by_name(typename);
if (klass == NULL) {
@@ -141,7 +142,15 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
return NULL;
}
- obj = object_new(typename);
+ /* Avoid creating multiple instances if the class is a singleton */
+ create = !object_class_is_singleton(klass) ||
+ !singleton_get_instance(klass);
+
+ if (create) {
+ obj = object_new(typename);
+ } else {
+ obj = singleton_get_instance(klass);
+ }
object_property_iter_init(&iter, obj);
while ((prop = object_property_iter_next(&iter))) {
@@ -172,7 +181,9 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
QAPI_LIST_PREPEND(prop_list, info);
}
- object_unref(obj);
+ if (create) {
+ object_unref(obj);
+ }
return prop_list;
}
@@ -199,7 +210,12 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
return NULL;
}
- if (object_class_is_abstract(klass)) {
+ /*
+ * Abstract classes are not for instantiations, meanwhile avoid
+ * creating temporary singleton objects because it can cause conflicts
+ * if there's already one created.
+ */
+ if (object_class_is_abstract(klass) || object_class_is_singleton(klass)) {
object_class_property_iter_init(&iter, klass);
} else {
obj = object_new(typename);
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c
index 44994ea0e1..1310f35c9f 100644
--- a/system/qdev-monitor.c
+++ b/system/qdev-monitor.c
@@ -36,6 +36,7 @@
#include "qemu/option.h"
#include "qemu/qemu-print.h"
#include "qemu/option_int.h"
+#include "qom/object_interfaces.h"
#include "sysemu/block-backend.h"
#include "migration/misc.h"
#include "qemu/cutils.h"
@@ -643,6 +644,12 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts,
return NULL;
}
+ if (singleton_get_instance(OBJECT_CLASS(dc))) {
+ error_setg(errp, "Class '%s' only supports one instance",
+ driver);
+ return NULL;
+ }
+
/* find bus */
path = qdict_get_try_str(opts, "bus");
if (path != NULL) {
--
2.45.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object
2024-10-24 16:56 [PATCH 0/4] QOM: Singleton interface Peter Xu
2024-10-24 16:56 ` [PATCH 1/4] qom: TYPE_SINGLETON interface Peter Xu
@ 2024-10-24 16:56 ` Peter Xu
2024-10-25 9:25 ` Markus Armbruster
2024-10-29 10:47 ` Daniel P. Berrangé
2024-10-24 16:56 ` [PATCH 3/4] migration: Make migration object " Peter Xu
` (2 subsequent siblings)
4 siblings, 2 replies; 38+ messages in thread
From: Peter Xu @ 2024-10-24 16:56 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Daniel P . Berrangé, Alex Williamson, Paolo Bonzini,
Peter Maydell
X86 IOMMUs cannot be created more than one on a system yet. Make it a
singleton so it guards the system from accidentally create yet another
IOMMU object when one already presents.
Now if someone tries to create more than one, e.g., via:
./qemu -M q35 -device intel-iommu -device intel-iommu
The error will change from:
qemu-system-x86_64: -device intel-iommu: QEMU does not support multiple vIOMMUs for x86 yet.
To:
qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only supports one instance
Unfortunately, yet we can't remove the singleton check in the machine
hook (pc_machine_device_pre_plug_cb), because there can also be
virtio-iommu involved, which doesn't share a common parent class yet.
But with this, it should be closer to reach that goal to check singleton by
QOM one day.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
hw/i386/x86-iommu.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c
index 60af896225..4bfeb08705 100644
--- a/hw/i386/x86-iommu.c
+++ b/hw/i386/x86-iommu.c
@@ -26,6 +26,7 @@
#include "qemu/error-report.h"
#include "trace.h"
#include "sysemu/kvm.h"
+#include "qom/object_interfaces.h"
void x86_iommu_iec_register_notifier(X86IOMMUState *iommu,
iec_notify_fn fn, void *data)
@@ -133,10 +134,19 @@ static Property x86_iommu_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+static Object *x86_iommu_get_instance(Error **errp)
+{
+ return OBJECT(x86_iommu_get_default());
+}
+
static void x86_iommu_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ SingletonClass *singleton = SINGLETON_CLASS(klass);
+
dc->realize = x86_iommu_realize;
+ singleton->get_instance = x86_iommu_get_instance;
+
device_class_set_props(dc, x86_iommu_properties);
}
@@ -152,6 +162,10 @@ static const TypeInfo x86_iommu_info = {
.class_init = x86_iommu_class_init,
.class_size = sizeof(X86IOMMUClass),
.abstract = true,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_SINGLETON },
+ { }
+ }
};
static void x86_iommu_register_types(void)
--
2.45.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH 3/4] migration: Make migration object a singleton object
2024-10-24 16:56 [PATCH 0/4] QOM: Singleton interface Peter Xu
2024-10-24 16:56 ` [PATCH 1/4] qom: TYPE_SINGLETON interface Peter Xu
2024-10-24 16:56 ` [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object Peter Xu
@ 2024-10-24 16:56 ` Peter Xu
2024-10-24 19:20 ` Fabiano Rosas
2024-10-24 16:56 ` [PATCH 4/4] migration: Reset current_migration properly Peter Xu
2024-10-25 7:38 ` [PATCH 0/4] QOM: Singleton interface Markus Armbruster
4 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-24 16:56 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Daniel P . Berrangé, Alex Williamson, Paolo Bonzini,
Peter Maydell
This makes the migration object a singleton unit. After this, we can do
something slightly tricky later on with the guarantee that nobody will be
able to create the object twice.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
migration/migration.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/migration/migration.c b/migration/migration.c
index bcb735869b..1b5285af95 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -45,6 +45,7 @@
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qnull.h"
#include "qemu/rcu.h"
+#include "qom/object_interfaces.h"
#include "postcopy-ram.h"
#include "qemu/thread.h"
#include "trace.h"
@@ -3855,11 +3856,19 @@ fail:
migrate_fd_cleanup(s);
}
+static Object* migration_get_instance(Error **errp)
+{
+ return OBJECT(current_migration);
+}
+
static void migration_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ SingletonClass *singleton = SINGLETON_CLASS(klass);
dc->user_creatable = false;
+ singleton->get_instance = migration_get_instance;
+
device_class_set_props(dc, migration_properties);
}
@@ -3932,6 +3941,10 @@ static const TypeInfo migration_type = {
.instance_size = sizeof(MigrationState),
.instance_init = migration_instance_init,
.instance_finalize = migration_instance_finalize,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_SINGLETON },
+ { }
+ }
};
static void register_migration_types(void)
--
2.45.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH 4/4] migration: Reset current_migration properly
2024-10-24 16:56 [PATCH 0/4] QOM: Singleton interface Peter Xu
` (2 preceding siblings ...)
2024-10-24 16:56 ` [PATCH 3/4] migration: Make migration object " Peter Xu
@ 2024-10-24 16:56 ` Peter Xu
2024-10-24 19:34 ` Fabiano Rosas
2024-10-25 7:38 ` [PATCH 0/4] QOM: Singleton interface Markus Armbruster
4 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-24 16:56 UTC (permalink / raw)
To: qemu-devel
Cc: peterx, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Daniel P . Berrangé, Alex Williamson, Paolo Bonzini,
Peter Maydell
current_migration is never reset, even if the migration object is freed
already. It means anyone references that can trigger UAF and it'll be hard
to debug.
Properly clear the pointer now, so far the only way to do is via
finalize() as we know there's only one instance of it, meanwhile QEMU won't
know who holds the refcount, so it can't reset the variable manually but
only in finalize().
To make it more readable, also initialize the variable in the
instance_init() so it's very well paired at least.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
migration/migration.c | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/migration/migration.c b/migration/migration.c
index 1b5285af95..74812ca785 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -233,9 +233,11 @@ static int migration_stop_vm(MigrationState *s, RunState state)
void migration_object_init(void)
{
- /* This can only be called once. */
- assert(!current_migration);
- current_migration = MIGRATION_OBJ(object_new(TYPE_MIGRATION));
+ /* This creates the singleton migration object */
+ object_new(TYPE_MIGRATION);
+
+ /* This should be set now when initialize the singleton object */
+ assert(current_migration);
/*
* Init the migrate incoming object as well no matter whether
@@ -3886,12 +3888,27 @@ static void migration_instance_finalize(Object *obj)
qemu_sem_destroy(&ms->rp_state.rp_pong_acks);
qemu_sem_destroy(&ms->postcopy_qemufile_src_sem);
error_free(ms->error);
+
+ /*
+ * We know we only have one intance of migration, and when reaching
+ * here it means migration object is gone. Clear the global reference
+ * to reflect that.
+ */
+ current_migration = NULL;
}
static void migration_instance_init(Object *obj)
{
MigrationState *ms = MIGRATION_OBJ(obj);
+ /*
+ * There can only be one migration object globally. Keep a record of
+ * the pointer in current_migration, which will be reset after the
+ * object finalize().
+ */
+ assert(!current_migration);
+ current_migration = ms;
+
ms->state = MIGRATION_STATUS_NONE;
ms->mbps = -1;
ms->pages_per_second = -1;
--
2.45.0
^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH 3/4] migration: Make migration object a singleton object
2024-10-24 16:56 ` [PATCH 3/4] migration: Make migration object " Peter Xu
@ 2024-10-24 19:20 ` Fabiano Rosas
0 siblings, 0 replies; 38+ messages in thread
From: Fabiano Rosas @ 2024-10-24 19:20 UTC (permalink / raw)
To: Peter Xu, qemu-devel
Cc: peterx, Igor Mammedov, Juraj Marcin, Michael S . Tsirkin,
Dr . David Alan Gilbert, Cédric Le Goater, Markus Armbruster,
Eduardo Habkost, Daniel P . Berrangé, Alex Williamson,
Paolo Bonzini, Peter Maydell
Peter Xu <peterx@redhat.com> writes:
> This makes the migration object a singleton unit. After this, we can do
> something slightly tricky later on with the guarantee that nobody will be
> able to create the object twice.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> migration/migration.c | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/migration/migration.c b/migration/migration.c
> index bcb735869b..1b5285af95 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -45,6 +45,7 @@
> #include "qapi/qmp/qerror.h"
> #include "qapi/qmp/qnull.h"
> #include "qemu/rcu.h"
> +#include "qom/object_interfaces.h"
> #include "postcopy-ram.h"
> #include "qemu/thread.h"
> #include "trace.h"
> @@ -3855,11 +3856,19 @@ fail:
> migrate_fd_cleanup(s);
> }
>
> +static Object* migration_get_instance(Error **errp)
static Object *migration_get_instance(Error **errp)
^
> +{
> + return OBJECT(current_migration);
> +}
> +
> static void migration_class_init(ObjectClass *klass, void *data)
> {
> DeviceClass *dc = DEVICE_CLASS(klass);
> + SingletonClass *singleton = SINGLETON_CLASS(klass);
>
> dc->user_creatable = false;
> + singleton->get_instance = migration_get_instance;
> +
> device_class_set_props(dc, migration_properties);
> }
>
> @@ -3932,6 +3941,10 @@ static const TypeInfo migration_type = {
> .instance_size = sizeof(MigrationState),
> .instance_init = migration_instance_init,
> .instance_finalize = migration_instance_finalize,
> + .interfaces = (InterfaceInfo[]) {
> + { TYPE_SINGLETON },
> + { }
> + }
> };
>
> static void register_migration_types(void)
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 4/4] migration: Reset current_migration properly
2024-10-24 16:56 ` [PATCH 4/4] migration: Reset current_migration properly Peter Xu
@ 2024-10-24 19:34 ` Fabiano Rosas
2024-10-24 20:15 ` Peter Xu
0 siblings, 1 reply; 38+ messages in thread
From: Fabiano Rosas @ 2024-10-24 19:34 UTC (permalink / raw)
To: Peter Xu, qemu-devel
Cc: peterx, Igor Mammedov, Juraj Marcin, Michael S . Tsirkin,
Dr . David Alan Gilbert, Cédric Le Goater, Markus Armbruster,
Eduardo Habkost, Daniel P . Berrangé, Alex Williamson,
Paolo Bonzini, Peter Maydell
Peter Xu <peterx@redhat.com> writes:
> current_migration is never reset, even if the migration object is freed
> already. It means anyone references that can trigger UAF and it'll be hard
> to debug.
>
> Properly clear the pointer now, so far the only way to do is via
> finalize() as we know there's only one instance of it, meanwhile QEMU won't
> know who holds the refcount, so it can't reset the variable manually but
> only in finalize().
>
> To make it more readable, also initialize the variable in the
> instance_init() so it's very well paired at least.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> migration/migration.c | 23 ++++++++++++++++++++---
> 1 file changed, 20 insertions(+), 3 deletions(-)
>
> diff --git a/migration/migration.c b/migration/migration.c
> index 1b5285af95..74812ca785 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -233,9 +233,11 @@ static int migration_stop_vm(MigrationState *s, RunState state)
>
> void migration_object_init(void)
> {
> - /* This can only be called once. */
> - assert(!current_migration);
> - current_migration = MIGRATION_OBJ(object_new(TYPE_MIGRATION));
> + /* This creates the singleton migration object */
> + object_new(TYPE_MIGRATION);
> +
> + /* This should be set now when initialize the singleton object */
> + assert(current_migration);
>
> /*
> * Init the migrate incoming object as well no matter whether
> @@ -3886,12 +3888,27 @@ static void migration_instance_finalize(Object *obj)
> qemu_sem_destroy(&ms->rp_state.rp_pong_acks);
> qemu_sem_destroy(&ms->postcopy_qemufile_src_sem);
> error_free(ms->error);
> +
> + /*
> + * We know we only have one intance of migration, and when reaching
instance
> + * here it means migration object is gone. Clear the global reference
> + * to reflect that.
Not really gone at this point. The free only happens when this function
returns.
> + */
> + current_migration = NULL;
> }
>
> static void migration_instance_init(Object *obj)
> {
> MigrationState *ms = MIGRATION_OBJ(obj);
>
> + /*
> + * There can only be one migration object globally. Keep a record of
> + * the pointer in current_migration, which will be reset after the
> + * object finalize().
> + */
> + assert(!current_migration);
> + current_migration = ms;
> +
> ms->state = MIGRATION_STATUS_NONE;
> ms->mbps = -1;
> ms->pages_per_second = -1;
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-24 16:56 ` [PATCH 1/4] qom: TYPE_SINGLETON interface Peter Xu
@ 2024-10-24 20:02 ` Philippe Mathieu-Daudé
2024-10-24 20:53 ` Peter Xu
2024-10-25 8:07 ` Markus Armbruster
2024-10-25 9:51 ` Daniel P. Berrangé
2 siblings, 1 reply; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-10-24 20:02 UTC (permalink / raw)
To: Peter Xu, qemu-devel, Mark Cave-Ayland
Cc: Fabiano Rosas, Igor Mammedov, Juraj Marcin, Michael S . Tsirkin,
Dr . David Alan Gilbert, Cédric Le Goater, Markus Armbruster,
Eduardo Habkost, Daniel P . Berrangé, Alex Williamson,
Paolo Bonzini, Peter Maydell
Hi Peter,
(Cc'ing Mark)
On 24/10/24 13:56, Peter Xu wrote:
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
> qom/object.c | 3 +++
> qom/object_interfaces.c | 24 +++++++++++++++++
> qom/qom-qmp-cmds.c | 22 ++++++++++++---
> system/qdev-monitor.c | 7 +++++
> 5 files changed, 100 insertions(+), 3 deletions(-)
> +/**
> + * SingletonClass:
> + *
> + * @parent_class: the base class
> + * @get_instance: fetch the singleton instance if it is created,
> + * NULL otherwise.
> + *
> + * Singleton class describes the type of object classes that can only
> + * provide one instance for the whole lifecycle of QEMU. It will fail the
> + * operation if one attemps to create more than one instance.
> + *
> + * One can fetch the single object using class's get_instance() callback if
> + * it was created before. This can be useful for operations like QMP
> + * qom-list-properties, where dynamically creating an object might not be
> + * feasible.
> + */
> +struct SingletonClass {
> + /* <private> */
> + InterfaceClass parent_class;
> + /* <public> */
> + Object *(*get_instance)(Error **errp);
IMHO asking get_instance() is overkill ...
> +};
> +
> +/**
> + * object_class_is_singleton:
> + *
> + * @class: the class to detect singleton
> + *
> + * Returns: true if it's a singleton class, false otherwise.
> + */
> +bool object_class_is_singleton(ObjectClass *class);
> +
> +/**
> + * singleton_get_instance:
> + *
> + * @class: the class to fetch singleton instance
> + *
> + * Returns: the object* if the class is a singleton class and the singleton
> + * object is created, NULL otherwise.
> + */
> +Object *singleton_get_instance(ObjectClass *class);
> +
> #endif
> diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
> index e0833c8bfe..6766060d0a 100644
> --- a/qom/object_interfaces.c
> +++ b/qom/object_interfaces.c
> @@ -354,6 +354,23 @@ void user_creatable_cleanup(void)
> object_unparent(object_get_objects_root());
> }
>
> +bool object_class_is_singleton(ObjectClass *class)
> +{
> + return !!object_class_dynamic_cast(class, TYPE_SINGLETON);
> +}
> +
> +Object *singleton_get_instance(ObjectClass *class)
> +{
... when we can use object_resolve_type_unambiguous:
return
object_resolve_type_unambiguous(object_class_get_name(class, NULL));
BTW should we pass Error** argument to singleton_get_instance()?
> + SingletonClass *singleton =
> + (SingletonClass *)object_class_dynamic_cast(class, TYPE_SINGLETON);
> +
> + if (!singleton) {
> + return NULL;
> + }
> +
> + return singleton->get_instance(&error_abort);
> +}
Alternatively call object_resolve_type_unambiguous() in instance_init()?
Regards,
Phil.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 4/4] migration: Reset current_migration properly
2024-10-24 19:34 ` Fabiano Rosas
@ 2024-10-24 20:15 ` Peter Xu
2024-10-24 20:51 ` Fabiano Rosas
0 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-24 20:15 UTC (permalink / raw)
To: Fabiano Rosas
Cc: qemu-devel, Igor Mammedov, Juraj Marcin, Michael S . Tsirkin,
Dr . David Alan Gilbert, Cédric Le Goater, Markus Armbruster,
Eduardo Habkost, Daniel P . Berrangé, Alex Williamson,
Paolo Bonzini, Peter Maydell
On Thu, Oct 24, 2024 at 04:34:44PM -0300, Fabiano Rosas wrote:
> > + * here it means migration object is gone. Clear the global reference
> > + * to reflect that.
>
> Not really gone at this point. The free only happens when this function
> returns.
How about "is going away"?
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 4/4] migration: Reset current_migration properly
2024-10-24 20:15 ` Peter Xu
@ 2024-10-24 20:51 ` Fabiano Rosas
0 siblings, 0 replies; 38+ messages in thread
From: Fabiano Rosas @ 2024-10-24 20:51 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Igor Mammedov, Juraj Marcin, Michael S . Tsirkin,
Dr . David Alan Gilbert, Cédric Le Goater, Markus Armbruster,
Eduardo Habkost, Daniel P . Berrangé, Alex Williamson,
Paolo Bonzini, Peter Maydell
Peter Xu <peterx@redhat.com> writes:
> On Thu, Oct 24, 2024 at 04:34:44PM -0300, Fabiano Rosas wrote:
>> > + * here it means migration object is gone. Clear the global reference
>> > + * to reflect that.
>>
>> Not really gone at this point. The free only happens when this function
>> returns.
>
> How about "is going away"?
Yep
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-24 20:02 ` Philippe Mathieu-Daudé
@ 2024-10-24 20:53 ` Peter Xu
2024-10-25 15:11 ` Philippe Mathieu-Daudé
0 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-24 20:53 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: qemu-devel, Mark Cave-Ayland, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Daniel P . Berrangé, Alex Williamson, Paolo Bonzini,
Peter Maydell
On Thu, Oct 24, 2024 at 05:02:19PM -0300, Philippe Mathieu-Daudé wrote:
> Hi Peter,
Hi, Phil,
Thanks for the quick reviews!
>
> (Cc'ing Mark)
>
> On 24/10/24 13:56, Peter Xu wrote:
> > Signed-off-by: Peter Xu <peterx@redhat.com>
> > ---
> > include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
> > qom/object.c | 3 +++
> > qom/object_interfaces.c | 24 +++++++++++++++++
> > qom/qom-qmp-cmds.c | 22 ++++++++++++---
> > system/qdev-monitor.c | 7 +++++
> > 5 files changed, 100 insertions(+), 3 deletions(-)
>
>
> > +/**
> > + * SingletonClass:
> > + *
> > + * @parent_class: the base class
> > + * @get_instance: fetch the singleton instance if it is created,
> > + * NULL otherwise.
> > + *
> > + * Singleton class describes the type of object classes that can only
> > + * provide one instance for the whole lifecycle of QEMU. It will fail the
> > + * operation if one attemps to create more than one instance.
> > + *
> > + * One can fetch the single object using class's get_instance() callback if
> > + * it was created before. This can be useful for operations like QMP
> > + * qom-list-properties, where dynamically creating an object might not be
> > + * feasible.
> > + */
> > +struct SingletonClass {
> > + /* <private> */
> > + InterfaceClass parent_class;
> > + /* <public> */
> > + Object *(*get_instance)(Error **errp);
>
> IMHO asking get_instance() is overkill ...
>
> > +};
> > +
> > +/**
> > + * object_class_is_singleton:
> > + *
> > + * @class: the class to detect singleton
> > + *
> > + * Returns: true if it's a singleton class, false otherwise.
> > + */
> > +bool object_class_is_singleton(ObjectClass *class);
> > +
> > +/**
> > + * singleton_get_instance:
> > + *
> > + * @class: the class to fetch singleton instance
> > + *
> > + * Returns: the object* if the class is a singleton class and the singleton
> > + * object is created, NULL otherwise.
> > + */
> > +Object *singleton_get_instance(ObjectClass *class);
> > +
> > #endif
>
> > diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
> > index e0833c8bfe..6766060d0a 100644
> > --- a/qom/object_interfaces.c
> > +++ b/qom/object_interfaces.c
> > @@ -354,6 +354,23 @@ void user_creatable_cleanup(void)
> > object_unparent(object_get_objects_root());
> > }
> > +bool object_class_is_singleton(ObjectClass *class)
> > +{
> > + return !!object_class_dynamic_cast(class, TYPE_SINGLETON);
> > +}
> > +
> > +Object *singleton_get_instance(ObjectClass *class)
> > +{
>
> ... when we can use object_resolve_type_unambiguous:
>
> return object_resolve_type_unambiguous(object_class_get_name(class,
> NULL));
I think an issue is migration object is nowhere to be find under
object_get_root(), so it won't work there. A side benefit is, it's also
faster..
How about I use object_resolve_type_unambiguous() as a fallback? Then it's
used only if get_instance() is not provided.
>
> BTW should we pass Error** argument to singleton_get_instance()?
I didn't expect it to fail, hence I didn't even add it to make it more like
"this will always success or it asserts" kind of things. I left Error** in
the hook just to be slightly flexible, but I always pass in error_abort()
in this patch.
I can either add Error** if anyone thinks it useful, or even drop Error**
in ->get_instance() since it's mostly not used at least for now.
Let me know!
>
> > + SingletonClass *singleton =
> > + (SingletonClass *)object_class_dynamic_cast(class, TYPE_SINGLETON);
> > +
> > + if (!singleton) {
> > + return NULL;
> > + }
> > +
> > + return singleton->get_instance(&error_abort);
> > +}
>
> Alternatively call object_resolve_type_unambiguous() in instance_init()?
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 0/4] QOM: Singleton interface
2024-10-24 16:56 [PATCH 0/4] QOM: Singleton interface Peter Xu
` (3 preceding siblings ...)
2024-10-24 16:56 ` [PATCH 4/4] migration: Reset current_migration properly Peter Xu
@ 2024-10-25 7:38 ` Markus Armbruster
2024-10-25 15:01 ` Peter Xu
4 siblings, 1 reply; 38+ messages in thread
From: Markus Armbruster @ 2024-10-25 7:38 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Daniel P . Berrangé, Alex Williamson, Paolo Bonzini,
Peter Maydell
Peter Xu <peterx@redhat.com> writes:
> This patchset introduces the singleton interface for QOM.
>
> The singleton interface is as simple as "this class can only create one
> instance".
>
> We used to have similar demand when working on all kinds of vIOMMUs,
> because in most cases that I am aware of, vIOMMU must be a singleton as
> it's closely bound to the machine and also the root PCIe systems. We used
> to have a bunch of special code guarding those, for example, X86 has
> pc_machine_device_pre_plug_cb() just to detect when vIOMMU is created more
> than once.
>
> There is a similar demand raising recently (even if the problem existed
> over years) when we were looking at a migration bug, that part of it was
> involved with the current_migration global pointer being referenced
> anywhere, even after the migration object was finalize()ed. So far without
> singleton support, there's no way to reset the variable properly.
> Declaring migration object to be a singleton also just makes sense, e.g.,
> dynamically creating migration objects on the fly with QMP commands doesn't
> sound right..
>
> The idea behind is pretty simple: any object that can only be created once
> can now declare the TYPE_SINGLETON interface, then QOM facilities will make
> sure it won't be created more than once. For example, qom-list-properties,
> device-list-properties, etc., will be smart enough to not try to create
> temporary singleton objects now.
QOM design assumption: object_new() followed by object_unref() is always
possible and has no side effect.
QOM introspection relies on this. Your PATCH 1 makes non-class
properties of singletons invisible in introspection. Making something
with such properties a singleton would be a regression.
Anything that violates the design assumption must be delayed to a
suitable state transition. For devices (subtypes of TYPE_DEVICE), this
is ->realize(). For user-creatable objects (provide interface
TYPE_USER_CREATABLE), this is ->complete(). For anything else, it's
something else that probably doesn't even exist, yet. See "Problem 5:
QOM lacks a clear life cycle" in
Subject: Dynamic & heterogeneous machines, initial configuration: problems
Date: Wed, 31 Jan 2024 21:14:21 +0100
Message-ID: <87o7d1i7ky.fsf@pond.sub.org>
https://lore.kernel.org/qemu-devel/87o7d1i7ky.fsf@pond.sub.org/
> Meanwhile, we also guard device-add paths
> so that it'll fail properly if it's created more than once (and iff it's a
> TYPE_DEVICE first).
[...]
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-24 16:56 ` [PATCH 1/4] qom: TYPE_SINGLETON interface Peter Xu
2024-10-24 20:02 ` Philippe Mathieu-Daudé
@ 2024-10-25 8:07 ` Markus Armbruster
2024-10-25 15:17 ` Peter Xu
2024-10-25 9:51 ` Daniel P. Berrangé
2 siblings, 1 reply; 38+ messages in thread
From: Markus Armbruster @ 2024-10-25 8:07 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Daniel P . Berrangé,
Alex Williamson, Paolo Bonzini, Peter Maydell
Peter Xu <peterx@redhat.com> writes:
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
> qom/object.c | 3 +++
> qom/object_interfaces.c | 24 +++++++++++++++++
> qom/qom-qmp-cmds.c | 22 ++++++++++++---
> system/qdev-monitor.c | 7 +++++
> 5 files changed, 100 insertions(+), 3 deletions(-)
>
> diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
> index 02b11a7ef0..9b2cc0e554 100644
> --- a/include/qom/object_interfaces.h
> +++ b/include/qom/object_interfaces.h
> @@ -177,4 +177,51 @@ bool user_creatable_del(const char *id, Error **errp);
> */
> void user_creatable_cleanup(void);
>
> +#define TYPE_SINGLETON "singleton"
> +
> +typedef struct SingletonClass SingletonClass;
> +DECLARE_CLASS_CHECKERS(SingletonClass, SINGLETON, TYPE_SINGLETON)
> +
> +/**
> + * SingletonClass:
> + *
> + * @parent_class: the base class
> + * @get_instance: fetch the singleton instance if it is created,
> + * NULL otherwise.
> + *
> + * Singleton class describes the type of object classes that can only
> + * provide one instance for the whole lifecycle of QEMU. It will fail the
> + * operation if one attemps to create more than one instance.
> + *
> + * One can fetch the single object using class's get_instance() callback if
> + * it was created before. This can be useful for operations like QMP
> + * qom-list-properties, where dynamically creating an object might not be
> + * feasible.
> + */
> +struct SingletonClass {
> + /* <private> */
> + InterfaceClass parent_class;
> + /* <public> */
> + Object *(*get_instance)(Error **errp);
> +};
> +
> +/**
> + * object_class_is_singleton:
> + *
> + * @class: the class to detect singleton
> + *
> + * Returns: true if it's a singleton class, false otherwise.
> + */
> +bool object_class_is_singleton(ObjectClass *class);
> +
> +/**
> + * singleton_get_instance:
> + *
> + * @class: the class to fetch singleton instance
> + *
> + * Returns: the object* if the class is a singleton class and the singleton
> + * object is created, NULL otherwise.
> + */
> +Object *singleton_get_instance(ObjectClass *class);
A non-null return value can become dangling when the object gets
destroyed. This could conceivably happen in another thread.
The obviously safe interface would take a reference the caller must
unref when done.
> +
> #endif
> diff --git a/qom/object.c b/qom/object.c
> index 11424cf471..ded299ae1a 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -553,6 +553,9 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type
> g_assert(type->abstract == false);
> g_assert(size >= type->instance_size);
>
> + /* Singleton class can only create one object */
> + g_assert(!singleton_get_instance(type->class));
> +
> memset(obj, 0, type->instance_size);
> obj->class = type->class;
> object_ref(obj);
> diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
> index e0833c8bfe..6766060d0a 100644
> --- a/qom/object_interfaces.c
> +++ b/qom/object_interfaces.c
> @@ -354,6 +354,23 @@ void user_creatable_cleanup(void)
> object_unparent(object_get_objects_root());
> }
>
> +bool object_class_is_singleton(ObjectClass *class)
> +{
> + return !!object_class_dynamic_cast(class, TYPE_SINGLETON);
> +}
> +
> +Object *singleton_get_instance(ObjectClass *class)
> +{
> + SingletonClass *singleton =
> + (SingletonClass *)object_class_dynamic_cast(class, TYPE_SINGLETON);
> +
> + if (!singleton) {
> + return NULL;
> + }
> +
> + return singleton->get_instance(&error_abort);
> +}
> +
> static void register_types(void)
> {
> static const TypeInfo uc_interface_info = {
> @@ -362,7 +379,14 @@ static void register_types(void)
> .class_size = sizeof(UserCreatableClass),
> };
>
> + static const TypeInfo singleton_interface_info = {
> + .name = TYPE_SINGLETON,
> + .parent = TYPE_INTERFACE,
> + .class_size = sizeof(SingletonClass),
> + };
> +
> type_register_static(&uc_interface_info);
> + type_register_static(&singleton_interface_info);
> }
>
> type_init(register_types)
> diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> index e91a235347..ecc1cf781c 100644
> --- a/qom/qom-qmp-cmds.c
> +++ b/qom/qom-qmp-cmds.c
> @@ -126,6 +126,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> ObjectProperty *prop;
> ObjectPropertyIterator iter;
> ObjectPropertyInfoList *prop_list = NULL;
> + bool create;
>
> klass = module_object_class_by_name(typename);
> if (klass == NULL) {
> @@ -141,7 +142,15 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> return NULL;
> }
>
> - obj = object_new(typename);
> + /* Avoid creating multiple instances if the class is a singleton */
> + create = !object_class_is_singleton(klass) ||
> + !singleton_get_instance(klass);
> +
> + if (create) {
> + obj = object_new(typename);
If the class is not a singleton or else if no instance exists, we create
a temporary instance.
> + } else {
> + obj = singleton_get_instance(klass);
If the class is a singleton and the instance exists, we use that
instead.
Any properties the instance has created dynamically after object_new()
are visible to introspection. This is not the case when we create a
temporary instance. Such subtle differences are problematic. If we
decide we're okay with this one, we need to document it.
> + }
>
> object_property_iter_init(&iter, obj);
> while ((prop = object_property_iter_next(&iter))) {
> @@ -172,7 +181,9 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> QAPI_LIST_PREPEND(prop_list, info);
> }
>
> - object_unref(obj);
> + if (create) {
> + object_unref(obj);
> + }
>
> return prop_list;
> }
> @@ -199,7 +210,12 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
> return NULL;
> }
>
> - if (object_class_is_abstract(klass)) {
> + /*
> + * Abstract classes are not for instantiations, meanwhile avoid
> + * creating temporary singleton objects because it can cause conflicts
> + * if there's already one created.
> + */
> + if (object_class_is_abstract(klass) || object_class_is_singleton(klass)) {
> object_class_property_iter_init(&iter, klass);
> } else {
> obj = object_new(typename);
If the class is a singleton, we treat it as if it was abstract. Its
instance properties, if any, are not visible in qom-list-properties.
This is a defect. If you make an existing class with instance
properties a singleton, the defect is a regression.
> diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c
> index 44994ea0e1..1310f35c9f 100644
> --- a/system/qdev-monitor.c
> +++ b/system/qdev-monitor.c
> @@ -36,6 +36,7 @@
> #include "qemu/option.h"
> #include "qemu/qemu-print.h"
> #include "qemu/option_int.h"
> +#include "qom/object_interfaces.h"
> #include "sysemu/block-backend.h"
> #include "migration/misc.h"
> #include "qemu/cutils.h"
> @@ -643,6 +644,12 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts,
> return NULL;
> }
>
> + if (singleton_get_instance(OBJECT_CLASS(dc))) {
> + error_setg(errp, "Class '%s' only supports one instance",
> + driver);
> + return NULL;
> + }
> +
> /* find bus */
> path = qdict_get_try_str(opts, "bus");
> if (path != NULL) {
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object
2024-10-24 16:56 ` [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object Peter Xu
@ 2024-10-25 9:25 ` Markus Armbruster
2024-10-25 21:55 ` Peter Xu
2024-10-29 10:47 ` Daniel P. Berrangé
1 sibling, 1 reply; 38+ messages in thread
From: Markus Armbruster @ 2024-10-25 9:25 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Daniel P . Berrangé,
Alex Williamson, Paolo Bonzini, Peter Maydell
Peter Xu <peterx@redhat.com> writes:
> X86 IOMMUs cannot be created more than one on a system yet. Make it a
> singleton so it guards the system from accidentally create yet another
> IOMMU object when one already presents.
>
> Now if someone tries to create more than one, e.g., via:
>
> ./qemu -M q35 -device intel-iommu -device intel-iommu
>
> The error will change from:
>
> qemu-system-x86_64: -device intel-iommu: QEMU does not support multiple vIOMMUs for x86 yet.
>
> To:
>
> qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only supports one instance
>
> Unfortunately, yet we can't remove the singleton check in the machine
> hook (pc_machine_device_pre_plug_cb), because there can also be
> virtio-iommu involved, which doesn't share a common parent class yet.
>
> But with this, it should be closer to reach that goal to check singleton by
> QOM one day.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>
$ qemu-system-x86_64 -device amd-iommu,help
/work/armbru/qemu/include/hw/boards.h:24:MACHINE: Object 0x56473906f960 is not an instance of type machine
Aborted (core dumped)
(gdb) bt
#0 0x00007ffff4e43834 in __pthread_kill_implementation () at /lib64/libc.so.6
#1 0x00007ffff4df18ee in raise () at /lib64/libc.so.6
#2 0x00007ffff4dd98ff in abort () at /lib64/libc.so.6
#3 0x0000555555f75ef3 in object_dynamic_cast_assert
(obj=0x555557e03960, typename=0x5555563c403e "machine", file=0x5555563c4018 "/work/armbru/qemu/include/hw/boards.h", line=24, func=0x5555563c4290 <__func__.7> "MACHINE") at ../qom/object.c:936
#4 0x0000555555d5db0f in MACHINE (obj=0x555557e03960)
at /work/armbru/qemu/include/hw/boards.h:24
#5 0x0000555555d5e030 in x86_iommu_get_default () at ../hw/i386/x86-iommu.c:83
#6 0x0000555555d5e262 in x86_iommu_get_instance
(errp=0x5555573d4918 <error_abort>) at ../hw/i386/x86-iommu.c:139
#7 0x0000555555f7c27c in singleton_get_instance (class=0x555557e00320)
at ../qom/object_interfaces.c:371
#8 0x000055555612a842 in qmp_device_list_properties
(typename=0x555557e001d0 "amd-iommu", errp=0x7fffffffda38)
at ../qom/qom-qmp-cmds.c:147
#9 0x0000555555bf20b2 in qdev_device_help (opts=0x555557e001f0)
at ../system/qdev-monitor.c:314
#10 0x0000555555bfe06d in device_help_func
(opaque=0x0, opts=0x555557e001f0, errp=0x0) at ../system/vl.c:1208
#11 0x0000555556217186 in qemu_opts_foreach
(list=0x55555729e5c0 <qemu_device_opts>, func=0x555555bfe04d <device_help_func>, opaque=0x0, errp=0x0) at ../util/qemu-option.c:1135
#12 0x0000555555c01d56 in qemu_process_help_options () at ../system/vl.c:2555
#13 0x0000555555c04d81 in qemu_init (argc=3, argv=0x7fffffffde28)
at ../system/vl.c:3654
#14 0x000055555612ffae in main (argc=3, argv=0x7fffffffde28)
at ../system/main.c:47
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-24 16:56 ` [PATCH 1/4] qom: TYPE_SINGLETON interface Peter Xu
2024-10-24 20:02 ` Philippe Mathieu-Daudé
2024-10-25 8:07 ` Markus Armbruster
@ 2024-10-25 9:51 ` Daniel P. Berrangé
2024-10-25 16:17 ` Peter Xu
2024-10-25 16:37 ` Peter Xu
2 siblings, 2 replies; 38+ messages in thread
From: Daniel P. Berrangé @ 2024-10-25 9:51 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Thu, Oct 24, 2024 at 12:56:24PM -0400, Peter Xu wrote:
Adding significant new functionality to QOM should really come
with a commit message explaining the rationale and the design
choices
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
> qom/object.c | 3 +++
> qom/object_interfaces.c | 24 +++++++++++++++++
> qom/qom-qmp-cmds.c | 22 ++++++++++++---
> system/qdev-monitor.c | 7 +++++
> 5 files changed, 100 insertions(+), 3 deletions(-)
snip
> + * Singleton class describes the type of object classes that can only
> + * provide one instance for the whole lifecycle of QEMU. It will fail the
> + * operation if one attemps to create more than one instance.
> + *
> + * One can fetch the single object using class's get_instance() callback if
> + * it was created before. This can be useful for operations like QMP
> + * qom-list-properties, where dynamically creating an object might not be
> + * feasible.
snip
> +/**
> + * singleton_get_instance:
> + *
> + * @class: the class to fetch singleton instance
> + *
> + * Returns: the object* if the class is a singleton class and the singleton
> + * object is created, NULL otherwise.
> + */
> +Object *singleton_get_instance(ObjectClass *class);
With this design, all code that uses a given type needs to know
whether or not it is intended to be a singleton. If some code
somewhere mistakenly calls 'object_new' instead of 'singleton_get_instance',
the singleton type is no longer a singleton, except you handle this by
adding an assert in object_initialize_with_type.
This is still a bit of a loaded foot-gun IMHO, as we don't want random
code asserting.
> diff --git a/qom/object.c b/qom/object.c
> index 11424cf471..ded299ae1a 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -553,6 +553,9 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type
> g_assert(type->abstract == false);
> g_assert(size >= type->instance_size);
>
> + /* Singleton class can only create one object */
> + g_assert(!singleton_get_instance(type->class));
> +
> memset(obj, 0, type->instance_size);
> obj->class = type->class;
> object_ref(obj);
> diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> index e91a235347..ecc1cf781c 100644
> --- a/qom/qom-qmp-cmds.c
> +++ b/qom/qom-qmp-cmds.c
> @@ -126,6 +126,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> ObjectProperty *prop;
> ObjectPropertyIterator iter;
> ObjectPropertyInfoList *prop_list = NULL;
> + bool create;
>
> klass = module_object_class_by_name(typename);
> if (klass == NULL) {
> @@ -141,7 +142,15 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> return NULL;
> }
>
> - obj = object_new(typename);
> + /* Avoid creating multiple instances if the class is a singleton */
> + create = !object_class_is_singleton(klass) ||
> + !singleton_get_instance(klass);
> +
> + if (create) {
> + obj = object_new(typename);
> + } else {
> + obj = singleton_get_instance(klass);
> + }
This is the first foot-gun example.
I really strongly dislike that the design pattern forces us to
create code like this, as we can never be confident we've
correctly identified all the places which may call object_new
on a singleton...
> @@ -172,7 +181,9 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> QAPI_LIST_PREPEND(prop_list, info);
> }
>
> - object_unref(obj);
> + if (create) {
> + object_unref(obj);
> + }
...and this just compounds the ugliness.
> @@ -199,7 +210,12 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
> return NULL;
> }
>
> - if (object_class_is_abstract(klass)) {
> + /*
> + * Abstract classes are not for instantiations, meanwhile avoid
> + * creating temporary singleton objects because it can cause conflicts
> + * if there's already one created.
> + */
Another example of the foot-gun firing at random code
> + if (object_class_is_abstract(klass) || object_class_is_singleton(klass)) {
> object_class_property_iter_init(&iter, klass);
> } else {
> obj = object_new(typename);
With changes to QOM, I think it is generally informative to look at how
GLib has handled the problem, since the QOM design has heavily borrowed
from its GObject design.
In GObject, singletons are handled in a very differnt way. It has a
concept of a "constructor" function against the class, which is what is
responsible for allocating the object. By default the 'constructor' will
call g_new0, but a class which wishes to become a singleton will override
the 'constructor' function to allocate on first call, and return the
cached object on subsequent calls. This is illustrated here:
https://gitlab.gnome.org/GNOME/glib/-/blob/main/gobject/gobject.h#L297
The key benefit of this is that everything can carry on calling
g_object_new() as before, as it will just "do the right thing"
in terms of allocation.
In QOM, we don't have a 'constructor' class function, we just directly
call g_malloc from object_new_with_type. This is because at the time,
we didn't see an immediate need for it. We could easily change that
though to introduce the concept of a 'constructor', which could
probably make singletons work without needing updates to existing code.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 0/4] QOM: Singleton interface
2024-10-25 7:38 ` [PATCH 0/4] QOM: Singleton interface Markus Armbruster
@ 2024-10-25 15:01 ` Peter Xu
2024-10-29 10:42 ` Daniel P. Berrangé
0 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-25 15:01 UTC (permalink / raw)
To: Markus Armbruster
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Daniel P . Berrangé,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Fri, Oct 25, 2024 at 09:38:31AM +0200, Markus Armbruster wrote:
> Peter Xu <peterx@redhat.com> writes:
>
> > This patchset introduces the singleton interface for QOM.
> >
> > The singleton interface is as simple as "this class can only create one
> > instance".
> >
> > We used to have similar demand when working on all kinds of vIOMMUs,
> > because in most cases that I am aware of, vIOMMU must be a singleton as
> > it's closely bound to the machine and also the root PCIe systems. We used
> > to have a bunch of special code guarding those, for example, X86 has
> > pc_machine_device_pre_plug_cb() just to detect when vIOMMU is created more
> > than once.
> >
> > There is a similar demand raising recently (even if the problem existed
> > over years) when we were looking at a migration bug, that part of it was
> > involved with the current_migration global pointer being referenced
> > anywhere, even after the migration object was finalize()ed. So far without
> > singleton support, there's no way to reset the variable properly.
> > Declaring migration object to be a singleton also just makes sense, e.g.,
> > dynamically creating migration objects on the fly with QMP commands doesn't
> > sound right..
> >
> > The idea behind is pretty simple: any object that can only be created once
> > can now declare the TYPE_SINGLETON interface, then QOM facilities will make
> > sure it won't be created more than once. For example, qom-list-properties,
> > device-list-properties, etc., will be smart enough to not try to create
> > temporary singleton objects now.
>
> QOM design assumption: object_new() followed by object_unref() is always
> possible and has no side effect.
I see.
>
> QOM introspection relies on this. Your PATCH 1 makes non-class
> properties of singletons invisible in introspection. Making something
> with such properties a singleton would be a regression.
>
> Anything that violates the design assumption must be delayed to a
> suitable state transition. For devices (subtypes of TYPE_DEVICE), this
> is ->realize(). For user-creatable objects (provide interface
> TYPE_USER_CREATABLE), this is ->complete(). For anything else, it's
> something else that probably doesn't even exist, yet. See "Problem 5:
> QOM lacks a clear life cycle" in
>
> Subject: Dynamic & heterogeneous machines, initial configuration: problems
> Date: Wed, 31 Jan 2024 21:14:21 +0100
> Message-ID: <87o7d1i7ky.fsf@pond.sub.org>
> https://lore.kernel.org/qemu-devel/87o7d1i7ky.fsf@pond.sub.org/
The major challenge here might be that, in migration's use case we want to
do something after the last refcount is released.
IOW, I don't yet see an easy way to explicit do qdev_unrealize() (even if
migration object will use realize(), while it doesn't yet..), because there
can be more than one thread holding refcount of the object, and we don't
know where to invoke it, and we don't want to do the cleanup if the other
one still holds a refcount.
The only sane place now is in instance_finalize(), which is the only clean
place so far that is invoked after last refcount dropped.
Maybe I can also try do that with a "magic property" with its set/get, as
that's indeed the other hook (basically, object_property_del_all()) that is
only invoked after the final refcount is released. However I think we
still need the singleton idea somehow..
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-24 20:53 ` Peter Xu
@ 2024-10-25 15:11 ` Philippe Mathieu-Daudé
2024-10-25 16:21 ` Peter Xu
0 siblings, 1 reply; 38+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-10-25 15:11 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Mark Cave-Ayland, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Daniel P. Berrangé, Alex Williamson, Paolo Bonzini,
Peter Maydell
On 24/10/24 17:53, Peter Xu wrote:
> On Thu, Oct 24, 2024 at 05:02:19PM -0300, Philippe Mathieu-Daudé wrote:
>> Hi Peter,
>
> Hi, Phil,
>
> Thanks for the quick reviews!
>
>>
>> (Cc'ing Mark)
>>
>> On 24/10/24 13:56, Peter Xu wrote:
>>> Signed-off-by: Peter Xu <peterx@redhat.com>
>>> ---
>>> include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
>>> qom/object.c | 3 +++
>>> qom/object_interfaces.c | 24 +++++++++++++++++
>>> qom/qom-qmp-cmds.c | 22 ++++++++++++---
>>> system/qdev-monitor.c | 7 +++++
>>> 5 files changed, 100 insertions(+), 3 deletions(-)
>>
>>
>>> +/**
>>> + * SingletonClass:
>>> + *
>>> + * @parent_class: the base class
>>> + * @get_instance: fetch the singleton instance if it is created,
>>> + * NULL otherwise.
>>> + *
>>> + * Singleton class describes the type of object classes that can only
>>> + * provide one instance for the whole lifecycle of QEMU. It will fail the
>>> + * operation if one attemps to create more than one instance.
>>> + *
>>> + * One can fetch the single object using class's get_instance() callback if
>>> + * it was created before. This can be useful for operations like QMP
>>> + * qom-list-properties, where dynamically creating an object might not be
>>> + * feasible.
>>> + */
>>> +struct SingletonClass {
>>> + /* <private> */
>>> + InterfaceClass parent_class;
>>> + /* <public> */
>>> + Object *(*get_instance)(Error **errp);
>>
>> IMHO asking get_instance() is overkill ...
>>
>>> +};
>>> +
>>> +/**
>>> + * object_class_is_singleton:
>>> + *
>>> + * @class: the class to detect singleton
>>> + *
>>> + * Returns: true if it's a singleton class, false otherwise.
>>> + */
>>> +bool object_class_is_singleton(ObjectClass *class);
>>> +
>>> +/**
>>> + * singleton_get_instance:
>>> + *
>>> + * @class: the class to fetch singleton instance
>>> + *
>>> + * Returns: the object* if the class is a singleton class and the singleton
>>> + * object is created, NULL otherwise.
>>> + */
>>> +Object *singleton_get_instance(ObjectClass *class);
>>> +
>>> #endif
>>
>>> diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
>>> index e0833c8bfe..6766060d0a 100644
>>> --- a/qom/object_interfaces.c
>>> +++ b/qom/object_interfaces.c
>>> @@ -354,6 +354,23 @@ void user_creatable_cleanup(void)
>>> object_unparent(object_get_objects_root());
>>> }
>>> +bool object_class_is_singleton(ObjectClass *class)
>>> +{
>>> + return !!object_class_dynamic_cast(class, TYPE_SINGLETON);
>>> +}
>>> +
>>> +Object *singleton_get_instance(ObjectClass *class)
>>> +{
>>
>> ... when we can use object_resolve_type_unambiguous:
>>
>> return object_resolve_type_unambiguous(object_class_get_name(class,
>> NULL));
>
> I think an issue is migration object is nowhere to be find under
> object_get_root(), so it won't work there. A side benefit is, it's also
> faster..
Maybe a simpler alternative is to add a field in ObjectClass, maintain
a single GHashTable to store TypeName -> Instance and retrieve as:
Object *singleton_get_instance(ObjectClass *class)
{
return g_hash_table_lookup(&singletons,
object_class_get_name(class));
}
TBH the TYPE_SINGLETON interface seems a bit over-engineered and its
implementation error prone.
> How about I use object_resolve_type_unambiguous() as a fallback? Then it's
> used only if get_instance() is not provided.
>
>>
>> BTW should we pass Error** argument to singleton_get_instance()?
>
> I didn't expect it to fail, hence I didn't even add it to make it more like
> "this will always success or it asserts" kind of things. I left Error** in
> the hook just to be slightly flexible, but I always pass in error_abort()
> in this patch.
>
> I can either add Error** if anyone thinks it useful, or even drop Error**
> in ->get_instance() since it's mostly not used at least for now.
>
> Let me know!
>
>>
>>> + SingletonClass *singleton =
>>> + (SingletonClass *)object_class_dynamic_cast(class, TYPE_SINGLETON);
>>> +
>>> + if (!singleton) {
>>> + return NULL;
>>> + }
>>> +
>>> + return singleton->get_instance(&error_abort);
>>> +}
>>
>> Alternatively call object_resolve_type_unambiguous() in instance_init()?
>
> Thanks,
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-25 8:07 ` Markus Armbruster
@ 2024-10-25 15:17 ` Peter Xu
0 siblings, 0 replies; 38+ messages in thread
From: Peter Xu @ 2024-10-25 15:17 UTC (permalink / raw)
To: Markus Armbruster
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Daniel P . Berrangé,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Fri, Oct 25, 2024 at 10:07:59AM +0200, Markus Armbruster wrote:
> Peter Xu <peterx@redhat.com> writes:
>
> > Signed-off-by: Peter Xu <peterx@redhat.com>
> > ---
> > include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
> > qom/object.c | 3 +++
> > qom/object_interfaces.c | 24 +++++++++++++++++
> > qom/qom-qmp-cmds.c | 22 ++++++++++++---
> > system/qdev-monitor.c | 7 +++++
> > 5 files changed, 100 insertions(+), 3 deletions(-)
> >
> > diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
> > index 02b11a7ef0..9b2cc0e554 100644
> > --- a/include/qom/object_interfaces.h
> > +++ b/include/qom/object_interfaces.h
> > @@ -177,4 +177,51 @@ bool user_creatable_del(const char *id, Error **errp);
> > */
> > void user_creatable_cleanup(void);
> >
> > +#define TYPE_SINGLETON "singleton"
> > +
> > +typedef struct SingletonClass SingletonClass;
> > +DECLARE_CLASS_CHECKERS(SingletonClass, SINGLETON, TYPE_SINGLETON)
> > +
> > +/**
> > + * SingletonClass:
> > + *
> > + * @parent_class: the base class
> > + * @get_instance: fetch the singleton instance if it is created,
> > + * NULL otherwise.
> > + *
> > + * Singleton class describes the type of object classes that can only
> > + * provide one instance for the whole lifecycle of QEMU. It will fail the
> > + * operation if one attemps to create more than one instance.
> > + *
> > + * One can fetch the single object using class's get_instance() callback if
> > + * it was created before. This can be useful for operations like QMP
> > + * qom-list-properties, where dynamically creating an object might not be
> > + * feasible.
> > + */
> > +struct SingletonClass {
> > + /* <private> */
> > + InterfaceClass parent_class;
> > + /* <public> */
> > + Object *(*get_instance)(Error **errp);
> > +};
> > +
> > +/**
> > + * object_class_is_singleton:
> > + *
> > + * @class: the class to detect singleton
> > + *
> > + * Returns: true if it's a singleton class, false otherwise.
> > + */
> > +bool object_class_is_singleton(ObjectClass *class);
> > +
> > +/**
> > + * singleton_get_instance:
> > + *
> > + * @class: the class to fetch singleton instance
> > + *
> > + * Returns: the object* if the class is a singleton class and the singleton
> > + * object is created, NULL otherwise.
> > + */
> > +Object *singleton_get_instance(ObjectClass *class);
>
> A non-null return value can become dangling when the object gets
> destroyed. This could conceivably happen in another thread.
>
> The obviously safe interface would take a reference the caller must
> unref when done.
Ouch, thanks for spotting this! Yes we definitely need a refcount at
least..
>
> > +
> > #endif
> > diff --git a/qom/object.c b/qom/object.c
> > index 11424cf471..ded299ae1a 100644
> > --- a/qom/object.c
> > +++ b/qom/object.c
> > @@ -553,6 +553,9 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type
> > g_assert(type->abstract == false);
> > g_assert(size >= type->instance_size);
> >
> > + /* Singleton class can only create one object */
> > + g_assert(!singleton_get_instance(type->class));
> > +
> > memset(obj, 0, type->instance_size);
> > obj->class = type->class;
> > object_ref(obj);
> > diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
> > index e0833c8bfe..6766060d0a 100644
> > --- a/qom/object_interfaces.c
> > +++ b/qom/object_interfaces.c
> > @@ -354,6 +354,23 @@ void user_creatable_cleanup(void)
> > object_unparent(object_get_objects_root());
> > }
> >
> > +bool object_class_is_singleton(ObjectClass *class)
> > +{
> > + return !!object_class_dynamic_cast(class, TYPE_SINGLETON);
> > +}
> > +
> > +Object *singleton_get_instance(ObjectClass *class)
> > +{
> > + SingletonClass *singleton =
> > + (SingletonClass *)object_class_dynamic_cast(class, TYPE_SINGLETON);
> > +
> > + if (!singleton) {
> > + return NULL;
> > + }
> > +
> > + return singleton->get_instance(&error_abort);
> > +}
> > +
> > static void register_types(void)
> > {
> > static const TypeInfo uc_interface_info = {
> > @@ -362,7 +379,14 @@ static void register_types(void)
> > .class_size = sizeof(UserCreatableClass),
> > };
> >
> > + static const TypeInfo singleton_interface_info = {
> > + .name = TYPE_SINGLETON,
> > + .parent = TYPE_INTERFACE,
> > + .class_size = sizeof(SingletonClass),
> > + };
> > +
> > type_register_static(&uc_interface_info);
> > + type_register_static(&singleton_interface_info);
> > }
> >
> > type_init(register_types)
> > diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> > index e91a235347..ecc1cf781c 100644
> > --- a/qom/qom-qmp-cmds.c
> > +++ b/qom/qom-qmp-cmds.c
> > @@ -126,6 +126,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > ObjectProperty *prop;
> > ObjectPropertyIterator iter;
> > ObjectPropertyInfoList *prop_list = NULL;
> > + bool create;
> >
> > klass = module_object_class_by_name(typename);
> > if (klass == NULL) {
> > @@ -141,7 +142,15 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > return NULL;
> > }
> >
> > - obj = object_new(typename);
> > + /* Avoid creating multiple instances if the class is a singleton */
> > + create = !object_class_is_singleton(klass) ||
> > + !singleton_get_instance(klass);
> > +
> > + if (create) {
> > + obj = object_new(typename);
>
> If the class is not a singleton or else if no instance exists, we create
> a temporary instance.
>
> > + } else {
> > + obj = singleton_get_instance(klass);
>
> If the class is a singleton and the instance exists, we use that
> instead.
>
> Any properties the instance has created dynamically after object_new()
> are visible to introspection. This is not the case when we create a
> temporary instance. Such subtle differences are problematic. If we
> decide we're okay with this one, we need to document it.
I am thinking what's the major use case for device-list-properties.
If it's for mgmt to query a device property list for set/get before
operating on a real instance (which represents already somewhere in the
device tree), I hope it's fine. Basically, it means whatever queried here
can always be used with qom-get/qom-set for this singleton as long as it's
already created and present.
I hope Libvirt cannot cache this results anyway, because we already have:
##
# @device-list-properties:
# ...
# .. note:: Objects can create properties at runtime, for example to
# describe links between different devices and/or objects. These
# properties are not included in the output of this command.
So I think it means mgmt cannot cache these results, but only query before
qom-set/qom-get to make sure it's valid. In that case, maybe we can change
that to s/are not included/may not be included/?
>
> > + }
> >
> > object_property_iter_init(&iter, obj);
> > while ((prop = object_property_iter_next(&iter))) {
> > @@ -172,7 +181,9 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > QAPI_LIST_PREPEND(prop_list, info);
> > }
> >
> > - object_unref(obj);
> > + if (create) {
> > + object_unref(obj);
> > + }
> >
> > return prop_list;
> > }
> > @@ -199,7 +210,12 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
> > return NULL;
> > }
> >
> > - if (object_class_is_abstract(klass)) {
> > + /*
> > + * Abstract classes are not for instantiations, meanwhile avoid
> > + * creating temporary singleton objects because it can cause conflicts
> > + * if there's already one created.
> > + */
> > + if (object_class_is_abstract(klass) || object_class_is_singleton(klass)) {
> > object_class_property_iter_init(&iter, klass);
> > } else {
> > obj = object_new(typename);
>
> If the class is a singleton, we treat it as if it was abstract. Its
> instance properties, if any, are not visible in qom-list-properties.
> This is a defect. If you make an existing class with instance
> properties a singleton, the defect is a regression.
I can switch to get_instance() for singleton when it's available. Would
that work?
Thanks!
>
> > diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c
> > index 44994ea0e1..1310f35c9f 100644
> > --- a/system/qdev-monitor.c
> > +++ b/system/qdev-monitor.c
> > @@ -36,6 +36,7 @@
> > #include "qemu/option.h"
> > #include "qemu/qemu-print.h"
> > #include "qemu/option_int.h"
> > +#include "qom/object_interfaces.h"
> > #include "sysemu/block-backend.h"
> > #include "migration/misc.h"
> > #include "qemu/cutils.h"
> > @@ -643,6 +644,12 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts,
> > return NULL;
> > }
> >
> > + if (singleton_get_instance(OBJECT_CLASS(dc))) {
> > + error_setg(errp, "Class '%s' only supports one instance",
> > + driver);
> > + return NULL;
> > + }
> > +
> > /* find bus */
> > path = qdict_get_try_str(opts, "bus");
> > if (path != NULL) {
>
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-25 9:51 ` Daniel P. Berrangé
@ 2024-10-25 16:17 ` Peter Xu
2024-10-25 16:22 ` Daniel P. Berrangé
2024-10-25 16:37 ` Peter Xu
1 sibling, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-25 16:17 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Fri, Oct 25, 2024 at 10:51:21AM +0100, Daniel P. Berrangé wrote:
> On Thu, Oct 24, 2024 at 12:56:24PM -0400, Peter Xu wrote:
>
> Adding significant new functionality to QOM should really come
> with a commit message explaining the rationale and the design
> choices
>
> > Signed-off-by: Peter Xu <peterx@redhat.com>
> > ---
> > include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
> > qom/object.c | 3 +++
> > qom/object_interfaces.c | 24 +++++++++++++++++
> > qom/qom-qmp-cmds.c | 22 ++++++++++++---
> > system/qdev-monitor.c | 7 +++++
> > 5 files changed, 100 insertions(+), 3 deletions(-)
>
> snip
>
> > + * Singleton class describes the type of object classes that can only
> > + * provide one instance for the whole lifecycle of QEMU. It will fail the
> > + * operation if one attemps to create more than one instance.
> > + *
> > + * One can fetch the single object using class's get_instance() callback if
> > + * it was created before. This can be useful for operations like QMP
> > + * qom-list-properties, where dynamically creating an object might not be
> > + * feasible.
>
> snip
>
> > +/**
> > + * singleton_get_instance:
> > + *
> > + * @class: the class to fetch singleton instance
> > + *
> > + * Returns: the object* if the class is a singleton class and the singleton
> > + * object is created, NULL otherwise.
> > + */
> > +Object *singleton_get_instance(ObjectClass *class);
>
> With this design, all code that uses a given type needs to know
> whether or not it is intended to be a singleton. If some code
> somewhere mistakenly calls 'object_new' instead of 'singleton_get_instance',
> the singleton type is no longer a singleton, except you handle this by
> adding an assert in object_initialize_with_type.
Yes, that's really the current goal, and why I added that assert(), so it
fails any attempts trying to create one more instance of it, because
normally it's by accident. The theory issue to solve is when some class is
not ready for being created more than once, so it must not happen.
My plan was to properly guard qdev creation with this which can fail
pretty, so it's a "programming error" otherwise.
>
> This is still a bit of a loaded foot-gun IMHO, as we don't want random
> code asserting.
>
> > diff --git a/qom/object.c b/qom/object.c
> > index 11424cf471..ded299ae1a 100644
> > --- a/qom/object.c
> > +++ b/qom/object.c
> > @@ -553,6 +553,9 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type
> > g_assert(type->abstract == false);
> > g_assert(size >= type->instance_size);
> >
> > + /* Singleton class can only create one object */
> > + g_assert(!singleton_get_instance(type->class));
> > +
> > memset(obj, 0, type->instance_size);
> > obj->class = type->class;
> > object_ref(obj);
>
> > diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> > index e91a235347..ecc1cf781c 100644
> > --- a/qom/qom-qmp-cmds.c
> > +++ b/qom/qom-qmp-cmds.c
> > @@ -126,6 +126,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > ObjectProperty *prop;
> > ObjectPropertyIterator iter;
> > ObjectPropertyInfoList *prop_list = NULL;
> > + bool create;
> >
> > klass = module_object_class_by_name(typename);
> > if (klass == NULL) {
> > @@ -141,7 +142,15 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > return NULL;
> > }
> >
> > - obj = object_new(typename);
> > + /* Avoid creating multiple instances if the class is a singleton */
> > + create = !object_class_is_singleton(klass) ||
> > + !singleton_get_instance(klass);
> > +
> > + if (create) {
> > + obj = object_new(typename);
> > + } else {
> > + obj = singleton_get_instance(klass);
> > + }
>
> This is the first foot-gun example.
>
> I really strongly dislike that the design pattern forces us to
> create code like this, as we can never be confident we've
> correctly identified all the places which may call object_new
> on a singleton...
Yeah I agree it's not pretty, IMHO it's a trade-off comparing to glib's,
I'll comment below for that.
Meanwhile I hope there should be very limited places in QEMU to randomly
create "any" object on the fly.. so far only qom/device-list-properties
that I see.
>
> > @@ -172,7 +181,9 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > QAPI_LIST_PREPEND(prop_list, info);
> > }
> >
> > - object_unref(obj);
> > + if (create) {
> > + object_unref(obj);
> > + }
>
> ...and this just compounds the ugliness.
>
> > @@ -199,7 +210,12 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
> > return NULL;
> > }
> >
> > - if (object_class_is_abstract(klass)) {
> > + /*
> > + * Abstract classes are not for instantiations, meanwhile avoid
> > + * creating temporary singleton objects because it can cause conflicts
> > + * if there's already one created.
> > + */
>
> Another example of the foot-gun firing at random code
>
> > + if (object_class_is_abstract(klass) || object_class_is_singleton(klass)) {
> > object_class_property_iter_init(&iter, klass);
> > } else {
> > obj = object_new(typename);
>
>
> With changes to QOM, I think it is generally informative to look at how
> GLib has handled the problem, since the QOM design has heavily borrowed
> from its GObject design.
>
> In GObject, singletons are handled in a very differnt way. It has a
> concept of a "constructor" function against the class, which is what is
> responsible for allocating the object. By default the 'constructor' will
> call g_new0, but a class which wishes to become a singleton will override
> the 'constructor' function to allocate on first call, and return the
> cached object on subsequent calls. This is illustrated here:
>
> https://gitlab.gnome.org/GNOME/glib/-/blob/main/gobject/gobject.h#L297
>
> The key benefit of this is that everything can carry on calling
> g_object_new() as before, as it will just "do the right thing"
> in terms of allocation.
>
> In QOM, we don't have a 'constructor' class function, we just directly
> call g_malloc from object_new_with_type. This is because at the time,
> we didn't see an immediate need for it. We could easily change that
> though to introduce the concept of a 'constructor', which could
> probably make singletons work without needing updates to existing code.
I think glib's implementation is not thread safe on its own... consider two
threads invoke g_object_new() on the singleton without proper locking. I
am guessing it could be relevant to glib's heavy event model.
And that's fundamentally what I want to have for QEMU's migration object,
so that it doesn't need locking on its own of the singleton idea: the
locking, if necessary, should be done in get_instance(), in this case.
So I did it wrong indeed in this current series at least there, where it
should need to take migration's new mutex, then take a refcount before
return, rightfully as Markus pointed out elsewhere.
The other concern I have with glib's singleton is, fundamentally
object_new() didn't really create a new object, which can be against the
gut feelings of whoever read it. I'm not sure whether that's really what
we want.. or maybe that's only my own concern, so it might be subjective.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-25 15:11 ` Philippe Mathieu-Daudé
@ 2024-10-25 16:21 ` Peter Xu
0 siblings, 0 replies; 38+ messages in thread
From: Peter Xu @ 2024-10-25 16:21 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: qemu-devel, Mark Cave-Ayland, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Daniel P. Berrangé, Alex Williamson, Paolo Bonzini,
Peter Maydell
On Fri, Oct 25, 2024 at 12:11:51PM -0300, Philippe Mathieu-Daudé wrote:
> On 24/10/24 17:53, Peter Xu wrote:
> > On Thu, Oct 24, 2024 at 05:02:19PM -0300, Philippe Mathieu-Daudé wrote:
> > > Hi Peter,
> >
> > Hi, Phil,
> >
> > Thanks for the quick reviews!
> >
> > >
> > > (Cc'ing Mark)
> > >
> > > On 24/10/24 13:56, Peter Xu wrote:
> > > > Signed-off-by: Peter Xu <peterx@redhat.com>
> > > > ---
> > > > include/qom/object_interfaces.h | 47 +++++++++++++++++++++++++++++++++
> > > > qom/object.c | 3 +++
> > > > qom/object_interfaces.c | 24 +++++++++++++++++
> > > > qom/qom-qmp-cmds.c | 22 ++++++++++++---
> > > > system/qdev-monitor.c | 7 +++++
> > > > 5 files changed, 100 insertions(+), 3 deletions(-)
> > >
> > >
> > > > +/**
> > > > + * SingletonClass:
> > > > + *
> > > > + * @parent_class: the base class
> > > > + * @get_instance: fetch the singleton instance if it is created,
> > > > + * NULL otherwise.
> > > > + *
> > > > + * Singleton class describes the type of object classes that can only
> > > > + * provide one instance for the whole lifecycle of QEMU. It will fail the
> > > > + * operation if one attemps to create more than one instance.
> > > > + *
> > > > + * One can fetch the single object using class's get_instance() callback if
> > > > + * it was created before. This can be useful for operations like QMP
> > > > + * qom-list-properties, where dynamically creating an object might not be
> > > > + * feasible.
> > > > + */
> > > > +struct SingletonClass {
> > > > + /* <private> */
> > > > + InterfaceClass parent_class;
> > > > + /* <public> */
> > > > + Object *(*get_instance)(Error **errp);
> > >
> > > IMHO asking get_instance() is overkill ...
> > >
> > > > +};
> > > > +
> > > > +/**
> > > > + * object_class_is_singleton:
> > > > + *
> > > > + * @class: the class to detect singleton
> > > > + *
> > > > + * Returns: true if it's a singleton class, false otherwise.
> > > > + */
> > > > +bool object_class_is_singleton(ObjectClass *class);
> > > > +
> > > > +/**
> > > > + * singleton_get_instance:
> > > > + *
> > > > + * @class: the class to fetch singleton instance
> > > > + *
> > > > + * Returns: the object* if the class is a singleton class and the singleton
> > > > + * object is created, NULL otherwise.
> > > > + */
> > > > +Object *singleton_get_instance(ObjectClass *class);
> > > > +
> > > > #endif
> > >
> > > > diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
> > > > index e0833c8bfe..6766060d0a 100644
> > > > --- a/qom/object_interfaces.c
> > > > +++ b/qom/object_interfaces.c
> > > > @@ -354,6 +354,23 @@ void user_creatable_cleanup(void)
> > > > object_unparent(object_get_objects_root());
> > > > }
> > > > +bool object_class_is_singleton(ObjectClass *class)
> > > > +{
> > > > + return !!object_class_dynamic_cast(class, TYPE_SINGLETON);
> > > > +}
> > > > +
> > > > +Object *singleton_get_instance(ObjectClass *class)
> > > > +{
> > >
> > > ... when we can use object_resolve_type_unambiguous:
> > >
> > > return object_resolve_type_unambiguous(object_class_get_name(class,
> > > NULL));
> >
> > I think an issue is migration object is nowhere to be find under
> > object_get_root(), so it won't work there. A side benefit is, it's also
> > faster..
>
> Maybe a simpler alternative is to add a field in ObjectClass, maintain
> a single GHashTable to store TypeName -> Instance and retrieve as:
>
> Object *singleton_get_instance(ObjectClass *class)
> {
> return g_hash_table_lookup(&singletons,
> object_class_get_name(class));
> }
This may need some proper locking too as Markus pointed out, so that the
object needs to boost its refcount before returned. It might be a problem
if the singleton class has its own locking.. I'll need to think about it.
In migration's case, there's going to be a new migration mutex protecting
the singleton object being updated (not included in this series, posted
elsewhere [1]).
[1] https://lore.kernel.org/r/20241024213056.1395400-9-peterx@redhat.com
>
> TBH the TYPE_SINGLETON interface seems a bit over-engineered and its
> implementation error prone.
Please feel free to suggest something else if you come up with.
The issue so far is qom/device-list-properties now can randomly create
migration object, which is defined to be global, while the plan is we need
to do something tricky in instance_finalize() for migration object (operate
on global vars; which is tricky but it could solve some issue we
encounter), so we must make sure it's never created more than once.
Thanks,
>
> > How about I use object_resolve_type_unambiguous() as a fallback? Then it's
> > used only if get_instance() is not provided.
> >
> > >
> > > BTW should we pass Error** argument to singleton_get_instance()?
> >
> > I didn't expect it to fail, hence I didn't even add it to make it more like
> > "this will always success or it asserts" kind of things. I left Error** in
> > the hook just to be slightly flexible, but I always pass in error_abort()
> > in this patch.
> >
> > I can either add Error** if anyone thinks it useful, or even drop Error**
> > in ->get_instance() since it's mostly not used at least for now.
> >
> > Let me know!
> >
> > >
> > > > + SingletonClass *singleton =
> > > > + (SingletonClass *)object_class_dynamic_cast(class, TYPE_SINGLETON);
> > > > +
> > > > + if (!singleton) {
> > > > + return NULL;
> > > > + }
> > > > +
> > > > + return singleton->get_instance(&error_abort);
> > > > +}
> > >
> > > Alternatively call object_resolve_type_unambiguous() in instance_init()?
> >
> > Thanks,
> >
>
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-25 16:17 ` Peter Xu
@ 2024-10-25 16:22 ` Daniel P. Berrangé
2024-10-25 22:10 ` Peter Xu
0 siblings, 1 reply; 38+ messages in thread
From: Daniel P. Berrangé @ 2024-10-25 16:22 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Fri, Oct 25, 2024 at 12:17:02PM -0400, Peter Xu wrote:
> On Fri, Oct 25, 2024 at 10:51:21AM +0100, Daniel P. Berrangé wrote:
> > > diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> > > index e91a235347..ecc1cf781c 100644
> > > --- a/qom/qom-qmp-cmds.c
> > > +++ b/qom/qom-qmp-cmds.c
> > > @@ -126,6 +126,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > > ObjectProperty *prop;
> > > ObjectPropertyIterator iter;
> > > ObjectPropertyInfoList *prop_list = NULL;
> > > + bool create;
> > >
> > > klass = module_object_class_by_name(typename);
> > > if (klass == NULL) {
> > > @@ -141,7 +142,15 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > > return NULL;
> > > }
> > >
> > > - obj = object_new(typename);
> > > + /* Avoid creating multiple instances if the class is a singleton */
> > > + create = !object_class_is_singleton(klass) ||
> > > + !singleton_get_instance(klass);
> > > +
> > > + if (create) {
> > > + obj = object_new(typename);
> > > + } else {
> > > + obj = singleton_get_instance(klass);
> > > + }
> >
> > This is the first foot-gun example.
> >
> > I really strongly dislike that the design pattern forces us to
> > create code like this, as we can never be confident we've
> > correctly identified all the places which may call object_new
> > on a singleton...
>
> Yeah I agree it's not pretty, IMHO it's a trade-off comparing to glib's,
> I'll comment below for that.
>
> Meanwhile I hope there should be very limited places in QEMU to randomly
> create "any" object on the fly.. so far only qom/device-list-properties
> that I see.
There's -object/-device CLI, and their corresponding object_add
/ device_add commands.
They are not relevant for the migration object, but you're adding
this feature to QOM, so we have to take them into account. If anyone
defines another Object or Device class as a singleton, we will have
quite a few places where we can trigger the assert. This is especially
bad if we trigger it from anything in QMP as that kills a running
guest.
>
> I think glib's implementation is not thread safe on its own... consider two
> threads invoke g_object_new() on the singleton without proper locking. I
> am guessing it could be relevant to glib's heavy event model.
I've not checked the full code path, to see if they have a serialization
point somewhere it the g_object_new call chain, but yes, it is a valid
concern that would need to be resolved.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-25 9:51 ` Daniel P. Berrangé
2024-10-25 16:17 ` Peter Xu
@ 2024-10-25 16:37 ` Peter Xu
1 sibling, 0 replies; 38+ messages in thread
From: Peter Xu @ 2024-10-25 16:37 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: QEMU Developers, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Alex Williamson, Paolo Bonzini, Peter Maydell
[-- Attachment #1: Type: text/plain, Size: 416 bytes --]
On Fri, Oct 25, 2024, 5:51 a.m. Daniel P. Berrangé <berrange@redhat.com>
wrote:
> On Thu, Oct 24, 2024 at 12:56:24PM -0400, Peter Xu wrote:
>
> Adding significant new functionality to QOM should really come
> with a commit message explaining the rationale and the design
> choices
>
Ohh i definitely missed it, my bad. Luckily i still wrote the cover
letter. I'll prepare that if there is v2.
>
[-- Attachment #2: Type: text/html, Size: 908 bytes --]
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object
2024-10-25 9:25 ` Markus Armbruster
@ 2024-10-25 21:55 ` Peter Xu
2024-10-25 22:13 ` Peter Xu
0 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-25 21:55 UTC (permalink / raw)
To: Markus Armbruster
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Daniel P . Berrangé,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Fri, Oct 25, 2024 at 11:25:23AM +0200, Markus Armbruster wrote:
> Peter Xu <peterx@redhat.com> writes:
>
> > X86 IOMMUs cannot be created more than one on a system yet. Make it a
> > singleton so it guards the system from accidentally create yet another
> > IOMMU object when one already presents.
> >
> > Now if someone tries to create more than one, e.g., via:
> >
> > ./qemu -M q35 -device intel-iommu -device intel-iommu
> >
> > The error will change from:
> >
> > qemu-system-x86_64: -device intel-iommu: QEMU does not support multiple vIOMMUs for x86 yet.
> >
> > To:
> >
> > qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only supports one instance
> >
> > Unfortunately, yet we can't remove the singleton check in the machine
> > hook (pc_machine_device_pre_plug_cb), because there can also be
> > virtio-iommu involved, which doesn't share a common parent class yet.
> >
> > But with this, it should be closer to reach that goal to check singleton by
> > QOM one day.
> >
> > Signed-off-by: Peter Xu <peterx@redhat.com>
>
> $ qemu-system-x86_64 -device amd-iommu,help
> /work/armbru/qemu/include/hw/boards.h:24:MACHINE: Object 0x56473906f960 is not an instance of type machine
> Aborted (core dumped)
>
> (gdb) bt
> #0 0x00007ffff4e43834 in __pthread_kill_implementation () at /lib64/libc.so.6
> #1 0x00007ffff4df18ee in raise () at /lib64/libc.so.6
> #2 0x00007ffff4dd98ff in abort () at /lib64/libc.so.6
> #3 0x0000555555f75ef3 in object_dynamic_cast_assert
> (obj=0x555557e03960, typename=0x5555563c403e "machine", file=0x5555563c4018 "/work/armbru/qemu/include/hw/boards.h", line=24, func=0x5555563c4290 <__func__.7> "MACHINE") at ../qom/object.c:936
> #4 0x0000555555d5db0f in MACHINE (obj=0x555557e03960)
> at /work/armbru/qemu/include/hw/boards.h:24
> #5 0x0000555555d5e030 in x86_iommu_get_default () at ../hw/i386/x86-iommu.c:83
> #6 0x0000555555d5e262 in x86_iommu_get_instance
> (errp=0x5555573d4918 <error_abort>) at ../hw/i386/x86-iommu.c:139
> #7 0x0000555555f7c27c in singleton_get_instance (class=0x555557e00320)
> at ../qom/object_interfaces.c:371
> #8 0x000055555612a842 in qmp_device_list_properties
> (typename=0x555557e001d0 "amd-iommu", errp=0x7fffffffda38)
> at ../qom/qom-qmp-cmds.c:147
> #9 0x0000555555bf20b2 in qdev_device_help (opts=0x555557e001f0)
> at ../system/qdev-monitor.c:314
> #10 0x0000555555bfe06d in device_help_func
> (opaque=0x0, opts=0x555557e001f0, errp=0x0) at ../system/vl.c:1208
> #11 0x0000555556217186 in qemu_opts_foreach
> (list=0x55555729e5c0 <qemu_device_opts>, func=0x555555bfe04d <device_help_func>, opaque=0x0, errp=0x0) at ../util/qemu-option.c:1135
> #12 0x0000555555c01d56 in qemu_process_help_options () at ../system/vl.c:2555
> #13 0x0000555555c04d81 in qemu_init (argc=3, argv=0x7fffffffde28)
> at ../system/vl.c:3654
> #14 0x000055555612ffae in main (argc=3, argv=0x7fffffffde28)
> at ../system/main.c:47
>
Thanks for the report!
It turns out that qdev_get_machine() cannot be invoked too early, and the
singleton code can make it earlier..
We may want a pre-requisite patch to allow qdev_get_machine() to be invoked
anytime, like:
===8<===
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index db36f54d91..7ceae47139 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -831,6 +831,16 @@ Object *qdev_get_machine(void)
{
static Object *dev;
+ if (!phase_check(PHASE_MACHINE_CREATED)) {
+ /*
+ * When the machine is not created, below can wrongly create
+ * /machine to be a container.. this enables qdev_get_machine() to
+ * be used at any time and return NULL properly when machine is not
+ * created.
+ */
+ return NULL;
+ }
+
if (dev == NULL) {
dev = container_get(object_get_root(), "/machine");
}
===8<===
I hope it makes sense on its own. Then callers who can be invoked earlier
could then handle NULL properly, in this case..
===8<===
diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c
index 4bfeb08705..fceb7adfe0 100644
--- a/hw/i386/x86-iommu.c
+++ b/hw/i386/x86-iommu.c
@@ -80,9 +80,15 @@ void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *msg_out)
X86IOMMUState *x86_iommu_get_default(void)
{
- MachineState *ms = MACHINE(qdev_get_machine());
- PCMachineState *pcms =
- PC_MACHINE(object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE));
+ Object *machine = qdev_get_machine();
+ PCMachineState *pcms;
+
+ /* If machine has not been created, so is the vIOMMU */
+ if (!machine) {
+ return NULL;
+ }
+
+ pcms = PC_MACHINE(object_dynamic_cast(machine, TYPE_PC_MACHINE));
if (pcms &&
object_dynamic_cast(OBJECT(pcms->iommu), TYPE_X86_IOMMU_DEVICE)) {
===8<===
I'll make sure this works if I'll repost.
Thanks,
--
Peter Xu
^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-25 16:22 ` Daniel P. Berrangé
@ 2024-10-25 22:10 ` Peter Xu
2024-10-29 0:01 ` Peter Xu
0 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-25 22:10 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Fri, Oct 25, 2024 at 05:22:01PM +0100, Daniel P. Berrangé wrote:
> On Fri, Oct 25, 2024 at 12:17:02PM -0400, Peter Xu wrote:
> > On Fri, Oct 25, 2024 at 10:51:21AM +0100, Daniel P. Berrangé wrote:
> > > > diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> > > > index e91a235347..ecc1cf781c 100644
> > > > --- a/qom/qom-qmp-cmds.c
> > > > +++ b/qom/qom-qmp-cmds.c
> > > > @@ -126,6 +126,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > > > ObjectProperty *prop;
> > > > ObjectPropertyIterator iter;
> > > > ObjectPropertyInfoList *prop_list = NULL;
> > > > + bool create;
> > > >
> > > > klass = module_object_class_by_name(typename);
> > > > if (klass == NULL) {
> > > > @@ -141,7 +142,15 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > > > return NULL;
> > > > }
> > > >
> > > > - obj = object_new(typename);
> > > > + /* Avoid creating multiple instances if the class is a singleton */
> > > > + create = !object_class_is_singleton(klass) ||
> > > > + !singleton_get_instance(klass);
> > > > +
> > > > + if (create) {
> > > > + obj = object_new(typename);
> > > > + } else {
> > > > + obj = singleton_get_instance(klass);
> > > > + }
> > >
> > > This is the first foot-gun example.
> > >
> > > I really strongly dislike that the design pattern forces us to
> > > create code like this, as we can never be confident we've
> > > correctly identified all the places which may call object_new
> > > on a singleton...
> >
> > Yeah I agree it's not pretty, IMHO it's a trade-off comparing to glib's,
> > I'll comment below for that.
> >
> > Meanwhile I hope there should be very limited places in QEMU to randomly
> > create "any" object on the fly.. so far only qom/device-list-properties
> > that I see.
>
> There's -object/-device CLI, and their corresponding object_add
> / device_add commands.
Ah I didn't mention that when reply, but this patch blocks properly any
device-add for singleton objects for more than once. Please see the change
in qdev_device_add_from_qdict().
For migration object, it means it'll always fail because migration object
is created very early, before someone can try to create it. Not to mention
it also has dc->hotpluggable==false, so things like -device will never work
on migration object.
For object-add, IIUC migration object should always be safe because it has
no USER_CREATABLE interface.
>
> They are not relevant for the migration object, but you're adding
> this feature to QOM, so we have to take them into account. If anyone
> defines another Object or Device class as a singleton, we will have
> quite a few places where we can trigger the assert. This is especially
> bad if we trigger it from anything in QMP as that kills a running
> guest.
>
> >
> > I think glib's implementation is not thread safe on its own... consider two
> > threads invoke g_object_new() on the singleton without proper locking. I
> > am guessing it could be relevant to glib's heavy event model.
>
> I've not checked the full code path, to see if they have a serialization
> point somewhere it the g_object_new call chain, but yes, it is a valid
> concern that would need to be resolved.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object
2024-10-25 21:55 ` Peter Xu
@ 2024-10-25 22:13 ` Peter Xu
2024-11-07 11:12 ` Markus Armbruster
0 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-25 22:13 UTC (permalink / raw)
To: Markus Armbruster
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Daniel P . Berrangé,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Fri, Oct 25, 2024 at 05:55:59PM -0400, Peter Xu wrote:
> On Fri, Oct 25, 2024 at 11:25:23AM +0200, Markus Armbruster wrote:
> > Peter Xu <peterx@redhat.com> writes:
> >
> > > X86 IOMMUs cannot be created more than one on a system yet. Make it a
> > > singleton so it guards the system from accidentally create yet another
> > > IOMMU object when one already presents.
> > >
> > > Now if someone tries to create more than one, e.g., via:
> > >
> > > ./qemu -M q35 -device intel-iommu -device intel-iommu
> > >
> > > The error will change from:
> > >
> > > qemu-system-x86_64: -device intel-iommu: QEMU does not support multiple vIOMMUs for x86 yet.
> > >
> > > To:
> > >
> > > qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only supports one instance
> > >
> > > Unfortunately, yet we can't remove the singleton check in the machine
> > > hook (pc_machine_device_pre_plug_cb), because there can also be
> > > virtio-iommu involved, which doesn't share a common parent class yet.
> > >
> > > But with this, it should be closer to reach that goal to check singleton by
> > > QOM one day.
> > >
> > > Signed-off-by: Peter Xu <peterx@redhat.com>
> >
> > $ qemu-system-x86_64 -device amd-iommu,help
> > /work/armbru/qemu/include/hw/boards.h:24:MACHINE: Object 0x56473906f960 is not an instance of type machine
> > Aborted (core dumped)
> >
> > (gdb) bt
> > #0 0x00007ffff4e43834 in __pthread_kill_implementation () at /lib64/libc.so.6
> > #1 0x00007ffff4df18ee in raise () at /lib64/libc.so.6
> > #2 0x00007ffff4dd98ff in abort () at /lib64/libc.so.6
> > #3 0x0000555555f75ef3 in object_dynamic_cast_assert
> > (obj=0x555557e03960, typename=0x5555563c403e "machine", file=0x5555563c4018 "/work/armbru/qemu/include/hw/boards.h", line=24, func=0x5555563c4290 <__func__.7> "MACHINE") at ../qom/object.c:936
> > #4 0x0000555555d5db0f in MACHINE (obj=0x555557e03960)
> > at /work/armbru/qemu/include/hw/boards.h:24
> > #5 0x0000555555d5e030 in x86_iommu_get_default () at ../hw/i386/x86-iommu.c:83
> > #6 0x0000555555d5e262 in x86_iommu_get_instance
> > (errp=0x5555573d4918 <error_abort>) at ../hw/i386/x86-iommu.c:139
> > #7 0x0000555555f7c27c in singleton_get_instance (class=0x555557e00320)
> > at ../qom/object_interfaces.c:371
> > #8 0x000055555612a842 in qmp_device_list_properties
> > (typename=0x555557e001d0 "amd-iommu", errp=0x7fffffffda38)
> > at ../qom/qom-qmp-cmds.c:147
> > #9 0x0000555555bf20b2 in qdev_device_help (opts=0x555557e001f0)
> > at ../system/qdev-monitor.c:314
> > #10 0x0000555555bfe06d in device_help_func
> > (opaque=0x0, opts=0x555557e001f0, errp=0x0) at ../system/vl.c:1208
> > #11 0x0000555556217186 in qemu_opts_foreach
> > (list=0x55555729e5c0 <qemu_device_opts>, func=0x555555bfe04d <device_help_func>, opaque=0x0, errp=0x0) at ../util/qemu-option.c:1135
> > #12 0x0000555555c01d56 in qemu_process_help_options () at ../system/vl.c:2555
> > #13 0x0000555555c04d81 in qemu_init (argc=3, argv=0x7fffffffde28)
> > at ../system/vl.c:3654
> > #14 0x000055555612ffae in main (argc=3, argv=0x7fffffffde28)
> > at ../system/main.c:47
> >
>
> Thanks for the report!
>
> It turns out that qdev_get_machine() cannot be invoked too early, and the
> singleton code can make it earlier..
>
> We may want a pre-requisite patch to allow qdev_get_machine() to be invoked
> anytime, like:
>
> ===8<===
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index db36f54d91..7ceae47139 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -831,6 +831,16 @@ Object *qdev_get_machine(void)
> {
> static Object *dev;
>
> + if (!phase_check(PHASE_MACHINE_CREATED)) {
> + /*
> + * When the machine is not created, below can wrongly create
> + * /machine to be a container.. this enables qdev_get_machine() to
> + * be used at any time and return NULL properly when machine is not
> + * created.
> + */
> + return NULL;
> + }
> +
> if (dev == NULL) {
> dev = container_get(object_get_root(), "/machine");
> }
> ===8<===
>
> I hope it makes sense on its own.
My apologies, spoke too soon here. This helper is used too after machine
is created, but right before switching to PHASE_MACHINE_CREATE stage..
So we need another way, like:
===8<===
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index db36f54d91..36a9fdb428 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -832,7 +832,13 @@ Object *qdev_get_machine(void)
static Object *dev;
if (dev == NULL) {
- dev = container_get(object_get_root(), "/machine");
+ /*
+ * NOTE: dev can keep being NULL if machine is not yet created!
+ * In which case the function will properly return NULL.
+ *
+ * Whenever machine object is created and found once, we cache it.
+ */
+ dev = object_resolve_path_component(object_get_root(), "machine");
}
return dev;
===8<===
The idea is still the same. Meanwhile I'll test more to see whether it has
other issues.
Thanks,
> Then callers who can be invoked earlier
> could then handle NULL properly, in this case..
>
> ===8<===
> diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c
> index 4bfeb08705..fceb7adfe0 100644
> --- a/hw/i386/x86-iommu.c
> +++ b/hw/i386/x86-iommu.c
> @@ -80,9 +80,15 @@ void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *msg_out)
>
> X86IOMMUState *x86_iommu_get_default(void)
> {
> - MachineState *ms = MACHINE(qdev_get_machine());
> - PCMachineState *pcms =
> - PC_MACHINE(object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE));
> + Object *machine = qdev_get_machine();
> + PCMachineState *pcms;
> +
> + /* If machine has not been created, so is the vIOMMU */
> + if (!machine) {
> + return NULL;
> + }
> +
> + pcms = PC_MACHINE(object_dynamic_cast(machine, TYPE_PC_MACHINE));
>
> if (pcms &&
> object_dynamic_cast(OBJECT(pcms->iommu), TYPE_X86_IOMMU_DEVICE)) {
> ===8<===
>
> I'll make sure this works if I'll repost.
>
> Thanks,
>
> --
> Peter Xu
--
Peter Xu
^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH 1/4] qom: TYPE_SINGLETON interface
2024-10-25 22:10 ` Peter Xu
@ 2024-10-29 0:01 ` Peter Xu
0 siblings, 0 replies; 38+ messages in thread
From: Peter Xu @ 2024-10-29 0:01 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Fri, Oct 25, 2024 at 06:10:46PM -0400, Peter Xu wrote:
> On Fri, Oct 25, 2024 at 05:22:01PM +0100, Daniel P. Berrangé wrote:
> > On Fri, Oct 25, 2024 at 12:17:02PM -0400, Peter Xu wrote:
> > > On Fri, Oct 25, 2024 at 10:51:21AM +0100, Daniel P. Berrangé wrote:
> > > > > diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> > > > > index e91a235347..ecc1cf781c 100644
> > > > > --- a/qom/qom-qmp-cmds.c
> > > > > +++ b/qom/qom-qmp-cmds.c
> > > > > @@ -126,6 +126,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > > > > ObjectProperty *prop;
> > > > > ObjectPropertyIterator iter;
> > > > > ObjectPropertyInfoList *prop_list = NULL;
> > > > > + bool create;
> > > > >
> > > > > klass = module_object_class_by_name(typename);
> > > > > if (klass == NULL) {
> > > > > @@ -141,7 +142,15 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
> > > > > return NULL;
> > > > > }
> > > > >
> > > > > - obj = object_new(typename);
> > > > > + /* Avoid creating multiple instances if the class is a singleton */
> > > > > + create = !object_class_is_singleton(klass) ||
> > > > > + !singleton_get_instance(klass);
> > > > > +
> > > > > + if (create) {
> > > > > + obj = object_new(typename);
> > > > > + } else {
> > > > > + obj = singleton_get_instance(klass);
> > > > > + }
> > > >
> > > > This is the first foot-gun example.
> > > >
> > > > I really strongly dislike that the design pattern forces us to
> > > > create code like this, as we can never be confident we've
> > > > correctly identified all the places which may call object_new
> > > > on a singleton...
> > >
> > > Yeah I agree it's not pretty, IMHO it's a trade-off comparing to glib's,
> > > I'll comment below for that.
> > >
> > > Meanwhile I hope there should be very limited places in QEMU to randomly
> > > create "any" object on the fly.. so far only qom/device-list-properties
> > > that I see.
> >
> > There's -object/-device CLI, and their corresponding object_add
> > / device_add commands.
>
> Ah I didn't mention that when reply, but this patch blocks properly any
> device-add for singleton objects for more than once. Please see the change
> in qdev_device_add_from_qdict().
>
> For migration object, it means it'll always fail because migration object
> is created very early, before someone can try to create it. Not to mention
> it also has dc->hotpluggable==false, so things like -device will never work
> on migration object.
>
> For object-add, IIUC migration object should always be safe because it has
> no USER_CREATABLE interface.
>
> >
> > They are not relevant for the migration object, but you're adding
> > this feature to QOM, so we have to take them into account. If anyone
> > defines another Object or Device class as a singleton, we will have
> > quite a few places where we can trigger the assert. This is especially
> > bad if we trigger it from anything in QMP as that kills a running
> > guest.
Sorry, for some reason I think I didn't notice the 2nd paragraph.. And
somehow I was only thinking the migration use case.
Indeed this part is not easy to get right. I hope this is not a blocker
yet so far. We can have a look when I send the new version; I'll start to
mark the series RFC.
Meanwhile I'll have a closer look, hopefully object_class_is_abstract() is
a good point of reference where I can track most of dynamic creations of
objects, and I'll go from there.
My current plan is we can have one helper object_new_allowed(), where it
should contain both the check over abstract & singleton classes before any
instantiations happen.
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 0/4] QOM: Singleton interface
2024-10-25 15:01 ` Peter Xu
@ 2024-10-29 10:42 ` Daniel P. Berrangé
2024-10-29 14:45 ` Peter Xu
0 siblings, 1 reply; 38+ messages in thread
From: Daniel P. Berrangé @ 2024-10-29 10:42 UTC (permalink / raw)
To: Peter Xu
Cc: Markus Armbruster, qemu-devel, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Alex Williamson,
Paolo Bonzini, Peter Maydell
On Fri, Oct 25, 2024 at 11:01:56AM -0400, Peter Xu wrote:
> On Fri, Oct 25, 2024 at 09:38:31AM +0200, Markus Armbruster wrote:
> > Peter Xu <peterx@redhat.com> writes:
> >
> > > This patchset introduces the singleton interface for QOM.
> > >
> > > The singleton interface is as simple as "this class can only create one
> > > instance".
> > >
> > > We used to have similar demand when working on all kinds of vIOMMUs,
> > > because in most cases that I am aware of, vIOMMU must be a singleton as
> > > it's closely bound to the machine and also the root PCIe systems. We used
> > > to have a bunch of special code guarding those, for example, X86 has
> > > pc_machine_device_pre_plug_cb() just to detect when vIOMMU is created more
> > > than once.
> > >
> > > There is a similar demand raising recently (even if the problem existed
> > > over years) when we were looking at a migration bug, that part of it was
> > > involved with the current_migration global pointer being referenced
> > > anywhere, even after the migration object was finalize()ed. So far without
> > > singleton support, there's no way to reset the variable properly.
> > > Declaring migration object to be a singleton also just makes sense, e.g.,
> > > dynamically creating migration objects on the fly with QMP commands doesn't
> > > sound right..
> > >
> > > The idea behind is pretty simple: any object that can only be created once
> > > can now declare the TYPE_SINGLETON interface, then QOM facilities will make
> > > sure it won't be created more than once. For example, qom-list-properties,
> > > device-list-properties, etc., will be smart enough to not try to create
> > > temporary singleton objects now.
> >
> > QOM design assumption: object_new() followed by object_unref() is always
> > possible and has no side effect.
>
> I see.
>
> >
> > QOM introspection relies on this. Your PATCH 1 makes non-class
> > properties of singletons invisible in introspection. Making something
> > with such properties a singleton would be a regression.
> >
> > Anything that violates the design assumption must be delayed to a
> > suitable state transition. For devices (subtypes of TYPE_DEVICE), this
> > is ->realize(). For user-creatable objects (provide interface
> > TYPE_USER_CREATABLE), this is ->complete(). For anything else, it's
> > something else that probably doesn't even exist, yet. See "Problem 5:
> > QOM lacks a clear life cycle" in
> >
> > Subject: Dynamic & heterogeneous machines, initial configuration: problems
> > Date: Wed, 31 Jan 2024 21:14:21 +0100
> > Message-ID: <87o7d1i7ky.fsf@pond.sub.org>
> > https://lore.kernel.org/qemu-devel/87o7d1i7ky.fsf@pond.sub.org/
>
> The major challenge here might be that, in migration's use case we want to
> do something after the last refcount is released.
This is rather a strange idea, and feels back to front. An object's last
refcount must never be released until code has entirely finished using
the object.
> IOW, I don't yet see an easy way to explicit do qdev_unrealize() (even if
> migration object will use realize(), while it doesn't yet..), because there
> can be more than one thread holding refcount of the object, and we don't
> know where to invoke it, and we don't want to do the cleanup if the other
> one still holds a refcount.
This sounds like the code is missing some synchronization mechanism
beween the threads, which need to communicate with each other when
they are "done", so that the "primary" thread can then finally
release any resources.
> Maybe I can also try do that with a "magic property" with its set/get, as
> that's indeed the other hook (basically, object_property_del_all()) that is
> only invoked after the final refcount is released. However I think we
> still need the singleton idea somehow..
Based on the description above it feels like the problem is outside
of QOM, around the lack of synchronization across threads.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object
2024-10-24 16:56 ` [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object Peter Xu
2024-10-25 9:25 ` Markus Armbruster
@ 2024-10-29 10:47 ` Daniel P. Berrangé
2024-10-29 14:32 ` Peter Xu
1 sibling, 1 reply; 38+ messages in thread
From: Daniel P. Berrangé @ 2024-10-29 10:47 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Thu, Oct 24, 2024 at 12:56:25PM -0400, Peter Xu wrote:
> X86 IOMMUs cannot be created more than one on a system yet. Make it a
> singleton so it guards the system from accidentally create yet another
> IOMMU object when one already presents.
>
> Now if someone tries to create more than one, e.g., via:
>
> ./qemu -M q35 -device intel-iommu -device intel-iommu
>
> The error will change from:
>
> qemu-system-x86_64: -device intel-iommu: QEMU does not support multiple vIOMMUs for x86 yet.
>
> To:
>
> qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only supports one instance
>
> Unfortunately, yet we can't remove the singleton check in the machine
> hook (pc_machine_device_pre_plug_cb), because there can also be
> virtio-iommu involved, which doesn't share a common parent class yet.
Presumably the 'class' reported is the one that the user requested,
but this would imply if we were to do
qemu-system-x86_64 -device intel-iommu -device virtio-iommu
Then QEMU would report
"Class 'virtio-iommu' only supports one instance"
at which point the user is wondering, huh, I only requested one virtio-iommu
instance ?
IOW, the current error message would be better as it is not referring to a
specific subclass, but rather to the more general fact that only a single
IOMMU is permitted, no matter what it's impl is.
>
> But with this, it should be closer to reach that goal to check singleton by
> QOM one day.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> hw/i386/x86-iommu.c | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c
> index 60af896225..4bfeb08705 100644
> --- a/hw/i386/x86-iommu.c
> +++ b/hw/i386/x86-iommu.c
> @@ -26,6 +26,7 @@
> #include "qemu/error-report.h"
> #include "trace.h"
> #include "sysemu/kvm.h"
> +#include "qom/object_interfaces.h"
>
> void x86_iommu_iec_register_notifier(X86IOMMUState *iommu,
> iec_notify_fn fn, void *data)
> @@ -133,10 +134,19 @@ static Property x86_iommu_properties[] = {
> DEFINE_PROP_END_OF_LIST(),
> };
>
> +static Object *x86_iommu_get_instance(Error **errp)
> +{
> + return OBJECT(x86_iommu_get_default());
> +}
> +
> static void x86_iommu_class_init(ObjectClass *klass, void *data)
> {
> DeviceClass *dc = DEVICE_CLASS(klass);
> + SingletonClass *singleton = SINGLETON_CLASS(klass);
> +
> dc->realize = x86_iommu_realize;
> + singleton->get_instance = x86_iommu_get_instance;
> +
> device_class_set_props(dc, x86_iommu_properties);
> }
>
> @@ -152,6 +162,10 @@ static const TypeInfo x86_iommu_info = {
> .class_init = x86_iommu_class_init,
> .class_size = sizeof(X86IOMMUClass),
> .abstract = true,
> + .interfaces = (InterfaceInfo[]) {
> + { TYPE_SINGLETON },
> + { }
> + }
> };
>
> static void x86_iommu_register_types(void)
> --
> 2.45.0
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object
2024-10-29 10:47 ` Daniel P. Berrangé
@ 2024-10-29 14:32 ` Peter Xu
0 siblings, 0 replies; 38+ messages in thread
From: Peter Xu @ 2024-10-29 14:32 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Markus Armbruster, Eduardo Habkost,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Tue, Oct 29, 2024 at 10:47:06AM +0000, Daniel P. Berrangé wrote:
> On Thu, Oct 24, 2024 at 12:56:25PM -0400, Peter Xu wrote:
> > X86 IOMMUs cannot be created more than one on a system yet. Make it a
> > singleton so it guards the system from accidentally create yet another
> > IOMMU object when one already presents.
> >
> > Now if someone tries to create more than one, e.g., via:
> >
> > ./qemu -M q35 -device intel-iommu -device intel-iommu
> >
> > The error will change from:
> >
> > qemu-system-x86_64: -device intel-iommu: QEMU does not support multiple vIOMMUs for x86 yet.
> >
> > To:
> >
> > qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only supports one instance
> >
> > Unfortunately, yet we can't remove the singleton check in the machine
> > hook (pc_machine_device_pre_plug_cb), because there can also be
> > virtio-iommu involved, which doesn't share a common parent class yet.
>
> Presumably the 'class' reported is the one that the user requested,
> but this would imply if we were to do
>
> qemu-system-x86_64 -device intel-iommu -device virtio-iommu
>
> Then QEMU would report
>
> "Class 'virtio-iommu' only supports one instance"
>
> at which point the user is wondering, huh, I only requested one virtio-iommu
> instance ?
>
> IOW, the current error message would be better as it is not referring to a
> specific subclass, but rather to the more general fact that only a single
> IOMMU is permitted, no matter what it's impl is.
True.. though IIUC this is more or less a cosmetic change only. E.g., if
we want (assuming after we could have object_new_allowed(Error **errp),
checking both abstract + singleton classes) we could make the error points
to the base class (rather than the top class to be initiated) that declared
TYPE_SINGLETON when it failed due to the singleton check.
One step further, we can even provide a custom Error for any singleton
class to say whatever it wants if it hits a duplicate.
So to me it's a separate issue from whether we would like to have a generic
way to define a singleton class. I am still ok if we want to avoid
introducing the singleton, but just to mention I believe it can report
something similar as before if we want.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 0/4] QOM: Singleton interface
2024-10-29 10:42 ` Daniel P. Berrangé
@ 2024-10-29 14:45 ` Peter Xu
2024-10-29 16:04 ` Daniel P. Berrangé
0 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-10-29 14:45 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Markus Armbruster, qemu-devel, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Alex Williamson,
Paolo Bonzini, Peter Maydell
On Tue, Oct 29, 2024 at 10:42:58AM +0000, Daniel P. Berrangé wrote:
> On Fri, Oct 25, 2024 at 11:01:56AM -0400, Peter Xu wrote:
> > On Fri, Oct 25, 2024 at 09:38:31AM +0200, Markus Armbruster wrote:
> > > Peter Xu <peterx@redhat.com> writes:
> > >
> > > > This patchset introduces the singleton interface for QOM.
> > > >
> > > > The singleton interface is as simple as "this class can only create one
> > > > instance".
> > > >
> > > > We used to have similar demand when working on all kinds of vIOMMUs,
> > > > because in most cases that I am aware of, vIOMMU must be a singleton as
> > > > it's closely bound to the machine and also the root PCIe systems. We used
> > > > to have a bunch of special code guarding those, for example, X86 has
> > > > pc_machine_device_pre_plug_cb() just to detect when vIOMMU is created more
> > > > than once.
> > > >
> > > > There is a similar demand raising recently (even if the problem existed
> > > > over years) when we were looking at a migration bug, that part of it was
> > > > involved with the current_migration global pointer being referenced
> > > > anywhere, even after the migration object was finalize()ed. So far without
> > > > singleton support, there's no way to reset the variable properly.
> > > > Declaring migration object to be a singleton also just makes sense, e.g.,
> > > > dynamically creating migration objects on the fly with QMP commands doesn't
> > > > sound right..
> > > >
> > > > The idea behind is pretty simple: any object that can only be created once
> > > > can now declare the TYPE_SINGLETON interface, then QOM facilities will make
> > > > sure it won't be created more than once. For example, qom-list-properties,
> > > > device-list-properties, etc., will be smart enough to not try to create
> > > > temporary singleton objects now.
> > >
> > > QOM design assumption: object_new() followed by object_unref() is always
> > > possible and has no side effect.
> >
> > I see.
> >
> > >
> > > QOM introspection relies on this. Your PATCH 1 makes non-class
> > > properties of singletons invisible in introspection. Making something
> > > with such properties a singleton would be a regression.
> > >
> > > Anything that violates the design assumption must be delayed to a
> > > suitable state transition. For devices (subtypes of TYPE_DEVICE), this
> > > is ->realize(). For user-creatable objects (provide interface
> > > TYPE_USER_CREATABLE), this is ->complete(). For anything else, it's
> > > something else that probably doesn't even exist, yet. See "Problem 5:
> > > QOM lacks a clear life cycle" in
> > >
> > > Subject: Dynamic & heterogeneous machines, initial configuration: problems
> > > Date: Wed, 31 Jan 2024 21:14:21 +0100
> > > Message-ID: <87o7d1i7ky.fsf@pond.sub.org>
> > > https://lore.kernel.org/qemu-devel/87o7d1i7ky.fsf@pond.sub.org/
> >
> > The major challenge here might be that, in migration's use case we want to
> > do something after the last refcount is released.
>
> This is rather a strange idea, and feels back to front. An object's last
> refcount must never be released until code has entirely finished using
> the object.
>
> > IOW, I don't yet see an easy way to explicit do qdev_unrealize() (even if
> > migration object will use realize(), while it doesn't yet..), because there
> > can be more than one thread holding refcount of the object, and we don't
> > know where to invoke it, and we don't want to do the cleanup if the other
> > one still holds a refcount.
>
> This sounds like the code is missing some synchronization mechanism
> beween the threads, which need to communicate with each other when
> they are "done", so that the "primary" thread can then finally
> release any resources.
>
> > Maybe I can also try do that with a "magic property" with its set/get, as
> > that's indeed the other hook (basically, object_property_del_all()) that is
> > only invoked after the final refcount is released. However I think we
> > still need the singleton idea somehow..
>
> Based on the description above it feels like the problem is outside
> of QOM, around the lack of synchronization across threads.
Right, this used to be discussed here and you were also in the loop:
https://lore.kernel.org/qemu-devel/20190228122822.GD4970@work-vm/
I kind of agree join() would be perfect, disregard the question on whether
QEMU would still benefit from a singleton interface, no matter migration
would rely on that for refcounting, or simply it'll also make sense to just
avoid people creating random migration objects.
So yes, I think migration can benefit from singleton idea for more than one
reason, while the refcount issue here might be even better resolved by
join() in main.
It's just that in the thread Dave raised a few points on possible
challenges on join() in the main thread. I'm not sure whether we should go
that route instead. Obviously I am digging one of our legacy rabbit holes
from when I started to look at this "access current_migration anywhere"
issue reported from VFIO. But if some of us think we should give it a
shot, I can try. After all, I started digging.
Another simpler but direct solution to "access current_migration" is, we
simply don't free it at all, leaving process exit() to do the job. Then I
can drop all object_[un]ref() for the migration object. I think that could
work too, but ugly, for the refcount issue.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 0/4] QOM: Singleton interface
2024-10-29 14:45 ` Peter Xu
@ 2024-10-29 16:04 ` Daniel P. Berrangé
2024-10-29 17:05 ` Peter Xu
0 siblings, 1 reply; 38+ messages in thread
From: Daniel P. Berrangé @ 2024-10-29 16:04 UTC (permalink / raw)
To: Peter Xu
Cc: Markus Armbruster, qemu-devel, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Alex Williamson,
Paolo Bonzini, Peter Maydell
On Tue, Oct 29, 2024 at 10:45:25AM -0400, Peter Xu wrote:
> On Tue, Oct 29, 2024 at 10:42:58AM +0000, Daniel P. Berrangé wrote:
> > On Fri, Oct 25, 2024 at 11:01:56AM -0400, Peter Xu wrote:
> > > On Fri, Oct 25, 2024 at 09:38:31AM +0200, Markus Armbruster wrote:
> > > > Peter Xu <peterx@redhat.com> writes:
> > > >
> > > > > This patchset introduces the singleton interface for QOM.
> > > > >
> > > > > The singleton interface is as simple as "this class can only create one
> > > > > instance".
> > > > >
> > > > > We used to have similar demand when working on all kinds of vIOMMUs,
> > > > > because in most cases that I am aware of, vIOMMU must be a singleton as
> > > > > it's closely bound to the machine and also the root PCIe systems. We used
> > > > > to have a bunch of special code guarding those, for example, X86 has
> > > > > pc_machine_device_pre_plug_cb() just to detect when vIOMMU is created more
> > > > > than once.
> > > > >
> > > > > There is a similar demand raising recently (even if the problem existed
> > > > > over years) when we were looking at a migration bug, that part of it was
> > > > > involved with the current_migration global pointer being referenced
> > > > > anywhere, even after the migration object was finalize()ed. So far without
> > > > > singleton support, there's no way to reset the variable properly.
> > > > > Declaring migration object to be a singleton also just makes sense, e.g.,
> > > > > dynamically creating migration objects on the fly with QMP commands doesn't
> > > > > sound right..
> > > > >
> > > > > The idea behind is pretty simple: any object that can only be created once
> > > > > can now declare the TYPE_SINGLETON interface, then QOM facilities will make
> > > > > sure it won't be created more than once. For example, qom-list-properties,
> > > > > device-list-properties, etc., will be smart enough to not try to create
> > > > > temporary singleton objects now.
> > > >
> > > > QOM design assumption: object_new() followed by object_unref() is always
> > > > possible and has no side effect.
> > >
> > > I see.
> > >
> > > >
> > > > QOM introspection relies on this. Your PATCH 1 makes non-class
> > > > properties of singletons invisible in introspection. Making something
> > > > with such properties a singleton would be a regression.
> > > >
> > > > Anything that violates the design assumption must be delayed to a
> > > > suitable state transition. For devices (subtypes of TYPE_DEVICE), this
> > > > is ->realize(). For user-creatable objects (provide interface
> > > > TYPE_USER_CREATABLE), this is ->complete(). For anything else, it's
> > > > something else that probably doesn't even exist, yet. See "Problem 5:
> > > > QOM lacks a clear life cycle" in
> > > >
> > > > Subject: Dynamic & heterogeneous machines, initial configuration: problems
> > > > Date: Wed, 31 Jan 2024 21:14:21 +0100
> > > > Message-ID: <87o7d1i7ky.fsf@pond.sub.org>
> > > > https://lore.kernel.org/qemu-devel/87o7d1i7ky.fsf@pond.sub.org/
> > >
> > > The major challenge here might be that, in migration's use case we want to
> > > do something after the last refcount is released.
> >
> > This is rather a strange idea, and feels back to front. An object's last
> > refcount must never be released until code has entirely finished using
> > the object.
> >
> > > IOW, I don't yet see an easy way to explicit do qdev_unrealize() (even if
> > > migration object will use realize(), while it doesn't yet..), because there
> > > can be more than one thread holding refcount of the object, and we don't
> > > know where to invoke it, and we don't want to do the cleanup if the other
> > > one still holds a refcount.
> >
> > This sounds like the code is missing some synchronization mechanism
> > beween the threads, which need to communicate with each other when
> > they are "done", so that the "primary" thread can then finally
> > release any resources.
> >
> > > Maybe I can also try do that with a "magic property" with its set/get, as
> > > that's indeed the other hook (basically, object_property_del_all()) that is
> > > only invoked after the final refcount is released. However I think we
> > > still need the singleton idea somehow..
> >
> > Based on the description above it feels like the problem is outside
> > of QOM, around the lack of synchronization across threads.
>
> Right, this used to be discussed here and you were also in the loop:
>
> https://lore.kernel.org/qemu-devel/20190228122822.GD4970@work-vm/
>
> I kind of agree join() would be perfect, disregard the question on whether
> QEMU would still benefit from a singleton interface, no matter migration
> would rely on that for refcounting, or simply it'll also make sense to just
> avoid people creating random migration objects.
>
> So yes, I think migration can benefit from singleton idea for more than one
> reason, while the refcount issue here might be even better resolved by
> join() in main.
>
> It's just that in the thread Dave raised a few points on possible
> challenges on join() in the main thread. I'm not sure whether we should go
> that route instead. Obviously I am digging one of our legacy rabbit holes
> from when I started to look at this "access current_migration anywhere"
> issue reported from VFIO. But if some of us think we should give it a
> shot, I can try. After all, I started digging.
>
> Another simpler but direct solution to "access current_migration" is, we
> simply don't free it at all, leaving process exit() to do the job. Then I
> can drop all object_[un]ref() for the migration object. I think that could
> work too, but ugly, for the refcount issue.
I tend to feel that having MigrationState exist for the whole lifetime
of QEMU is a bug, forced on us by the unfortunate need to call
migrate-set-parameters/capabilities separately from the migrate
command, and by the need to query migrate info an arbitrary amount of
time after it finishes.
This puts libvirt in the awkward position of having to manually reset
all migration parameters, just to ensure earlier settings don't
accidentally affect a future migration operation :-( This is a design
that encourages mistakes.
Rather than MigrationState become a singleton, I'd really encourage
trying to see if we can fix the lifecycle design problems, so that
we have a clear beginning & end of the migration operation, with the
state discarded at the end, such that every future migrate starts
from a clean base.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 0/4] QOM: Singleton interface
2024-10-29 16:04 ` Daniel P. Berrangé
@ 2024-10-29 17:05 ` Peter Xu
2024-10-29 17:17 ` Daniel P. Berrangé
2024-12-11 8:19 ` Markus Armbruster
0 siblings, 2 replies; 38+ messages in thread
From: Peter Xu @ 2024-10-29 17:05 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Markus Armbruster, qemu-devel, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Alex Williamson,
Paolo Bonzini, Peter Maydell
On Tue, Oct 29, 2024 at 04:04:50PM +0000, Daniel P. Berrangé wrote:
> On Tue, Oct 29, 2024 at 10:45:25AM -0400, Peter Xu wrote:
> > On Tue, Oct 29, 2024 at 10:42:58AM +0000, Daniel P. Berrangé wrote:
> > > On Fri, Oct 25, 2024 at 11:01:56AM -0400, Peter Xu wrote:
> > > > On Fri, Oct 25, 2024 at 09:38:31AM +0200, Markus Armbruster wrote:
> > > > > Peter Xu <peterx@redhat.com> writes:
> > > > >
> > > > > > This patchset introduces the singleton interface for QOM.
> > > > > >
> > > > > > The singleton interface is as simple as "this class can only create one
> > > > > > instance".
> > > > > >
> > > > > > We used to have similar demand when working on all kinds of vIOMMUs,
> > > > > > because in most cases that I am aware of, vIOMMU must be a singleton as
> > > > > > it's closely bound to the machine and also the root PCIe systems. We used
> > > > > > to have a bunch of special code guarding those, for example, X86 has
> > > > > > pc_machine_device_pre_plug_cb() just to detect when vIOMMU is created more
> > > > > > than once.
> > > > > >
> > > > > > There is a similar demand raising recently (even if the problem existed
> > > > > > over years) when we were looking at a migration bug, that part of it was
> > > > > > involved with the current_migration global pointer being referenced
> > > > > > anywhere, even after the migration object was finalize()ed. So far without
> > > > > > singleton support, there's no way to reset the variable properly.
> > > > > > Declaring migration object to be a singleton also just makes sense, e.g.,
> > > > > > dynamically creating migration objects on the fly with QMP commands doesn't
> > > > > > sound right..
> > > > > >
> > > > > > The idea behind is pretty simple: any object that can only be created once
> > > > > > can now declare the TYPE_SINGLETON interface, then QOM facilities will make
> > > > > > sure it won't be created more than once. For example, qom-list-properties,
> > > > > > device-list-properties, etc., will be smart enough to not try to create
> > > > > > temporary singleton objects now.
> > > > >
> > > > > QOM design assumption: object_new() followed by object_unref() is always
> > > > > possible and has no side effect.
> > > >
> > > > I see.
> > > >
> > > > >
> > > > > QOM introspection relies on this. Your PATCH 1 makes non-class
> > > > > properties of singletons invisible in introspection. Making something
> > > > > with such properties a singleton would be a regression.
> > > > >
> > > > > Anything that violates the design assumption must be delayed to a
> > > > > suitable state transition. For devices (subtypes of TYPE_DEVICE), this
> > > > > is ->realize(). For user-creatable objects (provide interface
> > > > > TYPE_USER_CREATABLE), this is ->complete(). For anything else, it's
> > > > > something else that probably doesn't even exist, yet. See "Problem 5:
> > > > > QOM lacks a clear life cycle" in
> > > > >
> > > > > Subject: Dynamic & heterogeneous machines, initial configuration: problems
> > > > > Date: Wed, 31 Jan 2024 21:14:21 +0100
> > > > > Message-ID: <87o7d1i7ky.fsf@pond.sub.org>
> > > > > https://lore.kernel.org/qemu-devel/87o7d1i7ky.fsf@pond.sub.org/
> > > >
> > > > The major challenge here might be that, in migration's use case we want to
> > > > do something after the last refcount is released.
> > >
> > > This is rather a strange idea, and feels back to front. An object's last
> > > refcount must never be released until code has entirely finished using
> > > the object.
> > >
> > > > IOW, I don't yet see an easy way to explicit do qdev_unrealize() (even if
> > > > migration object will use realize(), while it doesn't yet..), because there
> > > > can be more than one thread holding refcount of the object, and we don't
> > > > know where to invoke it, and we don't want to do the cleanup if the other
> > > > one still holds a refcount.
> > >
> > > This sounds like the code is missing some synchronization mechanism
> > > beween the threads, which need to communicate with each other when
> > > they are "done", so that the "primary" thread can then finally
> > > release any resources.
> > >
> > > > Maybe I can also try do that with a "magic property" with its set/get, as
> > > > that's indeed the other hook (basically, object_property_del_all()) that is
> > > > only invoked after the final refcount is released. However I think we
> > > > still need the singleton idea somehow..
> > >
> > > Based on the description above it feels like the problem is outside
> > > of QOM, around the lack of synchronization across threads.
> >
> > Right, this used to be discussed here and you were also in the loop:
> >
> > https://lore.kernel.org/qemu-devel/20190228122822.GD4970@work-vm/
> >
> > I kind of agree join() would be perfect, disregard the question on whether
> > QEMU would still benefit from a singleton interface, no matter migration
> > would rely on that for refcounting, or simply it'll also make sense to just
> > avoid people creating random migration objects.
> >
> > So yes, I think migration can benefit from singleton idea for more than one
> > reason, while the refcount issue here might be even better resolved by
> > join() in main.
> >
> > It's just that in the thread Dave raised a few points on possible
> > challenges on join() in the main thread. I'm not sure whether we should go
> > that route instead. Obviously I am digging one of our legacy rabbit holes
> > from when I started to look at this "access current_migration anywhere"
> > issue reported from VFIO. But if some of us think we should give it a
> > shot, I can try. After all, I started digging.
> >
> > Another simpler but direct solution to "access current_migration" is, we
> > simply don't free it at all, leaving process exit() to do the job. Then I
> > can drop all object_[un]ref() for the migration object. I think that could
> > work too, but ugly, for the refcount issue.
>
> I tend to feel that having MigrationState exist for the whole lifetime
> of QEMU is a bug, forced on us by the unfortunate need to call
> migrate-set-parameters/capabilities separately from the migrate
> command, and by the need to query migrate info an arbitrary amount of
> time after it finishes.
>
> This puts libvirt in the awkward position of having to manually reset
> all migration parameters, just to ensure earlier settings don't
> accidentally affect a future migration operation :-( This is a design
> that encourages mistakes.
I think it would still be easy to add "cap" & "params" arguments support
for the "migrate" QMP command without breaking the current API, iff that
helps in whatever form. When present, it simply applies the caps and/or
param list first before invoking the migrate command, fail the command if
cap / param check fails.
But I'm not sure whether that's a concern at all for Libvirt, if what
Libvirt currently does is having separate "migrate-set-*" commands prior to
the "migrate". I may have overlooked the real issue behind on how that
could complicate Libvirt.
>
> Rather than MigrationState become a singleton, I'd really encourage
> trying to see if we can fix the lifecycle design problems, so that
> we have a clear beginning & end of the migration operation, with the
> state discarded at the end, such that every future migrate starts
> from a clean base.
I assume it implies the join() path as a start. As long as we're ok we
risk slow quits of QEMU, as I would expect bug reports coming afterwards,
we could try. I believe there's no way we can resolve all such issues in
one shot. I doubt whether some of those can be too tricky so we may wish
to go back at some point, spending too much effort but without a direct
benefit yet so far.
Meanwhile we still have the immediate concern that current_migration can
points to freed memory right now with QEMU master branch. That's simply
wrong.
I thought it was still an improvement to have the singleton idea, so that
even afterwards we can join() properly the idea may still make sense on its
own. It is also not against the ultimate willingness that we want to
create an obj at start of migration, and clearly destroys it after
migration completes. When that comes we keep migration object singleton,
but then the tricky operations on current_migration can be gone.
That still guarantees from QOM level that e.g. nobody tries to initiate
migration twice, for example, or accidentally create it somewhere, like in
qom-list-properties.
There's yet another short term plan that I can fix UAF for
current_migration, which is to have a refcount for migration alone on top
of Object, then it only object_unref() on the migration object after we
know the last real refcount is released. That won't need any QOM change,
so keep the complexity internally. But I prefer the current proposal,
assuming it could be still helpful in general for QEMU, even though I'm not
sure.
So.. I'm not sure whether we should ignore the current UAF until a whole
refactor of any kind to land, or we should do something to fix it, which is
what I'm trying with the series.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 0/4] QOM: Singleton interface
2024-10-29 17:05 ` Peter Xu
@ 2024-10-29 17:17 ` Daniel P. Berrangé
2024-12-11 8:19 ` Markus Armbruster
1 sibling, 0 replies; 38+ messages in thread
From: Daniel P. Berrangé @ 2024-10-29 17:17 UTC (permalink / raw)
To: Peter Xu
Cc: Markus Armbruster, qemu-devel, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Alex Williamson,
Paolo Bonzini, Peter Maydell
On Tue, Oct 29, 2024 at 01:05:04PM -0400, Peter Xu wrote:
> But I'm not sure whether that's a concern at all for Libvirt, if what
> Libvirt currently does is having separate "migrate-set-*" commands prior to
> the "migrate". I may have overlooked the real issue behind on how that
> could complicate Libvirt.
>
> >
> > Rather than MigrationState become a singleton, I'd really encourage
> > trying to see if we can fix the lifecycle design problems, so that
> > we have a clear beginning & end of the migration operation, with the
> > state discarded at the end, such that every future migrate starts
> > from a clean base.
>
> I assume it implies the join() path as a start. As long as we're ok we
> risk slow quits of QEMU, as I would expect bug reports coming afterwards,
> we could try. I believe there's no way we can resolve all such issues in
> one shot. I doubt whether some of those can be too tricky so we may wish
> to go back at some point, spending too much effort but without a direct
> benefit yet so far.
>
> Meanwhile we still have the immediate concern that current_migration can
> points to freed memory right now with QEMU master branch. That's simply
> wrong.
Yeah, if we're accessing freed memory, that's a segv/abrt danger, and
needs fixing quickly.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object
2024-10-25 22:13 ` Peter Xu
@ 2024-11-07 11:12 ` Markus Armbruster
2024-11-07 15:29 ` Peter Xu
0 siblings, 1 reply; 38+ messages in thread
From: Markus Armbruster @ 2024-11-07 11:12 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Daniel P . Berrangé,
Alex Williamson, Paolo Bonzini, Peter Maydell
Peter Xu <peterx@redhat.com> writes:
> On Fri, Oct 25, 2024 at 05:55:59PM -0400, Peter Xu wrote:
>> On Fri, Oct 25, 2024 at 11:25:23AM +0200, Markus Armbruster wrote:
>> > Peter Xu <peterx@redhat.com> writes:
>> >
>> > > X86 IOMMUs cannot be created more than one on a system yet. Make it a
>> > > singleton so it guards the system from accidentally create yet another
>> > > IOMMU object when one already presents.
>> > >
>> > > Now if someone tries to create more than one, e.g., via:
>> > >
>> > > ./qemu -M q35 -device intel-iommu -device intel-iommu
>> > >
>> > > The error will change from:
>> > >
>> > > qemu-system-x86_64: -device intel-iommu: QEMU does not support multiple vIOMMUs for x86 yet.
>> > >
>> > > To:
>> > >
>> > > qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only supports one instance
>> > >
>> > > Unfortunately, yet we can't remove the singleton check in the machine
>> > > hook (pc_machine_device_pre_plug_cb), because there can also be
>> > > virtio-iommu involved, which doesn't share a common parent class yet.
>> > >
>> > > But with this, it should be closer to reach that goal to check singleton by
>> > > QOM one day.
>> > >
>> > > Signed-off-by: Peter Xu <peterx@redhat.com>
>> >
>> > $ qemu-system-x86_64 -device amd-iommu,help
>> > /work/armbru/qemu/include/hw/boards.h:24:MACHINE: Object 0x56473906f960 is not an instance of type machine
>> > Aborted (core dumped)
[...]
>> Thanks for the report!
>>
>> It turns out that qdev_get_machine() cannot be invoked too early, and the
>> singleton code can make it earlier..
>>
>> We may want a pre-requisite patch to allow qdev_get_machine() to be invoked
>> anytime, like:
>>
>> ===8<===
>> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
>> index db36f54d91..7ceae47139 100644
>> --- a/hw/core/qdev.c
>> +++ b/hw/core/qdev.c
>> @@ -831,6 +831,16 @@ Object *qdev_get_machine(void)
>> {
>> static Object *dev;
>>
>> + if (!phase_check(PHASE_MACHINE_CREATED)) {
>> + /*
>> + * When the machine is not created, below can wrongly create
>> + * /machine to be a container.. this enables qdev_get_machine() to
>> + * be used at any time and return NULL properly when machine is not
>> + * created.
>> + */
>> + return NULL;
>> + }
>> +
>> if (dev == NULL) {
>> dev = container_get(object_get_root(), "/machine");
>> }
>> ===8<===
>>
>> I hope it makes sense on its own.
>
> My apologies, spoke too soon here. This helper is used too after machine
> is created, but right before switching to PHASE_MACHINE_CREATE stage..
container_get() is a trap.
When the object to be gotten is always "container", it merely
complicates container creation: it's implicitly created on first get.
Which of the calls creates may be less than obvious.
When the object to be gotten is something else, such as a machine,
container_get() before creation is *wrong*, and will lead to trouble
later.
In my opinion:
* Hiding creation in getters is a bad idea unless creation has no
material side effects.
* Getting anything but a container with container_get() is in bad taste.
> So we need another way, like:
>
> ===8<===
>
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index db36f54d91..36a9fdb428 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -832,7 +832,13 @@ Object *qdev_get_machine(void)
> static Object *dev;
>
> if (dev == NULL) {
> - dev = container_get(object_get_root(), "/machine");
> + /*
> + * NOTE: dev can keep being NULL if machine is not yet created!
> + * In which case the function will properly return NULL.
> + *
> + * Whenever machine object is created and found once, we cache it.
> + */
> + dev = object_resolve_path_component(object_get_root(), "machine");
> }
>
> return dev;
Now returns null instead of a bogus container when called before machine
creation. Improvement of sorts. But none of the callers expect null...
shouldn't we assert(dev) here?
Hmm, below you add a caller that checks for null.
Another nice mess.
> ===8<===
>
> The idea is still the same. Meanwhile I'll test more to see whether it has
> other issues.
>
> Thanks,
>
>> Then callers who can be invoked earlier
>> could then handle NULL properly, in this case..
>>
>> ===8<===
>> diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c
>> index 4bfeb08705..fceb7adfe0 100644
>> --- a/hw/i386/x86-iommu.c
>> +++ b/hw/i386/x86-iommu.c
>> @@ -80,9 +80,15 @@ void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *msg_out)
>>
>> X86IOMMUState *x86_iommu_get_default(void)
>> {
>> - MachineState *ms = MACHINE(qdev_get_machine());
>> - PCMachineState *pcms =
>> - PC_MACHINE(object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE));
>> + Object *machine = qdev_get_machine();
>> + PCMachineState *pcms;
>> +
>> + /* If machine has not been created, so is the vIOMMU */
>> + if (!machine) {
>> + return NULL;
>> + }
>> +
>> + pcms = PC_MACHINE(object_dynamic_cast(machine, TYPE_PC_MACHINE));
>>
>> if (pcms &&
>> object_dynamic_cast(OBJECT(pcms->iommu), TYPE_X86_IOMMU_DEVICE)) {
>> ===8<===
>>
>> I'll make sure this works if I'll repost.
>>
>> Thanks,
>>
>> --
>> Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object
2024-11-07 11:12 ` Markus Armbruster
@ 2024-11-07 15:29 ` Peter Xu
2024-11-08 8:50 ` Markus Armbruster
0 siblings, 1 reply; 38+ messages in thread
From: Peter Xu @ 2024-11-07 15:29 UTC (permalink / raw)
To: Markus Armbruster
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Daniel P . Berrangé,
Alex Williamson, Paolo Bonzini, Peter Maydell
On Thu, Nov 07, 2024 at 12:12:10PM +0100, Markus Armbruster wrote:
> Peter Xu <peterx@redhat.com> writes:
>
> > On Fri, Oct 25, 2024 at 05:55:59PM -0400, Peter Xu wrote:
> >> On Fri, Oct 25, 2024 at 11:25:23AM +0200, Markus Armbruster wrote:
> >> > Peter Xu <peterx@redhat.com> writes:
> >> >
> >> > > X86 IOMMUs cannot be created more than one on a system yet. Make it a
> >> > > singleton so it guards the system from accidentally create yet another
> >> > > IOMMU object when one already presents.
> >> > >
> >> > > Now if someone tries to create more than one, e.g., via:
> >> > >
> >> > > ./qemu -M q35 -device intel-iommu -device intel-iommu
> >> > >
> >> > > The error will change from:
> >> > >
> >> > > qemu-system-x86_64: -device intel-iommu: QEMU does not support multiple vIOMMUs for x86 yet.
> >> > >
> >> > > To:
> >> > >
> >> > > qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only supports one instance
> >> > >
> >> > > Unfortunately, yet we can't remove the singleton check in the machine
> >> > > hook (pc_machine_device_pre_plug_cb), because there can also be
> >> > > virtio-iommu involved, which doesn't share a common parent class yet.
> >> > >
> >> > > But with this, it should be closer to reach that goal to check singleton by
> >> > > QOM one day.
> >> > >
> >> > > Signed-off-by: Peter Xu <peterx@redhat.com>
> >> >
> >> > $ qemu-system-x86_64 -device amd-iommu,help
> >> > /work/armbru/qemu/include/hw/boards.h:24:MACHINE: Object 0x56473906f960 is not an instance of type machine
> >> > Aborted (core dumped)
>
> [...]
>
> >> Thanks for the report!
> >>
> >> It turns out that qdev_get_machine() cannot be invoked too early, and the
> >> singleton code can make it earlier..
> >>
> >> We may want a pre-requisite patch to allow qdev_get_machine() to be invoked
> >> anytime, like:
> >>
> >> ===8<===
> >> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> >> index db36f54d91..7ceae47139 100644
> >> --- a/hw/core/qdev.c
> >> +++ b/hw/core/qdev.c
> >> @@ -831,6 +831,16 @@ Object *qdev_get_machine(void)
> >> {
> >> static Object *dev;
> >>
> >> + if (!phase_check(PHASE_MACHINE_CREATED)) {
> >> + /*
> >> + * When the machine is not created, below can wrongly create
> >> + * /machine to be a container.. this enables qdev_get_machine() to
> >> + * be used at any time and return NULL properly when machine is not
> >> + * created.
> >> + */
> >> + return NULL;
> >> + }
> >> +
> >> if (dev == NULL) {
> >> dev = container_get(object_get_root(), "/machine");
> >> }
> >> ===8<===
> >>
> >> I hope it makes sense on its own.
> >
> > My apologies, spoke too soon here. This helper is used too after machine
> > is created, but right before switching to PHASE_MACHINE_CREATE stage..
>
> container_get() is a trap.
I had the same feeling.. Though I'd confess I'm not familiar enough with
this part of code.
>
> When the object to be gotten is always "container", it merely
> complicates container creation: it's implicitly created on first get.
> Which of the calls creates may be less than obvious.
>
> When the object to be gotten is something else, such as a machine,
> container_get() before creation is *wrong*, and will lead to trouble
> later.
>
> In my opinion:
>
> * Hiding creation in getters is a bad idea unless creation has no
> material side effects.
>
> * Getting anything but a container with container_get() is in bad taste.
Agreed.
IMHO container_get() interface might still be ok to implicitly create
containers, but only if it will: (1) always make sure what it walks is a
container along the way, and (2) never return any non-container.
>
>
> > So we need another way, like:
> >
> > ===8<===
> >
> > diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> > index db36f54d91..36a9fdb428 100644
> > --- a/hw/core/qdev.c
> > +++ b/hw/core/qdev.c
> > @@ -832,7 +832,13 @@ Object *qdev_get_machine(void)
> > static Object *dev;
> >
> > if (dev == NULL) {
> > - dev = container_get(object_get_root(), "/machine");
> > + /*
> > + * NOTE: dev can keep being NULL if machine is not yet created!
> > + * In which case the function will properly return NULL.
> > + *
> > + * Whenever machine object is created and found once, we cache it.
> > + */
> > + dev = object_resolve_path_component(object_get_root(), "machine");
> > }
> >
> > return dev;
>
> Now returns null instead of a bogus container when called before machine
> creation. Improvement of sorts. But none of the callers expect null...
> shouldn't we assert(dev) here?
>
> Hmm, below you add a caller that checks for null.
>
> Another nice mess.
I plan to put aside the application of singletons to x86-iommu as of now,
due to the fact that qdev complexity may better be done separately.
IOW, before that, I wonder whether we should clean up the container_get()
as you discussed: it doesn't sound like a good interface to return
non-container objects.
I had a quick look, I only see two outliers of such, and besides the
"abuse" in qdev_get_machine(), the only other one is
e500_pcihost_bridge_realize():
*** hw/core/qdev.c:
qdev_get_machine[820] dev = container_get(object_get_root(), "/machine");
*** hw/pci-host/ppce500.c:
e500_pcihost_bridge_realize[422] PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(),
If any of us thinks this is the right way to go, I can try to clean it up
(for 10.0). qdev_get_machine() may still need to be able to return NULL
when singleton applies to IOMMUs, but that can be for later. Before that,
we can still assert(qdev), I think.
Just to mention I've posted rfcv2 for this series, again feel free to
ignore patch 3-5 as of now:
[PATCH RFC v2 0/7] QOM: Singleton interface
https://lore.kernel.org/r/20241029211607.2114845-1-peterx@redhat.com
I think the plan is Dan may keep collecting feedbacks on his other rfc:
[RFC 0/5] RFC: require error handling for dynamically created objects
https://lore.kernel.org/r/20241031155350.3240361-1-berrange@redhat.com
Then after Dan's lands, I'll rebase my rfcv2 on top of his, dropping
iommu/qdev changes.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object
2024-11-07 15:29 ` Peter Xu
@ 2024-11-08 8:50 ` Markus Armbruster
0 siblings, 0 replies; 38+ messages in thread
From: Markus Armbruster @ 2024-11-08 8:50 UTC (permalink / raw)
To: Peter Xu
Cc: qemu-devel, Fabiano Rosas, Igor Mammedov, Juraj Marcin,
Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Daniel P . Berrangé,
Alex Williamson, Paolo Bonzini, Peter Maydell
Peter Xu <peterx@redhat.com> writes:
> On Thu, Nov 07, 2024 at 12:12:10PM +0100, Markus Armbruster wrote:
>> Peter Xu <peterx@redhat.com> writes:
>>
>> > On Fri, Oct 25, 2024 at 05:55:59PM -0400, Peter Xu wrote:
>> >> On Fri, Oct 25, 2024 at 11:25:23AM +0200, Markus Armbruster wrote:
>> >> > Peter Xu <peterx@redhat.com> writes:
>> >> >
>> >> > > X86 IOMMUs cannot be created more than one on a system yet. Make it a
>> >> > > singleton so it guards the system from accidentally create yet another
>> >> > > IOMMU object when one already presents.
>> >> > >
>> >> > > Now if someone tries to create more than one, e.g., via:
>> >> > >
>> >> > > ./qemu -M q35 -device intel-iommu -device intel-iommu
>> >> > >
>> >> > > The error will change from:
>> >> > >
>> >> > > qemu-system-x86_64: -device intel-iommu: QEMU does not support multiple vIOMMUs for x86 yet.
>> >> > >
>> >> > > To:
>> >> > >
>> >> > > qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only supports one instance
>> >> > >
>> >> > > Unfortunately, yet we can't remove the singleton check in the machine
>> >> > > hook (pc_machine_device_pre_plug_cb), because there can also be
>> >> > > virtio-iommu involved, which doesn't share a common parent class yet.
>> >> > >
>> >> > > But with this, it should be closer to reach that goal to check singleton by
>> >> > > QOM one day.
>> >> > >
>> >> > > Signed-off-by: Peter Xu <peterx@redhat.com>
>> >> >
>> >> > $ qemu-system-x86_64 -device amd-iommu,help
>> >> > /work/armbru/qemu/include/hw/boards.h:24:MACHINE: Object 0x56473906f960 is not an instance of type machine
>> >> > Aborted (core dumped)
>>
>> [...]
>>
>> >> Thanks for the report!
>> >>
>> >> It turns out that qdev_get_machine() cannot be invoked too early, and the
>> >> singleton code can make it earlier..
>> >>
>> >> We may want a pre-requisite patch to allow qdev_get_machine() to be invoked
>> >> anytime, like:
>> >>
>> >> ===8<===
>> >> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
>> >> index db36f54d91..7ceae47139 100644
>> >> --- a/hw/core/qdev.c
>> >> +++ b/hw/core/qdev.c
>> >> @@ -831,6 +831,16 @@ Object *qdev_get_machine(void)
>> >> {
>> >> static Object *dev;
>> >>
>> >> + if (!phase_check(PHASE_MACHINE_CREATED)) {
>> >> + /*
>> >> + * When the machine is not created, below can wrongly create
>> >> + * /machine to be a container.. this enables qdev_get_machine() to
>> >> + * be used at any time and return NULL properly when machine is not
>> >> + * created.
>> >> + */
>> >> + return NULL;
>> >> + }
>> >> +
>> >> if (dev == NULL) {
>> >> dev = container_get(object_get_root(), "/machine");
>> >> }
>> >> ===8<===
>> >>
>> >> I hope it makes sense on its own.
>> >
>> > My apologies, spoke too soon here. This helper is used too after machine
>> > is created, but right before switching to PHASE_MACHINE_CREATE stage..
>>
>> container_get() is a trap.
>
> I had the same feeling.. Though I'd confess I'm not familiar enough with
> this part of code.
>
>>
>> When the object to be gotten is always "container", it merely
>> complicates container creation: it's implicitly created on first get.
>> Which of the calls creates may be less than obvious.
>>
>> When the object to be gotten is something else, such as a machine,
>> container_get() before creation is *wrong*, and will lead to trouble
>> later.
>>
>> In my opinion:
>>
>> * Hiding creation in getters is a bad idea unless creation has no
>> material side effects.
>>
>> * Getting anything but a container with container_get() is in bad taste.
>
> Agreed.
>
> IMHO container_get() interface might still be ok to implicitly create
> containers,
Creation on demand is fine when we want to create the thing only when
there is demand.
I guess it can also be okay when we want to create it always, but don't
want to decide when exactly (must be before first use), although I
suspect that's just lazy more often than not.
> but only if it will: (1) always make sure what it walks is a
> container along the way, and (2) never return any non-container.
Yes. Anything else invites abuse.
>> > So we need another way, like:
>> >
>> > ===8<===
>> >
>> > diff --git a/hw/core/qdev.c b/hw/core/qdev.c
>> > index db36f54d91..36a9fdb428 100644
>> > --- a/hw/core/qdev.c
>> > +++ b/hw/core/qdev.c
>> > @@ -832,7 +832,13 @@ Object *qdev_get_machine(void)
>> > static Object *dev;
>> >
>> > if (dev == NULL) {
>> > - dev = container_get(object_get_root(), "/machine");
>> > + /*
>> > + * NOTE: dev can keep being NULL if machine is not yet created!
>> > + * In which case the function will properly return NULL.
>> > + *
>> > + * Whenever machine object is created and found once, we cache it.
>> > + */
>> > + dev = object_resolve_path_component(object_get_root(), "machine");
>> > }
>> >
>> > return dev;
>>
>> Now returns null instead of a bogus container when called before machine
>> creation. Improvement of sorts. But none of the callers expect null...
>> shouldn't we assert(dev) here?
>>
>> Hmm, below you add a caller that checks for null.
>>
>> Another nice mess.
>
> I plan to put aside the application of singletons to x86-iommu as of now,
> due to the fact that qdev complexity may better be done separately.
>
> IOW, before that, I wonder whether we should clean up the container_get()
> as you discussed: it doesn't sound like a good interface to return
> non-container objects.
>
> I had a quick look, I only see two outliers of such, and besides the
> "abuse" in qdev_get_machine(), the only other one is
> e500_pcihost_bridge_realize():
>
> *** hw/core/qdev.c:
> qdev_get_machine[820] dev = container_get(object_get_root(), "/machine");
>
> *** hw/pci-host/ppce500.c:
> e500_pcihost_bridge_realize[422] PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(),
"/e500-ccsr"));
Yes, this abuses container_get() to get an "e500-ccsr", which is a
device, not a container.
By the way, intentation is confusing here.
> If any of us thinks this is the right way to go, I can try to clean it up
> (for 10.0). qdev_get_machine() may still need to be able to return NULL
> when singleton applies to IOMMUs, but that can be for later. Before that,
> we can still assert(qdev), I think.
I think it's worthwhile.
> Just to mention I've posted rfcv2 for this series, again feel free to
> ignore patch 3-5 as of now:
>
> [PATCH RFC v2 0/7] QOM: Singleton interface
> https://lore.kernel.org/r/20241029211607.2114845-1-peterx@redhat.com
>
> I think the plan is Dan may keep collecting feedbacks on his other rfc:
>
> [RFC 0/5] RFC: require error handling for dynamically created objects
> https://lore.kernel.org/r/20241031155350.3240361-1-berrange@redhat.com
>
> Then after Dan's lands, I'll rebase my rfcv2 on top of his, dropping
> iommu/qdev changes.
>
> Thanks,
Makes sense. Thanks!
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 0/4] QOM: Singleton interface
2024-10-29 17:05 ` Peter Xu
2024-10-29 17:17 ` Daniel P. Berrangé
@ 2024-12-11 8:19 ` Markus Armbruster
2024-12-11 22:10 ` Peter Xu
1 sibling, 1 reply; 38+ messages in thread
From: Markus Armbruster @ 2024-12-11 8:19 UTC (permalink / raw)
To: Peter Xu
Cc: Daniel P. Berrangé, qemu-devel, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Alex Williamson,
Paolo Bonzini, Peter Maydell
Looked at this thread again to refresh my memory on the proposed
singleton interface, and found I have something to add.
Peter Xu <peterx@redhat.com> writes:
> On Tue, Oct 29, 2024 at 04:04:50PM +0000, Daniel P. Berrangé wrote:
>> I tend to feel that having MigrationState exist for the whole lifetime
>> of QEMU is a bug, forced on us by the unfortunate need to call
>> migrate-set-parameters/capabilities separately from the migrate
>> command, and by the need to query migrate info an arbitrary amount of
>> time after it finishes.
>>
>> This puts libvirt in the awkward position of having to manually reset
>> all migration parameters, just to ensure earlier settings don't
>> accidentally affect a future migration operation :-( This is a design
>> that encourages mistakes.
>
> I think it would still be easy to add "cap" & "params" arguments support
> for the "migrate" QMP command without breaking the current API, iff that
> helps in whatever form. When present, it simply applies the caps and/or
> param list first before invoking the migrate command, fail the command if
> cap / param check fails.
>
> But I'm not sure whether that's a concern at all for Libvirt, if what
> Libvirt currently does is having separate "migrate-set-*" commands prior to
> the "migrate". I may have overlooked the real issue behind on how that
> could complicate Libvirt.
I think Daniel's point is that the interface's reliance on global state
makes it awkward to use.
Migration configuration is global state. It's split into "capabilities"
and "parameters", but that's detail. We have commands to query and
change this state.
When Libvirt connects to a QEMU process, it has no idea what the global
migration configuration is. To get it into a known state, it has to set
*everything*. It cannot rely on defaults.
It even has to set things it doesn't know! When we add a new parameter
to QEMU, libvirt needs to be updated to reset it to its default even
when libvirt has no need for it. When you use a version of libvirt that
lacks this update, it remains whatever it was. The migration interface
becomes accidentally stateful at the libvirt level, which is
undesirable.
Compare this to the more modern interface we have for other long-running
tasks: jobs.
There is a job-specific command that creates the job: blockdev-backup,
block-commit, blockdev-mirror, block-stream, blockdev-create,
snapshot-save, snapshot-load, snapshot-delete, ... Each command takes
the entire job configuration as arguments. Libvirt does not need
updating for new parameters: these simply remain at their default
values.
Bonus: there are generic commands to control and monitor jobs:
job-pause, job-resume, job-cancel, job-complete, job-dismiss,
job-finalize, query-jobs.
[...]
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 0/4] QOM: Singleton interface
2024-12-11 8:19 ` Markus Armbruster
@ 2024-12-11 22:10 ` Peter Xu
0 siblings, 0 replies; 38+ messages in thread
From: Peter Xu @ 2024-12-11 22:10 UTC (permalink / raw)
To: Markus Armbruster
Cc: Daniel P. Berrangé, qemu-devel, Fabiano Rosas, Igor Mammedov,
Juraj Marcin, Michael S . Tsirkin, Dr . David Alan Gilbert,
Cédric Le Goater, Eduardo Habkost, Alex Williamson,
Paolo Bonzini, Peter Maydell
On Wed, Dec 11, 2024 at 09:19:32AM +0100, Markus Armbruster wrote:
> Looked at this thread again to refresh my memory on the proposed
> singleton interface, and found I have something to add.
>
> Peter Xu <peterx@redhat.com> writes:
>
> > On Tue, Oct 29, 2024 at 04:04:50PM +0000, Daniel P. Berrangé wrote:
> >> I tend to feel that having MigrationState exist for the whole lifetime
> >> of QEMU is a bug, forced on us by the unfortunate need to call
> >> migrate-set-parameters/capabilities separately from the migrate
> >> command, and by the need to query migrate info an arbitrary amount of
> >> time after it finishes.
> >>
> >> This puts libvirt in the awkward position of having to manually reset
> >> all migration parameters, just to ensure earlier settings don't
> >> accidentally affect a future migration operation :-( This is a design
> >> that encourages mistakes.
> >
> > I think it would still be easy to add "cap" & "params" arguments support
> > for the "migrate" QMP command without breaking the current API, iff that
> > helps in whatever form. When present, it simply applies the caps and/or
> > param list first before invoking the migrate command, fail the command if
> > cap / param check fails.
> >
> > But I'm not sure whether that's a concern at all for Libvirt, if what
> > Libvirt currently does is having separate "migrate-set-*" commands prior to
> > the "migrate". I may have overlooked the real issue behind on how that
> > could complicate Libvirt.
>
> I think Daniel's point is that the interface's reliance on global state
> makes it awkward to use.
>
> Migration configuration is global state. It's split into "capabilities"
> and "parameters", but that's detail. We have commands to query and
> change this state.
>
> When Libvirt connects to a QEMU process, it has no idea what the global
> migration configuration is. To get it into a known state, it has to set
> *everything*. It cannot rely on defaults.
>
> It even has to set things it doesn't know! When we add a new parameter
> to QEMU, libvirt needs to be updated to reset it to its default even
> when libvirt has no need for it. When you use a version of libvirt that
> lacks this update, it remains whatever it was. The migration interface
> becomes accidentally stateful at the libvirt level, which is
> undesirable.
>
> Compare this to the more modern interface we have for other long-running
> tasks: jobs.
>
> There is a job-specific command that creates the job: blockdev-backup,
> block-commit, blockdev-mirror, block-stream, blockdev-create,
> snapshot-save, snapshot-load, snapshot-delete, ... Each command takes
> the entire job configuration as arguments. Libvirt does not need
> updating for new parameters: these simply remain at their default
> values.
>
> Bonus: there are generic commands to control and monitor jobs:
> job-pause, job-resume, job-cancel, job-complete, job-dismiss,
> job-finalize, query-jobs.
Yes, migration is state-ful from that regard. IMHO it is still ok because
unlike most jobs, migration task cannot have more than one.
Reusing jobs interface may work, but migation existed for so long a time
with its own APIs, I am not sure we'll get real benefit by reusing them.
At the meantime it may not 100% map to what migration wants (e.g.,
migrate_start_postcopy, postcopy recoveries, etc.).
OTOH, we definitely don't want to use the internal impl of jobs, because we
don't want to add either AIO or more coroutines into migration core - we
need to still use coroutine on dest QEMU but that's mostly only because of
legacy reasons.. and besides that and some very corner use case
(e.g. channel setups), migration is almost thread-based now. A mixture of
threads and coroutines is too error prone and undebuggable, IMHO.
Going back to the "allow migrate QMP command take caps / parameters", we
still try to do that at some point. But I recall we discussed about this
offlist (between Dan, probably Peter Krempa and myself), I believe the
conclusion is it'll make the API cleaner, but without no real benefit yet
so far. Meanwhile there're some parameters that must be still stateful,
like a few max*-bandwidth or downtime_limit parameters. They need to be
able to be changed on the fly, especially during migration task running.
Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2024-12-11 22:11 UTC | newest]
Thread overview: 38+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-24 16:56 [PATCH 0/4] QOM: Singleton interface Peter Xu
2024-10-24 16:56 ` [PATCH 1/4] qom: TYPE_SINGLETON interface Peter Xu
2024-10-24 20:02 ` Philippe Mathieu-Daudé
2024-10-24 20:53 ` Peter Xu
2024-10-25 15:11 ` Philippe Mathieu-Daudé
2024-10-25 16:21 ` Peter Xu
2024-10-25 8:07 ` Markus Armbruster
2024-10-25 15:17 ` Peter Xu
2024-10-25 9:51 ` Daniel P. Berrangé
2024-10-25 16:17 ` Peter Xu
2024-10-25 16:22 ` Daniel P. Berrangé
2024-10-25 22:10 ` Peter Xu
2024-10-29 0:01 ` Peter Xu
2024-10-25 16:37 ` Peter Xu
2024-10-24 16:56 ` [PATCH 2/4] x86/iommu: Make x86-iommu a singleton object Peter Xu
2024-10-25 9:25 ` Markus Armbruster
2024-10-25 21:55 ` Peter Xu
2024-10-25 22:13 ` Peter Xu
2024-11-07 11:12 ` Markus Armbruster
2024-11-07 15:29 ` Peter Xu
2024-11-08 8:50 ` Markus Armbruster
2024-10-29 10:47 ` Daniel P. Berrangé
2024-10-29 14:32 ` Peter Xu
2024-10-24 16:56 ` [PATCH 3/4] migration: Make migration object " Peter Xu
2024-10-24 19:20 ` Fabiano Rosas
2024-10-24 16:56 ` [PATCH 4/4] migration: Reset current_migration properly Peter Xu
2024-10-24 19:34 ` Fabiano Rosas
2024-10-24 20:15 ` Peter Xu
2024-10-24 20:51 ` Fabiano Rosas
2024-10-25 7:38 ` [PATCH 0/4] QOM: Singleton interface Markus Armbruster
2024-10-25 15:01 ` Peter Xu
2024-10-29 10:42 ` Daniel P. Berrangé
2024-10-29 14:45 ` Peter Xu
2024-10-29 16:04 ` Daniel P. Berrangé
2024-10-29 17:05 ` Peter Xu
2024-10-29 17:17 ` Daniel P. Berrangé
2024-12-11 8:19 ` Markus Armbruster
2024-12-11 22:10 ` Peter Xu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).