Features/QAPI

From QEMU
Revision as of 16:00, 8 June 2010 by AnthonyLiguori (talk | contribs) (Created page with '== Summary == Introduce a number of changes to the internal implementation of QMP to simplify maintainability and enable features such as asynchronous command completion and ric…')
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Summary

Introduce a number of changes to the internal implementation of QMP to simplify maintainability and enable features such as asynchronous command completion and rich error reporting. Minimize protocol-visible changes as much as possible.

Owner

Summary

QMP has evolved from the human monitor code and has inherited a number of the deficiencies of the human monitor as a result. In particular, error reporting and asynchronous commands really demonstrate the historic flaws in the human monitor. This spec describes the types of changes that are needed to decouple QMP from the human monitor and fix the deficiencies that are currently present.

Issues

Error Reporting

Error reporting works reasonably well when dealing with synchronous commands and when the errors are generated within the actual function implementing the command. Today we have two mechanisms to support errors. The first is QError which provides a QObject based error. The second mechanism is qerror_report() which combines the creation of a QError with the association of the error with the current monitor state. The current monitor state is defined as the monitor that is currently executing a synchronous command.

The reason qerror_report() behaves this way is to minimize change in non-monitor code within QEMU. Since most of these functions simply fprintf() an error, they provide no way to return a rich error and have no context associated with them that an rich error could be associated with. Usually these functions will return an integer error but it's usually just -1 and it's very often not propagated reliably.

The nature of qerror_report() makes it impossible to implement asynchronous commands. As an example, if you have to asynchronous commands in flight at once, say A and B, if A calls a function do_foo() as part of it's processing that calls qerror_report(), that error will be associated with the cur_mon state. However, when processing B's completion, it's not possible to tell whether that error was as a result of processing on behalf of command A or B.

Another related albeit less entangled problem is reporting errors in asynchronous events. qerror_report() has a clear behavior of associating an error with the cur_mon which in turn associates an error with whatever command is being processed. Under some circumstances, qerror_report() will actually output to standard error. There is no obvious way to have certain errors result in asynchronous events.

Relationship to the Human Monitor Protocol

Right now, QMP and HMP are implemented as a single dispatch table. HMP functions are implemented in terms of QMP functions with additional pretty printing. However, because errors are not returned by QMP functions, the HMP pretty printing functions are not able to add additional contextual information to the error output. The result is that we tend to have very specific error types because we need each error type to output a message based on specific context. In other words, we tend to have duplicate error types for every monitor command because we often want the same error to be printed differently for each command.

Additionally, the single dispatch table makes it very difficult to introduce HMP functions without a direct QMP mapping and vice versa. While there is significant overlap between QMP and HMP, some QMP commands don't make sense for HMP (like capabilities) and some HMP commands (like x/xp) don't make sense for QMP.

Potential Solutions

QErrors should be treated like normal QObjects and should be propagated as a return value. For functions that only return success or failure, their signatures can be converted to return a QError or NULL. For commands that return values upon success, an additional parameter can be added as a QError ** whereas an error can be propagated that way.

Additionally, the QMP signature should be changed to reflect this. An example would be:

QObject *qmp_command(const QDict *args);

Upon success, the QObject return would be a non-QError type. Upon failure, a QError would be returned.

For asynchronous commands, an appropriate signature would be:

void qmp_async_command(const QDict *args, QMPCompletionSlot *cb);

Where QMPCompletionSlot was a structure of the following form:

typedef struct QMPCompletionSlot {
   void (*complete)(struct QMPCompletionSlot *slot, QObject *result);
};

When the asynchronous command completes, it passes either a QError to cb->complete on failure or it passes the data generated by the command.

QError should also expose a function to interact with the QError information such as the error type. This allows callers to determine what the QError is and present an appropriate error. For instance, the following could implement the HMP monitor command memory_save

void hmp_memory_save(Monitor *mon, uint32_t size, const char *filename, target_long addr) {

  QObject *ret;
  ret = qmp_command("memory_save", "{'size': %d, 'filename': %s, 'val': %d}",
                    size, filename, addr);
  if (qobject_type(ret) == QOBJ_ERROR) {
     QError *err = qobject_to_qerror(ret);
     const char *type = qerror_get_type(err);
     if (strcmp(type, "FileNotFound") == 0) {
        hmp_printf(mon, "Failed to open file\n");
     } else if (strcmp(type, "IoError") == 0) {
        hmp_printf(mon, "An I/O error occurred while writing to memory file\n");
     } else {
        hmp_printf(mon, "Unexpected error: %s\n", qerror_to_str(err));
     }
  }

}

This also addresses how to decouple QMP from the monitor. The individual QMP functions should be stand alone with a separate dispatch table. A HMP function should be implemented mostly in terms of invoking the QMP function. In this case, a helper is used to make QMP invocation slightly easier.

Status

A very very rough sketch of this conversion is at the following location: