Features/QCFG

From QEMU
Revision as of 02:42, 12 March 2011 by AnthonyLiguori (talk | contribs)

Summary

Replace QemuOpts infrastructure with code generation leveraging QAPI.

Owner

Detailed Summary

Use code generator to generate demarshalling routines for command line arguments into structures. Pass structures to function calls to process QEMU command line.

For instance, consider the -vnc option. We would first add a type corresponding to the options -vnc takes:

{ 'type': 'VncConfig',
  'data': { 'addr': 'str', '*x509': 'str', '*x509verify': 'str',
            '*password': 'bool', '*tls': 'bool', '*sasl': 'bool' } }

We can generate an unmarshalling function that will convert a string of comma encoded options to a proper structure. It will generate the following code:

// qemu/qcfg-marshal.h
static VncConfig *qcfg_unmarshal_VncConfig(const char *value, Error **errp)
{
    VncConfig *config;
    Error *local_err = NULL;
    const char *ptr = value;
    char *key, *value;
    bool has_addr = false;

    config = qmp_alloc_VncConfig();

    while (qcfg_strsep(value, ',', &ptr, &key, &value)) {
        if (strequals(key, "addr")) {
            has_addr = true;
            config->addr = qcfg_unmarshal_str(value, &local_err);
        } else if (strequals(key, "x509")) {
            config->has_x509 = true;
            config->x509 = qcfg_unmarshal_str(value, &local_err);
        } else if (strequals(key, "x509verify")) {
            config->has_x509verify = true;
            config->x509verify = qcfg_unmarshal_str(value, &local_err);
        } else if (strequals(key, "password")) {
            config->has_password = true;
            config->password = qcfg_unmarshal_bool(value, &local_err);
        } else if (strequals(key, "tls")) {
            config->has_tls = true;
            config->tls = qcfg_unmarshal_bool(value, &local_err);
        } else if (strequals(key, "sasl")) {
            config->has_sasl = true;
            config->sasl = qcfg_unmarshal_bool(value, &local_err);
        } else {
            error_set(&local_err, QERR_INVALID_PARAMETER, key);
        }

        if (local_err) {
            goto out;
        }
    }

    if (!has_addr) {
        error_set(&local_err, QERR_MISSING_PARAMETER, "addr");
        goto out;
    }

    return config;

out:
    qmp_free_vnc_config(config);
    error_propagate(errp, local_err);
    return NULL;
}

There will still be the normal code generated in qmp-types and qmp-marshal-types which means that this type can also be encoded and decode via QMP.

In order to bridge this type to a function that is called to process a command line argument, we just need to add the following stanza:

// qemu/qcfg-schema.json
{ 'config': 'vnc', 'type': 'VncConfig' }

Which generates:

// qemu/qcfg.h
void qcfg_vnc_config(VncConfig * config, Error **errp);

static void qcfg_handle_vnc(const char *value, Error **errp)
{
    VncConfig *config;
    Error *local_err = NULL;

    config = qcfg_unmarshal_VncConfig(value, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

    qcfg_vnc(config, errp);

    qmp_free_VncConfig(config);
}

void qcfg_init(void)
{
    qcfg_register_handler("vnc", &qcfg_marshal_vnc);
}

The effect is that we can call a function that takes a string argument and will decode that to a strongly typed call to a function.

This function can also be reused by QMP trivially without going through multiple levels of marshaling and unmarshaling.

This provides a consistent way to document all options including the parameters that they take. It also provides a consistent way to do introspection of command line arguments.

This mechanism can also be applied to qdev which will allow for strongly typed qdev initialization functions that can also be trivially called from within QEMU.

For instance, for the serial device, we would do:

{ 'type': 'ISASerialConfig',
  'data': { '*index': 'int', '*iobase': 'int', '*irq': 'int',
            'chardev': 'CharDriverState' } }

This would generate a standard qmp-types entry and corresponding marshalling functions. This means the type is usuable via both QMP and through the command line. To generate a device factory interface, we just use the following schema entry:

{ 'device': 'isa-serial', 'config': 'ISASerialConfig' }

This would generate the following code:

DeviceState *qdev_ctor_isa_serial(ISASerialConfig *config, Error **errp);

static DeviceState *qdev_marshal_isa_serial(const char *value, Error **errp)
{
    ISASerialConfig *config;
    Error *local_err = NULL;
    DeviceState *dev;

    config = qcfg_unmarshal_ISASerialConfig(value, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

    dev = qdev_ctor_isa_serial(config, errp);

    qmp_free_isa_serial_config(config);

    return dev;
}

This lets QMP invoke qdev constructors directly without remarshaling to strings. It also provides a convenient interface for QEMU to consume internally to create devices which is fully type safe.

It also provides a mechanism to do full introspection of qdev and a consistent way to provide documentation.

Open Questions

  • Should we use a structure or marshal parameters as function calls?