Features/QOM

From QEMU

Device Relationships

Device Relationships in QDev

The two main concepts in QDev are devices and busses. A device is represented by a DeviceState and a bus is represented by a BusState. They do not share a common base class. Devices can have properties but busses cannot. A device has no direct relationship with other devices. The only relationship is indirect through busses.

A device may have zero or more busses associated with it via a has-a relationship. Each child bus may have multiple devices associated with it via a reference. All devices have a single parent bus and all busses have a single parent device. These relationships form a strict tree where every alternating level is a bus level followed by a device level. The root of the tree is the main system bus often referred to as SysBus.

Device Relationships in QOM

Everything in QOM is a device. The concept of busses are implemented as an interface that a device implements. Devices can have two types of relationships with other devices: device composition or device backlinks. Device composition involves one device directly creating another device. It will own life cycle management for the created device and will generally propagate events to that device (although there are exceptions).

Device backlinks allow one device to refer to another device in a looser fashion. While there can be only one device composition relationship that exists between two specific devices, a device can participate in an arbitrary number of backlinks.

Device composition and backlinks are both strongly typed and can be typed as a specific device type or as an interface. When typed as an interface, any device that implements that interface can be used.

There is no explicit notion of parents in QOM. A typical bus relationship would the bus having a backlink to the child device, and the child device having a backlink to the bus.

Without device backlinks, device composition forms a multi-rooted strict tree. With backlinks, the result are multiple directed graphs.

Naming

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>

Device Properties

Properties in QDev

Device properties in qdev are bound to classes and map directly to elements of the device structure. They are strongly typed. Each type is parsed directly from a string representation. There is no way to set qdev properties from anything but a string.

Device properties in qdev are only settable during construction. After construction, they are read-only. Devices cannot hook setting or getting of a property.

Properties in QOM

Device properties in QOM are bound to devices and are implemented by closures provided by the device. A Visitor is passed to the closure which effectively allows properties to be set/get through native C types. Mapping to any other type of representation (string or otherwise) is done by a Visitor with no knowledge of the device or property.

By convention, most device properties are implemented by writing a C typed public getter/setter for the device, and then using a property wrapper to translate those typed functions to an untyped closure that accepts a Visitor.

QOM has no notion of construction. All devices are created without properties. Properties can be set/get after initialization. Devices support the notion of "realize" which roughly corresponds to construction. More accurately, it corresponds to the moment before a device will be first consumed by a guest.

"unrealize" roughly corresponds to reset. A device may be realized and unrealized many times during its lifecycle.

Properties are, by default, locked while the device is realized. Exceptions can be made by the devices themselves in order to implement a way for a user to interact with a device while it is realized.

Two special types of properties are child<> and link<> properties. A child<> property is used to represent device composition. When a child<> property is added, the child device has its life cycle automatically tied to the parent device. The link<> property represents a reference to another device.

The child<> property type is always read-only and when read, will return the name of the child device.

The link<> property type is by default read/write and locked during realize. Hotplug can be implemented by allowing a link to be writable after realize and installing a custom setter function that implements hotplug behavior.

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