This memo is heavily indebted to Kevin Wolf. Its mistakes, however, are mine.
= Critique of current state = 1. QOM properties serve several purposes: initial configuration (external interface), run time control and monitoring (external interface), and internal purposes like versioning. Which purpose(s) a property serves is often unclear. Documentation of the external interfaces is for the most part poor to nonexistent. 2. QOM properties get created in handwritten C code. Creation can depend on state, typically other properties. This makes QOM introspection problematic. It also leads to (commonly undocumented) restrictions like "must set property X before property Y". 3. QOM introspection uses a primitive, under-documented type system that is weakly connected to QAPI via names: when qom-list-properties reports a property's type as "T", and "T" is also the name of a QAPI type, then the property's type is probably that QAPI type. This is not documented. 4. QOM doesn't define a life cycle. Various classes define one of the form create - set configuration properties - configuration done - operate. Variations of "configuration done": realize() for DeviceClass, complete() for UserCreatableClass, possibly more via machine_init_done_notifiers. 5. There are two external interfaces for object creation: one for the subtypes of DeviceClass (QMP: device_add), and one for objects providing interface UserCreatableClass (QMP: object-add). The former still bypasses the QAPI type system. The latter defines the configuration interface in the QAPI schema, enabling QAPI introspection. Properties used for initial configuration need to be specified twice: once in handwritten C, and once in the QAPI schema. Additionally, the type needs to me made user creatable: once in handwritten C (put TYPE_USER_CREATABLE into .interface), and twice in the QAPI schema (enum ObjectType, union ObjectOptions). Bothersome, and risks inconsistency. Doing DeviceClass the same way feels unappealing due to the sheer number of devices and properties. 6. We convert configuration data back and forth. QMP/JSON-text | | JSON parser v QMP/QObject | | QObject input visitor v ObjectOptions | | QObject output visitor v QObject | | convert & delete members "qom-type" and "id" v QDict | | qobject_input_visitor_new() v same wrapped in QObject input visitor | | for all members: set property v configuration data in the object 7. QAPI uses IDs to refer to things. For QOM things, it also uses QOM paths. Both are of QAPI type 'str'; what is being references is not expressed in the schema, only in documentation (hopefully). QOM does this better: link<T>. = Goals = Addressing all of the above in one go is not feasible. Smaller steps are. Here's one: specify the configuration interface in the QAPI schema instead of burying it in handwritten C code. For subclasses of DeviceClass, the configuration moves from C code to the QAPI schema. For classes providing interface UserCreatableClass, handwritten C code duplicating QAPI schema definitions goes away. Properties that are just for configuration are not created in C anymore. If needed, QAPI-generated code could create them (read-only). Introspection of object-add remains unchanged. Introspection of device_add becomes possible. = Proposal = Kevin posted RFC patches that work towards the goals above: Subject: [RFC PATCH 00/12] QOM/QAPI integration part 1 Date: Wed, 3 Nov 2021 18:29:50 +0100 Message-Id: <20211103173002.209906-1-kw...@redhat.com> https://lore.kernel.org/qemu-devel/20211103173002.209906-1-kw...@redhat.com/ Aside: yes, it's been a while. At the time, I didn't get how this works, and why it's good. Later I did (with Kevin's patient help), but had doubts on whether the benefits justify the investment. Since then, I've seen potential benefits in various contexts, and I've come around. The QAPI schema has QAPI type definitions ("enum", "struct", "union", "alternate"), and QMP definitions ("command", "event"). The QMP definitions use QAPI types. For convenience, they can define QAPI types implicitly in places (e.g. with "data", but not with "returns". The code generated for QMP is mostly about marshaling (command and event argument value) and unmarshaling (command return value). Kevin proposes to add a QOM definition "class". As for QMP definitions, "class" uses QAPI types (possibly defined implicitly), and the generated code is mostly about unmarshaling (instance configuration value). Syntax: CLASS = { 'class': STRING, 'parent': STRING, ( '*config': ( MEMBERS | STRING ), | 'config': STRING, 'config-boxed': true, ) '*if': COND, '*features': FEATURES } The value of @class is the name of the QOM class. @parent is the name of the QOM parent class. @if and @features work as usual. @config and @config-boxed work like command's @data and @boxed: they specify the arguments of a (handwritten) C function. For a command, that's qmp_COMMAND_NAME(), for a class, it's CLASS_NAME_config(). If you embed a boxed argument type in the state struct, CLASS_NAME_config() can be as simple as static bool CLASS_NAME_config(Object *obj, CLASS_NAME_Options *cfg, Error **errp) { obj->cfg = *cfg; return true; } "class" implicitly defines a QAPI type 'qom-config:CLASS-NAME' to hold its instance configuration, i.e. the product type of the class's @config with its parent class's 'qom-config:PARENT-NAME'. This is for use in object-add's argument type ObjectOptions, and later in device-add's QAPIfied argument type. As long as we define the argument type of instance creation commands like object-add manually in the QAPI schema, we need the instance configuration to be a QAPI type. Use of a name prefix like 'qom-config: has no precedence in QAPI. Better ideas welcome. We could perhaps keep the type out of QAPI if we generated some more. Whether that's worthwhile is unclear. QOM classes provide a new method .instance_config() that consume their instance configuration. qmp_object_add() has to shunt configuration data from ObjectOptions to the .instance_config() involved, i.e. the class's and all its parents'. Employs visitor wizardry. Incremental QAPIfication of QOM instance configuration makes this somewhat hairy. Support for unconverted parent classes in particular: first, the .instance_config consume their arguments from the visitor, then what's left in the visitor gets consumed the old-fashioned way with object_property_set(). = Check against critique of current state = 1. Configuration is now clearly separate from other QOM properties: it's in the QAPI schema. 2. Configuration is now compile-time static: it's in the QAPI schema. Restrictions like "must set property X before property Y" are no longer possible. Introspection is reliably complete. 3. The weakness of QOM introspection no longer matters, since we can use query-qmp-schema instead. 4. Life cycle is out of scope. 5. Duplication around object-add is reduced (we drop the handwritten C code), but not yet eliminated (we still need to add to both ObjectType and ObjectOptions in the schema). Further work seems desirable there, and might be necessary to make full QAPIfication of device_add practical. We have more than 2000 devices... 6. We still convert configuration data back and forth. I figure we can't get rid of that as long as we need visitor wizardry.