Features/QOM

From QEMU
Revision as of 16:34, 14 September 2011 by AnthonyLiguori (talk | contribs) (→‎TODO)

QEMU Object Model

This page describes the QEMU Object Model (QOM), a unified approach to expressing relationships among devices and between devices and backends.

Rationale

Much of the complexity of QEMU involves creating discrete objects (devices or backends), describing the relationships between these objects, and providing easy ways to express complex relationships (machines). A large part of this complexity comes from the fact that all backends implement ad-hoc object models. Historically, devices have also used an ad-hoc object model. qdev attempted to standardize the device object model, but fell short of what's needed to properly model these relationships.

QOM attempts to fix the short-comings of qdev and also generalize the object model enough that it can be also used to model backends and their relationships with devices.

Concepts

All objects are described by a struct. Inheritance is expressed by setting the first member of the struct to the parent object. All new types have an optional class structure associated with them. Classes express inheritance by setting the first member of the struct to the parent class.

Only a single instance of a class will ever exist for a given type. Classes contain a reference to the type which is used by the type system. They also contain any virtual or pure virtual methods associated with the class.

Consider the following example:

#include "type.h"

typedef struct Person {
    TypeInstance parent;

    /* public */
    bool gender;
} Person;

typedef struct PersonClass {
    TypeClass parent_class;

    /* public */
    void (*jump)(Person *person, int distance);
} PersonClass;

#define TYPE_PERSON "person"
#define PERSON(obj) TYPE_CHECK(Person, obj, TYPE_PERSON)
#define PERSON_CLASS(class) TYPE_CLASS_CHECK(PersonClass, class, TYPE_PERSON)

In this example, we also introduce three macros that are useful in working with the type. The first macro, TYPE_PERSON is the string name of the type. This is used to identify the type in the type system. The second macro, PERSON(obj) will perform a dynamic cast of an arbitrary typed object to a Person object. It can perform both down casts and up casts across multiple levels. If the obj is not a valid Person instance, it currently asserts. PERSON_CLASS(class) is similar to PERSON(obj) but performs casts of the class type.

Instantiating Objects

QOM can create new objects in a generic fashion, but it's useful to provide type safe wrappers for initializing objects. This involves introducing a couple functions that are just thin wrappers around generic functions.

void person_initialize(Person *obj, const char *id);

void person_finalize(Person *obj);

The person_initialize(obj, id) and person_finalize(obj) functions operate in-place. It's the caller's responsibility to allocate and free the memory that holds the Person object. The actual function implementations are trivial:

void person_initialize(Person *obj, const char *id)
{
    type_initialize(obj, TYPE_PERSON, id);
}

void person_finalize(Person *obj)
{
    type_finalize(obj);
}

One thing to note about initialization is that the only parameter passed is id. All objects are given a unique name in a flat namespace. There are no exceptions to this rule. As a convenient, if an object needs to initialize a sub-object, it should use a name constructed with its own name followed by :: and a unique sub-identifier. See the section on Object Composition for more details.

Properties

All devices can have zero or more properties associated with it. Properties are typed and accessed through a Visitor interface. There are two special property types, Plugs and Sockets.

TODO

1. Eliminate anonymously named devices.
 a. Will require touching any place in the tree that creates a qdev object and
    giving a meaningful unique name.

2. Refactor any device that creates 2 or more busses to only create a single
   bus. This will mean using composition.
 a. An example: IDE controller creates two busses, one for the primary and one
    for the secondary.  Instead, IDE device should have two IDE controller sub
    devices, each device having a single bus.

3. Unify the bus/device namespaces 
 a. This is a compatibility breaker
 b. Depends on (1) and (2)

4. Modify qdev properties to allow devices to register setters/getters that use
   visitors to access properties.
 a. Implement existing properties in terms of visitors

5. Modify qdev properties to be stored in the object, not in the class

6. Expose children as named properties
 a. Read only to start with

7. Change qdev to use QOM typing
 a. Depends on (3)
 b. Must change all init functions to use QOM init functions
 c. Change all DO_UPCASTS to QOM macro casts
 d. Can be largely done via sed

7. Change children to be based on plug and socket properties
 a. Eliminate children list in each device
 b. Compatibility breaker

8. Improve object model
 a. Compatibility breaker