Features/QOM: Difference between revisions

From QEMU
No edit summary
Line 1: Line 1:
== QEMU Object Model ==
=== Naming in QDev ===


This page describes the QEMU Object Model (QOM), a unified approach to expressing relationships among devices and between devices and backends.
QDev has three namespaces: the device namespace, the bus namespace, and property
namespaces.


== Rationale ==
The device namespace contains the names of qdev devices.  qdev supports the
ability to have anonymous devices.  Anonymous devices are usually created
through composition and are anonymous because the user controls the device
namespace and the user has no way of allocating names for devices created
through composition.


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.
The bus namespace is parallel to the device namespace.  Unlike the device
namespace, busses cannot be anonymousFor busses that are created as a result
of composition, a name is derived either from the device name or via the type
name.


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.
In qdev, implicit bus names are not considered stable and may change across
invocations and/or versions of QEMU.


== Concepts ==
Property namespaces are local to devices.  The 'id' property is reserved to
refer to the name of the device.  Property names do not reference child devices.


All objects are described by a structInheritance 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.
Paths cannot be meaningfully constructed in QDevDevices can only be addressed
directly by their name as there is no stable way to refer to busses under a
device, or children under a bus.


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.
=== Naming in QOM ===


Consider the following example:
In QOM, there are only two namespaces, the device namespace and the property
namespace.


  #include "type.h"
All devices have unique names. There are no exceptionsDevices created
   
through composition are given unique names by deriving the name based on the
typedef struct Person {
parent device name and a special separator, "::", that cannot be used in user
    TypeInstance parent;
supplied names.
 
    /* public */
Since a bus is-a device in QOM, there is no notion of having multiple busses
    bool gender;
under the same device. A device can implement multiple bus interfaces, but can
} Person;
only be a single bus of any given bus interface.
 
  typedef struct PersonClass {
Device names are completely independent of pathnames. For devices that are no
    TypeClass parent_class;
user created, device names should be treated as opaque blobs with absolutely no
semantic meaning.
    /* 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 typeThe 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 levelsIf the ''obj'' is not a valid Person instance, it currently asserts.  ''PERSON_CLASS(class)'' is similar to ''PERSON(obj)'' but performs casts of the
All device relationships are identified as named propertiesA QOM path name
class type.
consists of a named device, followed by a series of properties which may or may
not refer to other devicesFor instance, all of the following are valid paths:


== Instantiating Objects ==
/i440fx/piix3/i8042/aux
/i440fx/slot[1.0]/i8042/aux
/i440fx/slot[1.0]/bus/piix3/i8042/aux


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.
All of these path names are interpreted as follows:


  void person_initialize(Person *obj, const char *id);
  def resolve_pathname(pathname):
    root, props = pathname[1:].split('/')
void person_finalize(Person *obj);
    dev = find_device(root)
    for prop in props:
        device_name = get_property(dev, prop)
        dev = find_device(device_name)
    return dev


The ''person_initialize(obj, id)'' and ''person_finalize(obj)'' functions operate in-placeIt's the caller's responsibility to allocate and free the memory that holds the Person object.  The actual function implementations are trivial:
In this specific example, the i440fx object has two properties that both refer
to the PIIX3 deviceThe 'piix3' device is a property that reflects a device
composition relationship.  The 'slot[1.0]' property represents a device backlink
relationship.


void person_initialize(Person *obj, const char *id)
The PIIX3 device has a 'i8042' property based on device composition of the PC
  {
Keyboard Controller device. It also has a device backlink property, 'bus', that
    type_initialize(obj, TYPE_PERSON, id);
points to the bus that it sits on (which is the 'i440fx' object).
}
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.
Finally, the PC Keyboard Controller device has an 'aux' property which is a
device backlink property that can point to a PS/2 Mouse device.


== Properties ==
The full set of devices names and properties used in this example are below:


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.
Type: I440FX
Is-a: Device
Implements: PciBus
Name: i440fx
Properties:
  piix3: Composition<PIIX3>
  slot[1.0]: Backlink<PciDevice>
Type: PIIX3
Isa-a: PciDevice
Implements: IsaBus
Name: i440fx::piix3
  Properties:
  i8042: Composition<I8042>
  bus: Backlink<PciDevice> (inherited from PciDevice)
Type: I8042
Isa-a: Device
Implements: Ps2Controller
Name: i440fx::piix3::i8042
  Properties:
  aux: Backlink<Ps2Mouse>


== TODO ==
== TODO ==


  1. Eliminate anonymously named devices.
  1. Eliminate anonymous devices.
   a. Will require touching any place in the tree that creates a qdev object and
   a. Will require touching any place in the tree that creates a qdev object and
     giving a meaningful unique name.
     giving a meaningful unique name.
Line 97: Line 129:
   d. Can be largely done via sed
   d. Can be largely done via sed
   
   
  7. Change children to be based on plug and socket properties
  8. Change children to be based on plug and socket properties
   a. Eliminate children list in each device
   a. Eliminate children list in each device
   b. Compatibility breaker
   b. Compatibility breaker
   
   
  8. Improve object model
  9. Improve object model
   a. Compatibility breaker
   a. Compatibility breaker

Revision as of 17:07, 14 September 2011

Naming in QDev

QDev has three namespaces: the device namespace, the bus namespace, and property namespaces.

The device namespace contains the names of qdev devices. qdev supports the ability to have anonymous devices. Anonymous devices are usually created through composition and are anonymous because the user controls the device namespace and the user has no way of allocating names for devices created through composition.

The bus namespace is parallel to the device namespace. Unlike the device namespace, busses cannot be anonymous. For busses that are created as a result of composition, a name is derived either from the device name or via the type name.

In qdev, implicit bus names are not considered stable and may change across invocations and/or versions of QEMU.

Property namespaces are local to devices. The 'id' property is reserved to refer to the name of the device. Property names do not reference child devices.

Paths cannot be meaningfully constructed in QDev. Devices can only be addressed directly by their name as there is no stable way to refer to busses under a device, or children under a bus.

Naming in QOM

In QOM, there are only two namespaces, the device namespace and the property namespace.

All devices have unique names. There are no exceptions. Devices created through composition are given unique names by deriving the name based on the parent device name and a special separator, "::", that cannot be used in user supplied names.

Since a bus is-a device in QOM, there is no notion of having multiple busses under the same device. A device can implement multiple bus interfaces, but can only be a single bus of any given bus interface.

Device names are completely independent of pathnames. For devices that are no user created, device names should be treated as opaque blobs with absolutely no semantic meaning.

All device relationships are identified as named properties. A QOM path name consists of a named device, followed by a series of properties which may or may not refer to other devices. For instance, all of the following are valid paths:

/i440fx/piix3/i8042/aux
/i440fx/slot[1.0]/i8042/aux
/i440fx/slot[1.0]/bus/piix3/i8042/aux

All of these path names are interpreted as follows:

def resolve_pathname(pathname):
    root, props = pathname[1:].split('/')
    dev = find_device(root)
    for prop in props:
        device_name = get_property(dev, prop)
        dev = find_device(device_name)
    return dev

In this specific example, the i440fx object has two properties that both refer to the PIIX3 device. The 'piix3' device is a property that reflects a device composition relationship. The 'slot[1.0]' property represents a device backlink relationship.

The PIIX3 device has a 'i8042' property based on device composition of the PC Keyboard Controller device. It also has a device backlink property, 'bus', that points to the bus that it sits on (which is the 'i440fx' object).

Finally, the PC Keyboard Controller device has an 'aux' property which is a device backlink property that can point to a PS/2 Mouse device.

The full set of devices names and properties used in this example are below:

Type: I440FX
Is-a: Device
Implements: PciBus
Name: i440fx
Properties:
 piix3: Composition<PIIX3>
 slot[1.0]: Backlink<PciDevice>

Type: PIIX3
Isa-a: PciDevice
Implements: IsaBus
Name: i440fx::piix3
Properties:
 i8042: Composition<I8042>
 bus: Backlink<PciDevice> (inherited from PciDevice)

Type: I8042
Isa-a: Device
Implements: Ps2Controller
Name: i440fx::piix3::i8042
Properties:
 aux: Backlink<Ps2Mouse>

TODO

1. Eliminate anonymous 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

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

9. Improve object model
 a. Compatibility breaker