diff mbox

[RFC,05/16] Visitor: Binary compatible output visitor

Message ID 1395778647-30925-6-git-send-email-dgilbert@redhat.com
State New
Headers show

Commit Message

Dr. David Alan Gilbert March 25, 2014, 8:17 p.m. UTC
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>

Binary compatible output visitor to write to a QEMUFile in current migration
format.

This is based on Michael Roth's series :
https://lists.gnu.org/archive/html/qemu-devel/2011-09/msg02466.html

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
---
 include/qapi/qemu-file-binary-output-visitor.h |  26 ++
 qapi/Makefile.objs                             |   2 +-
 qapi/qemu-file-binary-output-visitor.c         | 564 +++++++++++++++++++++++++
 3 files changed, 591 insertions(+), 1 deletion(-)
 create mode 100644 include/qapi/qemu-file-binary-output-visitor.h
 create mode 100644 qapi/qemu-file-binary-output-visitor.c
diff mbox

Patch

diff --git a/include/qapi/qemu-file-binary-output-visitor.h b/include/qapi/qemu-file-binary-output-visitor.h
new file mode 100644
index 0000000..f563407
--- /dev/null
+++ b/include/qapi/qemu-file-binary-output-visitor.h
@@ -0,0 +1,26 @@ 
+/*
+ * QEMUFile Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Michael Roth   <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_FILE_BINARY_OUTPUT_VISITOR_H
+#define QEMU_FILE_BINARY_OUTPUT_VISITOR_H
+
+#include "visitor.h"
+
+typedef struct QemuFileBinOutputVisitor QemuFileBinOutputVisitor;
+
+QemuFileBinOutputVisitor *qemu_file_bin_output_visitor_new(QEMUFile *f);
+void qemu_file_bin_output_visitor_cleanup(QemuFileBinOutputVisitor *d);
+
+Visitor *qemu_file_bin_output_get_visitor(QemuFileBinOutputVisitor *v);
+
+#endif
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 1f9c973..a054d52 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -1,5 +1,5 @@ 
 util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
 util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
-
+util-obj-y += qemu-file-binary-output-visitor.o
 util-obj-y += opts-visitor.o
diff --git a/qapi/qemu-file-binary-output-visitor.c b/qapi/qemu-file-binary-output-visitor.c
new file mode 100644
index 0000000..bc3f419
--- /dev/null
+++ b/qapi/qemu-file-binary-output-visitor.c
@@ -0,0 +1,564 @@ 
+/*
+ * QEMUFile Output Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ * Copyright 2014 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ *  Michael Roth   <mdroth@linux.vnet.ibm.com>
+ *  David Gilbert  <dgilbert@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qemu-file-binary-output-visitor.h"
+#include "qapi/visitor-impl.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "migration/migration.h"
+
+#if 0
+#define DPRINTF(fmt, ...) \
+    do { \
+        fprintf(stderr, "qfbov/%s/%d: " fmt "\n", __func__, \
+                __LINE__, ## __VA_ARGS__); \
+    } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+/* TODO: This needs sharing between input/output binary visitors */
+typedef struct {
+    size_t elem_count;
+    size_t elem_size;
+    size_t pos;
+} ArrayInfo;
+
+typedef struct {
+    Visit_seq_compat_mode mode;
+    const void           *data;
+} SeqCompatInfo;
+
+typedef struct StackEntry {
+    enum {
+        QFOV_ARRAY,
+        QFOV_LIST,
+        QFOV_STRUCT,
+        QFOV_SEQCOMPAT,
+    } type;
+    ArrayInfo array_info;
+    SeqCompatInfo seqcompat_info;
+    bool is_list_head;
+    QTAILQ_ENTRY(StackEntry) node;
+} StackEntry;
+
+struct QemuFileBinOutputVisitor {
+    Visitor visitor;
+    QTAILQ_HEAD(, StackEntry) stack;
+    QEMUFile *file;
+};
+
+static QemuFileBinOutputVisitor *to_ov(Visitor *v)
+{
+    return container_of(v, QemuFileBinOutputVisitor, visitor);
+}
+
+static void qfbo_push(QemuFileBinOutputVisitor *ov, StackEntry *e)
+{
+    QTAILQ_INSERT_HEAD(&ov->stack, e, node);
+}
+
+static void qfbo_push_array(QemuFileBinOutputVisitor *ov, ArrayInfo ai)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_ARRAY;
+    e->array_info = ai;
+    qfbo_push(ov, e);
+}
+
+static void qfbo_push_list(QemuFileBinOutputVisitor *ov)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_LIST;
+    e->is_list_head = true;
+    qfbo_push(ov, e);
+}
+
+static void qfbo_push_seqcompat(QemuFileBinOutputVisitor *ov,
+                                SeqCompatInfo sci)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_SEQCOMPAT;
+    e->seqcompat_info = sci;
+    qfbo_push(ov, e);
+}
+
+static void qfbo_push_struct(QemuFileBinOutputVisitor *ov)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_STRUCT;
+    qfbo_push(ov, e);
+}
+
+static StackEntry *qfbo_pop(QemuFileBinOutputVisitor *ov)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    QTAILQ_REMOVE(&ov->stack, e, node);
+    return e;
+}
+
+static bool qfbo_is_array(QemuFileBinOutputVisitor *ov)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    return e && e->type == QFOV_ARRAY;
+}
+
+static bool qfbo_is_list(QemuFileBinOutputVisitor *ov)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    return e && e->type == QFOV_LIST;
+}
+
+/* If we are in a seqcompat list return true and fill in
+ * sci with the compat mode
+ */
+static bool qfbo_is_seqcompat(QemuFileBinOutputVisitor *ov,
+                              SeqCompatInfo *sci)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    if (e && e->type == QFOV_SEQCOMPAT) {
+        *sci = e->seqcompat_info;
+        return true;
+    }
+    return false;
+}
+
+static void qfbo_start_struct(Visitor *v, void **obj,
+                                          const char *kind, const char *name,
+                                          size_t unused, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+
+    qfbo_push_struct(ov);
+}
+
+static void qfbo_end_struct(Visitor *v, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    StackEntry *e = qfbo_pop(ov);
+
+    if (!e || e->type != QFOV_STRUCT) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qfbo_start_list(Visitor *v, const char *name,
+                                        Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    qfbo_push_list(ov);
+}
+
+static GenericList *qfbo_next_list(Visitor *v, GenericList **list,
+                                           Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    GenericList *entry;
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+
+    /* Some users don't actually have a list */
+    if (!list) {
+        return NULL;
+    }
+    entry = *list;
+
+    if (!entry || !qfbo_is_list(ov)) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    /* The way the list iterator is currently used unfortunately clobbers
+     * **list by subseqently assigning our return value to the same container.
+     * This can cause an infinite loop, but we can get around this by tracking
+     * a bit of state to note when we should pass back the next entry rather
+     * than the current one.
+     */
+    if (e->is_list_head) {
+        e->is_list_head = false;
+        return entry;
+    }
+
+    *list = entry->next;
+    return entry->next;
+}
+
+static void qfbo_end_list(Visitor *v, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    StackEntry *e = qfbo_pop(ov);
+    if (!e || e->type != QFOV_LIST) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+    g_free(e);
+}
+
+static void qfbo_start_array(Visitor *v, void **obj,
+                                         const char *name,
+                                         size_t elem_count,
+                                         size_t elem_size, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    ArrayInfo ai = {
+        .elem_count = elem_count,
+        .elem_size = elem_size,
+        .pos = 0
+    };
+    qfbo_push_array(ov, ai);
+}
+
+static void qfbo_next_array(Visitor *v, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    if (!qfbo_is_array(ov) ||
+        e->array_info.pos >= e->array_info.elem_count) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    e->array_info.pos++;
+}
+
+static void qfbo_end_array(Visitor *v, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    StackEntry *e = qfbo_pop(ov);
+    if (!e || e->type != QFOV_ARRAY) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qfbo_type_str(Visitor *v, char **obj, const char *name,
+                                      Error **errp)
+{
+    if (obj) {
+        g_free(*obj);
+    }
+}
+
+/* A string upto 256 bytes in length (including terminator)
+ *   output as length byte (not including term) followed by text
+ *   (also not including term)
+ */
+static void qfbo_type_str256(Visitor *v, char *obj, const char *name,
+                                      Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    size_t len = strlen(obj);
+
+    DPRINTF("qfbo_type_str256: len=%d str=%s", (int)len, obj);
+
+    if (len > 255) {
+        error_set(errp, QERR_BUFFER_OVERRUN);
+        return;
+    }
+    qemu_put_byte(ov->file, (uint8_t)len);
+    qemu_put_buffer(ov->file, (uint8_t *)obj, len);
+}
+
+static void qfbo_type_buffer(Visitor *v, void *data, size_t len, bool async,
+                                          const char *name, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    if (async) {
+        qemu_put_buffer_async(ov->file, data, len);
+    } else {
+        qemu_put_buffer(ov->file, data, len);
+    }
+}
+
+static void qfbo_type_uint8(Visitor *v, uint8_t *obj,
+                                          const char *name,
+                                          Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    qemu_put_byte(ov->file, *obj);
+}
+
+static void qfbo_type_uint16(Visitor *v, uint16_t *obj,
+                                           const char *name, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    qemu_put_be16(ov->file, *obj);
+}
+
+static void qfbo_type_uint32(Visitor *v, uint32_t *obj,
+                                           const char *name, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    qemu_put_be32(ov->file, *obj);
+}
+
+static void qfbo_type_uint64(Visitor *v, uint64_t *obj,
+                                           const char *name, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    qemu_put_be64(ov->file, *obj);
+}
+
+static void qfbo_type_int8(Visitor *v, int8_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    qemu_put_byte(ov->file, *obj);
+}
+
+static void qfbo_type_int16(Visitor *v, int16_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    qemu_put_sbe16(ov->file, *obj);
+}
+
+static void qfbo_type_int32(Visitor *v, int32_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    qemu_put_sbe32(ov->file, *obj);
+}
+
+static void qfbo_type_int64(Visitor *v, int64_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    qemu_put_sbe64(ov->file, *obj);
+}
+
+static void qfbo_type_bool(Visitor *v, bool *obj, const char *name,
+                                   Error **errp)
+{
+    uint8_t val = *obj;
+    qfbo_type_uint8(v, &val, name, errp);
+}
+
+static QEMUFile *qfbo_get_qemufile(Visitor *v)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+
+    return ov->file;
+}
+
+/* this is a bit odd - I'm using 'get_next_type' to plant the compatibility
+ * bytes in lists.
+ */
+static void qfbo_get_next_type(Visitor *v, int *kind, const int *qobjects,
+                               const char *name, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    SeqCompatInfo sci;
+
+    if (qfbo_is_seqcompat(ov, &sci)) {
+        /* The binary formats used here have a byte on each entry
+         * identifying the type, or a terminator (that varies
+         * in different lists).
+         */
+        uint8_t tmpbyte;
+        tmpbyte = *kind & 0xff;
+
+        DPRINTF("qfbo_get_next_type for %s", name);
+
+        switch (sci.mode) {
+        case VISIT_SEQ_COMPAT_BYTE0TERM:
+            qemu_put_byte(ov->file, tmpbyte);
+            break;
+
+        default:
+            error_set(errp, QERR_UNDEFINED_ERROR);
+            return;
+        }
+        return;
+    }
+
+    /* Only dealing with SeqCompat's for the moment */
+    error_set(errp, QERR_UNDEFINED_ERROR);
+}
+
+static void qfbo_start_sequence_compat(Visitor *v, const char *name,
+                                       Visit_seq_compat_mode compat_mode,
+                                       void *opaque, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    SeqCompatInfo sci = {
+        .mode = compat_mode,
+        .data = opaque
+    };
+    SectionHeader *sh;
+    ramsecentry_header *rse_hdr;
+
+    switch (compat_mode) {
+    case VISIT_SEQ_COMPAT_FILE:
+        qemu_put_be32(ov->file, QEMU_VM_FILE_MAGIC);
+        qemu_put_be32(ov->file, QEMU_VM_FILE_VERSION);
+        break;
+
+    case VISIT_SEQ_COMPAT_SECTION_HEADER:
+        /*
+         *  for VM_SECTION_FULL and VM_SECTION_START
+         * 'opaque' points to a struct Sectionheader
+         */
+        sh = opaque;
+        qemu_put_be32(ov->file, sh->section_id);
+        qemu_put_byte(ov->file, strlen(sh->idstr));
+        qemu_put_buffer(ov->file, (uint8_t *)sh->idstr, strlen(sh->idstr));
+        qemu_put_be32(ov->file, sh->instance_id);
+        qemu_put_be32(ov->file, sh->version_id);
+        break;
+
+    case VISIT_SEQ_COMPAT_SECTION_MIN:
+        /* for VM_SECTION_PART/END,  the section name->ID is already known */
+        sh = opaque;
+        qemu_put_be32(ov->file, sh->section_id);
+        break;
+
+    case VISIT_SEQ_COMPAT_SUBSECTION: /* An element in a subsection list */
+        sh = opaque;
+        qemu_put_byte(ov->file, QEMU_VM_SUBSECTION);
+        qemu_put_byte(ov->file, strlen(sh->idstr));
+        qemu_put_buffer(ov->file, (uint8_t *)sh->idstr, strlen(sh->idstr));
+        qemu_put_be32(ov->file, sh->version_id);
+        break;
+
+    case VISIT_SEQ_COMPAT_BYTE0TERM:
+    case VISIT_SEQ_COMPAT_SUBSECLIST:
+    case VISIT_SEQ_COMPAT_RAMSECLIST:
+    case VISIT_SEQ_COMPAT_VMSTATE:
+        /* These don't need anything in the header on the compatibility side */
+        break;
+
+    case VISIT_SEQ_COMPAT_RAMSECENTRY:
+        rse_hdr = opaque;
+        qemu_put_be64(ov->file, rse_hdr->addr | rse_hdr->flags);
+        if ((rse_hdr->flags &
+             (RAM_SAVE_FLAG_CONTINUE | RAM_SAVE_FLAG_MEM_SIZE |
+              RAM_SAVE_FLAG_HOOK)) == 0) {
+            qemu_put_byte(ov->file, strlen(rse_hdr->idstr));
+            qemu_put_buffer(ov->file, (uint8_t *)rse_hdr->idstr,
+                            strlen(rse_hdr->idstr));
+        }
+        break;
+
+    case VISIT_SEQ_COMPAT_BLOB:
+        /*
+         * Nothing in the header, but opaque gets a copy of our QEMUFile -
+         * other implementations might give a different QEMUFile
+         */
+        *(QEMUFile **)opaque = ov->file;
+        break;
+
+    }
+
+    DPRINTF("qfbo_start_sequence_compat for %s\n", name);
+    qfbo_push_seqcompat(ov, sci);
+    /* We don't need to read anything at this point */
+}
+
+static void qfbo_end_sequence_compat(Visitor *v, const char *name,
+                                     Visit_seq_compat_mode compat_mode,
+                                     Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+    StackEntry *e = qfbo_pop(ov);
+    if (!e || e->type != QFOV_SEQCOMPAT) {
+        error_setg(errp, "bad struct stack %d", e ? e->type : -1);
+        if (e) {
+            g_free(e);
+        }
+        return;
+    }
+    switch (e->seqcompat_info.mode) {
+    case VISIT_SEQ_COMPAT_BYTE0TERM:
+        qemu_put_byte(ov->file, 0);
+        break;
+
+    case VISIT_SEQ_COMPAT_RAMSECLIST:
+        qemu_put_be64(ov->file, RAM_SAVE_FLAG_EOS);
+        break;
+
+    default:
+        break;
+    }
+
+    if (e->seqcompat_info.mode != compat_mode) {
+        error_setg(errp, "mismatched seqcompat mode %d/%d", compat_mode,
+                   e->seqcompat_info.mode);
+    }
+
+    DPRINTF("qfbo_end_sequence_compat %s\n", name);
+    g_free(e);
+}
+
+static void qfbo_destroy(Visitor *v, Error **errp)
+{
+    QemuFileBinOutputVisitor *ov = to_ov(v);
+
+    qemu_file_bin_output_visitor_cleanup(ov);
+}
+
+Visitor *qemu_file_bin_output_get_visitor(QemuFileBinOutputVisitor *v)
+{
+    return &v->visitor;
+}
+
+void qemu_file_bin_output_visitor_cleanup(QemuFileBinOutputVisitor *ov)
+{
+    g_free(ov);
+}
+
+QemuFileBinOutputVisitor *qemu_file_bin_output_visitor_new(QEMUFile *f)
+{
+    QemuFileBinOutputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->file = f;
+
+    v->visitor.start_struct = qfbo_start_struct;
+    v->visitor.end_struct = qfbo_end_struct;
+    v->visitor.start_list = qfbo_start_list;
+    v->visitor.next_list = qfbo_next_list;
+    v->visitor.end_list = qfbo_end_list;
+    v->visitor.start_array = qfbo_start_array;
+    v->visitor.next_array = qfbo_next_array;
+    v->visitor.end_array = qfbo_end_array;
+    v->visitor.type_buffer = qfbo_type_buffer;
+    v->visitor.type_int = qfbo_type_int64;
+    v->visitor.type_uint8 = qfbo_type_uint8;
+    v->visitor.type_uint16 = qfbo_type_uint16;
+    v->visitor.type_uint32 = qfbo_type_uint32;
+    v->visitor.type_uint64 = qfbo_type_uint64;
+    v->visitor.type_int8 = qfbo_type_int8;
+    v->visitor.type_int16 = qfbo_type_int16;
+    v->visitor.type_int32 = qfbo_type_int32;
+    v->visitor.type_int64 = qfbo_type_int64;
+    v->visitor.type_bool = qfbo_type_bool;
+    v->visitor.type_str = qfbo_type_str;
+    v->visitor.type_str256 = qfbo_type_str256;
+    v->visitor.destroy = qfbo_destroy;
+    v->visitor.start_sequence_compat = qfbo_start_sequence_compat;
+    v->visitor.get_next_type = qfbo_get_next_type;
+    v->visitor.end_sequence_compat = qfbo_end_sequence_compat;
+    v->visitor.get_qemufile = qfbo_get_qemufile;
+
+    v->visitor.flags = VISITOR_SAVING;
+
+    QTAILQ_INIT(&v->stack);
+
+    return v;
+}