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.


Reply via email to