All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
@ 2011-09-19 14:41 Michael Roth
  2011-09-19 14:41 ` [Qemu-devel] [RFC 1/8] qapi: add Visitor interfaces for uint*_t and int*_t Michael Roth
                   ` (9 more replies)
  0 siblings, 10 replies; 50+ messages in thread
From: Michael Roth @ 2011-09-19 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, mdroth

OVERVIEW

This patch series implements a QEMUFile Visitor class that's intended to abstract away direct calls to qemu_put_*/qemu_get_* for save/load functions. Currently this is done by always creating a QEMUFileInputVisitor/QEMUFileOutputVisitor pair with each call to qemu_fopen_ops() and maintaining a QEMUFile->I/O Visitor mapping. save/load functions that are to be converted would them simply use lookup routines to get a Visitor based on their QEMUFile arguments. Once these save/load functions are all converted, we can then change the interfaces in bulk and switch over to passing in the Visitor directly.

An example conversion of Slirp, which still uses old-style save/load routines, is included as part of this series. Anthony I believe has VMState converted over to using Visitors, so theoretically all VMStatified devices are good to go (Anthony: if that's the case feel free to rebase on this or point me to the repo to pull in, and I'll work off the resulting branch).

PLANS

Ultimately, the goal is to implement a new migration protocol that is self-describing, such that incompatibilities or migration errors can be more easilly detected. Currently, a simple change in data types for a particular device can introduce subtle bugs that won't be detected by the target, since the target interprets the data according to it's own expectation of what those data types are. Currently the plan is to use ASN.1 BER in place of QEMUFile.

By using a Visitor interface for migration, we also gain the ability to generate a migration schema (via, say, a JSONSchemaVisitor). This is similar to the VMState schema generator in Anthony's "Add live migration unit tests" series (http://thread.gmane.org/gmane.comp.emulators.qemu/97754/focus=97754), except that it is applicable to both VMState and old-style save/load functions.

There is still quite a bit a work to get to that point. Anthony addressed some of the issues on the VMState side of things in the aforementioned series, and I believe he already has some patches that convert VMState over to using visitors insteads of qemu_put_*/qemu_get_* directly. That being the case, what's left is:

1) Convert old-style save/load functions over to using visitors. I'm not sure what the best approach is for this. We basically have 2 options: either by converting them to VMState, or converting the functions over to using visitors, such as with the example slirp conversion. Even if we do option 2 it would likely need to be done in 2 phases:

phase 1: a mechanical conversion where we basically replace each qemu_put*/qemu_get* with a visit_*. This allows use to plop in a new, say BERVisitor to switch to using the new migration protocol. It should also be possible to do this without breaking migration compatibility with older versions. It does not however result in a well-specified schema if we were to plop in, say, a JSONSchemaVisitor. This is where we need phase 2.

phase 2: Currently, runtime state modifies our "schema" the way things are currently done. Slirp instance and virtqueue enumeration, for instance:

slirp/slirp.c:
    for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
        if (ex_ptr->ex_pty == 3) {
            struct socket *so;
            so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr,
                                       ntohs(ex_ptr->ex_fport));
            if (!so)
                continue;

            qemu_put_byte(f, 42);
            slirp_socket_save(f, so);
        }

hw/virtio.c:
    for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
        if (vdev->vq[i].vring.num == 0)
            break;

        qemu_put_be32(f, vdev->vq[i].vring.num);
        qemu_put_be64(f, vdev->vq[i].pa);
        qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
        if (vdev->binding->save_queue)
            vdev->binding->save_queue(vdev->binding_opaque, i, f);
    }


To be able to build a schema around this we'd need to do a visit_start_array() or visit_start_list(), to denote a list of entries, and ideally we'd also describe structure of these entries. But often there is no structure of these entries...they may just be values taken from various data structures. So, either we do hacky things like using visitor intefaces to create "fake" structs (we say we're dealing with a struct when we're really not), or we actually put these into an intermediate struct which we then use to assign to store/load migration fields from.

phase 2 sounds a whole lot like converting over to VMState. So I think we'll only want to go with option 2 in places where converting to VMState is not practical/planned. Or we can hold off on the phase 2 stuff and just focus on getting the new protocol in place ASAP. After which, we can rework things to support generating a well-specified schema.

2) Once the conversion stuff is done, we can modify the save/load interfaces in bulk to accept a Visitor in place of a QEMUFile (but continue using the old protocol/QEMUFile*Visitor for the time being)

3) Implement migration capabilities negotiation. This was discussed to some extent in the XBZRLE v4 thread (http://lists.gnu.org/archive/html/qemu-devel/2011-08/msg01155.html), basically having a set of migration capabilities that can be queried via QMP. Management tools would then choose the optimal migration settings from the intersection of these.

4) Implement the BERVisitor and make this the default migration protocol.

Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.

Thoughts?

 Makefile                        |    4 +-
 Makefile.objs                   |   19 +-
 cutils.c                        |    8 +
 hw/hw.h                         |   20 ++
 qapi/qapi-visit-core.c          |   78 ++++++
 qapi/qapi-visit-core.h          |   30 ++
 qapi/qemu-file-input-visitor.c  |  350 +++++++++++++++++++++++
 qapi/qemu-file-input-visitor.h  |   26 ++
 qapi/qemu-file-output-visitor.c |  353 +++++++++++++++++++++++
 qapi/qemu-file-output-visitor.h |   26 ++
 qemu-common.h                   |    1 +
 qemu-file.c                     |  585 +++++++++++++++++++++++++++++++++++++++
 savevm.c                        |  494 ---------------------------------
 slirp/slirp.c                   |  366 ++++++++++++++----------
 test-visitor.c                  |  404 +++++++++++++++++++++++++++
 15 files changed, 2110 insertions(+), 654 deletions(-)

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [Qemu-devel] [RFC 1/8] qapi: add Visitor interfaces for uint*_t and int*_t
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
@ 2011-09-19 14:41 ` Michael Roth
  2011-09-19 14:41 ` [Qemu-devel] [RFC 2/8] qapi: add QemuFileOutputVisitor Michael Roth
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 50+ messages in thread
From: Michael Roth @ 2011-09-19 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, mdroth


Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 qapi/qapi-visit-core.c |   78 ++++++++++++++++++++++++++++++++++++++++++++++++
 qapi/qapi-visit-core.h |   30 ++++++++++++++++++
 2 files changed, 108 insertions(+), 0 deletions(-)

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index ddef3ed..d2e7d3e 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -66,6 +66,28 @@ void visit_end_list(Visitor *v, Error **errp)
     }
 }
 
+void visit_start_array(Visitor *v, void **obj, const char *name, size_t elem_count,
+                       size_t elem_size, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->start_array(v, obj, name, elem_count, elem_size, errp);
+    }
+}
+
+void visit_next_array(Visitor *v, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->next_array(v, errp);
+    }
+}
+
+void visit_end_array(Visitor *v, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->end_array(v, errp);
+    }
+}
+
 void visit_start_optional(Visitor *v, bool *present, const char *name,
                           Error **errp)
 {
@@ -96,6 +118,62 @@ void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
     }
 }
 
+void visit_type_uint8_t(Visitor *v, uint8_t *obj, const char *name, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_uint8_t(v, obj, name, errp);
+    }
+}
+
+void visit_type_uint16_t(Visitor *v, uint16_t *obj, const char *name, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_uint16_t(v, obj, name, errp);
+    }
+}
+
+void visit_type_uint32_t(Visitor *v, uint32_t *obj, const char *name, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_uint32_t(v, obj, name, errp);
+    }
+}
+
+void visit_type_uint64_t(Visitor *v, uint64_t *obj, const char *name, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_uint64_t(v, obj, name, errp);
+    }
+}
+
+void visit_type_int8_t(Visitor *v, int8_t *obj, const char *name, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_int8_t(v, obj, name, errp);
+    }
+}
+
+void visit_type_int16_t(Visitor *v, int16_t *obj, const char *name, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_int16_t(v, obj, name, errp);
+    }
+}
+
+void visit_type_int32_t(Visitor *v, int32_t *obj, const char *name, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_int32_t(v, obj, name, errp);
+    }
+}
+
+void visit_type_int64_t(Visitor *v, int64_t *obj, const char *name, Error **errp)
+{
+    if (!error_is_set(errp)) {
+        v->type_int64_t(v, obj, name, errp);
+    }
+}
+
 void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
 {
     if (!error_is_set(errp)) {
diff --git a/qapi/qapi-visit-core.h b/qapi/qapi-visit-core.h
index e850746..3e3ac4e 100644
--- a/qapi/qapi-visit-core.h
+++ b/qapi/qapi-visit-core.h
@@ -22,6 +22,11 @@ typedef struct GenericList
     struct GenericList *next;
 } GenericList;
 
+typedef struct GenericItem
+{
+    void *value;
+} GenericItem;
+
 typedef struct Visitor Visitor;
 
 struct Visitor
@@ -35,10 +40,23 @@ struct Visitor
     GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp);
     void (*end_list)(Visitor *v, Error **errp);
 
+    void (*start_array)(Visitor *v, void **obj, const char *name,
+                        size_t elem_count, size_t elem_size, Error **errp);
+    void (*next_array)(Visitor *v, Error **errp);
+    void (*end_array)(Visitor *v, Error **errp);
+
     void (*type_enum)(Visitor *v, int *obj, const char *strings[],
                       const char *kind, const char *name, Error **errp);
 
     void (*type_int)(Visitor *v, int64_t *obj, const char *name, Error **errp);
+    void (*type_uint8_t)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
+    void (*type_uint16_t)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
+    void (*type_uint32_t)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
+    void (*type_uint64_t)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
+    void (*type_int8_t)(Visitor *v, int8_t *obj, const char *name, Error **errp);
+    void (*type_int16_t)(Visitor *v, int16_t *obj, const char *name, Error **errp);
+    void (*type_int32_t)(Visitor *v, int32_t *obj, const char *name, Error **errp);
+    void (*type_int64_t)(Visitor *v, int64_t *obj, const char *name, Error **errp);
     void (*type_bool)(Visitor *v, bool *obj, const char *name, Error **errp);
     void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
     void (*type_number)(Visitor *v, double *obj, const char *name,
@@ -63,12 +81,24 @@ void visit_end_struct(Visitor *v, Error **errp);
 void visit_start_list(Visitor *v, const char *name, Error **errp);
 GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
 void visit_end_list(Visitor *v, Error **errp);
+void visit_start_array(Visitor *v, void **obj, const char *name, size_t elem_count,
+                       size_t elem_size, Error **errp);
+void visit_next_array(Visitor *v, Error **errp);
+void visit_end_array(Visitor *v, Error **errp);
 void visit_start_optional(Visitor *v, bool *present, const char *name,
                           Error **errp);
 void visit_end_optional(Visitor *v, Error **errp);
 void visit_type_enum(Visitor *v, int *obj, const char *strings[],
                      const char *kind, const char *name, Error **errp);
 void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp);
+void visit_type_uint8_t(Visitor *v, uint8_t *obj, const char *name, Error **errp);
+void visit_type_uint16_t(Visitor *v, uint16_t *obj, const char *name, Error **errp);
+void visit_type_uint32_t(Visitor *v, uint32_t *obj, const char *name, Error **errp);
+void visit_type_uint64_t(Visitor *v, uint64_t *obj, const char *name, Error **errp);
+void visit_type_int8_t(Visitor *v, int8_t *obj, const char *name, Error **errp);
+void visit_type_int16_t(Visitor *v, int16_t *obj, const char *name, Error **errp);
+void visit_type_int32_t(Visitor *v, int32_t *obj, const char *name, Error **errp);
+void visit_type_int64_t(Visitor *v, int64_t *obj, const char *name, Error **errp);
 void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
 void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
 void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [Qemu-devel] [RFC 2/8] qapi: add QemuFileOutputVisitor
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
  2011-09-19 14:41 ` [Qemu-devel] [RFC 1/8] qapi: add Visitor interfaces for uint*_t and int*_t Michael Roth
@ 2011-09-19 14:41 ` Michael Roth
  2011-09-19 14:41 ` [Qemu-devel] [RFC 3/8] qapi: add QemuFileInputVisitor Michael Roth
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 50+ messages in thread
From: Michael Roth @ 2011-09-19 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, mdroth

Visitor interface to write values to a QEMUFile.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 Makefile.objs                   |   16 +-
 qapi/qemu-file-output-visitor.c |  353 +++++++++++++++++++++++++++++++++++++++
 qapi/qemu-file-output-visitor.h |   26 +++
 3 files changed, 388 insertions(+), 7 deletions(-)
 create mode 100644 qapi/qemu-file-output-visitor.c
 create mode 100644 qapi/qemu-file-output-visitor.h

diff --git a/Makefile.objs b/Makefile.objs
index 62020d7..48fe0c4 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -20,6 +20,13 @@ coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o
 endif
 coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
 
+######################################################################
+# qapi
+
+qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o
+qapi-nested-y += qmp-registry.o qmp-dispatch.o
+qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y))
+
 #######################################################################
 # block-obj-y is code used by both qemu system emulation and qemu-img
 
@@ -73,6 +80,8 @@ fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y))
 # CPUs and machines.
 
 common-obj-y = $(block-obj-y) blockdev.o
+common-obj-y += $(qapi-obj-y)
+common-obj-y += qapi/qemu-file-output-visitor.o
 common-obj-y += $(net-obj-y)
 common-obj-y += $(qobject-obj-y)
 common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
@@ -399,13 +408,6 @@ $(trace-obj-y): $(GENERATED_HEADERS)
 libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
 
 ######################################################################
-# qapi
-
-qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o
-qapi-nested-y += qmp-registry.o qmp-dispatch.o
-qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y))
-
-######################################################################
 # guest agent
 
 qga-nested-y = guest-agent-commands.o guest-agent-command-state.o
diff --git a/qapi/qemu-file-output-visitor.c b/qapi/qemu-file-output-visitor.c
new file mode 100644
index 0000000..c44b76c
--- /dev/null
+++ b/qapi/qemu-file-output-visitor.c
@@ -0,0 +1,353 @@
+/*
+ * QEMUFile Output 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.
+ *
+ */
+
+#include "qemu-file-output-visitor.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qemu-objects.h"
+#include "hw/hw.h"
+#include "qerror.h"
+
+typedef struct {
+    size_t elem_count;
+    size_t elem_size;
+    size_t pos;
+} ArrayInfo;
+
+typedef struct StackEntry
+{
+    enum {
+        QFOV_ARRAY,
+        QFOV_LIST,
+        QFOV_STRUCT,
+    } type;
+    ArrayInfo array_info;
+    bool is_list_head;
+    QTAILQ_ENTRY(StackEntry) node;
+} StackEntry;
+
+struct QemuFileOutputVisitor
+{
+    Visitor visitor;
+    QTAILQ_HEAD(, StackEntry) stack;
+    QEMUFile *file;
+};
+
+static QemuFileOutputVisitor *to_ov(Visitor *v)
+{
+    return container_of(v, QemuFileOutputVisitor, visitor);
+}
+
+static void qemu_file_output_push(QemuFileOutputVisitor *ov, StackEntry *e)
+{
+    QTAILQ_INSERT_HEAD(&ov->stack, e, node);
+}
+
+static void qemu_file_output_push_array(QemuFileOutputVisitor *ov, ArrayInfo ai)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_ARRAY;
+    e->array_info = ai;
+    qemu_file_output_push(ov, e);
+}
+
+static void qemu_file_output_push_list(QemuFileOutputVisitor *ov)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_LIST;
+    e->is_list_head = true;
+    qemu_file_output_push(ov, e);
+}
+
+static void qemu_file_output_push_struct(QemuFileOutputVisitor *ov)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFOV_STRUCT;
+    qemu_file_output_push(ov, e);
+}
+
+static StackEntry *qemu_file_output_pop(QemuFileOutputVisitor *ov)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    QTAILQ_REMOVE(&ov->stack, e, node);
+    return e;
+}
+
+static bool qemu_file_output_is_array(QemuFileOutputVisitor *ov)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    return e && e->type == QFOV_ARRAY;
+}
+
+static bool qemu_file_output_is_list(QemuFileOutputVisitor *ov)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    return e && e->type == QFOV_LIST;
+}
+
+static void qemu_file_output_start_struct(Visitor *v, void **obj,
+                                          const char *kind, const char *name,
+                                          size_t unused, Error **errp)
+{
+    QemuFileOutputVisitor *ov = to_ov(v);
+
+    if (!obj || *obj == NULL) {
+        error_set(errp, QERR_INVALID_PARAMETER, "obj");
+    }
+    qemu_file_output_push_struct(ov);
+}
+
+static void qemu_file_output_end_struct(Visitor *v, Error **errp)
+{
+    QemuFileOutputVisitor *ov = to_ov(v);
+    StackEntry *e = qemu_file_output_pop(ov);
+
+    if (!e || e->type != QFOV_STRUCT) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qemu_file_output_start_list(Visitor *v, const char *name,
+                                        Error **errp)
+{
+    QemuFileOutputVisitor *ov = to_ov(v);
+    qemu_file_output_push_list(ov);
+}
+
+static GenericList *qemu_file_output_next_list(Visitor *v, GenericList **list,
+                                           Error **errp)
+{
+    QemuFileOutputVisitor *ov = to_ov(v);
+    GenericList *entry = *list;
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+
+    if (!entry || !qemu_file_output_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 qemu_file_output_end_list(Visitor *v, Error **errp)
+{
+    QemuFileOutputVisitor *ov = to_ov(v);
+    StackEntry *e = qemu_file_output_pop(ov);
+    if (!e || e->type != QFOV_LIST) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+    g_free(e);
+}
+
+static void qemu_file_output_start_array(Visitor *v, void **obj,
+                                         const char *name,
+                                         size_t elem_count,
+                                         size_t elem_size, Error **errp)
+{
+    QemuFileOutputVisitor *ov = to_ov(v);
+    ArrayInfo ai = {
+        .elem_count = elem_count,
+        .elem_size = elem_size,
+        .pos = 0
+    };
+    qemu_file_output_push_array(ov, ai);
+}
+
+static void qemu_file_output_next_array(Visitor *v, Error **errp)
+{
+    QemuFileOutputVisitor *ov = to_ov(v);
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    if (!qemu_file_output_is_array(ov) ||
+        e->array_info.pos >= e->array_info.elem_count) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    e->array_info.pos++;
+}
+
+static void qemu_file_output_end_array(Visitor *v, Error **errp)
+{
+    QemuFileOutputVisitor *ov = to_ov(v);
+    StackEntry *e = qemu_file_output_pop(ov);
+    if (!e || e->type != QFOV_ARRAY) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qemu_file_output_type_str(Visitor *v, char **obj, const char *name,
+                                      Error **errp)
+{
+    if (obj) {
+        g_free(*obj);
+    }
+}
+
+static void qemu_file_output_type_uint8_t(Visitor *v, uint8_t *obj,
+                                          const char *name,
+                                          Error **errp)
+{
+    QemuFileOutputVisitor *ov = container_of(v, QemuFileOutputVisitor, visitor);
+    qemu_put_byte(ov->file, *obj);
+}
+
+static void qemu_file_output_type_uint16_t(Visitor *v, uint16_t *obj,
+                                           const char *name, Error **errp)
+{
+    QemuFileOutputVisitor *ov = container_of(v, QemuFileOutputVisitor, visitor);
+    qemu_put_byte(ov->file, *obj >> 8);
+    qemu_put_byte(ov->file, *obj);
+}
+
+static void qemu_file_output_type_uint32_t(Visitor *v, uint32_t *obj,
+                                           const char *name, Error **errp)
+{
+    QemuFileOutputVisitor *ov = container_of(v, QemuFileOutputVisitor, visitor);
+    qemu_put_byte(ov->file, *obj >> 24);
+    qemu_put_byte(ov->file, *obj >> 16);
+    qemu_put_byte(ov->file, *obj >> 8);
+    qemu_put_byte(ov->file, *obj);
+}
+
+static void qemu_file_output_type_uint64_t(Visitor *v, uint64_t *obj,
+                                           const char *name, Error **errp)
+{
+    QemuFileOutputVisitor *ov = container_of(v, QemuFileOutputVisitor, visitor);
+    qemu_put_byte(ov->file, *obj >> 56);
+    qemu_put_byte(ov->file, *obj >> 48);
+    qemu_put_byte(ov->file, *obj >> 40);
+    qemu_put_byte(ov->file, *obj >> 32);
+    qemu_put_byte(ov->file, *obj >> 24);
+    qemu_put_byte(ov->file, *obj >> 16);
+    qemu_put_byte(ov->file, *obj >> 8);
+    qemu_put_byte(ov->file, *obj);
+}
+
+static void qemu_file_output_type_int8_t(Visitor *v, int8_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileOutputVisitor *ov = container_of(v, QemuFileOutputVisitor, visitor);
+    qemu_put_sbyte(ov->file, *obj);
+}
+
+static void qemu_file_output_type_int16_t(Visitor *v, int16_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileOutputVisitor *ov = container_of(v, QemuFileOutputVisitor, visitor);
+    qemu_put_sbyte(ov->file, *obj >> 8);
+    qemu_put_sbyte(ov->file, *obj);
+}
+
+static void qemu_file_output_type_int32_t(Visitor *v, int32_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileOutputVisitor *ov = container_of(v, QemuFileOutputVisitor, visitor);
+    qemu_put_sbyte(ov->file, *obj >> 24);
+    qemu_put_sbyte(ov->file, *obj >> 16);
+    qemu_put_sbyte(ov->file, *obj >> 8);
+    qemu_put_sbyte(ov->file, *obj);
+}
+
+static void qemu_file_output_type_int64_t(Visitor *v, int64_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileOutputVisitor *ov = container_of(v, QemuFileOutputVisitor, visitor);
+    qemu_put_sbyte(ov->file, *obj >> 56);
+    qemu_put_sbyte(ov->file, *obj >> 48);
+    qemu_put_sbyte(ov->file, *obj >> 40);
+    qemu_put_sbyte(ov->file, *obj >> 32);
+    qemu_put_sbyte(ov->file, *obj >> 24);
+    qemu_put_sbyte(ov->file, *obj >> 16);
+    qemu_put_sbyte(ov->file, *obj >> 8);
+    qemu_put_sbyte(ov->file, *obj);
+}
+
+static void qemu_file_output_type_bool(Visitor *v, bool *obj, const char *name,
+                                   Error **errp)
+{
+    uint8_t val = *obj;
+    qemu_file_output_type_uint8_t(v, &val, name, errp);
+}
+
+static void qemu_file_output_type_number(Visitor *v, double *obj,
+                                         const char *name, Error **errp)
+{
+    uint64_t *val = (uint64_t *)obj;
+    qemu_file_output_type_uint64_t(v, val, name, errp);
+}
+
+static void qemu_file_output_type_enum(Visitor *v, int *obj,
+                                       const char *strings[],
+                                       const char *kind, const char *name,
+                                       Error **errp)
+{
+}
+
+Visitor *qemu_file_output_get_visitor(QemuFileOutputVisitor *v)
+{
+    return &v->visitor;
+}
+
+void qemu_file_output_visitor_cleanup(QemuFileOutputVisitor *ov)
+{
+    g_free(ov);
+}
+
+QemuFileOutputVisitor *qemu_file_output_visitor_new(QEMUFile *f)
+{
+    QemuFileOutputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->file = f;
+
+    v->visitor.start_struct = qemu_file_output_start_struct;
+    v->visitor.end_struct = qemu_file_output_end_struct;
+    v->visitor.start_list = qemu_file_output_start_list;
+    v->visitor.next_list = qemu_file_output_next_list;
+    v->visitor.end_list = qemu_file_output_end_list;
+    v->visitor.start_array = qemu_file_output_start_array;
+    v->visitor.next_array = qemu_file_output_next_array;
+    v->visitor.end_array = qemu_file_output_end_array;
+    v->visitor.type_enum = qemu_file_output_type_enum;
+    v->visitor.type_int = qemu_file_output_type_int64_t;
+    v->visitor.type_uint8_t = qemu_file_output_type_uint8_t;
+    v->visitor.type_uint16_t = qemu_file_output_type_uint16_t;
+    v->visitor.type_uint32_t = qemu_file_output_type_uint32_t;
+    v->visitor.type_uint64_t = qemu_file_output_type_uint64_t;
+    v->visitor.type_int8_t = qemu_file_output_type_int8_t;
+    v->visitor.type_int16_t = qemu_file_output_type_int16_t;
+    v->visitor.type_int32_t = qemu_file_output_type_int32_t;
+    v->visitor.type_int64_t = qemu_file_output_type_int64_t;
+    v->visitor.type_bool = qemu_file_output_type_bool;
+    v->visitor.type_str = qemu_file_output_type_str;
+    v->visitor.type_number = qemu_file_output_type_number;
+
+    QTAILQ_INIT(&v->stack);
+
+    return v;
+}
diff --git a/qapi/qemu-file-output-visitor.h b/qapi/qemu-file-output-visitor.h
new file mode 100644
index 0000000..e446f08
--- /dev/null
+++ b/qapi/qemu-file-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_OUTPUT_VISITOR_H
+#define QEMU_FILE_OUTPUT_VISITOR_H
+
+#include "qapi-visit-core.h"
+
+typedef struct QemuFileOutputVisitor QemuFileOutputVisitor;
+
+QemuFileOutputVisitor *qemu_file_output_visitor_new(QEMUFile *f);
+void qemu_file_output_visitor_cleanup(QemuFileOutputVisitor *d);
+
+Visitor *qemu_file_output_get_visitor(QemuFileOutputVisitor *v);
+
+#endif
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [Qemu-devel] [RFC 3/8] qapi: add QemuFileInputVisitor
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
  2011-09-19 14:41 ` [Qemu-devel] [RFC 1/8] qapi: add Visitor interfaces for uint*_t and int*_t Michael Roth
  2011-09-19 14:41 ` [Qemu-devel] [RFC 2/8] qapi: add QemuFileOutputVisitor Michael Roth
@ 2011-09-19 14:41 ` Michael Roth
  2011-10-24 23:59   ` Chris Krumme
  2011-09-19 14:41 ` [Qemu-devel] [RFC 4/8] savevm: move QEMUFile interfaces into qemu-file.c Michael Roth
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 50+ messages in thread
From: Michael Roth @ 2011-09-19 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, mdroth

Visitor interfaces to read values from a QEMUFile

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 Makefile.objs                  |    1 +
 qapi/qemu-file-input-visitor.c |  350 ++++++++++++++++++++++++++++++++++++++++
 qapi/qemu-file-input-visitor.h |   26 +++
 3 files changed, 377 insertions(+), 0 deletions(-)
 create mode 100644 qapi/qemu-file-input-visitor.c
 create mode 100644 qapi/qemu-file-input-visitor.h

diff --git a/Makefile.objs b/Makefile.objs
index 48fe0c4..6bc8555 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -82,6 +82,7 @@ fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y))
 common-obj-y = $(block-obj-y) blockdev.o
 common-obj-y += $(qapi-obj-y)
 common-obj-y += qapi/qemu-file-output-visitor.o
+common-obj-y += qapi/qemu-file-input-visitor.o
 common-obj-y += $(net-obj-y)
 common-obj-y += $(qobject-obj-y)
 common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
diff --git a/qapi/qemu-file-input-visitor.c b/qapi/qemu-file-input-visitor.c
new file mode 100644
index 0000000..7217125
--- /dev/null
+++ b/qapi/qemu-file-input-visitor.c
@@ -0,0 +1,350 @@
+/*
+ * QEMUFile Output 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.
+ *
+ */
+
+#include "qemu-file-input-visitor.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qemu-objects.h"
+#include "hw/hw.h"
+#include "qerror.h"
+
+typedef struct {
+    size_t elem_count;
+    size_t elem_size;
+    size_t pos;
+} ArrayInfo;
+
+typedef struct StackEntry
+{
+    enum {
+        QFIV_ARRAY,
+        QFIV_LIST,
+        QFIV_STRUCT,
+    } type;
+    ArrayInfo array_info;
+    QTAILQ_ENTRY(StackEntry) node;
+} StackEntry;
+
+struct QemuFileInputVisitor
+{
+    Visitor visitor;
+    QTAILQ_HEAD(, StackEntry) stack;
+    QEMUFile *file;
+};
+
+static QemuFileInputVisitor *to_iv(Visitor *v)
+{
+    return container_of(v, QemuFileInputVisitor, visitor);
+}
+
+static void qemu_file_input_push(QemuFileInputVisitor *iv, StackEntry *e)
+{
+    QTAILQ_INSERT_HEAD(&iv->stack, e, node);
+}
+
+static void qemu_file_input_push_array(QemuFileInputVisitor *iv, ArrayInfo ai)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_ARRAY;
+    e->array_info = ai;
+    qemu_file_input_push(iv, e);
+}
+
+static void qemu_file_input_push_list(QemuFileInputVisitor *iv)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_LIST;
+    qemu_file_input_push(iv, e);
+}
+
+static void qemu_file_input_push_struct(QemuFileInputVisitor *iv)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_STRUCT;
+    qemu_file_input_push(iv, e);
+}
+
+static void *qemu_file_input_pop(QemuFileInputVisitor *iv)
+{
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+    QTAILQ_REMOVE(&iv->stack, e, node);
+    return e;
+}
+
+static bool qemu_file_input_is_array(QemuFileInputVisitor *iv)
+{
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+    return e->type == QFIV_ARRAY;
+}
+
+static bool qemu_file_input_is_list(QemuFileInputVisitor *ov)
+{
+    StackEntry *e = QTAILQ_FIRST(&ov->stack);
+    return e && e->type == QFIV_LIST;
+}
+
+static void qemu_file_input_start_struct(Visitor *v, void **obj,
+                                         const char *kind,
+                                         const char *name, size_t size,
+                                         Error **errp)
+{
+    QemuFileInputVisitor *iv = to_iv(v);
+
+    if (obj && *obj == NULL) {
+        *obj = g_malloc0(size);
+    }
+    qemu_file_input_push_struct(iv);
+}
+
+static void qemu_file_input_end_struct(Visitor *v, Error **errp)
+{
+    QemuFileInputVisitor *iv = to_iv(v);
+    StackEntry *e = qemu_file_input_pop(iv);
+
+    if (!e || e->type != QFIV_STRUCT) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qemu_file_input_start_list(Visitor *v, const char *name,
+                                       Error **errp)
+{
+    QemuFileInputVisitor *iv = to_iv(v);
+    qemu_file_input_push_list(iv);
+}
+
+static GenericList *qemu_file_input_next_list(Visitor *v, GenericList **list,
+                                           Error **errp)
+{
+    QemuFileInputVisitor *iv = to_iv(v);
+    GenericList *entry;
+
+    if (!qemu_file_input_is_list(iv)) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    entry = g_malloc0(sizeof(*entry));
+    if (*list) {
+        (*list)->next = entry;
+    }
+
+    *list = entry;
+    return entry;
+}
+
+static void qemu_file_input_end_list(Visitor *v, Error **errp)
+{
+    QemuFileInputVisitor *iv = to_iv(v);
+    StackEntry *e = qemu_file_input_pop(iv);
+    if (!e || e->type != QFIV_LIST) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qemu_file_input_start_array(Visitor *v, void **obj,
+                                        const char *name,
+                                        size_t elem_count,
+                                        size_t elem_size,
+                                        Error **errp)
+{
+    QemuFileInputVisitor *iv = to_iv(v);
+    ArrayInfo ai = {
+        .elem_count = elem_count,
+        .elem_size = elem_size,
+        .pos = 0
+    };
+    if (*obj == NULL) {
+        *obj = g_malloc0(elem_count * elem_size);
+    }
+    qemu_file_input_push_array(iv, ai);
+}
+
+static void qemu_file_input_next_array(Visitor *v, Error **errp)
+{
+    QemuFileInputVisitor *iv = to_iv(v);
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+
+    if (!qemu_file_input_is_array(iv) ||
+        e->array_info.pos >= e->array_info.elem_count) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    e->array_info.pos++;
+}
+
+static void qemu_file_input_end_array(Visitor *v, Error **errp)
+{
+    QemuFileInputVisitor *iv = to_iv(v);
+    StackEntry *e = qemu_file_input_pop(iv);
+    if (!e || e->type != QFIV_ARRAY) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qemu_file_input_type_str(Visitor *v, char **obj, const char *name,
+                                  Error **errp)
+{
+    if (obj) {
+        g_free(*obj);
+    }
+}
+
+static void qemu_file_input_type_uint8_t(Visitor *v, uint8_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
+    *obj = qemu_get_byte(ov->file);
+}
+
+static void qemu_file_input_type_uint16_t(Visitor *v, uint16_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
+    *obj = qemu_get_byte(ov->file) << 8;
+    *obj |= qemu_get_byte(ov->file);
+}
+
+static void qemu_file_input_type_uint32_t(Visitor *v, uint32_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
+    *obj = qemu_get_byte(ov->file) << 24;
+    *obj |= qemu_get_byte(ov->file) << 16;
+    *obj |= qemu_get_byte(ov->file) << 8;
+    *obj |= qemu_get_byte(ov->file);
+}
+
+static void qemu_file_input_type_uint64_t(Visitor *v, uint64_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
+    *obj = (uint64_t)qemu_get_byte(ov->file) << 56;
+    *obj |= (uint64_t)qemu_get_byte(ov->file) << 48;
+    *obj |= (uint64_t)qemu_get_byte(ov->file) << 40;
+    *obj |= (uint64_t)qemu_get_byte(ov->file) << 32;
+    *obj |= qemu_get_byte(ov->file) << 24;
+    *obj |= qemu_get_byte(ov->file) << 16;
+    *obj |= qemu_get_byte(ov->file) << 8;
+    *obj |= qemu_get_byte(ov->file);
+}
+
+static void qemu_file_input_type_int8_t(Visitor *v, int8_t *obj,
+                                        const char *name, Error **errp)
+{
+    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
+    *obj = qemu_get_sbyte(ov->file);
+}
+
+static void qemu_file_input_type_int16_t(Visitor *v, int16_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
+    *obj = qemu_get_sbyte(ov->file) << 8;
+    *obj |= qemu_get_sbyte(ov->file);
+}
+
+static void qemu_file_input_type_int32_t(Visitor *v, int32_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
+    *obj = qemu_get_sbyte(ov->file) << 24;
+    *obj |= qemu_get_sbyte(ov->file) << 16;
+    *obj |= qemu_get_sbyte(ov->file) << 8;
+    *obj |= qemu_get_sbyte(ov->file);
+}
+
+static void qemu_file_input_type_int64_t(Visitor *v, int64_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
+    *obj = (int64_t)qemu_get_sbyte(ov->file) << 56;
+    *obj |= (int64_t)qemu_get_sbyte(ov->file) << 48;
+    *obj |= (int64_t)qemu_get_sbyte(ov->file) << 40;
+    *obj |= (int64_t)qemu_get_sbyte(ov->file) << 32;
+    *obj |= qemu_get_sbyte(ov->file) << 24;
+    *obj |= qemu_get_sbyte(ov->file) << 16;
+    *obj |= qemu_get_sbyte(ov->file) << 8;
+    *obj |= qemu_get_sbyte(ov->file);
+}
+
+static void qemu_file_input_type_bool(Visitor *v, bool *obj, const char *name,
+                                     Error **errp)
+{
+    uint8_t val;
+    qemu_file_input_type_uint8_t(v, &val, name, errp);
+    *obj = val;
+}
+
+static void qemu_file_input_type_number(Visitor *v, double *obj,
+                                        const char *name, Error **errp)
+{
+    uint64_t *val = (uint64_t *)obj;
+    qemu_file_input_type_uint64_t(v, val, name, errp);
+}
+
+static void qemu_file_input_type_enum(Visitor *v, int *obj,
+                                      const char *strings[], const char *kind,
+                                      const char *name, Error **errp)
+{
+}
+
+Visitor *qemu_file_input_get_visitor(QemuFileInputVisitor *ov)
+{
+    return &ov->visitor;
+}
+
+void qemu_file_input_visitor_cleanup(QemuFileInputVisitor *ov)
+{
+    g_free(ov);
+}
+
+QemuFileInputVisitor *qemu_file_input_visitor_new(QEMUFile *f)
+{
+    QemuFileInputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->file = f;
+
+    v->visitor.start_struct = qemu_file_input_start_struct;
+    v->visitor.end_struct = qemu_file_input_end_struct;
+    v->visitor.start_list = qemu_file_input_start_list;
+    v->visitor.next_list = qemu_file_input_next_list;
+    v->visitor.end_list = qemu_file_input_end_list;
+    v->visitor.start_array = qemu_file_input_start_array;
+    v->visitor.next_array = qemu_file_input_next_array;
+    v->visitor.end_array = qemu_file_input_end_array;
+    v->visitor.type_enum = qemu_file_input_type_enum;
+    v->visitor.type_int = qemu_file_input_type_int64_t;
+    v->visitor.type_uint8_t = qemu_file_input_type_uint8_t;
+    v->visitor.type_uint16_t = qemu_file_input_type_uint16_t;
+    v->visitor.type_uint32_t = qemu_file_input_type_uint32_t;
+    v->visitor.type_uint64_t = qemu_file_input_type_uint64_t;
+    v->visitor.type_int8_t = qemu_file_input_type_int8_t;
+    v->visitor.type_int16_t = qemu_file_input_type_int16_t;
+    v->visitor.type_int32_t = qemu_file_input_type_int32_t;
+    v->visitor.type_int64_t = qemu_file_input_type_int64_t;
+    v->visitor.type_bool = qemu_file_input_type_bool;
+    v->visitor.type_str = qemu_file_input_type_str;
+    v->visitor.type_number = qemu_file_input_type_number;
+
+    QTAILQ_INIT(&v->stack);
+
+    return v;
+}
diff --git a/qapi/qemu-file-input-visitor.h b/qapi/qemu-file-input-visitor.h
new file mode 100644
index 0000000..67b5554
--- /dev/null
+++ b/qapi/qemu-file-input-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_INPUT_VISITOR_H
+#define QEMU_FILE_INPUT_VISITOR_H
+
+#include "qapi-visit-core.h"
+
+typedef struct QemuFileInputVisitor QemuFileInputVisitor;
+
+QemuFileInputVisitor *qemu_file_input_visitor_new(QEMUFile *f);
+void qemu_file_input_visitor_cleanup(QemuFileInputVisitor *d);
+
+Visitor *qemu_file_input_get_visitor(QemuFileInputVisitor *v);
+
+#endif
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [Qemu-devel] [RFC 4/8] savevm: move QEMUFile interfaces into qemu-file.c
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
                   ` (2 preceding siblings ...)
  2011-09-19 14:41 ` [Qemu-devel] [RFC 3/8] qapi: add QemuFileInputVisitor Michael Roth
@ 2011-09-19 14:41 ` Michael Roth
  2011-09-24  7:23   ` Blue Swirl
  2011-09-19 14:41 ` [Qemu-devel] [RFC 5/8] qapi: test cases for QEMUFile input/output visitors Michael Roth
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 50+ messages in thread
From: Michael Roth @ 2011-09-19 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, mdroth


Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 Makefile.objs |    2 +-
 hw/hw.h       |    1 +
 qemu-file.c   |  521 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 savevm.c      |  494 ------------------------------------------------------
 4 files changed, 523 insertions(+), 495 deletions(-)
 create mode 100644 qemu-file.c

diff --git a/Makefile.objs b/Makefile.objs
index 6bc8555..34bf58f 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -119,7 +119,7 @@ common-obj-$(CONFIG_SD) += sd.o
 common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
 common-obj-y += bt-hci-csr.o
 common-obj-y += buffered_file.o migration.o migration-tcp.o
-common-obj-y += qemu-char.o savevm.o #aio.o
+common-obj-y += qemu-char.o savevm.o qemu-file.o #aio.o
 common-obj-y += msmouse.o ps2.o
 common-obj-y += qdev.o qdev-properties.o
 common-obj-y += block-migration.o iohandler.o
diff --git a/hw/hw.h b/hw/hw.h
index a124da9..244bfb9 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -58,6 +58,7 @@ void qemu_fflush(QEMUFile *f);
 int qemu_fclose(QEMUFile *f);
 void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
 void qemu_put_byte(QEMUFile *f, int v);
+int qemu_peek_byte(QEMUFile *f);
 
 static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
 {
diff --git a/qemu-file.c b/qemu-file.c
new file mode 100644
index 0000000..fb91b91
--- /dev/null
+++ b/qemu-file.c
@@ -0,0 +1,521 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include "hw/hw.h"
+
+#define IO_BUF_SIZE 32768
+
+struct QEMUFile {
+    QEMUFilePutBufferFunc *put_buffer;
+    QEMUFileGetBufferFunc *get_buffer;
+    QEMUFileCloseFunc *close;
+    QEMUFileRateLimit *rate_limit;
+    QEMUFileSetRateLimit *set_rate_limit;
+    QEMUFileGetRateLimit *get_rate_limit;
+    void *opaque;
+    int is_write;
+
+    int64_t buf_offset; /* start of buffer when writing, end of buffer
+                           when reading */
+    int buf_index;
+    int buf_size; /* 0 when writing */
+    uint8_t buf[IO_BUF_SIZE];
+
+    int has_error;
+};
+
+QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
+                         QEMUFileGetBufferFunc *get_buffer,
+                         QEMUFileCloseFunc *close,
+                         QEMUFileRateLimit *rate_limit,
+                         QEMUFileSetRateLimit *set_rate_limit,
+                         QEMUFileGetRateLimit *get_rate_limit)
+{
+    QEMUFile *f;
+
+    f = g_malloc0(sizeof(QEMUFile));
+
+    f->opaque = opaque;
+    f->put_buffer = put_buffer;
+    f->get_buffer = get_buffer;
+    f->close = close;
+    f->rate_limit = rate_limit;
+    f->set_rate_limit = set_rate_limit;
+    f->get_rate_limit = get_rate_limit;
+    f->is_write = 0;
+
+    return f;
+}
+
+int qemu_file_has_error(QEMUFile *f)
+{
+    return f->has_error;
+}
+
+void qemu_file_set_error(QEMUFile *f)
+{
+    f->has_error = 1;
+}
+
+void qemu_fflush(QEMUFile *f)
+{
+    if (!f->put_buffer)
+        return;
+
+    if (f->is_write && f->buf_index > 0) {
+        int len;
+
+        len = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
+        if (len > 0)
+            f->buf_offset += f->buf_index;
+        else
+            f->has_error = 1;
+        f->buf_index = 0;
+    }
+}
+
+static void qemu_fill_buffer(QEMUFile *f)
+{
+    int len;
+
+    if (!f->get_buffer)
+        return;
+
+    if (f->is_write)
+        abort();
+
+    len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
+    if (len > 0) {
+        f->buf_index = 0;
+        f->buf_size = len;
+        f->buf_offset += len;
+    } else if (len != -EAGAIN)
+        f->has_error = 1;
+}
+
+int qemu_fclose(QEMUFile *f)
+{
+    int ret = 0;
+    qemu_fflush(f);
+    if (f->close)
+        ret = f->close(f->opaque);
+    g_free(f);
+    return ret;
+}
+
+void qemu_file_put_notify(QEMUFile *f)
+{
+    f->put_buffer(f->opaque, NULL, 0, 0);
+}
+
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+{
+    int l;
+
+    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+        fprintf(stderr,
+                "Attempted to write to buffer while read buffer is not empty\n");
+        abort();
+    }
+
+    while (!f->has_error && size > 0) {
+        l = IO_BUF_SIZE - f->buf_index;
+        if (l > size)
+            l = size;
+        memcpy(f->buf + f->buf_index, buf, l);
+        f->is_write = 1;
+        f->buf_index += l;
+        buf += l;
+        size -= l;
+        if (f->buf_index >= IO_BUF_SIZE)
+            qemu_fflush(f);
+    }
+}
+
+void qemu_put_byte(QEMUFile *f, int v)
+{
+    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+        fprintf(stderr,
+                "Attempted to write to buffer while read buffer is not empty\n");
+        abort();
+    }
+
+    f->buf[f->buf_index++] = v;
+    f->is_write = 1;
+    if (f->buf_index >= IO_BUF_SIZE)
+        qemu_fflush(f);
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
+{
+    int size, l;
+
+    if (f->is_write)
+        abort();
+
+    size = size1;
+    while (size > 0) {
+        l = f->buf_size - f->buf_index;
+        if (l == 0) {
+            qemu_fill_buffer(f);
+            l = f->buf_size - f->buf_index;
+            if (l == 0)
+                break;
+        }
+        if (l > size)
+            l = size;
+        memcpy(buf, f->buf + f->buf_index, l);
+        f->buf_index += l;
+        buf += l;
+        size -= l;
+    }
+    return size1 - size;
+}
+
+int qemu_get_byte(QEMUFile *f)
+{
+    if (f->is_write)
+        abort();
+
+    if (f->buf_index >= f->buf_size) {
+        qemu_fill_buffer(f);
+        if (f->buf_index >= f->buf_size)
+            return 0;
+    }
+    return f->buf[f->buf_index++];
+}
+
+int64_t qemu_ftell(QEMUFile *f)
+{
+    return f->buf_offset - f->buf_size + f->buf_index;
+}
+
+int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
+{
+    if (whence == SEEK_SET) {
+        /* nothing to do */
+    } else if (whence == SEEK_CUR) {
+        pos += qemu_ftell(f);
+    } else {
+        /* SEEK_END not supported */
+        return -1;
+    }
+    if (f->put_buffer) {
+        qemu_fflush(f);
+        f->buf_offset = pos;
+    } else {
+        f->buf_offset = pos;
+        f->buf_index = 0;
+        f->buf_size = 0;
+    }
+    return pos;
+}
+
+int qemu_peek_byte(QEMUFile *f)
+{
+    if (f->is_write)
+        abort();
+
+    if (f->buf_index >= f->buf_size) {
+        qemu_fill_buffer(f);
+        if (f->buf_index >= f->buf_size)
+            return 0;
+    }
+    return f->buf[f->buf_index];
+}
+
+int qemu_file_rate_limit(QEMUFile *f)
+{
+    if (f->rate_limit)
+        return f->rate_limit(f->opaque);
+
+    return 0;
+}
+
+int64_t qemu_file_get_rate_limit(QEMUFile *f)
+{
+    if (f->get_rate_limit)
+        return f->get_rate_limit(f->opaque);
+
+    return 0;
+}
+
+int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate)
+{
+    /* any failed or completed migration keeps its state to allow probing of
+     * migration data, but has no associated file anymore */
+    if (f && f->set_rate_limit)
+        return f->set_rate_limit(f->opaque, new_rate);
+
+    return 0;
+}
+
+void qemu_put_be16(QEMUFile *f, unsigned int v)
+{
+    qemu_put_byte(f, v >> 8);
+    qemu_put_byte(f, v);
+}
+
+void qemu_put_be32(QEMUFile *f, unsigned int v)
+{
+    qemu_put_byte(f, v >> 24);
+    qemu_put_byte(f, v >> 16);
+    qemu_put_byte(f, v >> 8);
+    qemu_put_byte(f, v);
+}
+
+void qemu_put_be64(QEMUFile *f, uint64_t v)
+{
+    qemu_put_be32(f, v >> 32);
+    qemu_put_be32(f, v);
+}
+
+unsigned int qemu_get_be16(QEMUFile *f)
+{
+    unsigned int v;
+    v = qemu_get_byte(f) << 8;
+    v |= qemu_get_byte(f);
+    return v;
+}
+
+unsigned int qemu_get_be32(QEMUFile *f)
+{
+    unsigned int v;
+    v = qemu_get_byte(f) << 24;
+    v |= qemu_get_byte(f) << 16;
+    v |= qemu_get_byte(f) << 8;
+    v |= qemu_get_byte(f);
+    return v;
+}
+
+uint64_t qemu_get_be64(QEMUFile *f)
+{
+    uint64_t v;
+    v = (uint64_t)qemu_get_be32(f) << 32;
+    v |= qemu_get_be32(f);
+    return v;
+}
+
+typedef struct QEMUFileStdio
+{
+    FILE *stdio_file;
+    QEMUFile *file;
+} QEMUFileStdio;
+
+typedef struct QEMUFileSocket
+{
+    int fd;
+    QEMUFile *file;
+} QEMUFileSocket;
+
+static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFileSocket *s = opaque;
+    ssize_t len;
+
+    do {
+        len = qemu_recv(s->fd, buf, size, 0);
+    } while (len == -1 && socket_error() == EINTR);
+
+    if (len == -1)
+        len = -socket_error();
+
+    return len;
+}
+
+static int socket_close(void *opaque)
+{
+    QEMUFileSocket *s = opaque;
+    g_free(s);
+    return 0;
+}
+
+int qemu_stdio_fd(QEMUFile *f)
+{
+    QEMUFileStdio *p;
+    int fd;
+
+    p = (QEMUFileStdio *)f->opaque;
+    fd = fileno(p->stdio_file);
+
+    return fd;
+}
+
+static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFileStdio *s = opaque;
+    return fwrite(buf, 1, size, s->stdio_file);
+}
+
+static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFileStdio *s = opaque;
+    FILE *fp = s->stdio_file;
+    int bytes;
+
+    do {
+        clearerr(fp);
+        bytes = fread(buf, 1, size, fp);
+    } while ((bytes == 0) && ferror(fp) && (errno == EINTR));
+    return bytes;
+}
+
+static int stdio_pclose(void *opaque)
+{
+    QEMUFileStdio *s = opaque;
+    int ret;
+    ret = pclose(s->stdio_file);
+    g_free(s);
+    return ret;
+}
+
+static int stdio_fclose(void *opaque)
+{
+    QEMUFileStdio *s = opaque;
+    fclose(s->stdio_file);
+    g_free(s);
+    return 0;
+}
+
+QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
+{
+    QEMUFileStdio *s;
+
+    if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+        fprintf(stderr, "qemu_popen: Argument validity check failed\n");
+        return NULL;
+    }
+
+    s = g_malloc0(sizeof(QEMUFileStdio));
+
+    s->stdio_file = stdio_file;
+
+    if(mode[0] == 'r') {
+        s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_pclose, 
+				 NULL, NULL, NULL);
+    } else {
+        s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_pclose, 
+				 NULL, NULL, NULL);
+    }
+    return s->file;
+}
+
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
+{
+    FILE *popen_file;
+
+    popen_file = popen(command, mode);
+    if(popen_file == NULL) {
+        return NULL;
+    }
+
+    return qemu_popen(popen_file, mode);
+}
+
+QEMUFile *qemu_fdopen(int fd, const char *mode)
+{
+    QEMUFileStdio *s;
+
+    if (mode == NULL ||
+	(mode[0] != 'r' && mode[0] != 'w') ||
+	mode[1] != 'b' || mode[2] != 0) {
+        fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
+        return NULL;
+    }
+
+    s = g_malloc0(sizeof(QEMUFileStdio));
+    s->stdio_file = fdopen(fd, mode);
+    if (!s->stdio_file)
+        goto fail;
+
+    if(mode[0] == 'r') {
+        s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_fclose, 
+				 NULL, NULL, NULL);
+    } else {
+        s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_fclose, 
+				 NULL, NULL, NULL);
+    }
+    return s->file;
+
+fail:
+    g_free(s);
+    return NULL;
+}
+
+QEMUFile *qemu_fopen_socket(int fd)
+{
+    QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
+
+    s->fd = fd;
+    s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close, 
+			     NULL, NULL, NULL);
+    return s->file;
+}
+
+static int file_put_buffer(void *opaque, const uint8_t *buf,
+                            int64_t pos, int size)
+{
+    QEMUFileStdio *s = opaque;
+    fseek(s->stdio_file, pos, SEEK_SET);
+    return fwrite(buf, 1, size, s->stdio_file);
+}
+
+static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFileStdio *s = opaque;
+    fseek(s->stdio_file, pos, SEEK_SET);
+    return fread(buf, 1, size, s->stdio_file);
+}
+
+QEMUFile *qemu_fopen(const char *filename, const char *mode)
+{
+    QEMUFileStdio *s;
+
+    if (mode == NULL ||
+	(mode[0] != 'r' && mode[0] != 'w') ||
+	mode[1] != 'b' || mode[2] != 0) {
+        fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
+        return NULL;
+    }
+
+    s = g_malloc0(sizeof(QEMUFileStdio));
+
+    s->stdio_file = fopen(filename, mode);
+    if (!s->stdio_file)
+        goto fail;
+    
+    if(mode[0] == 'w') {
+        s->file = qemu_fopen_ops(s, file_put_buffer, NULL, stdio_fclose, 
+				 NULL, NULL, NULL);
+    } else {
+        s->file = qemu_fopen_ops(s, NULL, file_get_buffer, stdio_fclose, 
+			       NULL, NULL, NULL);
+    }
+    return s->file;
+fail:
+    g_free(s);
+    return NULL;
+}
diff --git a/savevm.c b/savevm.c
index 1feaa70..db1e0cd 100644
--- a/savevm.c
+++ b/savevm.c
@@ -155,228 +155,6 @@ void qemu_announce_self(void)
 /***********************************************************/
 /* savevm/loadvm support */
 
-#define IO_BUF_SIZE 32768
-
-struct QEMUFile {
-    QEMUFilePutBufferFunc *put_buffer;
-    QEMUFileGetBufferFunc *get_buffer;
-    QEMUFileCloseFunc *close;
-    QEMUFileRateLimit *rate_limit;
-    QEMUFileSetRateLimit *set_rate_limit;
-    QEMUFileGetRateLimit *get_rate_limit;
-    void *opaque;
-    int is_write;
-
-    int64_t buf_offset; /* start of buffer when writing, end of buffer
-                           when reading */
-    int buf_index;
-    int buf_size; /* 0 when writing */
-    uint8_t buf[IO_BUF_SIZE];
-
-    int has_error;
-};
-
-typedef struct QEMUFileStdio
-{
-    FILE *stdio_file;
-    QEMUFile *file;
-} QEMUFileStdio;
-
-typedef struct QEMUFileSocket
-{
-    int fd;
-    QEMUFile *file;
-} QEMUFileSocket;
-
-static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileSocket *s = opaque;
-    ssize_t len;
-
-    do {
-        len = qemu_recv(s->fd, buf, size, 0);
-    } while (len == -1 && socket_error() == EINTR);
-
-    if (len == -1)
-        len = -socket_error();
-
-    return len;
-}
-
-static int socket_close(void *opaque)
-{
-    QEMUFileSocket *s = opaque;
-    g_free(s);
-    return 0;
-}
-
-static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileStdio *s = opaque;
-    return fwrite(buf, 1, size, s->stdio_file);
-}
-
-static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileStdio *s = opaque;
-    FILE *fp = s->stdio_file;
-    int bytes;
-
-    do {
-        clearerr(fp);
-        bytes = fread(buf, 1, size, fp);
-    } while ((bytes == 0) && ferror(fp) && (errno == EINTR));
-    return bytes;
-}
-
-static int stdio_pclose(void *opaque)
-{
-    QEMUFileStdio *s = opaque;
-    int ret;
-    ret = pclose(s->stdio_file);
-    g_free(s);
-    return ret;
-}
-
-static int stdio_fclose(void *opaque)
-{
-    QEMUFileStdio *s = opaque;
-    fclose(s->stdio_file);
-    g_free(s);
-    return 0;
-}
-
-QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
-{
-    QEMUFileStdio *s;
-
-    if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
-        fprintf(stderr, "qemu_popen: Argument validity check failed\n");
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileStdio));
-
-    s->stdio_file = stdio_file;
-
-    if(mode[0] == 'r') {
-        s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_pclose, 
-				 NULL, NULL, NULL);
-    } else {
-        s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_pclose, 
-				 NULL, NULL, NULL);
-    }
-    return s->file;
-}
-
-QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
-{
-    FILE *popen_file;
-
-    popen_file = popen(command, mode);
-    if(popen_file == NULL) {
-        return NULL;
-    }
-
-    return qemu_popen(popen_file, mode);
-}
-
-int qemu_stdio_fd(QEMUFile *f)
-{
-    QEMUFileStdio *p;
-    int fd;
-
-    p = (QEMUFileStdio *)f->opaque;
-    fd = fileno(p->stdio_file);
-
-    return fd;
-}
-
-QEMUFile *qemu_fdopen(int fd, const char *mode)
-{
-    QEMUFileStdio *s;
-
-    if (mode == NULL ||
-	(mode[0] != 'r' && mode[0] != 'w') ||
-	mode[1] != 'b' || mode[2] != 0) {
-        fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileStdio));
-    s->stdio_file = fdopen(fd, mode);
-    if (!s->stdio_file)
-        goto fail;
-
-    if(mode[0] == 'r') {
-        s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_fclose, 
-				 NULL, NULL, NULL);
-    } else {
-        s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_fclose, 
-				 NULL, NULL, NULL);
-    }
-    return s->file;
-
-fail:
-    g_free(s);
-    return NULL;
-}
-
-QEMUFile *qemu_fopen_socket(int fd)
-{
-    QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
-
-    s->fd = fd;
-    s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close, 
-			     NULL, NULL, NULL);
-    return s->file;
-}
-
-static int file_put_buffer(void *opaque, const uint8_t *buf,
-                            int64_t pos, int size)
-{
-    QEMUFileStdio *s = opaque;
-    fseek(s->stdio_file, pos, SEEK_SET);
-    return fwrite(buf, 1, size, s->stdio_file);
-}
-
-static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileStdio *s = opaque;
-    fseek(s->stdio_file, pos, SEEK_SET);
-    return fread(buf, 1, size, s->stdio_file);
-}
-
-QEMUFile *qemu_fopen(const char *filename, const char *mode)
-{
-    QEMUFileStdio *s;
-
-    if (mode == NULL ||
-	(mode[0] != 'r' && mode[0] != 'w') ||
-	mode[1] != 'b' || mode[2] != 0) {
-        fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileStdio));
-
-    s->stdio_file = fopen(filename, mode);
-    if (!s->stdio_file)
-        goto fail;
-    
-    if(mode[0] == 'w') {
-        s->file = qemu_fopen_ops(s, file_put_buffer, NULL, stdio_fclose, 
-				 NULL, NULL, NULL);
-    } else {
-        s->file = qemu_fopen_ops(s, NULL, file_get_buffer, stdio_fclose, 
-			       NULL, NULL, NULL);
-    }
-    return s->file;
-fail:
-    g_free(s);
-    return NULL;
-}
-
 static int block_put_buffer(void *opaque, const uint8_t *buf,
                            int64_t pos, int size)
 {
@@ -402,278 +180,6 @@ static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
     return qemu_fopen_ops(bs, NULL, block_get_buffer, bdrv_fclose, NULL, NULL, NULL);
 }
 
-QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
-                         QEMUFileGetBufferFunc *get_buffer,
-                         QEMUFileCloseFunc *close,
-                         QEMUFileRateLimit *rate_limit,
-                         QEMUFileSetRateLimit *set_rate_limit,
-                         QEMUFileGetRateLimit *get_rate_limit)
-{
-    QEMUFile *f;
-
-    f = g_malloc0(sizeof(QEMUFile));
-
-    f->opaque = opaque;
-    f->put_buffer = put_buffer;
-    f->get_buffer = get_buffer;
-    f->close = close;
-    f->rate_limit = rate_limit;
-    f->set_rate_limit = set_rate_limit;
-    f->get_rate_limit = get_rate_limit;
-    f->is_write = 0;
-
-    return f;
-}
-
-int qemu_file_has_error(QEMUFile *f)
-{
-    return f->has_error;
-}
-
-void qemu_file_set_error(QEMUFile *f)
-{
-    f->has_error = 1;
-}
-
-void qemu_fflush(QEMUFile *f)
-{
-    if (!f->put_buffer)
-        return;
-
-    if (f->is_write && f->buf_index > 0) {
-        int len;
-
-        len = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
-        if (len > 0)
-            f->buf_offset += f->buf_index;
-        else
-            f->has_error = 1;
-        f->buf_index = 0;
-    }
-}
-
-static void qemu_fill_buffer(QEMUFile *f)
-{
-    int len;
-
-    if (!f->get_buffer)
-        return;
-
-    if (f->is_write)
-        abort();
-
-    len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
-    if (len > 0) {
-        f->buf_index = 0;
-        f->buf_size = len;
-        f->buf_offset += len;
-    } else if (len != -EAGAIN)
-        f->has_error = 1;
-}
-
-int qemu_fclose(QEMUFile *f)
-{
-    int ret = 0;
-    qemu_fflush(f);
-    if (f->close)
-        ret = f->close(f->opaque);
-    g_free(f);
-    return ret;
-}
-
-void qemu_file_put_notify(QEMUFile *f)
-{
-    f->put_buffer(f->opaque, NULL, 0, 0);
-}
-
-void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
-{
-    int l;
-
-    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
-        fprintf(stderr,
-                "Attempted to write to buffer while read buffer is not empty\n");
-        abort();
-    }
-
-    while (!f->has_error && size > 0) {
-        l = IO_BUF_SIZE - f->buf_index;
-        if (l > size)
-            l = size;
-        memcpy(f->buf + f->buf_index, buf, l);
-        f->is_write = 1;
-        f->buf_index += l;
-        buf += l;
-        size -= l;
-        if (f->buf_index >= IO_BUF_SIZE)
-            qemu_fflush(f);
-    }
-}
-
-void qemu_put_byte(QEMUFile *f, int v)
-{
-    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
-        fprintf(stderr,
-                "Attempted to write to buffer while read buffer is not empty\n");
-        abort();
-    }
-
-    f->buf[f->buf_index++] = v;
-    f->is_write = 1;
-    if (f->buf_index >= IO_BUF_SIZE)
-        qemu_fflush(f);
-}
-
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
-{
-    int size, l;
-
-    if (f->is_write)
-        abort();
-
-    size = size1;
-    while (size > 0) {
-        l = f->buf_size - f->buf_index;
-        if (l == 0) {
-            qemu_fill_buffer(f);
-            l = f->buf_size - f->buf_index;
-            if (l == 0)
-                break;
-        }
-        if (l > size)
-            l = size;
-        memcpy(buf, f->buf + f->buf_index, l);
-        f->buf_index += l;
-        buf += l;
-        size -= l;
-    }
-    return size1 - size;
-}
-
-static int qemu_peek_byte(QEMUFile *f)
-{
-    if (f->is_write)
-        abort();
-
-    if (f->buf_index >= f->buf_size) {
-        qemu_fill_buffer(f);
-        if (f->buf_index >= f->buf_size)
-            return 0;
-    }
-    return f->buf[f->buf_index];
-}
-
-int qemu_get_byte(QEMUFile *f)
-{
-    if (f->is_write)
-        abort();
-
-    if (f->buf_index >= f->buf_size) {
-        qemu_fill_buffer(f);
-        if (f->buf_index >= f->buf_size)
-            return 0;
-    }
-    return f->buf[f->buf_index++];
-}
-
-int64_t qemu_ftell(QEMUFile *f)
-{
-    return f->buf_offset - f->buf_size + f->buf_index;
-}
-
-int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
-{
-    if (whence == SEEK_SET) {
-        /* nothing to do */
-    } else if (whence == SEEK_CUR) {
-        pos += qemu_ftell(f);
-    } else {
-        /* SEEK_END not supported */
-        return -1;
-    }
-    if (f->put_buffer) {
-        qemu_fflush(f);
-        f->buf_offset = pos;
-    } else {
-        f->buf_offset = pos;
-        f->buf_index = 0;
-        f->buf_size = 0;
-    }
-    return pos;
-}
-
-int qemu_file_rate_limit(QEMUFile *f)
-{
-    if (f->rate_limit)
-        return f->rate_limit(f->opaque);
-
-    return 0;
-}
-
-int64_t qemu_file_get_rate_limit(QEMUFile *f)
-{
-    if (f->get_rate_limit)
-        return f->get_rate_limit(f->opaque);
-
-    return 0;
-}
-
-int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate)
-{
-    /* any failed or completed migration keeps its state to allow probing of
-     * migration data, but has no associated file anymore */
-    if (f && f->set_rate_limit)
-        return f->set_rate_limit(f->opaque, new_rate);
-
-    return 0;
-}
-
-void qemu_put_be16(QEMUFile *f, unsigned int v)
-{
-    qemu_put_byte(f, v >> 8);
-    qemu_put_byte(f, v);
-}
-
-void qemu_put_be32(QEMUFile *f, unsigned int v)
-{
-    qemu_put_byte(f, v >> 24);
-    qemu_put_byte(f, v >> 16);
-    qemu_put_byte(f, v >> 8);
-    qemu_put_byte(f, v);
-}
-
-void qemu_put_be64(QEMUFile *f, uint64_t v)
-{
-    qemu_put_be32(f, v >> 32);
-    qemu_put_be32(f, v);
-}
-
-unsigned int qemu_get_be16(QEMUFile *f)
-{
-    unsigned int v;
-    v = qemu_get_byte(f) << 8;
-    v |= qemu_get_byte(f);
-    return v;
-}
-
-unsigned int qemu_get_be32(QEMUFile *f)
-{
-    unsigned int v;
-    v = qemu_get_byte(f) << 24;
-    v |= qemu_get_byte(f) << 16;
-    v |= qemu_get_byte(f) << 8;
-    v |= qemu_get_byte(f);
-    return v;
-}
-
-uint64_t qemu_get_be64(QEMUFile *f)
-{
-    uint64_t v;
-    v = (uint64_t)qemu_get_be32(f) << 32;
-    v |= qemu_get_be32(f);
-    return v;
-}
-
 /* bool */
 
 static int get_bool(QEMUFile *f, void *pv, size_t size)
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [Qemu-devel] [RFC 5/8] qapi: test cases for QEMUFile input/output visitors
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
                   ` (3 preceding siblings ...)
  2011-09-19 14:41 ` [Qemu-devel] [RFC 4/8] savevm: move QEMUFile interfaces into qemu-file.c Michael Roth
@ 2011-09-19 14:41 ` Michael Roth
  2011-09-19 14:41 ` [Qemu-devel] [RFC 6/8] savevm: add QEMUFile->visitor lookup routines Michael Roth
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 50+ messages in thread
From: Michael Roth @ 2011-09-19 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, mdroth


Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 Makefile       |    4 +-
 test-visitor.c |  404 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 406 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index 7e9382f..2b68e39 100644
--- a/Makefile
+++ b/Makefile
@@ -155,7 +155,7 @@ check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qj
 
 CHECK_PROG_DEPS = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o
 
-check-qint: check-qint.o qint.o $(CHECK_PROG_DEPS)
+CHECK-QINt: check-qint.o qint.o $(CHECK_PROG_DEPS)
 check-qstring: check-qstring.o qstring.o $(CHECK_PROG_DEPS)
 check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(CHECK_PROG_DEPS)
 check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
@@ -188,7 +188,7 @@ $(qapi-dir)/qga-qmp-marshal.c: $(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/sc
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, "  GEN   $@")
 
 test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o qapi/qemu-file-input-visitor.o qapi/qemu-file-output-visitor.o qemu-file.o
 
 test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
 test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
diff --git a/test-visitor.c b/test-visitor.c
index b7717de..1e8fd0c 100644
--- a/test-visitor.c
+++ b/test-visitor.c
@@ -4,6 +4,10 @@
 #include "test-qapi-types.h"
 #include "test-qapi-visit.h"
 #include "qemu-objects.h"
+#include "qapi/qemu-file-output-visitor.h"
+#include "qapi/qemu-file-input-visitor.h"
+#include "hw/hw.h"
+#include "qemu-common.h"
 
 typedef struct TestStruct
 {
@@ -291,6 +295,404 @@ static void test_nested_enums(void)
     qapi_free_NestedEnumsOne(nested_enums_cpy);
 }
 
+#define TEST_QEMU_FILE_PATH "/tmp/test_qemu_file_visitors"
+
+typedef struct QEMUFileValue {
+    union {
+        bool boolean;
+        double number;
+        uint8_t u8;
+        uint16_t u16;
+        uint32_t u32;
+        uint64_t u64;
+        int8_t s8;
+        int16_t s16;
+        int32_t s32;
+        int64_t s64;
+        uintmax_t umax;
+    } value;
+    union {
+        uint8_t u8[32];
+        uint16_t u16[32];
+        uint32_t u32[32];
+        uint64_t u64[32];
+        int8_t s8[32];
+        int16_t s16[32];
+        int32_t s32[32];
+        int64_t s64[32];
+        uintmax_t umax[32];
+    } array;
+    size_t array_len;
+    enum {
+        QFV_BOOL = 0,
+        QFV_NUMBER,
+        QFV_U8,
+        QFV_U16,
+        QFV_U32,
+        QFV_U64,
+        QFV_S8,
+        QFV_S16,
+        QFV_S32,
+        QFV_S64,
+        QFV_U8_ARRAY,
+        QFV_EOL,
+    } type;
+} QEMUFileValue;
+
+QEMUFileValue qfvalues[] = {
+    { .value.boolean = true, .type = QFV_BOOL },
+    { .value.boolean = false, .type = QFV_BOOL },
+    { .value.number = 3.14159265, .type = QFV_NUMBER },
+    { .value.u8 = 0, .type = QFV_U8 },
+    { .value.u8 = 1, .type = QFV_U8 },
+    { .value.u8 = 128, .type = QFV_U8 },
+    { .value.u8 = 255u, .type = QFV_U8 },
+    { .value.u16 = 0, .type = QFV_U16 },
+    { .value.u16 = 1, .type = QFV_U16 },
+    { .value.u16 = 32768, .type = QFV_U16 },
+    { .value.u16 = 65535u, .type = QFV_U16 },
+    { .value.u32 = 0, .type = QFV_U32 },
+    { .value.u32 = 1, .type = QFV_U32 },
+    { .value.u32 = 2147483648, .type = QFV_U32 },
+    { .value.u32 = 4294967295u, .type = QFV_U32 },
+    { .value.u64 = 0, .type = QFV_U64 },
+    { .value.u64 = 1, .type = QFV_U64 },
+    { .value.u64 = 9223372036854775808u, .type = QFV_U64 },
+    { .value.u64 = 18446744073709551615u, .type = QFV_U64 },
+    { .value.s8 = 0, .type = QFV_S8 },
+    { .value.s8 = 1, .type = QFV_S8 },
+    { .value.s8 = 128, .type = QFV_S8 },
+    { .value.s8 = -1, .type = QFV_S8 },
+    { .value.s16 = 0, .type = QFV_S16 },
+    { .value.s16 = 1, .type = QFV_S16 },
+    { .value.s16 = 32768, .type = QFV_S16 },
+    { .value.s16 = -1, .type = QFV_S16 },
+    { .value.s32 = 0, .type = QFV_S32 },
+    { .value.s32 = 1, .type = QFV_S32 },
+    { .value.s32 = 2147483648, .type = QFV_S32 },
+    { .value.s32 = -1, .type = QFV_S32 },
+    { .value.s64 = 0, .type = QFV_S64 },
+    { .value.s64 = 1, .type = QFV_S64 },
+    { .value.s64 = 9223372036854775808u, .type = QFV_S64 },
+    { .value.s64 = -1, .type = QFV_S64 },
+    { .array.u8 = { }, .array_len = 0, .type = QFV_U8_ARRAY },
+    { .array.u8 = { 1, 2, 3, 4 }, .array_len = 4, .type = QFV_U8_ARRAY },
+    { .type = QFV_EOL }
+};
+
+static void qfv_process(QEMUFileValue *qfv, bool visitor, bool write,
+                        void *opaque)
+{
+    int i;
+    void *ptr;
+
+    switch (qfv->type) {
+    case QFV_BOOL:
+        if (visitor) {
+            visit_type_bool(opaque, &qfv->value.boolean, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_byte(opaque, qfv->value.boolean);
+            } else {
+                qfv->value.boolean = qemu_get_byte(opaque);
+            }
+        }
+        break;
+    case QFV_NUMBER:
+        if (visitor) {
+            visit_type_number(opaque, &qfv->value.number, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_be64s(opaque, (uint64_t *)&qfv->value.number);
+            } else {
+                qemu_get_be64s(opaque, (uint64_t *)&qfv->value.number);
+            }
+        }
+        break;
+    case QFV_U8:
+        if (visitor) {
+            visit_type_uint8_t(opaque, &qfv->value.u8, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_byte(opaque, qfv->value.u8);
+            } else {
+                qfv->value.u8 = qemu_get_byte(opaque);
+            }
+        }
+        break;
+    case QFV_U16:
+        if (visitor) {
+            visit_type_uint16_t(opaque, &qfv->value.u16, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_be16(opaque, qfv->value.u16);
+            } else {
+                qfv->value.u16 = qemu_get_be16(opaque);
+            }
+        }
+        break;
+    case QFV_U32:
+        if (visitor) {
+            visit_type_uint32_t(opaque, &qfv->value.u32, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_be32(opaque, qfv->value.u32);
+            } else {
+                qfv->value.u32 = qemu_get_be32(opaque);
+            }
+        }
+        break;
+    case QFV_U64:
+        if (visitor) {
+            visit_type_uint64_t(opaque, &qfv->value.u64, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_be64(opaque, qfv->value.u64);
+            } else {
+                qfv->value.u64 = qemu_get_be64(opaque);
+            }
+        }
+        break;
+    case QFV_S8:
+        if (visitor) {
+            visit_type_int8_t(opaque, &qfv->value.s8, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_byte(opaque, qfv->value.s8);
+            } else {
+                qfv->value.s8 = qemu_get_byte(opaque);
+            }
+        }
+        break;
+    case QFV_S16:
+        if (visitor) {
+            visit_type_int16_t(opaque, &qfv->value.s16, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_be16(opaque, qfv->value.s16);
+            } else {
+                qfv->value.s16 = qemu_get_be16(opaque);
+            }
+        }
+        break;
+    case QFV_S32:
+        if (visitor) {
+            visit_type_int32_t(opaque, &qfv->value.s32, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_be32(opaque, qfv->value.s32);
+            } else {
+                qfv->value.s32 = qemu_get_be32(opaque);
+            }
+        }
+        break;
+    case QFV_S64:
+        if (visitor) {
+            visit_type_int64_t(opaque, &qfv->value.s64, NULL, NULL);
+        } else {
+            if (write) {
+                qemu_put_be64(opaque, qfv->value.s64);
+            } else {
+                qfv->value.s64 = qemu_get_be64(opaque);
+            }
+        }
+        break;
+    case QFV_U8_ARRAY:
+        if (visitor) {
+            ptr = qfv->array.u8;
+            visit_start_array(opaque, (void **)&ptr, NULL,
+                              qfv->array_len, sizeof(uint8_t), NULL);
+            for (i = 0; i < qfv->array_len; i++, visit_next_array(opaque, NULL)) {
+                visit_type_uint8_t(opaque, &((uint8_t *)ptr)[i], NULL, NULL);
+            }
+            visit_end_array(opaque, NULL);
+        } else {
+            for (i = 0; i < qfv->array_len; i++) {
+                if (write) {
+                    qemu_put_byte(opaque, qfv->array.u8[i]);
+                } else {
+                    qfv->array.u8[i] = qemu_get_byte(opaque);
+                }
+            }
+        }
+        break;
+    default:
+        return;
+    }
+}
+
+static void qfv_visitor_write(QEMUFileValue *qfv, Visitor *v)
+{
+    qfv_process(qfv, true, true, v);
+}
+
+static void qfv_visitor_read(QEMUFileValue *qfv, Visitor *v)
+{
+    qfv_process(qfv, true, false, v);
+}
+
+static void qfv_write(QEMUFileValue *qfv, QEMUFile *f)
+{
+    qfv_process(qfv, false, true, f);
+}
+
+static void qfv_read(QEMUFileValue *qfv, QEMUFile *f)
+{
+    qfv_process(qfv, false, false, f);
+}
+
+static void test_qemu_file_in_visitor(void)
+{
+    QEMUFile *f1, *f2;
+    QemuFileInputVisitor *qfi;
+    QemuFileOutputVisitor *qfo;
+    Visitor *v;
+    QEMUFileValue qfval1, qfval2;
+    int i, j;
+    TestStruct ts, *pts;
+    TestStructList *lts;
+
+    /* write our test scalars/arrays */
+    f1 = qemu_fopen(TEST_QEMU_FILE_PATH, "wb");
+    g_assert(f1);
+    qfo = qemu_file_output_visitor_new(f1);
+    v = qemu_file_output_get_visitor(qfo);
+    for (i = 0; qfvalues[i].type != QFV_EOL; i++) {
+        qfv_write(&qfvalues[i], f1);
+    }
+    /* write our test struct/list. qemu_put_* interfaces have
+     * no analogue for this and instead rely on byte arrays,
+     * so we'll write this using a visitor and simply test
+     * visitor input/output compatibility
+     */
+    /* write a simple struct */
+    ts.x = 42;
+    ts.y = 43;
+    pts = &ts;
+    visit_type_TestStruct(v, &pts, NULL, NULL);
+    /* throw in a linked list as well */
+    lts = g_malloc0(sizeof(*lts));
+    lts->value = g_malloc0(sizeof(TestStruct));
+    lts->value->x = 44;
+    lts->value->y = 45;
+    lts->next = g_malloc0(sizeof(*lts));
+    lts->next->value = g_malloc0(sizeof(TestStruct));
+    lts->next->value->x = 46;
+    lts->next->value->y = 47;
+    visit_type_TestStructList(v, &lts, NULL, NULL);
+    g_free(lts->next->value);
+    g_free(lts->next);
+    g_free(lts->value);
+    g_free(lts);
+
+    qemu_file_output_visitor_cleanup(qfo);
+    qemu_fclose(f1);
+
+    /* make sure qemu_get_be* and input visitor read same/correct input */
+    f1 = qemu_fopen(TEST_QEMU_FILE_PATH, "rb");
+    f2 = qemu_fopen(TEST_QEMU_FILE_PATH, "rb");
+    qfi = qemu_file_input_visitor_new(f2);
+    g_assert(qfi);
+    v = qemu_file_input_get_visitor(qfi);
+    g_assert(v);
+    for (i = 0; qfvalues[i].type != QFV_EOL; i++) {
+        qfval1.value.umax = qfval2.value.umax = 0;
+        memset(qfval1.array.umax, 0, sizeof(qfval1.array.umax));
+        memset(qfval2.array.umax, 0, sizeof(qfval2.array.umax));
+        qfval1.type = qfval2.type = qfvalues[i].type;
+        qfval1.array_len = qfval2.array_len = qfvalues[i].array_len;
+        qfv_read(&qfval1, f1);
+        qfv_visitor_read(&qfval2, v);
+        if (qfvalues[i].type >= QFV_U8_ARRAY) {
+            for (j = 0; j < qfvalues[i].array_len; j++) { 
+                g_assert(qfval1.array.u8[j] == qfval2.array.u8[j]);
+                g_assert(qfval2.array.u8[j] == qfvalues[i].array.u8[j]);
+            }
+        } else {
+            g_assert(qfval1.value.umax == qfval2.value.umax);
+            g_assert(qfval2.value.umax == qfvalues[i].value.umax);
+        }
+    }
+    qemu_file_input_visitor_cleanup(qfi);
+    qemu_fclose(f1);
+    qemu_fclose(f2);
+    unlink(TEST_QEMU_FILE_PATH);
+}
+
+static void test_qemu_file_out_visitor(void)
+{
+    QEMUFile *f;
+    QemuFileOutputVisitor *qfo;
+    Visitor *v;
+    QEMUFileValue qfval1;
+    int i, j;
+    TestStruct ts, *pts;
+    TestStructList *lts;
+
+    /* write test scalars/arrays using an output visitor */
+    f = qemu_fopen(TEST_QEMU_FILE_PATH, "wb");
+    g_assert(f);
+    qfo = qemu_file_output_visitor_new(f);
+    g_assert(qfo);
+    v = qemu_file_output_get_visitor(qfo);
+    g_assert(v);
+    for (i = 0; qfvalues[i].type != QFV_EOL; i++) {
+        qfv_visitor_write(&qfvalues[i], v);
+    }
+    /* write a simple struct */
+    ts.x = 42;
+    ts.y = 43;
+    pts = &ts;
+    visit_type_TestStruct(v, &pts, NULL, NULL);
+    /* throw in a linked list as well */
+    lts = g_malloc0(sizeof(*lts));
+    lts->value = g_malloc0(sizeof(TestStruct));
+    lts->value->x = 44;
+    lts->value->y = 45;
+    lts->next = g_malloc0(sizeof(*lts));
+    lts->next->value = g_malloc0(sizeof(TestStruct));
+    lts->next->value->x = 46;
+    lts->next->value->y = 47;
+    visit_type_TestStructList(v, &lts, NULL, NULL);
+    g_free(lts->next->value);
+    g_free(lts->next);
+    g_free(lts->value);
+    g_free(lts);
+
+    qemu_file_output_visitor_cleanup(qfo);
+    qemu_fclose(f);
+
+    /* make sure output visitor wrote the expected values */
+    f = qemu_fopen(TEST_QEMU_FILE_PATH, "rb");
+    g_assert(f);
+    for (i = 0; qfvalues[i].type != QFV_EOL; i++) {
+        qfval1.type = qfvalues[i].type;
+        qfval1.value.umax = 0;
+        memset(qfval1.array.umax, 0, sizeof(qfval1.array.umax));
+        qfval1.array_len = qfvalues[i].array_len;
+
+        qfv_read(&qfval1, f);
+        if (qfvalues[i].type >= QFV_U8_ARRAY) {
+            for (j = 0; j < qfvalues[i].array_len; j++) { 
+                g_assert(qfval1.array.u8[j] == qfvalues[i].array.u8[j]);
+            }
+        } else {
+            g_assert(qfval1.value.umax == qfvalues[i].value.umax);
+        }
+    }
+    /* test the struct */
+    g_assert(qemu_get_be64(f) == ts.x);
+    g_assert(qemu_get_be64(f) == ts.y);
+    /* test the linked list */
+    g_assert(qemu_get_be64(f) == 44);
+    g_assert(qemu_get_be64(f) == 45);
+    g_assert(qemu_get_be64(f) == 46);
+    g_assert(qemu_get_be64(f) == 47);
+
+    qemu_fclose(f);
+    unlink(TEST_QEMU_FILE_PATH);
+}
+
 int main(int argc, char **argv)
 {
     g_test_init(&argc, &argv, NULL);
@@ -299,6 +701,8 @@ int main(int argc, char **argv)
     g_test_add_func("/0.15/nested_structs", test_nested_structs);
     g_test_add_func("/0.15/enums", test_enums);
     g_test_add_func("/0.15/nested_enums", test_nested_enums);
+    g_test_add_func("/1.0/qemu_file_input_visitor", test_qemu_file_in_visitor);
+    g_test_add_func("/1.0/qemu_file_output_visitor", test_qemu_file_out_visitor);
 
     g_test_run();
 
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [Qemu-devel] [RFC 6/8] savevm: add QEMUFile->visitor lookup routines
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
                   ` (4 preceding siblings ...)
  2011-09-19 14:41 ` [Qemu-devel] [RFC 5/8] qapi: test cases for QEMUFile input/output visitors Michael Roth
@ 2011-09-19 14:41 ` Michael Roth
  2011-09-19 14:41 ` [Qemu-devel] [RFC 7/8] cutil: add strocat(), to concat a string to an offset in another Michael Roth
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 50+ messages in thread
From: Michael Roth @ 2011-09-19 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, mdroth


Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 hw/hw.h     |   19 +++++++++++++++++
 qemu-file.c |   64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 83 insertions(+), 0 deletions(-)

diff --git a/hw/hw.h b/hw/hw.h
index 244bfb9..e6d7c6a 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -10,6 +10,25 @@
 
 #include "ioport.h"
 #include "irq.h"
+#include "qemu-queue.h"
+#include "qapi/qapi-visit-core.h"
+#include "qapi/qemu-file-output-visitor.h"
+#include "qapi/qemu-file-input-visitor.h"
+
+/* QEMUFile->Visitor lookup routines to support legacy qemu_put_* calls */
+typedef struct QemuFileVisitorNode {
+    void *opaque; /* Visitor type */
+    QemuFileOutputVisitor *ov;
+    QemuFileInputVisitor *iv;
+    QEMUFile *f;
+    QTAILQ_ENTRY(QemuFileVisitorNode) entry;
+} QemuFileVisitorNode;
+
+Visitor *qemu_file_get_input_visitor(QEMUFile *f);
+Visitor *qemu_file_get_output_visitor(QEMUFile *f);
+QemuFileVisitorNode *qemu_file_get_visitor_node(QEMUFile *f);
+void qemu_file_put_visitor_node(QemuFileVisitorNode *n);
+void qemu_file_remove_visitor_node(QemuFileVisitorNode *n);
 
 /* VM Load/Save */
 
diff --git a/qemu-file.c b/qemu-file.c
index fb91b91..ec69627 100644
--- a/qemu-file.c
+++ b/qemu-file.c
@@ -25,6 +25,8 @@
 #include "qemu-common.h"
 #include "qemu_socket.h"
 #include "hw/hw.h"
+#include "qapi/qemu-file-output-visitor.h"
+#include "qapi/qemu-file-input-visitor.h"
 
 #define IO_BUF_SIZE 32768
 
@@ -47,6 +49,61 @@ struct QEMUFile {
     int has_error;
 };
 
+/* TODO: temporary mechanism to support existing function signatures by
+ * creating a 1-to-1 mapping between QEMUFile's and the actual Visitor type.
+ * In the case of QemuFileOutputVisitor/QemuFileInputVisitor, the QEMUFile
+ * arg corresponds to the handle used by the visitor for reads/writes. For
+ * other visitors, the QEMUFile will serve purely as a key.
+ *
+ * Once all interfaces are converted to using Visitor directly, we will
+ * remove this lookup logic and pass the Visitor to the registered save/load
+ * functions directly.
+ */
+static QTAILQ_HEAD(, QemuFileVisitorNode) qemu_file_visitors =
+    QTAILQ_HEAD_INITIALIZER(qemu_file_visitors);
+
+QemuFileVisitorNode *qemu_file_get_visitor_node(QEMUFile *f)
+{
+    QemuFileVisitorNode *node;
+    QTAILQ_FOREACH(node, &qemu_file_visitors, entry) {
+        if (node->f == f) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+Visitor *qemu_file_get_output_visitor(QEMUFile *f)
+{
+    QemuFileVisitorNode *node = qemu_file_get_visitor_node(f);
+
+    if (node && node->ov) {
+        return qemu_file_output_get_visitor(node->ov);
+    }
+    return NULL;
+}
+
+Visitor *qemu_file_get_input_visitor(QEMUFile *f)
+{
+    QemuFileVisitorNode *node = qemu_file_get_visitor_node(f);
+
+    if (node && node->iv) {
+        return qemu_file_input_get_visitor(node->iv);
+    }
+    return NULL;
+}
+
+void qemu_file_put_visitor_node(QemuFileVisitorNode *node)
+{
+    QTAILQ_INSERT_TAIL(&qemu_file_visitors, node, entry);
+}
+
+void qemu_file_remove_visitor_node(QemuFileVisitorNode *node)
+{
+    QTAILQ_REMOVE(&qemu_file_visitors, node, entry);
+    g_free(node);
+}
+
 QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
                          QEMUFileGetBufferFunc *get_buffer,
                          QEMUFileCloseFunc *close,
@@ -55,6 +112,7 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
                          QEMUFileGetRateLimit *get_rate_limit)
 {
     QEMUFile *f;
+    QemuFileVisitorNode *node;
 
     f = g_malloc0(sizeof(QEMUFile));
 
@@ -67,6 +125,12 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
     f->get_rate_limit = get_rate_limit;
     f->is_write = 0;
 
+    node = g_malloc0(sizeof(QemuFileVisitorNode));
+    node->ov = qemu_file_output_visitor_new(f);
+    node->iv = qemu_file_input_visitor_new(f);
+    node->f = f;
+    qemu_file_put_visitor_node(node);
+
     return f;
 }
 
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [Qemu-devel] [RFC 7/8] cutil: add strocat(), to concat a string to an offset in another
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
                   ` (5 preceding siblings ...)
  2011-09-19 14:41 ` [Qemu-devel] [RFC 6/8] savevm: add QEMUFile->visitor lookup routines Michael Roth
@ 2011-09-19 14:41 ` Michael Roth
  2011-09-20 10:43   ` Paolo Bonzini
  2011-09-19 14:41 ` [Qemu-devel] [RFC 8/8] slirp: convert save/load function to visitor interface Michael Roth
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 50+ messages in thread
From: Michael Roth @ 2011-09-19 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, mdroth


Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 cutils.c      |    8 ++++++++
 qemu-common.h |    1 +
 2 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/cutils.c b/cutils.c
index c91f887..0835834 100644
--- a/cutils.c
+++ b/cutils.c
@@ -84,6 +84,14 @@ int stristart(const char *str, const char *val, const char **ptr)
     return 1;
 }
 
+/* concat first 'offset' chars in 'dest' with 'src' */
+char *strocat(char *buf, const char *src, int offset)
+{
+    memcpy(&buf[offset], src, strlen(src));
+    buf[offset+strlen(src)] = '\0';
+    return buf;
+}
+
 /* XXX: use host strnlen if available ? */
 int qemu_strnlen(const char *s, int max_len)
 {
diff --git a/qemu-common.h b/qemu-common.h
index 404c421..0a9b919 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -138,6 +138,7 @@ void pstrcpy(char *buf, int buf_size, const char *str);
 char *pstrcat(char *buf, int buf_size, const char *s);
 int strstart(const char *str, const char *val, const char **ptr);
 int stristart(const char *str, const char *val, const char **ptr);
+char *strocat(char *dest, const char *src, int offset);
 int qemu_strnlen(const char *s, int max_len);
 time_t mktimegm(struct tm *tm);
 int qemu_fls(int i);
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* [Qemu-devel] [RFC 8/8] slirp: convert save/load function to visitor interface
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
                   ` (6 preceding siblings ...)
  2011-09-19 14:41 ` [Qemu-devel] [RFC 7/8] cutil: add strocat(), to concat a string to an offset in another Michael Roth
@ 2011-09-19 14:41 ` Michael Roth
  2011-09-30 13:39   ` Anthony Liguori
  2011-10-02 20:21 ` [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Stefan Berger
  2011-10-03  6:46 ` Michael S. Tsirkin
  9 siblings, 1 reply; 50+ messages in thread
From: Michael Roth @ 2011-09-19 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori, mdroth

Where possible common routines are used for both input and output, thus
save on lines of code, theoretically. The added lines here are mostly
due to extra logic for each save/load routine to manipulate strings into
a unique field name for each saved field, and in some cases a few extra
Visitor calls to due list/struct i/o. With some reworking we can
probably optimize all of these to reduce the amount of added code.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 slirp/slirp.c |  366 +++++++++++++++++++++++++++++++++-----------------------
 1 files changed, 216 insertions(+), 150 deletions(-)

diff --git a/slirp/slirp.c b/slirp/slirp.c
index 19d69eb..8783626 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -26,6 +26,9 @@
 #include "qemu-char.h"
 #include "slirp.h"
 #include "hw/hw.h"
+#include "qemu-error.h"
+
+#define SLIRP_DELIMITER 42 /* used to separate slirp instances in save/load */
 
 /* host loopback address */
 struct in_addr loopback_addr;
@@ -871,96 +874,171 @@ void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
         tcp_output(sototcpcb(so));
 }
 
-static void slirp_tcp_save(QEMUFile *f, struct tcpcb *tp)
+static void slirp_tcp_visit(Visitor *v, struct tcpcb *tp, const char *pfield, Error *err)
 {
-    int i;
-
-    qemu_put_sbe16(f, tp->t_state);
-    for (i = 0; i < TCPT_NTIMERS; i++)
-        qemu_put_sbe16(f, tp->t_timer[i]);
-    qemu_put_sbe16(f, tp->t_rxtshift);
-    qemu_put_sbe16(f, tp->t_rxtcur);
-    qemu_put_sbe16(f, tp->t_dupacks);
-    qemu_put_be16(f, tp->t_maxseg);
-    qemu_put_sbyte(f, tp->t_force);
-    qemu_put_be16(f, tp->t_flags);
-    qemu_put_be32(f, tp->snd_una);
-    qemu_put_be32(f, tp->snd_nxt);
-    qemu_put_be32(f, tp->snd_up);
-    qemu_put_be32(f, tp->snd_wl1);
-    qemu_put_be32(f, tp->snd_wl2);
-    qemu_put_be32(f, tp->iss);
-    qemu_put_be32(f, tp->snd_wnd);
-    qemu_put_be32(f, tp->rcv_wnd);
-    qemu_put_be32(f, tp->rcv_nxt);
-    qemu_put_be32(f, tp->rcv_up);
-    qemu_put_be32(f, tp->irs);
-    qemu_put_be32(f, tp->rcv_adv);
-    qemu_put_be32(f, tp->snd_max);
-    qemu_put_be32(f, tp->snd_cwnd);
-    qemu_put_be32(f, tp->snd_ssthresh);
-    qemu_put_sbe16(f, tp->t_idle);
-    qemu_put_sbe16(f, tp->t_rtt);
-    qemu_put_be32(f, tp->t_rtseq);
-    qemu_put_sbe16(f, tp->t_srtt);
-    qemu_put_sbe16(f, tp->t_rttvar);
-    qemu_put_be16(f, tp->t_rttmin);
-    qemu_put_be32(f, tp->max_sndwnd);
-    qemu_put_byte(f, tp->t_oobflags);
-    qemu_put_byte(f, tp->t_iobc);
-    qemu_put_sbe16(f, tp->t_softerror);
-    qemu_put_byte(f, tp->snd_scale);
-    qemu_put_byte(f, tp->rcv_scale);
-    qemu_put_byte(f, tp->request_r_scale);
-    qemu_put_byte(f, tp->requested_s_scale);
-    qemu_put_be32(f, tp->ts_recent);
-    qemu_put_be32(f, tp->ts_recent_age);
-    qemu_put_be32(f, tp->last_ack_sent);
+    char f[128];
+    int i, l = 0;
+    int16_t *ptr;
+
+    if (pfield) {
+        assert(strlen(pfield) < sizeof(f));
+        strcpy(f, pfield);
+        l = strlen(pfield);
+    }
+
+    visit_type_int16_t(v, &tp->t_state, strocat(f, ".t_state", l), &err);
+    ptr = tp->t_timer;
+    visit_start_array(v, (void **)&ptr, strocat(f, ".t_timer", l), TCPT_NTIMERS, sizeof(*ptr), &err);
+    for (i = 0; i < TCPT_NTIMERS; ++i) {
+        visit_type_int16_t(v, &ptr[i], NULL, &err);
+    }
+    visit_end_array(v, &err);
+    visit_type_int16_t(v, &tp->t_rxtshift, strocat(f, ".t_rxtshift", l), &err); 
+    visit_type_int16_t(v, &tp->t_rxtcur, strocat(f, ".t_rxtcur", l), &err);
+    visit_type_int16_t(v, &tp->t_dupacks, strocat(f, ".t_dupacks", l), &err);
+    visit_type_uint16_t(v, &tp->t_maxseg, strocat(f, ".t_maxseg", l), &err); 
+    visit_type_uint8_t(v, (uint8_t *)&tp->t_force, strocat(f, ".t_force", l), &err); 
+    visit_type_uint16_t(v, &tp->t_flags, strocat(f, ".t_flags", l), &err); 
+    visit_type_uint32_t(v, &tp->snd_una, strocat(f, ".snd_una", l), &err); 
+    visit_type_uint32_t(v, &tp->snd_nxt, strocat(f, ".snd_nxt", l), &err); 
+    visit_type_uint32_t(v, &tp->snd_up, strocat(f, ".snd_up", l), &err); 
+    visit_type_uint32_t(v, &tp->snd_wl1, strocat(f, ".snd_wl1", l), &err); 
+    visit_type_uint32_t(v, &tp->snd_wl2, strocat(f, ".snd_wl2", l), &err); 
+    visit_type_uint32_t(v, &tp->iss, strocat(f, ".iss", l), &err); 
+    visit_type_uint32_t(v, &tp->snd_wnd, strocat(f, ".snd_wnd", l), &err); 
+    visit_type_uint32_t(v, &tp->rcv_wnd, strocat(f, ".rcv_wnd", l), &err); 
+    visit_type_uint32_t(v, &tp->rcv_nxt, strocat(f, ".rcv_nxt", l), &err); 
+    visit_type_uint32_t(v, &tp->rcv_up, strocat(f, ".rcv_up", l), &err); 
+    visit_type_uint32_t(v, &tp->irs, strocat(f, ".irs", l), &err); 
+    visit_type_uint32_t(v, &tp->rcv_adv, strocat(f, ".rcv_adv", l), &err); 
+    visit_type_uint32_t(v, &tp->snd_max, strocat(f, ".snd_max", l), &err); 
+    visit_type_uint32_t(v, &tp->snd_cwnd, strocat(f, ".snd_cwnd", l), &err); 
+    visit_type_uint32_t(v, &tp->snd_ssthresh, strocat(f, ".snd_ssthresh", l), &err); 
+    visit_type_int16_t(v, &tp->t_idle, strocat(f, ".t_idle", l), &err); 
+    visit_type_int16_t(v, &tp->t_rtt, strocat(f, ".t_rtt", l), &err); 
+    visit_type_uint32_t(v, &tp->t_rtseq, strocat(f, ".t_rtseq", l), &err); 
+    visit_type_int16_t(v, &tp->t_srtt, strocat(f, ".t_srtt", l), &err); 
+    visit_type_int16_t(v, &tp->t_rttvar, strocat(f, ".t_rttvar", l), &err); 
+    visit_type_uint16_t(v, &tp->t_rttmin, strocat(f, ".t_rttmin", l), &err); 
+    visit_type_uint32_t(v, &tp->max_sndwnd, strocat(f, ".max_sndwnd", l), &err); 
+    visit_type_uint8_t(v, (uint8_t *)&tp->t_oobflags, strocat(f, ".t_oobflags", l), &err); 
+    visit_type_uint8_t(v, (uint8_t *)&tp->t_iobc, strocat(f, ".t_iobc", l), &err); 
+    visit_type_int16_t(v, &tp->t_softerror, strocat(f, ".t_softerror", l), &err); 
+    visit_type_uint8_t(v, &tp->snd_scale, strocat(f, ".snd_scale", l), &err); 
+    visit_type_uint8_t(v, &tp->rcv_scale, strocat(f, ".rcv_scale", l), &err); 
+    visit_type_uint8_t(v, &tp->request_r_scale, strocat(f, ".request_r_scale", l), &err); 
+    visit_type_uint8_t(v, &tp->requested_s_scale, strocat(f, ".requested_s_scale", l), &err); 
+    visit_type_uint32_t(v, &tp->ts_recent, strocat(f, ".ts_recent", l), &err); 
+    visit_type_uint32_t(v, &tp->ts_recent_age, strocat(f, ".ts_recent_age", l), &err); 
+    visit_type_uint32_t(v, &tp->last_ack_sent, strocat(f, ".last_ack_sent", l), &err); 
 }
 
-static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf)
+static void slirp_tcp_save(Visitor *v, struct tcpcb *tp, const char *pfield, Error *err)
 {
-    uint32_t off;
+    slirp_tcp_visit(v, tp, pfield, err);
+}
 
-    qemu_put_be32(f, sbuf->sb_cc);
-    qemu_put_be32(f, sbuf->sb_datalen);
+static void slirp_sbuf_save(Visitor *v, struct sbuf *sbuf, const char *pfield, Error *err)
+{
+    int32_t off;
+    char f[128];
+    int i, l = 0;
+
+    if (pfield) {
+        assert(strlen(pfield) < sizeof(f));
+        strcpy(f, pfield);
+        l = strlen(f);
+    }
+
+    visit_type_uint32_t(v, &sbuf->sb_cc, strocat(f, ".sb_cc", l), &err);
+    visit_type_uint32_t(v, &sbuf->sb_datalen, strocat(f, ".sb_datalen", l), &err);
     off = (uint32_t)(sbuf->sb_wptr - sbuf->sb_data);
-    qemu_put_sbe32(f, off);
+    visit_type_int32_t(v, &off, strocat(f, ".sb_wptr_off", l), &err);
     off = (uint32_t)(sbuf->sb_rptr - sbuf->sb_data);
-    qemu_put_sbe32(f, off);
-    qemu_put_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
+    visit_type_int32_t(v, &off, strocat(f, ".sb_rptr_off", l), &err);
+
+    visit_start_array(v, (void **)&sbuf->sb_data, strocat(f, ".sb_data", l),
+                      sbuf->sb_datalen, sizeof(*sbuf->sb_data), &err);
+    for (i = 0; i < sbuf->sb_datalen; i++) {
+        visit_type_uint8_t(v, (uint8_t *)&sbuf->sb_data[i], NULL, &err);
+    }
+    visit_end_array(v, &err);
+}
+
+static void slirp_socket_visit(Visitor *v, struct socket *so, const char *pfield, Error *err)
+{
+    char f[64];
+    int l = 0;
+
+    if (pfield) {
+        assert(strlen(pfield) < sizeof(f));
+        strcpy(f, pfield);
+        l = strlen(f);
+    }
+    visit_type_int32_t(v, &so->so_urgc, strocat(f, ".so_urgc", l), &err);
+    visit_type_uint32_t(v, &so->so_faddr.s_addr, strocat(f, ".so_faddr.s_addr", l), &err);
+    visit_type_uint32_t(v, &so->so_laddr.s_addr, strocat(f, ".so_urgc", l), &err);
+    visit_type_uint16_t(v, &so->so_fport, strocat(f, ".so_fport", l), &err);
+    visit_type_uint16_t(v, &so->so_lport, strocat(f, ".so_lport", l), &err);
+    visit_type_uint8_t(v, &so->so_iptos, strocat(f, ".so_iptos", l), &err);
+    visit_type_uint8_t(v, &so->so_emu, strocat(f, ".so_emu", l), &err);
+    visit_type_uint8_t(v, &so->so_type, strocat(f, ".so_type", l), &err);
+    visit_type_int32_t(v, &so->so_state, strocat(f, ".so_state", l), &err);
 }
 
-static void slirp_socket_save(QEMUFile *f, struct socket *so)
+static void slirp_socket_save(Visitor *v, struct socket *so, const char *pfield, Error *err)
 {
-    qemu_put_be32(f, so->so_urgc);
-    qemu_put_be32(f, so->so_faddr.s_addr);
-    qemu_put_be32(f, so->so_laddr.s_addr);
-    qemu_put_be16(f, so->so_fport);
-    qemu_put_be16(f, so->so_lport);
-    qemu_put_byte(f, so->so_iptos);
-    qemu_put_byte(f, so->so_emu);
-    qemu_put_byte(f, so->so_type);
-    qemu_put_be32(f, so->so_state);
-    slirp_sbuf_save(f, &so->so_rcv);
-    slirp_sbuf_save(f, &so->so_snd);
-    slirp_tcp_save(f, so->so_tcpcb);
+    char f[64];
+    int l = 0;
+
+    if (pfield) {
+        assert(strlen(pfield) < sizeof(f));
+        strcpy(f, pfield);
+        l = strlen(f);
+    }
+
+    slirp_socket_visit(v, so, f, err);
+
+    slirp_sbuf_save(v, &so->so_rcv, strocat(f, ".so_rcv", l), err);
+    slirp_sbuf_save(v, &so->so_snd, strocat(f, ".so_snd", l), err);
+    slirp_tcp_save(v, so->so_tcpcb, strocat(f, ".so_tcpcb", l), err);
 }
 
-static void slirp_bootp_save(QEMUFile *f, Slirp *slirp)
+static void slirp_bootp_visit(Visitor *v, Slirp *slirp, const char *pfield, Error *err)
 {
-    int i;
+    int i, j, l;
+    void *ptr;
+    char f[64];
+
+    if (pfield) {
+        assert(strlen(pfield) < sizeof(f));
+        strcpy(f, pfield);
+        l = strlen(f);
+    }
 
+    ptr = slirp->bootp_clients;
+    visit_start_array(v, &ptr, strocat(f, ".bootp_clients", l), NB_BOOTP_CLIENTS, 8, &err);
     for (i = 0; i < NB_BOOTP_CLIENTS; i++) {
-        qemu_put_be16(f, slirp->bootp_clients[i].allocated);
-        qemu_put_buffer(f, slirp->bootp_clients[i].macaddr, 6);
+        visit_type_uint16_t(v, &slirp->bootp_clients[i].allocated, "allocated", &err);
+        ptr = slirp->bootp_clients[i].macaddr;
+        visit_start_array(v, &ptr, "macaddr", 6, 1, &err);
+        for (j = 0; j < 6; j++) {
+            visit_type_uint8_t(v, (uint8_t *)&slirp->bootp_clients[j], NULL, &err);
+        }
+        visit_end_array(v, &err);
     }
+    visit_end_array(v, &err);
 }
 
 static void slirp_state_save(QEMUFile *f, void *opaque)
 {
     Slirp *slirp = opaque;
     struct ex_list *ex_ptr;
+    int i = 0;
+    uint8_t padding;
+    Visitor *v = qemu_file_get_output_visitor(f);
+    Error *err = NULL;
+    char id[32];
 
     for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
         if (ex_ptr->ex_pty == 3) {
@@ -970,70 +1048,44 @@ static void slirp_state_save(QEMUFile *f, void *opaque)
             if (!so)
                 continue;
 
-            qemu_put_byte(f, 42);
-            slirp_socket_save(f, so);
+            padding = SLIRP_DELIMITER;
+            visit_type_uint8_t(v, &padding, "padding", &err);
+            slirp_socket_save(v, so, strocat(id, "so", strlen(id)), err);
         }
-    qemu_put_byte(f, 0);
+    padding = 0;
+    visit_type_uint8_t(v, &padding, "padding", &err);
+
+    visit_type_uint16_t(v, &slirp->ip_id, "slirp.ip_id", &err);
 
-    qemu_put_be16(f, slirp->ip_id);
+    slirp_bootp_visit(v, slirp, "slirp", err);
 
-    slirp_bootp_save(f, slirp);
+    if (err) {
+        error_report("error saving slirp state: %s", error_get_pretty(err));
+        error_free(err);
+    }
 }
 
-static void slirp_tcp_load(QEMUFile *f, struct tcpcb *tp)
+static void slirp_tcp_load(Visitor *v, struct tcpcb *tp, const char *pfield, Error *err)
 {
-    int i;
-
-    tp->t_state = qemu_get_sbe16(f);
-    for (i = 0; i < TCPT_NTIMERS; i++)
-        tp->t_timer[i] = qemu_get_sbe16(f);
-    tp->t_rxtshift = qemu_get_sbe16(f);
-    tp->t_rxtcur = qemu_get_sbe16(f);
-    tp->t_dupacks = qemu_get_sbe16(f);
-    tp->t_maxseg = qemu_get_be16(f);
-    tp->t_force = qemu_get_sbyte(f);
-    tp->t_flags = qemu_get_be16(f);
-    tp->snd_una = qemu_get_be32(f);
-    tp->snd_nxt = qemu_get_be32(f);
-    tp->snd_up = qemu_get_be32(f);
-    tp->snd_wl1 = qemu_get_be32(f);
-    tp->snd_wl2 = qemu_get_be32(f);
-    tp->iss = qemu_get_be32(f);
-    tp->snd_wnd = qemu_get_be32(f);
-    tp->rcv_wnd = qemu_get_be32(f);
-    tp->rcv_nxt = qemu_get_be32(f);
-    tp->rcv_up = qemu_get_be32(f);
-    tp->irs = qemu_get_be32(f);
-    tp->rcv_adv = qemu_get_be32(f);
-    tp->snd_max = qemu_get_be32(f);
-    tp->snd_cwnd = qemu_get_be32(f);
-    tp->snd_ssthresh = qemu_get_be32(f);
-    tp->t_idle = qemu_get_sbe16(f);
-    tp->t_rtt = qemu_get_sbe16(f);
-    tp->t_rtseq = qemu_get_be32(f);
-    tp->t_srtt = qemu_get_sbe16(f);
-    tp->t_rttvar = qemu_get_sbe16(f);
-    tp->t_rttmin = qemu_get_be16(f);
-    tp->max_sndwnd = qemu_get_be32(f);
-    tp->t_oobflags = qemu_get_byte(f);
-    tp->t_iobc = qemu_get_byte(f);
-    tp->t_softerror = qemu_get_sbe16(f);
-    tp->snd_scale = qemu_get_byte(f);
-    tp->rcv_scale = qemu_get_byte(f);
-    tp->request_r_scale = qemu_get_byte(f);
-    tp->requested_s_scale = qemu_get_byte(f);
-    tp->ts_recent = qemu_get_be32(f);
-    tp->ts_recent_age = qemu_get_be32(f);
-    tp->last_ack_sent = qemu_get_be32(f);
+    slirp_tcp_visit(v, tp, pfield, err);
+
     tcp_template(tp);
 }
 
-static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
+static int slirp_sbuf_load(Visitor *v, struct sbuf *sbuf, const char *pfield, Error *err)
 {
     uint32_t off, sb_cc, sb_datalen;
+    int l = 0, i;
+    char f[64];
+
+    if (pfield) {
+        assert(strlen(pfield) < sizeof(f));
+        strcpy(f, pfield);
+        l = strlen(f);
+    }
 
-    sb_cc = qemu_get_be32(f);
-    sb_datalen = qemu_get_be32(f);
+    visit_type_uint32_t(v, &sb_cc, strocat(f, ".sb_cc", l), &err);
+    visit_type_uint32_t(v, &sb_datalen, strocat(f, ".sb_datalen", l), &err);
 
     sbreserve(sbuf, sb_datalen);
 
@@ -1042,61 +1094,68 @@ static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
 
     sbuf->sb_cc = sb_cc;
 
-    off = qemu_get_sbe32(f);
+    visit_type_uint32_t(v, &off, strocat(f, ".sb_wptr_off", l), &err);
     sbuf->sb_wptr = sbuf->sb_data + off;
-    off = qemu_get_sbe32(f);
+    visit_type_uint32_t(v, &off, strocat(f, ".sb_rptr_off", l), &err);
     sbuf->sb_rptr = sbuf->sb_data + off;
-    qemu_get_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
+
+    visit_start_array(v, (void **)&sbuf->sb_data, strocat(f, ".sb_data", l),
+                      sbuf->sb_datalen, sizeof(*sbuf->sb_data), &err);
+    for (i = 0; i < sbuf->sb_datalen; i++) {
+        visit_type_uint8_t(v, (uint8_t *)&sbuf->sb_data[i], NULL, &err);
+    }
+    visit_end_array(v, &err);
 
     return 0;
 }
 
-static int slirp_socket_load(QEMUFile *f, struct socket *so)
+static int slirp_socket_load(Visitor *v, struct socket *so, const char *pfield, Error *err)
 {
+    char f[64];
+    int l = 0;
+
+    if (pfield) {
+        assert(strlen(pfield) < sizeof(f));
+        strcpy(f, pfield);
+        l = strlen(f);
+    }
+
+    assert(v);
     if (tcp_attach(so) < 0)
         return -ENOMEM;
 
-    so->so_urgc = qemu_get_be32(f);
-    so->so_faddr.s_addr = qemu_get_be32(f);
-    so->so_laddr.s_addr = qemu_get_be32(f);
-    so->so_fport = qemu_get_be16(f);
-    so->so_lport = qemu_get_be16(f);
-    so->so_iptos = qemu_get_byte(f);
-    so->so_emu = qemu_get_byte(f);
-    so->so_type = qemu_get_byte(f);
-    so->so_state = qemu_get_be32(f);
-    if (slirp_sbuf_load(f, &so->so_rcv) < 0)
+    slirp_socket_visit(v, so, pfield, err);
+
+    if (slirp_sbuf_load(v, &so->so_rcv, strocat(f, ".so_rcv", l), err) < 0)
         return -ENOMEM;
-    if (slirp_sbuf_load(f, &so->so_snd) < 0)
+    if (slirp_sbuf_load(v, &so->so_snd, strocat(f, ".so_snd", l), err) < 0)
         return -ENOMEM;
-    slirp_tcp_load(f, so->so_tcpcb);
+    slirp_tcp_load(v, so->so_tcpcb, strocat(f, ".so_tcpcb", l), err);
 
     return 0;
 }
 
-static void slirp_bootp_load(QEMUFile *f, Slirp *slirp)
-{
-    int i;
-
-    for (i = 0; i < NB_BOOTP_CLIENTS; i++) {
-        slirp->bootp_clients[i].allocated = qemu_get_be16(f);
-        qemu_get_buffer(f, slirp->bootp_clients[i].macaddr, 6);
-    }
-}
-
 static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
 {
     Slirp *slirp = opaque;
     struct ex_list *ex_ptr;
+    Visitor *v = qemu_file_get_input_visitor(f);
+    Error *err = NULL;
+    char id[32];
+    uint8_t padding, i = 0;
 
-    while (qemu_get_byte(f)) {
+    visit_type_uint8_t(v, &padding, "padding", &err);
+
+    while (padding == SLIRP_DELIMITER) {
         int ret;
         struct socket *so = socreate(slirp);
 
         if (!so)
             return -ENOMEM;
 
-        ret = slirp_socket_load(f, so);
+        sprintf(id, "slirp[%d]", i++);
+
+        ret = slirp_socket_load(v, so, strocat(id, "so", strlen(id)), err);
 
         if (ret < 0)
             return ret;
@@ -1118,12 +1177,19 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
         so->extra = (void *)ex_ptr->ex_exec;
     }
 
+
     if (version_id >= 2) {
-        slirp->ip_id = qemu_get_be16(f);
+        visit_type_uint16_t(v, &slirp->ip_id, "slirp.ip_id", &err);
     }
 
     if (version_id >= 3) {
-        slirp_bootp_load(f, slirp);
+        slirp_bootp_visit(v, slirp, "slirp", err);
+    }
+
+    if (err) {
+        error_report("error loading slirp state: %s", error_get_pretty(err));
+        error_free(err);
+        return -EINVAL;
     }
 
     return 0;
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC 7/8] cutil: add strocat(), to concat a string to an offset in another
  2011-09-19 14:41 ` [Qemu-devel] [RFC 7/8] cutil: add strocat(), to concat a string to an offset in another Michael Roth
@ 2011-09-20 10:43   ` Paolo Bonzini
  0 siblings, 0 replies; 50+ messages in thread
From: Paolo Bonzini @ 2011-09-20 10:43 UTC (permalink / raw)
  To: Michael Roth; +Cc: aliguori, qemu-devel

On 09/19/2011 04:41 PM, Michael Roth wrote:
> +/* concat first 'offset' chars in 'dest' with 'src' */
> +char *strocat(char *buf, const char *src, int offset)
> +{
> +    memcpy(&buf[offset], src, strlen(src));
> +    buf[offset+strlen(src)] = '\0';

This is just strcpy(&buf[offset], src);

> +    return buf;
> +}

Paolo

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC 4/8] savevm: move QEMUFile interfaces into qemu-file.c
  2011-09-19 14:41 ` [Qemu-devel] [RFC 4/8] savevm: move QEMUFile interfaces into qemu-file.c Michael Roth
@ 2011-09-24  7:23   ` Blue Swirl
  0 siblings, 0 replies; 50+ messages in thread
From: Blue Swirl @ 2011-09-24  7:23 UTC (permalink / raw)
  To: Michael Roth; +Cc: aliguori, qemu-devel

On Mon, Sep 19, 2011 at 2:41 PM, Michael Roth <mdroth@linux.vnet.ibm.com> wrote:
>
> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
> ---
>  Makefile.objs |    2 +-
>  hw/hw.h       |    1 +
>  qemu-file.c   |  521 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  savevm.c      |  494 ------------------------------------------------------
>  4 files changed, 523 insertions(+), 495 deletions(-)
>  create mode 100644 qemu-file.c
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 6bc8555..34bf58f 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -119,7 +119,7 @@ common-obj-$(CONFIG_SD) += sd.o
>  common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
>  common-obj-y += bt-hci-csr.o
>  common-obj-y += buffered_file.o migration.o migration-tcp.o
> -common-obj-y += qemu-char.o savevm.o #aio.o
> +common-obj-y += qemu-char.o savevm.o qemu-file.o #aio.o
>  common-obj-y += msmouse.o ps2.o
>  common-obj-y += qdev.o qdev-properties.o
>  common-obj-y += block-migration.o iohandler.o
> diff --git a/hw/hw.h b/hw/hw.h
> index a124da9..244bfb9 100644
> --- a/hw/hw.h
> +++ b/hw/hw.h
> @@ -58,6 +58,7 @@ void qemu_fflush(QEMUFile *f);
>  int qemu_fclose(QEMUFile *f);
>  void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
>  void qemu_put_byte(QEMUFile *f, int v);
> +int qemu_peek_byte(QEMUFile *f);
>
>  static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
>  {
> diff --git a/qemu-file.c b/qemu-file.c
> new file mode 100644
> index 0000000..fb91b91
> --- /dev/null
> +++ b/qemu-file.c
> @@ -0,0 +1,521 @@
> +/*
> + * QEMU System Emulator
> + *
> + * Copyright (c) 2003-2008 Fabrice Bellard
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "qemu-common.h"
> +#include "qemu_socket.h"
> +#include "hw/hw.h"
> +
> +#define IO_BUF_SIZE 32768
> +
> +struct QEMUFile {
> +    QEMUFilePutBufferFunc *put_buffer;
> +    QEMUFileGetBufferFunc *get_buffer;
> +    QEMUFileCloseFunc *close;
> +    QEMUFileRateLimit *rate_limit;
> +    QEMUFileSetRateLimit *set_rate_limit;
> +    QEMUFileGetRateLimit *get_rate_limit;
> +    void *opaque;
> +    int is_write;
> +
> +    int64_t buf_offset; /* start of buffer when writing, end of buffer
> +                           when reading */
> +    int buf_index;
> +    int buf_size; /* 0 when writing */
> +    uint8_t buf[IO_BUF_SIZE];
> +
> +    int has_error;
> +};
> +
> +QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
> +                         QEMUFileGetBufferFunc *get_buffer,
> +                         QEMUFileCloseFunc *close,
> +                         QEMUFileRateLimit *rate_limit,
> +                         QEMUFileSetRateLimit *set_rate_limit,
> +                         QEMUFileGetRateLimit *get_rate_limit)
> +{
> +    QEMUFile *f;
> +
> +    f = g_malloc0(sizeof(QEMUFile));
> +
> +    f->opaque = opaque;
> +    f->put_buffer = put_buffer;
> +    f->get_buffer = get_buffer;
> +    f->close = close;
> +    f->rate_limit = rate_limit;
> +    f->set_rate_limit = set_rate_limit;
> +    f->get_rate_limit = get_rate_limit;
> +    f->is_write = 0;
> +
> +    return f;
> +}
> +
> +int qemu_file_has_error(QEMUFile *f)
> +{
> +    return f->has_error;
> +}
> +
> +void qemu_file_set_error(QEMUFile *f)
> +{
> +    f->has_error = 1;
> +}
> +
> +void qemu_fflush(QEMUFile *f)
> +{
> +    if (!f->put_buffer)
> +        return;
> +
> +    if (f->is_write && f->buf_index > 0) {
> +        int len;
> +
> +        len = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
> +        if (len > 0)

Please fix CODING_STYLE issues before moving, so the old style will
not persist forever.

> +            f->buf_offset += f->buf_index;
> +        else
> +            f->has_error = 1;
> +        f->buf_index = 0;
> +    }
> +}
> +
> +static void qemu_fill_buffer(QEMUFile *f)
> +{
> +    int len;
> +
> +    if (!f->get_buffer)
> +        return;
> +
> +    if (f->is_write)
> +        abort();
> +
> +    len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
> +    if (len > 0) {
> +        f->buf_index = 0;
> +        f->buf_size = len;
> +        f->buf_offset += len;
> +    } else if (len != -EAGAIN)
> +        f->has_error = 1;
> +}
> +
> +int qemu_fclose(QEMUFile *f)
> +{
> +    int ret = 0;
> +    qemu_fflush(f);
> +    if (f->close)
> +        ret = f->close(f->opaque);
> +    g_free(f);
> +    return ret;
> +}
> +
> +void qemu_file_put_notify(QEMUFile *f)
> +{
> +    f->put_buffer(f->opaque, NULL, 0, 0);
> +}
> +
> +void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
> +{
> +    int l;
> +
> +    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
> +        fprintf(stderr,
> +                "Attempted to write to buffer while read buffer is not empty\n");
> +        abort();
> +    }
> +
> +    while (!f->has_error && size > 0) {
> +        l = IO_BUF_SIZE - f->buf_index;
> +        if (l > size)
> +            l = size;
> +        memcpy(f->buf + f->buf_index, buf, l);
> +        f->is_write = 1;
> +        f->buf_index += l;
> +        buf += l;
> +        size -= l;
> +        if (f->buf_index >= IO_BUF_SIZE)
> +            qemu_fflush(f);
> +    }
> +}
> +
> +void qemu_put_byte(QEMUFile *f, int v)
> +{
> +    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
> +        fprintf(stderr,
> +                "Attempted to write to buffer while read buffer is not empty\n");
> +        abort();
> +    }
> +
> +    f->buf[f->buf_index++] = v;
> +    f->is_write = 1;
> +    if (f->buf_index >= IO_BUF_SIZE)
> +        qemu_fflush(f);
> +}
> +
> +int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
> +{
> +    int size, l;
> +
> +    if (f->is_write)
> +        abort();
> +
> +    size = size1;
> +    while (size > 0) {
> +        l = f->buf_size - f->buf_index;
> +        if (l == 0) {
> +            qemu_fill_buffer(f);
> +            l = f->buf_size - f->buf_index;
> +            if (l == 0)
> +                break;
> +        }
> +        if (l > size)
> +            l = size;
> +        memcpy(buf, f->buf + f->buf_index, l);
> +        f->buf_index += l;
> +        buf += l;
> +        size -= l;
> +    }
> +    return size1 - size;
> +}
> +
> +int qemu_get_byte(QEMUFile *f)
> +{
> +    if (f->is_write)
> +        abort();
> +
> +    if (f->buf_index >= f->buf_size) {
> +        qemu_fill_buffer(f);
> +        if (f->buf_index >= f->buf_size)
> +            return 0;
> +    }
> +    return f->buf[f->buf_index++];
> +}
> +
> +int64_t qemu_ftell(QEMUFile *f)
> +{
> +    return f->buf_offset - f->buf_size + f->buf_index;
> +}
> +
> +int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
> +{
> +    if (whence == SEEK_SET) {
> +        /* nothing to do */
> +    } else if (whence == SEEK_CUR) {
> +        pos += qemu_ftell(f);
> +    } else {
> +        /* SEEK_END not supported */
> +        return -1;
> +    }
> +    if (f->put_buffer) {
> +        qemu_fflush(f);
> +        f->buf_offset = pos;
> +    } else {
> +        f->buf_offset = pos;
> +        f->buf_index = 0;
> +        f->buf_size = 0;
> +    }
> +    return pos;
> +}
> +
> +int qemu_peek_byte(QEMUFile *f)
> +{
> +    if (f->is_write)
> +        abort();
> +
> +    if (f->buf_index >= f->buf_size) {
> +        qemu_fill_buffer(f);
> +        if (f->buf_index >= f->buf_size)
> +            return 0;
> +    }
> +    return f->buf[f->buf_index];
> +}
> +
> +int qemu_file_rate_limit(QEMUFile *f)
> +{
> +    if (f->rate_limit)
> +        return f->rate_limit(f->opaque);
> +
> +    return 0;
> +}
> +
> +int64_t qemu_file_get_rate_limit(QEMUFile *f)
> +{
> +    if (f->get_rate_limit)
> +        return f->get_rate_limit(f->opaque);
> +
> +    return 0;
> +}
> +
> +int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate)
> +{
> +    /* any failed or completed migration keeps its state to allow probing of
> +     * migration data, but has no associated file anymore */
> +    if (f && f->set_rate_limit)
> +        return f->set_rate_limit(f->opaque, new_rate);
> +
> +    return 0;
> +}
> +
> +void qemu_put_be16(QEMUFile *f, unsigned int v)
> +{
> +    qemu_put_byte(f, v >> 8);
> +    qemu_put_byte(f, v);
> +}
> +
> +void qemu_put_be32(QEMUFile *f, unsigned int v)
> +{
> +    qemu_put_byte(f, v >> 24);
> +    qemu_put_byte(f, v >> 16);
> +    qemu_put_byte(f, v >> 8);
> +    qemu_put_byte(f, v);
> +}
> +
> +void qemu_put_be64(QEMUFile *f, uint64_t v)
> +{
> +    qemu_put_be32(f, v >> 32);
> +    qemu_put_be32(f, v);
> +}
> +
> +unsigned int qemu_get_be16(QEMUFile *f)
> +{
> +    unsigned int v;
> +    v = qemu_get_byte(f) << 8;
> +    v |= qemu_get_byte(f);
> +    return v;
> +}
> +
> +unsigned int qemu_get_be32(QEMUFile *f)
> +{
> +    unsigned int v;
> +    v = qemu_get_byte(f) << 24;
> +    v |= qemu_get_byte(f) << 16;
> +    v |= qemu_get_byte(f) << 8;
> +    v |= qemu_get_byte(f);
> +    return v;
> +}
> +
> +uint64_t qemu_get_be64(QEMUFile *f)
> +{
> +    uint64_t v;
> +    v = (uint64_t)qemu_get_be32(f) << 32;
> +    v |= qemu_get_be32(f);
> +    return v;
> +}
> +
> +typedef struct QEMUFileStdio
> +{
> +    FILE *stdio_file;
> +    QEMUFile *file;
> +} QEMUFileStdio;
> +
> +typedef struct QEMUFileSocket
> +{
> +    int fd;
> +    QEMUFile *file;
> +} QEMUFileSocket;
> +
> +static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
> +{
> +    QEMUFileSocket *s = opaque;
> +    ssize_t len;
> +
> +    do {
> +        len = qemu_recv(s->fd, buf, size, 0);
> +    } while (len == -1 && socket_error() == EINTR);
> +
> +    if (len == -1)
> +        len = -socket_error();
> +
> +    return len;
> +}
> +
> +static int socket_close(void *opaque)
> +{
> +    QEMUFileSocket *s = opaque;
> +    g_free(s);
> +    return 0;
> +}
> +
> +int qemu_stdio_fd(QEMUFile *f)
> +{
> +    QEMUFileStdio *p;
> +    int fd;
> +
> +    p = (QEMUFileStdio *)f->opaque;
> +    fd = fileno(p->stdio_file);
> +
> +    return fd;
> +}
> +
> +static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
> +{
> +    QEMUFileStdio *s = opaque;
> +    return fwrite(buf, 1, size, s->stdio_file);
> +}
> +
> +static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
> +{
> +    QEMUFileStdio *s = opaque;
> +    FILE *fp = s->stdio_file;
> +    int bytes;
> +
> +    do {
> +        clearerr(fp);
> +        bytes = fread(buf, 1, size, fp);
> +    } while ((bytes == 0) && ferror(fp) && (errno == EINTR));
> +    return bytes;
> +}
> +
> +static int stdio_pclose(void *opaque)
> +{
> +    QEMUFileStdio *s = opaque;
> +    int ret;
> +    ret = pclose(s->stdio_file);
> +    g_free(s);
> +    return ret;
> +}
> +
> +static int stdio_fclose(void *opaque)
> +{
> +    QEMUFileStdio *s = opaque;
> +    fclose(s->stdio_file);
> +    g_free(s);
> +    return 0;
> +}
> +
> +QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
> +{
> +    QEMUFileStdio *s;
> +
> +    if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
> +        fprintf(stderr, "qemu_popen: Argument validity check failed\n");
> +        return NULL;
> +    }
> +
> +    s = g_malloc0(sizeof(QEMUFileStdio));
> +
> +    s->stdio_file = stdio_file;
> +
> +    if(mode[0] == 'r') {
> +        s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_pclose,
> +                                NULL, NULL, NULL);
> +    } else {
> +        s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_pclose,
> +                                NULL, NULL, NULL);
> +    }
> +    return s->file;
> +}
> +
> +QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
> +{
> +    FILE *popen_file;
> +
> +    popen_file = popen(command, mode);
> +    if(popen_file == NULL) {
> +        return NULL;
> +    }
> +
> +    return qemu_popen(popen_file, mode);
> +}
> +
> +QEMUFile *qemu_fdopen(int fd, const char *mode)
> +{
> +    QEMUFileStdio *s;
> +
> +    if (mode == NULL ||
> +       (mode[0] != 'r' && mode[0] != 'w') ||
> +       mode[1] != 'b' || mode[2] != 0) {
> +        fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
> +        return NULL;
> +    }
> +
> +    s = g_malloc0(sizeof(QEMUFileStdio));
> +    s->stdio_file = fdopen(fd, mode);
> +    if (!s->stdio_file)
> +        goto fail;
> +
> +    if(mode[0] == 'r') {
> +        s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_fclose,
> +                                NULL, NULL, NULL);
> +    } else {
> +        s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_fclose,
> +                                NULL, NULL, NULL);
> +    }
> +    return s->file;
> +
> +fail:
> +    g_free(s);
> +    return NULL;
> +}
> +
> +QEMUFile *qemu_fopen_socket(int fd)
> +{
> +    QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
> +
> +    s->fd = fd;
> +    s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close,
> +                            NULL, NULL, NULL);
> +    return s->file;
> +}
> +
> +static int file_put_buffer(void *opaque, const uint8_t *buf,
> +                            int64_t pos, int size)
> +{
> +    QEMUFileStdio *s = opaque;
> +    fseek(s->stdio_file, pos, SEEK_SET);
> +    return fwrite(buf, 1, size, s->stdio_file);
> +}
> +
> +static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
> +{
> +    QEMUFileStdio *s = opaque;
> +    fseek(s->stdio_file, pos, SEEK_SET);
> +    return fread(buf, 1, size, s->stdio_file);
> +}
> +
> +QEMUFile *qemu_fopen(const char *filename, const char *mode)
> +{
> +    QEMUFileStdio *s;
> +
> +    if (mode == NULL ||
> +       (mode[0] != 'r' && mode[0] != 'w') ||
> +       mode[1] != 'b' || mode[2] != 0) {
> +        fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
> +        return NULL;
> +    }
> +
> +    s = g_malloc0(sizeof(QEMUFileStdio));
> +
> +    s->stdio_file = fopen(filename, mode);
> +    if (!s->stdio_file)
> +        goto fail;
> +
> +    if(mode[0] == 'w') {
> +        s->file = qemu_fopen_ops(s, file_put_buffer, NULL, stdio_fclose,
> +                                NULL, NULL, NULL);
> +    } else {
> +        s->file = qemu_fopen_ops(s, NULL, file_get_buffer, stdio_fclose,
> +                              NULL, NULL, NULL);
> +    }
> +    return s->file;
> +fail:
> +    g_free(s);
> +    return NULL;
> +}
> diff --git a/savevm.c b/savevm.c
> index 1feaa70..db1e0cd 100644
> --- a/savevm.c
> +++ b/savevm.c
> @@ -155,228 +155,6 @@ void qemu_announce_self(void)
>  /***********************************************************/
>  /* savevm/loadvm support */
>
> -#define IO_BUF_SIZE 32768
> -
> -struct QEMUFile {
> -    QEMUFilePutBufferFunc *put_buffer;
> -    QEMUFileGetBufferFunc *get_buffer;
> -    QEMUFileCloseFunc *close;
> -    QEMUFileRateLimit *rate_limit;
> -    QEMUFileSetRateLimit *set_rate_limit;
> -    QEMUFileGetRateLimit *get_rate_limit;
> -    void *opaque;
> -    int is_write;
> -
> -    int64_t buf_offset; /* start of buffer when writing, end of buffer
> -                           when reading */
> -    int buf_index;
> -    int buf_size; /* 0 when writing */
> -    uint8_t buf[IO_BUF_SIZE];
> -
> -    int has_error;
> -};
> -
> -typedef struct QEMUFileStdio
> -{
> -    FILE *stdio_file;
> -    QEMUFile *file;
> -} QEMUFileStdio;
> -
> -typedef struct QEMUFileSocket
> -{
> -    int fd;
> -    QEMUFile *file;
> -} QEMUFileSocket;
> -
> -static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
> -{
> -    QEMUFileSocket *s = opaque;
> -    ssize_t len;
> -
> -    do {
> -        len = qemu_recv(s->fd, buf, size, 0);
> -    } while (len == -1 && socket_error() == EINTR);
> -
> -    if (len == -1)
> -        len = -socket_error();
> -
> -    return len;
> -}
> -
> -static int socket_close(void *opaque)
> -{
> -    QEMUFileSocket *s = opaque;
> -    g_free(s);
> -    return 0;
> -}
> -
> -static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
> -{
> -    QEMUFileStdio *s = opaque;
> -    return fwrite(buf, 1, size, s->stdio_file);
> -}
> -
> -static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
> -{
> -    QEMUFileStdio *s = opaque;
> -    FILE *fp = s->stdio_file;
> -    int bytes;
> -
> -    do {
> -        clearerr(fp);
> -        bytes = fread(buf, 1, size, fp);
> -    } while ((bytes == 0) && ferror(fp) && (errno == EINTR));
> -    return bytes;
> -}
> -
> -static int stdio_pclose(void *opaque)
> -{
> -    QEMUFileStdio *s = opaque;
> -    int ret;
> -    ret = pclose(s->stdio_file);
> -    g_free(s);
> -    return ret;
> -}
> -
> -static int stdio_fclose(void *opaque)
> -{
> -    QEMUFileStdio *s = opaque;
> -    fclose(s->stdio_file);
> -    g_free(s);
> -    return 0;
> -}
> -
> -QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
> -{
> -    QEMUFileStdio *s;
> -
> -    if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
> -        fprintf(stderr, "qemu_popen: Argument validity check failed\n");
> -        return NULL;
> -    }
> -
> -    s = g_malloc0(sizeof(QEMUFileStdio));
> -
> -    s->stdio_file = stdio_file;
> -
> -    if(mode[0] == 'r') {
> -        s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_pclose,
> -                                NULL, NULL, NULL);
> -    } else {
> -        s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_pclose,
> -                                NULL, NULL, NULL);
> -    }
> -    return s->file;
> -}
> -
> -QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
> -{
> -    FILE *popen_file;
> -
> -    popen_file = popen(command, mode);
> -    if(popen_file == NULL) {
> -        return NULL;
> -    }
> -
> -    return qemu_popen(popen_file, mode);
> -}
> -
> -int qemu_stdio_fd(QEMUFile *f)
> -{
> -    QEMUFileStdio *p;
> -    int fd;
> -
> -    p = (QEMUFileStdio *)f->opaque;
> -    fd = fileno(p->stdio_file);
> -
> -    return fd;
> -}
> -
> -QEMUFile *qemu_fdopen(int fd, const char *mode)
> -{
> -    QEMUFileStdio *s;
> -
> -    if (mode == NULL ||
> -       (mode[0] != 'r' && mode[0] != 'w') ||
> -       mode[1] != 'b' || mode[2] != 0) {
> -        fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
> -        return NULL;
> -    }
> -
> -    s = g_malloc0(sizeof(QEMUFileStdio));
> -    s->stdio_file = fdopen(fd, mode);
> -    if (!s->stdio_file)
> -        goto fail;
> -
> -    if(mode[0] == 'r') {
> -        s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_fclose,
> -                                NULL, NULL, NULL);
> -    } else {
> -        s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_fclose,
> -                                NULL, NULL, NULL);
> -    }
> -    return s->file;
> -
> -fail:
> -    g_free(s);
> -    return NULL;
> -}
> -
> -QEMUFile *qemu_fopen_socket(int fd)
> -{
> -    QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
> -
> -    s->fd = fd;
> -    s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close,
> -                            NULL, NULL, NULL);
> -    return s->file;
> -}
> -
> -static int file_put_buffer(void *opaque, const uint8_t *buf,
> -                            int64_t pos, int size)
> -{
> -    QEMUFileStdio *s = opaque;
> -    fseek(s->stdio_file, pos, SEEK_SET);
> -    return fwrite(buf, 1, size, s->stdio_file);
> -}
> -
> -static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
> -{
> -    QEMUFileStdio *s = opaque;
> -    fseek(s->stdio_file, pos, SEEK_SET);
> -    return fread(buf, 1, size, s->stdio_file);
> -}
> -
> -QEMUFile *qemu_fopen(const char *filename, const char *mode)
> -{
> -    QEMUFileStdio *s;
> -
> -    if (mode == NULL ||
> -       (mode[0] != 'r' && mode[0] != 'w') ||
> -       mode[1] != 'b' || mode[2] != 0) {
> -        fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
> -        return NULL;
> -    }
> -
> -    s = g_malloc0(sizeof(QEMUFileStdio));
> -
> -    s->stdio_file = fopen(filename, mode);
> -    if (!s->stdio_file)
> -        goto fail;
> -
> -    if(mode[0] == 'w') {
> -        s->file = qemu_fopen_ops(s, file_put_buffer, NULL, stdio_fclose,
> -                                NULL, NULL, NULL);
> -    } else {
> -        s->file = qemu_fopen_ops(s, NULL, file_get_buffer, stdio_fclose,
> -                              NULL, NULL, NULL);
> -    }
> -    return s->file;
> -fail:
> -    g_free(s);
> -    return NULL;
> -}
> -
>  static int block_put_buffer(void *opaque, const uint8_t *buf,
>                            int64_t pos, int size)
>  {
> @@ -402,278 +180,6 @@ static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
>     return qemu_fopen_ops(bs, NULL, block_get_buffer, bdrv_fclose, NULL, NULL, NULL);
>  }
>
> -QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
> -                         QEMUFileGetBufferFunc *get_buffer,
> -                         QEMUFileCloseFunc *close,
> -                         QEMUFileRateLimit *rate_limit,
> -                         QEMUFileSetRateLimit *set_rate_limit,
> -                         QEMUFileGetRateLimit *get_rate_limit)
> -{
> -    QEMUFile *f;
> -
> -    f = g_malloc0(sizeof(QEMUFile));
> -
> -    f->opaque = opaque;
> -    f->put_buffer = put_buffer;
> -    f->get_buffer = get_buffer;
> -    f->close = close;
> -    f->rate_limit = rate_limit;
> -    f->set_rate_limit = set_rate_limit;
> -    f->get_rate_limit = get_rate_limit;
> -    f->is_write = 0;
> -
> -    return f;
> -}
> -
> -int qemu_file_has_error(QEMUFile *f)
> -{
> -    return f->has_error;
> -}
> -
> -void qemu_file_set_error(QEMUFile *f)
> -{
> -    f->has_error = 1;
> -}
> -
> -void qemu_fflush(QEMUFile *f)
> -{
> -    if (!f->put_buffer)
> -        return;
> -
> -    if (f->is_write && f->buf_index > 0) {
> -        int len;
> -
> -        len = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
> -        if (len > 0)
> -            f->buf_offset += f->buf_index;
> -        else
> -            f->has_error = 1;
> -        f->buf_index = 0;
> -    }
> -}
> -
> -static void qemu_fill_buffer(QEMUFile *f)
> -{
> -    int len;
> -
> -    if (!f->get_buffer)
> -        return;
> -
> -    if (f->is_write)
> -        abort();
> -
> -    len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
> -    if (len > 0) {
> -        f->buf_index = 0;
> -        f->buf_size = len;
> -        f->buf_offset += len;
> -    } else if (len != -EAGAIN)
> -        f->has_error = 1;
> -}
> -
> -int qemu_fclose(QEMUFile *f)
> -{
> -    int ret = 0;
> -    qemu_fflush(f);
> -    if (f->close)
> -        ret = f->close(f->opaque);
> -    g_free(f);
> -    return ret;
> -}
> -
> -void qemu_file_put_notify(QEMUFile *f)
> -{
> -    f->put_buffer(f->opaque, NULL, 0, 0);
> -}
> -
> -void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
> -{
> -    int l;
> -
> -    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
> -        fprintf(stderr,
> -                "Attempted to write to buffer while read buffer is not empty\n");
> -        abort();
> -    }
> -
> -    while (!f->has_error && size > 0) {
> -        l = IO_BUF_SIZE - f->buf_index;
> -        if (l > size)
> -            l = size;
> -        memcpy(f->buf + f->buf_index, buf, l);
> -        f->is_write = 1;
> -        f->buf_index += l;
> -        buf += l;
> -        size -= l;
> -        if (f->buf_index >= IO_BUF_SIZE)
> -            qemu_fflush(f);
> -    }
> -}
> -
> -void qemu_put_byte(QEMUFile *f, int v)
> -{
> -    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
> -        fprintf(stderr,
> -                "Attempted to write to buffer while read buffer is not empty\n");
> -        abort();
> -    }
> -
> -    f->buf[f->buf_index++] = v;
> -    f->is_write = 1;
> -    if (f->buf_index >= IO_BUF_SIZE)
> -        qemu_fflush(f);
> -}
> -
> -int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
> -{
> -    int size, l;
> -
> -    if (f->is_write)
> -        abort();
> -
> -    size = size1;
> -    while (size > 0) {
> -        l = f->buf_size - f->buf_index;
> -        if (l == 0) {
> -            qemu_fill_buffer(f);
> -            l = f->buf_size - f->buf_index;
> -            if (l == 0)
> -                break;
> -        }
> -        if (l > size)
> -            l = size;
> -        memcpy(buf, f->buf + f->buf_index, l);
> -        f->buf_index += l;
> -        buf += l;
> -        size -= l;
> -    }
> -    return size1 - size;
> -}
> -
> -static int qemu_peek_byte(QEMUFile *f)
> -{
> -    if (f->is_write)
> -        abort();
> -
> -    if (f->buf_index >= f->buf_size) {
> -        qemu_fill_buffer(f);
> -        if (f->buf_index >= f->buf_size)
> -            return 0;
> -    }
> -    return f->buf[f->buf_index];
> -}
> -
> -int qemu_get_byte(QEMUFile *f)
> -{
> -    if (f->is_write)
> -        abort();
> -
> -    if (f->buf_index >= f->buf_size) {
> -        qemu_fill_buffer(f);
> -        if (f->buf_index >= f->buf_size)
> -            return 0;
> -    }
> -    return f->buf[f->buf_index++];
> -}
> -
> -int64_t qemu_ftell(QEMUFile *f)
> -{
> -    return f->buf_offset - f->buf_size + f->buf_index;
> -}
> -
> -int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
> -{
> -    if (whence == SEEK_SET) {
> -        /* nothing to do */
> -    } else if (whence == SEEK_CUR) {
> -        pos += qemu_ftell(f);
> -    } else {
> -        /* SEEK_END not supported */
> -        return -1;
> -    }
> -    if (f->put_buffer) {
> -        qemu_fflush(f);
> -        f->buf_offset = pos;
> -    } else {
> -        f->buf_offset = pos;
> -        f->buf_index = 0;
> -        f->buf_size = 0;
> -    }
> -    return pos;
> -}
> -
> -int qemu_file_rate_limit(QEMUFile *f)
> -{
> -    if (f->rate_limit)
> -        return f->rate_limit(f->opaque);
> -
> -    return 0;
> -}
> -
> -int64_t qemu_file_get_rate_limit(QEMUFile *f)
> -{
> -    if (f->get_rate_limit)
> -        return f->get_rate_limit(f->opaque);
> -
> -    return 0;
> -}
> -
> -int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate)
> -{
> -    /* any failed or completed migration keeps its state to allow probing of
> -     * migration data, but has no associated file anymore */
> -    if (f && f->set_rate_limit)
> -        return f->set_rate_limit(f->opaque, new_rate);
> -
> -    return 0;
> -}
> -
> -void qemu_put_be16(QEMUFile *f, unsigned int v)
> -{
> -    qemu_put_byte(f, v >> 8);
> -    qemu_put_byte(f, v);
> -}
> -
> -void qemu_put_be32(QEMUFile *f, unsigned int v)
> -{
> -    qemu_put_byte(f, v >> 24);
> -    qemu_put_byte(f, v >> 16);
> -    qemu_put_byte(f, v >> 8);
> -    qemu_put_byte(f, v);
> -}
> -
> -void qemu_put_be64(QEMUFile *f, uint64_t v)
> -{
> -    qemu_put_be32(f, v >> 32);
> -    qemu_put_be32(f, v);
> -}
> -
> -unsigned int qemu_get_be16(QEMUFile *f)
> -{
> -    unsigned int v;
> -    v = qemu_get_byte(f) << 8;
> -    v |= qemu_get_byte(f);
> -    return v;
> -}
> -
> -unsigned int qemu_get_be32(QEMUFile *f)
> -{
> -    unsigned int v;
> -    v = qemu_get_byte(f) << 24;
> -    v |= qemu_get_byte(f) << 16;
> -    v |= qemu_get_byte(f) << 8;
> -    v |= qemu_get_byte(f);
> -    return v;
> -}
> -
> -uint64_t qemu_get_be64(QEMUFile *f)
> -{
> -    uint64_t v;
> -    v = (uint64_t)qemu_get_be32(f) << 32;
> -    v |= qemu_get_be32(f);
> -    return v;
> -}
> -
>  /* bool */
>
>  static int get_bool(QEMUFile *f, void *pv, size_t size)
> --
> 1.7.0.4
>
>
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC 8/8] slirp: convert save/load function to visitor interface
  2011-09-19 14:41 ` [Qemu-devel] [RFC 8/8] slirp: convert save/load function to visitor interface Michael Roth
@ 2011-09-30 13:39   ` Anthony Liguori
  2011-09-30 14:08     ` Michael Roth
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-09-30 13:39 UTC (permalink / raw)
  To: Michael Roth; +Cc: aliguori, qemu-devel

On 09/19/2011 09:41 AM, Michael Roth wrote:
> Where possible common routines are used for both input and output, thus
> save on lines of code, theoretically. The added lines here are mostly
> due to extra logic for each save/load routine to manipulate strings into
> a unique field name for each saved field, and in some cases a few extra
> Visitor calls to due list/struct i/o. With some reworking we can
> probably optimize all of these to reduce the amount of added code.
>
> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
> ---
>   slirp/slirp.c |  366 +++++++++++++++++++++++++++++++++-----------------------
>   1 files changed, 216 insertions(+), 150 deletions(-)
>
> diff --git a/slirp/slirp.c b/slirp/slirp.c
> index 19d69eb..8783626 100644
> --- a/slirp/slirp.c
> +++ b/slirp/slirp.c
> @@ -26,6 +26,9 @@
>   #include "qemu-char.h"
>   #include "slirp.h"
>   #include "hw/hw.h"
> +#include "qemu-error.h"
> +
> +#define SLIRP_DELIMITER 42 /* used to separate slirp instances in save/load */
>
>   /* host loopback address */
>   struct in_addr loopback_addr;
> @@ -871,96 +874,171 @@ void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
>           tcp_output(sototcpcb(so));
>   }
>
> -static void slirp_tcp_save(QEMUFile *f, struct tcpcb *tp)
> +static void slirp_tcp_visit(Visitor *v, struct tcpcb *tp, const char *pfield, Error *err)
>   {
> -    int i;
> -
> -    qemu_put_sbe16(f, tp->t_state);
> -    for (i = 0; i<  TCPT_NTIMERS; i++)
> -        qemu_put_sbe16(f, tp->t_timer[i]);
> -    qemu_put_sbe16(f, tp->t_rxtshift);
> -    qemu_put_sbe16(f, tp->t_rxtcur);
> -    qemu_put_sbe16(f, tp->t_dupacks);
> -    qemu_put_be16(f, tp->t_maxseg);
> -    qemu_put_sbyte(f, tp->t_force);
> -    qemu_put_be16(f, tp->t_flags);
> -    qemu_put_be32(f, tp->snd_una);
> -    qemu_put_be32(f, tp->snd_nxt);
> -    qemu_put_be32(f, tp->snd_up);
> -    qemu_put_be32(f, tp->snd_wl1);
> -    qemu_put_be32(f, tp->snd_wl2);
> -    qemu_put_be32(f, tp->iss);
> -    qemu_put_be32(f, tp->snd_wnd);
> -    qemu_put_be32(f, tp->rcv_wnd);
> -    qemu_put_be32(f, tp->rcv_nxt);
> -    qemu_put_be32(f, tp->rcv_up);
> -    qemu_put_be32(f, tp->irs);
> -    qemu_put_be32(f, tp->rcv_adv);
> -    qemu_put_be32(f, tp->snd_max);
> -    qemu_put_be32(f, tp->snd_cwnd);
> -    qemu_put_be32(f, tp->snd_ssthresh);
> -    qemu_put_sbe16(f, tp->t_idle);
> -    qemu_put_sbe16(f, tp->t_rtt);
> -    qemu_put_be32(f, tp->t_rtseq);
> -    qemu_put_sbe16(f, tp->t_srtt);
> -    qemu_put_sbe16(f, tp->t_rttvar);
> -    qemu_put_be16(f, tp->t_rttmin);
> -    qemu_put_be32(f, tp->max_sndwnd);
> -    qemu_put_byte(f, tp->t_oobflags);
> -    qemu_put_byte(f, tp->t_iobc);
> -    qemu_put_sbe16(f, tp->t_softerror);
> -    qemu_put_byte(f, tp->snd_scale);
> -    qemu_put_byte(f, tp->rcv_scale);
> -    qemu_put_byte(f, tp->request_r_scale);
> -    qemu_put_byte(f, tp->requested_s_scale);
> -    qemu_put_be32(f, tp->ts_recent);
> -    qemu_put_be32(f, tp->ts_recent_age);
> -    qemu_put_be32(f, tp->last_ack_sent);
> +    char f[128];
> +    int i, l = 0;
> +    int16_t *ptr;
> +
> +    if (pfield) {
> +        assert(strlen(pfield)<  sizeof(f));
> +        strcpy(f, pfield);
> +        l = strlen(pfield);
> +    }
> +
> +    visit_type_int16_t(v,&tp->t_state, strocat(f, ".t_state", l),&err);
> +    ptr = tp->t_timer;
> +    visit_start_array(v, (void **)&ptr, strocat(f, ".t_timer", l), TCPT_NTIMERS, sizeof(*ptr),&err);
> +    for (i = 0; i<  TCPT_NTIMERS; ++i) {
> +        visit_type_int16_t(v,&ptr[i], NULL,&err);
> +    }
> +    visit_end_array(v,&err);
> +    visit_type_int16_t(v,&tp->t_rxtshift, strocat(f, ".t_rxtshift", l),&err);


Hrm, you should never concat a name like this.  A better approach would be:

visit_start_struct(v, NULL, f);
visit_type_int16_t(v, &tp->t_rxtshift, "t_rxtshift", &err);
...
visit_end_struct(v, NULL, f);

structs indicate hierarchy.  They don't have to correspond to real types.

Regards,

Anthony Liguori

> +    visit_type_int16_t(v,&tp->t_rxtcur, strocat(f, ".t_rxtcur", l),&err);
> +    visit_type_int16_t(v,&tp->t_dupacks, strocat(f, ".t_dupacks", l),&err);
> +    visit_type_uint16_t(v,&tp->t_maxseg, strocat(f, ".t_maxseg", l),&err);
> +    visit_type_uint8_t(v, (uint8_t *)&tp->t_force, strocat(f, ".t_force", l),&err);
> +    visit_type_uint16_t(v,&tp->t_flags, strocat(f, ".t_flags", l),&err);
> +    visit_type_uint32_t(v,&tp->snd_una, strocat(f, ".snd_una", l),&err);
> +    visit_type_uint32_t(v,&tp->snd_nxt, strocat(f, ".snd_nxt", l),&err);
> +    visit_type_uint32_t(v,&tp->snd_up, strocat(f, ".snd_up", l),&err);
> +    visit_type_uint32_t(v,&tp->snd_wl1, strocat(f, ".snd_wl1", l),&err);
> +    visit_type_uint32_t(v,&tp->snd_wl2, strocat(f, ".snd_wl2", l),&err);
> +    visit_type_uint32_t(v,&tp->iss, strocat(f, ".iss", l),&err);
> +    visit_type_uint32_t(v,&tp->snd_wnd, strocat(f, ".snd_wnd", l),&err);
> +    visit_type_uint32_t(v,&tp->rcv_wnd, strocat(f, ".rcv_wnd", l),&err);
> +    visit_type_uint32_t(v,&tp->rcv_nxt, strocat(f, ".rcv_nxt", l),&err);
> +    visit_type_uint32_t(v,&tp->rcv_up, strocat(f, ".rcv_up", l),&err);
> +    visit_type_uint32_t(v,&tp->irs, strocat(f, ".irs", l),&err);
> +    visit_type_uint32_t(v,&tp->rcv_adv, strocat(f, ".rcv_adv", l),&err);
> +    visit_type_uint32_t(v,&tp->snd_max, strocat(f, ".snd_max", l),&err);
> +    visit_type_uint32_t(v,&tp->snd_cwnd, strocat(f, ".snd_cwnd", l),&err);
> +    visit_type_uint32_t(v,&tp->snd_ssthresh, strocat(f, ".snd_ssthresh", l),&err);
> +    visit_type_int16_t(v,&tp->t_idle, strocat(f, ".t_idle", l),&err);
> +    visit_type_int16_t(v,&tp->t_rtt, strocat(f, ".t_rtt", l),&err);
> +    visit_type_uint32_t(v,&tp->t_rtseq, strocat(f, ".t_rtseq", l),&err);
> +    visit_type_int16_t(v,&tp->t_srtt, strocat(f, ".t_srtt", l),&err);
> +    visit_type_int16_t(v,&tp->t_rttvar, strocat(f, ".t_rttvar", l),&err);
> +    visit_type_uint16_t(v,&tp->t_rttmin, strocat(f, ".t_rttmin", l),&err);
> +    visit_type_uint32_t(v,&tp->max_sndwnd, strocat(f, ".max_sndwnd", l),&err);
> +    visit_type_uint8_t(v, (uint8_t *)&tp->t_oobflags, strocat(f, ".t_oobflags", l),&err);
> +    visit_type_uint8_t(v, (uint8_t *)&tp->t_iobc, strocat(f, ".t_iobc", l),&err);
> +    visit_type_int16_t(v,&tp->t_softerror, strocat(f, ".t_softerror", l),&err);
> +    visit_type_uint8_t(v,&tp->snd_scale, strocat(f, ".snd_scale", l),&err);
> +    visit_type_uint8_t(v,&tp->rcv_scale, strocat(f, ".rcv_scale", l),&err);
> +    visit_type_uint8_t(v,&tp->request_r_scale, strocat(f, ".request_r_scale", l),&err);
> +    visit_type_uint8_t(v,&tp->requested_s_scale, strocat(f, ".requested_s_scale", l),&err);
> +    visit_type_uint32_t(v,&tp->ts_recent, strocat(f, ".ts_recent", l),&err);
> +    visit_type_uint32_t(v,&tp->ts_recent_age, strocat(f, ".ts_recent_age", l),&err);
> +    visit_type_uint32_t(v,&tp->last_ack_sent, strocat(f, ".last_ack_sent", l),&err);
>   }
>
> -static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf)
> +static void slirp_tcp_save(Visitor *v, struct tcpcb *tp, const char *pfield, Error *err)
>   {
> -    uint32_t off;
> +    slirp_tcp_visit(v, tp, pfield, err);
> +}
>
> -    qemu_put_be32(f, sbuf->sb_cc);
> -    qemu_put_be32(f, sbuf->sb_datalen);
> +static void slirp_sbuf_save(Visitor *v, struct sbuf *sbuf, const char *pfield, Error *err)
> +{
> +    int32_t off;
> +    char f[128];
> +    int i, l = 0;
> +
> +    if (pfield) {
> +        assert(strlen(pfield)<  sizeof(f));
> +        strcpy(f, pfield);
> +        l = strlen(f);
> +    }
> +
> +    visit_type_uint32_t(v,&sbuf->sb_cc, strocat(f, ".sb_cc", l),&err);
> +    visit_type_uint32_t(v,&sbuf->sb_datalen, strocat(f, ".sb_datalen", l),&err);
>       off = (uint32_t)(sbuf->sb_wptr - sbuf->sb_data);
> -    qemu_put_sbe32(f, off);
> +    visit_type_int32_t(v,&off, strocat(f, ".sb_wptr_off", l),&err);
>       off = (uint32_t)(sbuf->sb_rptr - sbuf->sb_data);
> -    qemu_put_sbe32(f, off);
> -    qemu_put_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
> +    visit_type_int32_t(v,&off, strocat(f, ".sb_rptr_off", l),&err);
> +
> +    visit_start_array(v, (void **)&sbuf->sb_data, strocat(f, ".sb_data", l),
> +                      sbuf->sb_datalen, sizeof(*sbuf->sb_data),&err);
> +    for (i = 0; i<  sbuf->sb_datalen; i++) {
> +        visit_type_uint8_t(v, (uint8_t *)&sbuf->sb_data[i], NULL,&err);
> +    }
> +    visit_end_array(v,&err);
> +}
> +
> +static void slirp_socket_visit(Visitor *v, struct socket *so, const char *pfield, Error *err)
> +{
> +    char f[64];
> +    int l = 0;
> +
> +    if (pfield) {
> +        assert(strlen(pfield)<  sizeof(f));
> +        strcpy(f, pfield);
> +        l = strlen(f);
> +    }
> +    visit_type_int32_t(v,&so->so_urgc, strocat(f, ".so_urgc", l),&err);
> +    visit_type_uint32_t(v,&so->so_faddr.s_addr, strocat(f, ".so_faddr.s_addr", l),&err);
> +    visit_type_uint32_t(v,&so->so_laddr.s_addr, strocat(f, ".so_urgc", l),&err);
> +    visit_type_uint16_t(v,&so->so_fport, strocat(f, ".so_fport", l),&err);
> +    visit_type_uint16_t(v,&so->so_lport, strocat(f, ".so_lport", l),&err);
> +    visit_type_uint8_t(v,&so->so_iptos, strocat(f, ".so_iptos", l),&err);
> +    visit_type_uint8_t(v,&so->so_emu, strocat(f, ".so_emu", l),&err);
> +    visit_type_uint8_t(v,&so->so_type, strocat(f, ".so_type", l),&err);
> +    visit_type_int32_t(v,&so->so_state, strocat(f, ".so_state", l),&err);
>   }
>
> -static void slirp_socket_save(QEMUFile *f, struct socket *so)
> +static void slirp_socket_save(Visitor *v, struct socket *so, const char *pfield, Error *err)
>   {
> -    qemu_put_be32(f, so->so_urgc);
> -    qemu_put_be32(f, so->so_faddr.s_addr);
> -    qemu_put_be32(f, so->so_laddr.s_addr);
> -    qemu_put_be16(f, so->so_fport);
> -    qemu_put_be16(f, so->so_lport);
> -    qemu_put_byte(f, so->so_iptos);
> -    qemu_put_byte(f, so->so_emu);
> -    qemu_put_byte(f, so->so_type);
> -    qemu_put_be32(f, so->so_state);
> -    slirp_sbuf_save(f,&so->so_rcv);
> -    slirp_sbuf_save(f,&so->so_snd);
> -    slirp_tcp_save(f, so->so_tcpcb);
> +    char f[64];
> +    int l = 0;
> +
> +    if (pfield) {
> +        assert(strlen(pfield)<  sizeof(f));
> +        strcpy(f, pfield);
> +        l = strlen(f);
> +    }
> +
> +    slirp_socket_visit(v, so, f, err);
> +
> +    slirp_sbuf_save(v,&so->so_rcv, strocat(f, ".so_rcv", l), err);
> +    slirp_sbuf_save(v,&so->so_snd, strocat(f, ".so_snd", l), err);
> +    slirp_tcp_save(v, so->so_tcpcb, strocat(f, ".so_tcpcb", l), err);
>   }
>
> -static void slirp_bootp_save(QEMUFile *f, Slirp *slirp)
> +static void slirp_bootp_visit(Visitor *v, Slirp *slirp, const char *pfield, Error *err)
>   {
> -    int i;
> +    int i, j, l;
> +    void *ptr;
> +    char f[64];
> +
> +    if (pfield) {
> +        assert(strlen(pfield)<  sizeof(f));
> +        strcpy(f, pfield);
> +        l = strlen(f);
> +    }
>
> +    ptr = slirp->bootp_clients;
> +    visit_start_array(v,&ptr, strocat(f, ".bootp_clients", l), NB_BOOTP_CLIENTS, 8,&err);
>       for (i = 0; i<  NB_BOOTP_CLIENTS; i++) {
> -        qemu_put_be16(f, slirp->bootp_clients[i].allocated);
> -        qemu_put_buffer(f, slirp->bootp_clients[i].macaddr, 6);
> +        visit_type_uint16_t(v,&slirp->bootp_clients[i].allocated, "allocated",&err);
> +        ptr = slirp->bootp_clients[i].macaddr;
> +        visit_start_array(v,&ptr, "macaddr", 6, 1,&err);
> +        for (j = 0; j<  6; j++) {
> +            visit_type_uint8_t(v, (uint8_t *)&slirp->bootp_clients[j], NULL,&err);
> +        }
> +        visit_end_array(v,&err);
>       }
> +    visit_end_array(v,&err);
>   }
>
>   static void slirp_state_save(QEMUFile *f, void *opaque)
>   {
>       Slirp *slirp = opaque;
>       struct ex_list *ex_ptr;
> +    int i = 0;
> +    uint8_t padding;
> +    Visitor *v = qemu_file_get_output_visitor(f);
> +    Error *err = NULL;
> +    char id[32];
>
>       for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
>           if (ex_ptr->ex_pty == 3) {
> @@ -970,70 +1048,44 @@ static void slirp_state_save(QEMUFile *f, void *opaque)
>               if (!so)
>                   continue;
>
> -            qemu_put_byte(f, 42);
> -            slirp_socket_save(f, so);
> +            padding = SLIRP_DELIMITER;
> +            visit_type_uint8_t(v,&padding, "padding",&err);
> +            slirp_socket_save(v, so, strocat(id, "so", strlen(id)), err);
>           }
> -    qemu_put_byte(f, 0);
> +    padding = 0;
> +    visit_type_uint8_t(v,&padding, "padding",&err);
> +
> +    visit_type_uint16_t(v,&slirp->ip_id, "slirp.ip_id",&err);
>
> -    qemu_put_be16(f, slirp->ip_id);
> +    slirp_bootp_visit(v, slirp, "slirp", err);
>
> -    slirp_bootp_save(f, slirp);
> +    if (err) {
> +        error_report("error saving slirp state: %s", error_get_pretty(err));
> +        error_free(err);
> +    }
>   }
>
> -static void slirp_tcp_load(QEMUFile *f, struct tcpcb *tp)
> +static void slirp_tcp_load(Visitor *v, struct tcpcb *tp, const char *pfield, Error *err)
>   {
> -    int i;
> -
> -    tp->t_state = qemu_get_sbe16(f);
> -    for (i = 0; i<  TCPT_NTIMERS; i++)
> -        tp->t_timer[i] = qemu_get_sbe16(f);
> -    tp->t_rxtshift = qemu_get_sbe16(f);
> -    tp->t_rxtcur = qemu_get_sbe16(f);
> -    tp->t_dupacks = qemu_get_sbe16(f);
> -    tp->t_maxseg = qemu_get_be16(f);
> -    tp->t_force = qemu_get_sbyte(f);
> -    tp->t_flags = qemu_get_be16(f);
> -    tp->snd_una = qemu_get_be32(f);
> -    tp->snd_nxt = qemu_get_be32(f);
> -    tp->snd_up = qemu_get_be32(f);
> -    tp->snd_wl1 = qemu_get_be32(f);
> -    tp->snd_wl2 = qemu_get_be32(f);
> -    tp->iss = qemu_get_be32(f);
> -    tp->snd_wnd = qemu_get_be32(f);
> -    tp->rcv_wnd = qemu_get_be32(f);
> -    tp->rcv_nxt = qemu_get_be32(f);
> -    tp->rcv_up = qemu_get_be32(f);
> -    tp->irs = qemu_get_be32(f);
> -    tp->rcv_adv = qemu_get_be32(f);
> -    tp->snd_max = qemu_get_be32(f);
> -    tp->snd_cwnd = qemu_get_be32(f);
> -    tp->snd_ssthresh = qemu_get_be32(f);
> -    tp->t_idle = qemu_get_sbe16(f);
> -    tp->t_rtt = qemu_get_sbe16(f);
> -    tp->t_rtseq = qemu_get_be32(f);
> -    tp->t_srtt = qemu_get_sbe16(f);
> -    tp->t_rttvar = qemu_get_sbe16(f);
> -    tp->t_rttmin = qemu_get_be16(f);
> -    tp->max_sndwnd = qemu_get_be32(f);
> -    tp->t_oobflags = qemu_get_byte(f);
> -    tp->t_iobc = qemu_get_byte(f);
> -    tp->t_softerror = qemu_get_sbe16(f);
> -    tp->snd_scale = qemu_get_byte(f);
> -    tp->rcv_scale = qemu_get_byte(f);
> -    tp->request_r_scale = qemu_get_byte(f);
> -    tp->requested_s_scale = qemu_get_byte(f);
> -    tp->ts_recent = qemu_get_be32(f);
> -    tp->ts_recent_age = qemu_get_be32(f);
> -    tp->last_ack_sent = qemu_get_be32(f);
> +    slirp_tcp_visit(v, tp, pfield, err);
> +
>       tcp_template(tp);
>   }
>
> -static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
> +static int slirp_sbuf_load(Visitor *v, struct sbuf *sbuf, const char *pfield, Error *err)
>   {
>       uint32_t off, sb_cc, sb_datalen;
> +    int l = 0, i;
> +    char f[64];
> +
> +    if (pfield) {
> +        assert(strlen(pfield)<  sizeof(f));
> +        strcpy(f, pfield);
> +        l = strlen(f);
> +    }
>
> -    sb_cc = qemu_get_be32(f);
> -    sb_datalen = qemu_get_be32(f);
> +    visit_type_uint32_t(v,&sb_cc, strocat(f, ".sb_cc", l),&err);
> +    visit_type_uint32_t(v,&sb_datalen, strocat(f, ".sb_datalen", l),&err);
>
>       sbreserve(sbuf, sb_datalen);
>
> @@ -1042,61 +1094,68 @@ static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
>
>       sbuf->sb_cc = sb_cc;
>
> -    off = qemu_get_sbe32(f);
> +    visit_type_uint32_t(v,&off, strocat(f, ".sb_wptr_off", l),&err);
>       sbuf->sb_wptr = sbuf->sb_data + off;
> -    off = qemu_get_sbe32(f);
> +    visit_type_uint32_t(v,&off, strocat(f, ".sb_rptr_off", l),&err);
>       sbuf->sb_rptr = sbuf->sb_data + off;
> -    qemu_get_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
> +
> +    visit_start_array(v, (void **)&sbuf->sb_data, strocat(f, ".sb_data", l),
> +                      sbuf->sb_datalen, sizeof(*sbuf->sb_data),&err);
> +    for (i = 0; i<  sbuf->sb_datalen; i++) {
> +        visit_type_uint8_t(v, (uint8_t *)&sbuf->sb_data[i], NULL,&err);
> +    }
> +    visit_end_array(v,&err);
>
>       return 0;
>   }
>
> -static int slirp_socket_load(QEMUFile *f, struct socket *so)
> +static int slirp_socket_load(Visitor *v, struct socket *so, const char *pfield, Error *err)
>   {
> +    char f[64];
> +    int l = 0;
> +
> +    if (pfield) {
> +        assert(strlen(pfield)<  sizeof(f));
> +        strcpy(f, pfield);
> +        l = strlen(f);
> +    }
> +
> +    assert(v);
>       if (tcp_attach(so)<  0)
>           return -ENOMEM;
>
> -    so->so_urgc = qemu_get_be32(f);
> -    so->so_faddr.s_addr = qemu_get_be32(f);
> -    so->so_laddr.s_addr = qemu_get_be32(f);
> -    so->so_fport = qemu_get_be16(f);
> -    so->so_lport = qemu_get_be16(f);
> -    so->so_iptos = qemu_get_byte(f);
> -    so->so_emu = qemu_get_byte(f);
> -    so->so_type = qemu_get_byte(f);
> -    so->so_state = qemu_get_be32(f);
> -    if (slirp_sbuf_load(f,&so->so_rcv)<  0)
> +    slirp_socket_visit(v, so, pfield, err);
> +
> +    if (slirp_sbuf_load(v,&so->so_rcv, strocat(f, ".so_rcv", l), err)<  0)
>           return -ENOMEM;
> -    if (slirp_sbuf_load(f,&so->so_snd)<  0)
> +    if (slirp_sbuf_load(v,&so->so_snd, strocat(f, ".so_snd", l), err)<  0)
>           return -ENOMEM;
> -    slirp_tcp_load(f, so->so_tcpcb);
> +    slirp_tcp_load(v, so->so_tcpcb, strocat(f, ".so_tcpcb", l), err);
>
>       return 0;
>   }
>
> -static void slirp_bootp_load(QEMUFile *f, Slirp *slirp)
> -{
> -    int i;
> -
> -    for (i = 0; i<  NB_BOOTP_CLIENTS; i++) {
> -        slirp->bootp_clients[i].allocated = qemu_get_be16(f);
> -        qemu_get_buffer(f, slirp->bootp_clients[i].macaddr, 6);
> -    }
> -}
> -
>   static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
>   {
>       Slirp *slirp = opaque;
>       struct ex_list *ex_ptr;
> +    Visitor *v = qemu_file_get_input_visitor(f);
> +    Error *err = NULL;
> +    char id[32];
> +    uint8_t padding, i = 0;
>
> -    while (qemu_get_byte(f)) {
> +    visit_type_uint8_t(v,&padding, "padding",&err);
> +
> +    while (padding == SLIRP_DELIMITER) {
>           int ret;
>           struct socket *so = socreate(slirp);
>
>           if (!so)
>               return -ENOMEM;
>
> -        ret = slirp_socket_load(f, so);
> +        sprintf(id, "slirp[%d]", i++);
> +
> +        ret = slirp_socket_load(v, so, strocat(id, "so", strlen(id)), err);
>
>           if (ret<  0)
>               return ret;
> @@ -1118,12 +1177,19 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
>           so->extra = (void *)ex_ptr->ex_exec;
>       }
>
> +
>       if (version_id>= 2) {
> -        slirp->ip_id = qemu_get_be16(f);
> +        visit_type_uint16_t(v,&slirp->ip_id, "slirp.ip_id",&err);
>       }
>
>       if (version_id>= 3) {
> -        slirp_bootp_load(f, slirp);
> +        slirp_bootp_visit(v, slirp, "slirp", err);
> +    }
> +
> +    if (err) {
> +        error_report("error loading slirp state: %s", error_get_pretty(err));
> +        error_free(err);
> +        return -EINVAL;
>       }
>
>       return 0;

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC 8/8] slirp: convert save/load function to visitor interface
  2011-09-30 13:39   ` Anthony Liguori
@ 2011-09-30 14:08     ` Michael Roth
  0 siblings, 0 replies; 50+ messages in thread
From: Michael Roth @ 2011-09-30 14:08 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: aliguori, qemu-devel

On Fri, 30 Sep 2011 08:39:49 -0500, Anthony Liguori <anthony@codemonkey.ws> wrote:
> On 09/19/2011 09:41 AM, Michael Roth wrote:
> > Where possible common routines are used for both input and output, thus
> > save on lines of code, theoretically. The added lines here are mostly
> > due to extra logic for each save/load routine to manipulate strings into
> > a unique field name for each saved field, and in some cases a few extra
> > Visitor calls to due list/struct i/o. With some reworking we can
> > probably optimize all of these to reduce the amount of added code.
> >
> > Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
> > ---
> >   slirp/slirp.c |  366 +++++++++++++++++++++++++++++++++-----------------------
> >   1 files changed, 216 insertions(+), 150 deletions(-)
> >
> > diff --git a/slirp/slirp.c b/slirp/slirp.c
> > index 19d69eb..8783626 100644
> > --- a/slirp/slirp.c
> > +++ b/slirp/slirp.c
> > @@ -26,6 +26,9 @@
> >   #include "qemu-char.h"
> >   #include "slirp.h"
> >   #include "hw/hw.h"
> > +#include "qemu-error.h"
> > +
> > +#define SLIRP_DELIMITER 42 /* used to separate slirp instances in save/load */
> >
> >   /* host loopback address */
> >   struct in_addr loopback_addr;
> > @@ -871,96 +874,171 @@ void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
> >           tcp_output(sototcpcb(so));
> >   }
> >
> > -static void slirp_tcp_save(QEMUFile *f, struct tcpcb *tp)
> > +static void slirp_tcp_visit(Visitor *v, struct tcpcb *tp, const char *pfield, Error *err)
> >   {
> > -    int i;
> > -
> > -    qemu_put_sbe16(f, tp->t_state);
> > -    for (i = 0; i<  TCPT_NTIMERS; i++)
> > -        qemu_put_sbe16(f, tp->t_timer[i]);
> > -    qemu_put_sbe16(f, tp->t_rxtshift);
> > -    qemu_put_sbe16(f, tp->t_rxtcur);
> > -    qemu_put_sbe16(f, tp->t_dupacks);
> > -    qemu_put_be16(f, tp->t_maxseg);
> > -    qemu_put_sbyte(f, tp->t_force);
> > -    qemu_put_be16(f, tp->t_flags);
> > -    qemu_put_be32(f, tp->snd_una);
> > -    qemu_put_be32(f, tp->snd_nxt);
> > -    qemu_put_be32(f, tp->snd_up);
> > -    qemu_put_be32(f, tp->snd_wl1);
> > -    qemu_put_be32(f, tp->snd_wl2);
> > -    qemu_put_be32(f, tp->iss);
> > -    qemu_put_be32(f, tp->snd_wnd);
> > -    qemu_put_be32(f, tp->rcv_wnd);
> > -    qemu_put_be32(f, tp->rcv_nxt);
> > -    qemu_put_be32(f, tp->rcv_up);
> > -    qemu_put_be32(f, tp->irs);
> > -    qemu_put_be32(f, tp->rcv_adv);
> > -    qemu_put_be32(f, tp->snd_max);
> > -    qemu_put_be32(f, tp->snd_cwnd);
> > -    qemu_put_be32(f, tp->snd_ssthresh);
> > -    qemu_put_sbe16(f, tp->t_idle);
> > -    qemu_put_sbe16(f, tp->t_rtt);
> > -    qemu_put_be32(f, tp->t_rtseq);
> > -    qemu_put_sbe16(f, tp->t_srtt);
> > -    qemu_put_sbe16(f, tp->t_rttvar);
> > -    qemu_put_be16(f, tp->t_rttmin);
> > -    qemu_put_be32(f, tp->max_sndwnd);
> > -    qemu_put_byte(f, tp->t_oobflags);
> > -    qemu_put_byte(f, tp->t_iobc);
> > -    qemu_put_sbe16(f, tp->t_softerror);
> > -    qemu_put_byte(f, tp->snd_scale);
> > -    qemu_put_byte(f, tp->rcv_scale);
> > -    qemu_put_byte(f, tp->request_r_scale);
> > -    qemu_put_byte(f, tp->requested_s_scale);
> > -    qemu_put_be32(f, tp->ts_recent);
> > -    qemu_put_be32(f, tp->ts_recent_age);
> > -    qemu_put_be32(f, tp->last_ack_sent);
> > +    char f[128];
> > +    int i, l = 0;
> > +    int16_t *ptr;
> > +
> > +    if (pfield) {
> > +        assert(strlen(pfield)<  sizeof(f));
> > +        strcpy(f, pfield);
> > +        l = strlen(pfield);
> > +    }
> > +
> > +    visit_type_int16_t(v,&tp->t_state, strocat(f, ".t_state", l),&err);
> > +    ptr = tp->t_timer;
> > +    visit_start_array(v, (void **)&ptr, strocat(f, ".t_timer", l), TCPT_NTIMERS, sizeof(*ptr),&err);
> > +    for (i = 0; i<  TCPT_NTIMERS; ++i) {
> > +        visit_type_int16_t(v,&ptr[i], NULL,&err);
> > +    }
> > +    visit_end_array(v,&err);
> > +    visit_type_int16_t(v,&tp->t_rxtshift, strocat(f, ".t_rxtshift", l),&err);
> 
> 
> Hrm, you should never concat a name like this.  A better approach would be:
> 
> visit_start_struct(v, NULL, f);
> visit_type_int16_t(v, &tp->t_rxtshift, "t_rxtshift", &err);
> ...
> visit_end_struct(v, NULL, f);
> 
> structs indicate hierarchy.  They don't have to correspond to real types.

Yeah, I thought a lot about using visit_start_struct purely for namespacing,
but wasn't sure if that was too hackish. I like it a whole lot better than
manipulating the field names though... I'll rework it and see.

> 
> Regards,
> 
> Anthony Liguori
> 
> > +    visit_type_int16_t(v,&tp->t_rxtcur, strocat(f, ".t_rxtcur", l),&err);
> > +    visit_type_int16_t(v,&tp->t_dupacks, strocat(f, ".t_dupacks", l),&err);
> > +    visit_type_uint16_t(v,&tp->t_maxseg, strocat(f, ".t_maxseg", l),&err);
> > +    visit_type_uint8_t(v, (uint8_t *)&tp->t_force, strocat(f, ".t_force", l),&err);
> > +    visit_type_uint16_t(v,&tp->t_flags, strocat(f, ".t_flags", l),&err);
> > +    visit_type_uint32_t(v,&tp->snd_una, strocat(f, ".snd_una", l),&err);
> > +    visit_type_uint32_t(v,&tp->snd_nxt, strocat(f, ".snd_nxt", l),&err);
> > +    visit_type_uint32_t(v,&tp->snd_up, strocat(f, ".snd_up", l),&err);
> > +    visit_type_uint32_t(v,&tp->snd_wl1, strocat(f, ".snd_wl1", l),&err);
> > +    visit_type_uint32_t(v,&tp->snd_wl2, strocat(f, ".snd_wl2", l),&err);
> > +    visit_type_uint32_t(v,&tp->iss, strocat(f, ".iss", l),&err);
> > +    visit_type_uint32_t(v,&tp->snd_wnd, strocat(f, ".snd_wnd", l),&err);
> > +    visit_type_uint32_t(v,&tp->rcv_wnd, strocat(f, ".rcv_wnd", l),&err);
> > +    visit_type_uint32_t(v,&tp->rcv_nxt, strocat(f, ".rcv_nxt", l),&err);
> > +    visit_type_uint32_t(v,&tp->rcv_up, strocat(f, ".rcv_up", l),&err);
> > +    visit_type_uint32_t(v,&tp->irs, strocat(f, ".irs", l),&err);
> > +    visit_type_uint32_t(v,&tp->rcv_adv, strocat(f, ".rcv_adv", l),&err);
> > +    visit_type_uint32_t(v,&tp->snd_max, strocat(f, ".snd_max", l),&err);
> > +    visit_type_uint32_t(v,&tp->snd_cwnd, strocat(f, ".snd_cwnd", l),&err);
> > +    visit_type_uint32_t(v,&tp->snd_ssthresh, strocat(f, ".snd_ssthresh", l),&err);
> > +    visit_type_int16_t(v,&tp->t_idle, strocat(f, ".t_idle", l),&err);
> > +    visit_type_int16_t(v,&tp->t_rtt, strocat(f, ".t_rtt", l),&err);
> > +    visit_type_uint32_t(v,&tp->t_rtseq, strocat(f, ".t_rtseq", l),&err);
> > +    visit_type_int16_t(v,&tp->t_srtt, strocat(f, ".t_srtt", l),&err);
> > +    visit_type_int16_t(v,&tp->t_rttvar, strocat(f, ".t_rttvar", l),&err);
> > +    visit_type_uint16_t(v,&tp->t_rttmin, strocat(f, ".t_rttmin", l),&err);
> > +    visit_type_uint32_t(v,&tp->max_sndwnd, strocat(f, ".max_sndwnd", l),&err);
> > +    visit_type_uint8_t(v, (uint8_t *)&tp->t_oobflags, strocat(f, ".t_oobflags", l),&err);
> > +    visit_type_uint8_t(v, (uint8_t *)&tp->t_iobc, strocat(f, ".t_iobc", l),&err);
> > +    visit_type_int16_t(v,&tp->t_softerror, strocat(f, ".t_softerror", l),&err);
> > +    visit_type_uint8_t(v,&tp->snd_scale, strocat(f, ".snd_scale", l),&err);
> > +    visit_type_uint8_t(v,&tp->rcv_scale, strocat(f, ".rcv_scale", l),&err);
> > +    visit_type_uint8_t(v,&tp->request_r_scale, strocat(f, ".request_r_scale", l),&err);
> > +    visit_type_uint8_t(v,&tp->requested_s_scale, strocat(f, ".requested_s_scale", l),&err);
> > +    visit_type_uint32_t(v,&tp->ts_recent, strocat(f, ".ts_recent", l),&err);
> > +    visit_type_uint32_t(v,&tp->ts_recent_age, strocat(f, ".ts_recent_age", l),&err);
> > +    visit_type_uint32_t(v,&tp->last_ack_sent, strocat(f, ".last_ack_sent", l),&err);
> >   }
> >
> > -static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf)
> > +static void slirp_tcp_save(Visitor *v, struct tcpcb *tp, const char *pfield, Error *err)
> >   {
> > -    uint32_t off;
> > +    slirp_tcp_visit(v, tp, pfield, err);
> > +}
> >
> > -    qemu_put_be32(f, sbuf->sb_cc);
> > -    qemu_put_be32(f, sbuf->sb_datalen);
> > +static void slirp_sbuf_save(Visitor *v, struct sbuf *sbuf, const char *pfield, Error *err)
> > +{
> > +    int32_t off;
> > +    char f[128];
> > +    int i, l = 0;
> > +
> > +    if (pfield) {
> > +        assert(strlen(pfield)<  sizeof(f));
> > +        strcpy(f, pfield);
> > +        l = strlen(f);
> > +    }
> > +
> > +    visit_type_uint32_t(v,&sbuf->sb_cc, strocat(f, ".sb_cc", l),&err);
> > +    visit_type_uint32_t(v,&sbuf->sb_datalen, strocat(f, ".sb_datalen", l),&err);
> >       off = (uint32_t)(sbuf->sb_wptr - sbuf->sb_data);
> > -    qemu_put_sbe32(f, off);
> > +    visit_type_int32_t(v,&off, strocat(f, ".sb_wptr_off", l),&err);
> >       off = (uint32_t)(sbuf->sb_rptr - sbuf->sb_data);
> > -    qemu_put_sbe32(f, off);
> > -    qemu_put_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
> > +    visit_type_int32_t(v,&off, strocat(f, ".sb_rptr_off", l),&err);
> > +
> > +    visit_start_array(v, (void **)&sbuf->sb_data, strocat(f, ".sb_data", l),
> > +                      sbuf->sb_datalen, sizeof(*sbuf->sb_data),&err);
> > +    for (i = 0; i<  sbuf->sb_datalen; i++) {
> > +        visit_type_uint8_t(v, (uint8_t *)&sbuf->sb_data[i], NULL,&err);
> > +    }
> > +    visit_end_array(v,&err);
> > +}
> > +
> > +static void slirp_socket_visit(Visitor *v, struct socket *so, const char *pfield, Error *err)
> > +{
> > +    char f[64];
> > +    int l = 0;
> > +
> > +    if (pfield) {
> > +        assert(strlen(pfield)<  sizeof(f));
> > +        strcpy(f, pfield);
> > +        l = strlen(f);
> > +    }
> > +    visit_type_int32_t(v,&so->so_urgc, strocat(f, ".so_urgc", l),&err);
> > +    visit_type_uint32_t(v,&so->so_faddr.s_addr, strocat(f, ".so_faddr.s_addr", l),&err);
> > +    visit_type_uint32_t(v,&so->so_laddr.s_addr, strocat(f, ".so_urgc", l),&err);
> > +    visit_type_uint16_t(v,&so->so_fport, strocat(f, ".so_fport", l),&err);
> > +    visit_type_uint16_t(v,&so->so_lport, strocat(f, ".so_lport", l),&err);
> > +    visit_type_uint8_t(v,&so->so_iptos, strocat(f, ".so_iptos", l),&err);
> > +    visit_type_uint8_t(v,&so->so_emu, strocat(f, ".so_emu", l),&err);
> > +    visit_type_uint8_t(v,&so->so_type, strocat(f, ".so_type", l),&err);
> > +    visit_type_int32_t(v,&so->so_state, strocat(f, ".so_state", l),&err);
> >   }
> >
> > -static void slirp_socket_save(QEMUFile *f, struct socket *so)
> > +static void slirp_socket_save(Visitor *v, struct socket *so, const char *pfield, Error *err)
> >   {
> > -    qemu_put_be32(f, so->so_urgc);
> > -    qemu_put_be32(f, so->so_faddr.s_addr);
> > -    qemu_put_be32(f, so->so_laddr.s_addr);
> > -    qemu_put_be16(f, so->so_fport);
> > -    qemu_put_be16(f, so->so_lport);
> > -    qemu_put_byte(f, so->so_iptos);
> > -    qemu_put_byte(f, so->so_emu);
> > -    qemu_put_byte(f, so->so_type);
> > -    qemu_put_be32(f, so->so_state);
> > -    slirp_sbuf_save(f,&so->so_rcv);
> > -    slirp_sbuf_save(f,&so->so_snd);
> > -    slirp_tcp_save(f, so->so_tcpcb);
> > +    char f[64];
> > +    int l = 0;
> > +
> > +    if (pfield) {
> > +        assert(strlen(pfield)<  sizeof(f));
> > +        strcpy(f, pfield);
> > +        l = strlen(f);
> > +    }
> > +
> > +    slirp_socket_visit(v, so, f, err);
> > +
> > +    slirp_sbuf_save(v,&so->so_rcv, strocat(f, ".so_rcv", l), err);
> > +    slirp_sbuf_save(v,&so->so_snd, strocat(f, ".so_snd", l), err);
> > +    slirp_tcp_save(v, so->so_tcpcb, strocat(f, ".so_tcpcb", l), err);
> >   }
> >
> > -static void slirp_bootp_save(QEMUFile *f, Slirp *slirp)
> > +static void slirp_bootp_visit(Visitor *v, Slirp *slirp, const char *pfield, Error *err)
> >   {
> > -    int i;
> > +    int i, j, l;
> > +    void *ptr;
> > +    char f[64];
> > +
> > +    if (pfield) {
> > +        assert(strlen(pfield)<  sizeof(f));
> > +        strcpy(f, pfield);
> > +        l = strlen(f);
> > +    }
> >
> > +    ptr = slirp->bootp_clients;
> > +    visit_start_array(v,&ptr, strocat(f, ".bootp_clients", l), NB_BOOTP_CLIENTS, 8,&err);
> >       for (i = 0; i<  NB_BOOTP_CLIENTS; i++) {
> > -        qemu_put_be16(f, slirp->bootp_clients[i].allocated);
> > -        qemu_put_buffer(f, slirp->bootp_clients[i].macaddr, 6);
> > +        visit_type_uint16_t(v,&slirp->bootp_clients[i].allocated, "allocated",&err);
> > +        ptr = slirp->bootp_clients[i].macaddr;
> > +        visit_start_array(v,&ptr, "macaddr", 6, 1,&err);
> > +        for (j = 0; j<  6; j++) {
> > +            visit_type_uint8_t(v, (uint8_t *)&slirp->bootp_clients[j], NULL,&err);
> > +        }
> > +        visit_end_array(v,&err);
> >       }
> > +    visit_end_array(v,&err);
> >   }
> >
> >   static void slirp_state_save(QEMUFile *f, void *opaque)
> >   {
> >       Slirp *slirp = opaque;
> >       struct ex_list *ex_ptr;
> > +    int i = 0;
> > +    uint8_t padding;
> > +    Visitor *v = qemu_file_get_output_visitor(f);
> > +    Error *err = NULL;
> > +    char id[32];
> >
> >       for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
> >           if (ex_ptr->ex_pty == 3) {
> > @@ -970,70 +1048,44 @@ static void slirp_state_save(QEMUFile *f, void *opaque)
> >               if (!so)
> >                   continue;
> >
> > -            qemu_put_byte(f, 42);
> > -            slirp_socket_save(f, so);
> > +            padding = SLIRP_DELIMITER;
> > +            visit_type_uint8_t(v,&padding, "padding",&err);
> > +            slirp_socket_save(v, so, strocat(id, "so", strlen(id)), err);
> >           }
> > -    qemu_put_byte(f, 0);
> > +    padding = 0;
> > +    visit_type_uint8_t(v,&padding, "padding",&err);
> > +
> > +    visit_type_uint16_t(v,&slirp->ip_id, "slirp.ip_id",&err);
> >
> > -    qemu_put_be16(f, slirp->ip_id);
> > +    slirp_bootp_visit(v, slirp, "slirp", err);
> >
> > -    slirp_bootp_save(f, slirp);
> > +    if (err) {
> > +        error_report("error saving slirp state: %s", error_get_pretty(err));
> > +        error_free(err);
> > +    }
> >   }
> >
> > -static void slirp_tcp_load(QEMUFile *f, struct tcpcb *tp)
> > +static void slirp_tcp_load(Visitor *v, struct tcpcb *tp, const char *pfield, Error *err)
> >   {
> > -    int i;
> > -
> > -    tp->t_state = qemu_get_sbe16(f);
> > -    for (i = 0; i<  TCPT_NTIMERS; i++)
> > -        tp->t_timer[i] = qemu_get_sbe16(f);
> > -    tp->t_rxtshift = qemu_get_sbe16(f);
> > -    tp->t_rxtcur = qemu_get_sbe16(f);
> > -    tp->t_dupacks = qemu_get_sbe16(f);
> > -    tp->t_maxseg = qemu_get_be16(f);
> > -    tp->t_force = qemu_get_sbyte(f);
> > -    tp->t_flags = qemu_get_be16(f);
> > -    tp->snd_una = qemu_get_be32(f);
> > -    tp->snd_nxt = qemu_get_be32(f);
> > -    tp->snd_up = qemu_get_be32(f);
> > -    tp->snd_wl1 = qemu_get_be32(f);
> > -    tp->snd_wl2 = qemu_get_be32(f);
> > -    tp->iss = qemu_get_be32(f);
> > -    tp->snd_wnd = qemu_get_be32(f);
> > -    tp->rcv_wnd = qemu_get_be32(f);
> > -    tp->rcv_nxt = qemu_get_be32(f);
> > -    tp->rcv_up = qemu_get_be32(f);
> > -    tp->irs = qemu_get_be32(f);
> > -    tp->rcv_adv = qemu_get_be32(f);
> > -    tp->snd_max = qemu_get_be32(f);
> > -    tp->snd_cwnd = qemu_get_be32(f);
> > -    tp->snd_ssthresh = qemu_get_be32(f);
> > -    tp->t_idle = qemu_get_sbe16(f);
> > -    tp->t_rtt = qemu_get_sbe16(f);
> > -    tp->t_rtseq = qemu_get_be32(f);
> > -    tp->t_srtt = qemu_get_sbe16(f);
> > -    tp->t_rttvar = qemu_get_sbe16(f);
> > -    tp->t_rttmin = qemu_get_be16(f);
> > -    tp->max_sndwnd = qemu_get_be32(f);
> > -    tp->t_oobflags = qemu_get_byte(f);
> > -    tp->t_iobc = qemu_get_byte(f);
> > -    tp->t_softerror = qemu_get_sbe16(f);
> > -    tp->snd_scale = qemu_get_byte(f);
> > -    tp->rcv_scale = qemu_get_byte(f);
> > -    tp->request_r_scale = qemu_get_byte(f);
> > -    tp->requested_s_scale = qemu_get_byte(f);
> > -    tp->ts_recent = qemu_get_be32(f);
> > -    tp->ts_recent_age = qemu_get_be32(f);
> > -    tp->last_ack_sent = qemu_get_be32(f);
> > +    slirp_tcp_visit(v, tp, pfield, err);
> > +
> >       tcp_template(tp);
> >   }
> >
> > -static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
> > +static int slirp_sbuf_load(Visitor *v, struct sbuf *sbuf, const char *pfield, Error *err)
> >   {
> >       uint32_t off, sb_cc, sb_datalen;
> > +    int l = 0, i;
> > +    char f[64];
> > +
> > +    if (pfield) {
> > +        assert(strlen(pfield)<  sizeof(f));
> > +        strcpy(f, pfield);
> > +        l = strlen(f);
> > +    }
> >
> > -    sb_cc = qemu_get_be32(f);
> > -    sb_datalen = qemu_get_be32(f);
> > +    visit_type_uint32_t(v,&sb_cc, strocat(f, ".sb_cc", l),&err);
> > +    visit_type_uint32_t(v,&sb_datalen, strocat(f, ".sb_datalen", l),&err);
> >
> >       sbreserve(sbuf, sb_datalen);
> >
> > @@ -1042,61 +1094,68 @@ static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
> >
> >       sbuf->sb_cc = sb_cc;
> >
> > -    off = qemu_get_sbe32(f);
> > +    visit_type_uint32_t(v,&off, strocat(f, ".sb_wptr_off", l),&err);
> >       sbuf->sb_wptr = sbuf->sb_data + off;
> > -    off = qemu_get_sbe32(f);
> > +    visit_type_uint32_t(v,&off, strocat(f, ".sb_rptr_off", l),&err);
> >       sbuf->sb_rptr = sbuf->sb_data + off;
> > -    qemu_get_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
> > +
> > +    visit_start_array(v, (void **)&sbuf->sb_data, strocat(f, ".sb_data", l),
> > +                      sbuf->sb_datalen, sizeof(*sbuf->sb_data),&err);
> > +    for (i = 0; i<  sbuf->sb_datalen; i++) {
> > +        visit_type_uint8_t(v, (uint8_t *)&sbuf->sb_data[i], NULL,&err);
> > +    }
> > +    visit_end_array(v,&err);
> >
> >       return 0;
> >   }
> >
> > -static int slirp_socket_load(QEMUFile *f, struct socket *so)
> > +static int slirp_socket_load(Visitor *v, struct socket *so, const char *pfield, Error *err)
> >   {
> > +    char f[64];
> > +    int l = 0;
> > +
> > +    if (pfield) {
> > +        assert(strlen(pfield)<  sizeof(f));
> > +        strcpy(f, pfield);
> > +        l = strlen(f);
> > +    }
> > +
> > +    assert(v);
> >       if (tcp_attach(so)<  0)
> >           return -ENOMEM;
> >
> > -    so->so_urgc = qemu_get_be32(f);
> > -    so->so_faddr.s_addr = qemu_get_be32(f);
> > -    so->so_laddr.s_addr = qemu_get_be32(f);
> > -    so->so_fport = qemu_get_be16(f);
> > -    so->so_lport = qemu_get_be16(f);
> > -    so->so_iptos = qemu_get_byte(f);
> > -    so->so_emu = qemu_get_byte(f);
> > -    so->so_type = qemu_get_byte(f);
> > -    so->so_state = qemu_get_be32(f);
> > -    if (slirp_sbuf_load(f,&so->so_rcv)<  0)
> > +    slirp_socket_visit(v, so, pfield, err);
> > +
> > +    if (slirp_sbuf_load(v,&so->so_rcv, strocat(f, ".so_rcv", l), err)<  0)
> >           return -ENOMEM;
> > -    if (slirp_sbuf_load(f,&so->so_snd)<  0)
> > +    if (slirp_sbuf_load(v,&so->so_snd, strocat(f, ".so_snd", l), err)<  0)
> >           return -ENOMEM;
> > -    slirp_tcp_load(f, so->so_tcpcb);
> > +    slirp_tcp_load(v, so->so_tcpcb, strocat(f, ".so_tcpcb", l), err);
> >
> >       return 0;
> >   }
> >
> > -static void slirp_bootp_load(QEMUFile *f, Slirp *slirp)
> > -{
> > -    int i;
> > -
> > -    for (i = 0; i<  NB_BOOTP_CLIENTS; i++) {
> > -        slirp->bootp_clients[i].allocated = qemu_get_be16(f);
> > -        qemu_get_buffer(f, slirp->bootp_clients[i].macaddr, 6);
> > -    }
> > -}
> > -
> >   static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
> >   {
> >       Slirp *slirp = opaque;
> >       struct ex_list *ex_ptr;
> > +    Visitor *v = qemu_file_get_input_visitor(f);
> > +    Error *err = NULL;
> > +    char id[32];
> > +    uint8_t padding, i = 0;
> >
> > -    while (qemu_get_byte(f)) {
> > +    visit_type_uint8_t(v,&padding, "padding",&err);
> > +
> > +    while (padding == SLIRP_DELIMITER) {
> >           int ret;
> >           struct socket *so = socreate(slirp);
> >
> >           if (!so)
> >               return -ENOMEM;
> >
> > -        ret = slirp_socket_load(f, so);
> > +        sprintf(id, "slirp[%d]", i++);
> > +
> > +        ret = slirp_socket_load(v, so, strocat(id, "so", strlen(id)), err);
> >
> >           if (ret<  0)
> >               return ret;
> > @@ -1118,12 +1177,19 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
> >           so->extra = (void *)ex_ptr->ex_exec;
> >       }
> >
> > +
> >       if (version_id>= 2) {
> > -        slirp->ip_id = qemu_get_be16(f);
> > +        visit_type_uint16_t(v,&slirp->ip_id, "slirp.ip_id",&err);
> >       }
> >
> >       if (version_id>= 3) {
> > -        slirp_bootp_load(f, slirp);
> > +        slirp_bootp_visit(v, slirp, "slirp", err);
> > +    }
> > +
> > +    if (err) {
> > +        error_report("error loading slirp state: %s", error_get_pretty(err));
> > +        error_free(err);
> > +        return -EINVAL;
> >       }
> >
> >       return 0;
> 

-- 
Sincerely,
Mike Roth
IBM Linux Technology Center

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
                   ` (7 preceding siblings ...)
  2011-09-19 14:41 ` [Qemu-devel] [RFC 8/8] slirp: convert save/load function to visitor interface Michael Roth
@ 2011-10-02 20:21 ` Stefan Berger
  2011-10-02 21:08   ` Michael S. Tsirkin
  2011-10-03  6:46 ` Michael S. Tsirkin
  9 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2011-10-02 20:21 UTC (permalink / raw)
  To: Michael Roth; +Cc: aliguori, qemu-devel, Michael S. Tsirkin


> 4) Implement the BERVisitor and make this the default migration protocol.
>
> Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
>
> Thoughts?
Is anyone working on this? If not I may give it a shot (tomorrow++) for 
at least some of the primitives... for enabling vNVRAM metadata of 
course. Indefinite length encoding of constructed data types I suppose 
won't be used otherwise the visitor interface seems wrong for parsing 
and skipping of extra data towards the end of a structure if version n 
wrote the stream and appended some of its version n data and now version 
m < n is trying to read the struct and needs to skip the version [m+1, n 
] data fields ... in that case the de-serialization of the stream should 
probably be stream-driven rather than structure-driven.

    Stefan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-02 20:21 ` [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Stefan Berger
@ 2011-10-02 21:08   ` Michael S. Tsirkin
  2011-10-03 12:55     ` Anthony Liguori
  0 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-02 21:08 UTC (permalink / raw)
  To: Stefan Berger; +Cc: aliguori, Michael Roth, qemu-devel

On Sun, Oct 02, 2011 at 04:21:47PM -0400, Stefan Berger wrote:
> 
> >4) Implement the BERVisitor and make this the default migration protocol.
> >
> >Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
> >
> >Thoughts?
> Is anyone working on this? If not I may give it a shot (tomorrow++)
> for at least some of the primitives... for enabling vNVRAM metadata
> of course. Indefinite length encoding of constructed data types I
> suppose won't be used otherwise the visitor interface seems wrong
> for parsing and skipping of extra data towards the end of a
> structure if version n wrote the stream and appended some of its
> version n data and now version m < n is trying to read the struct
> and needs to skip the version [m+1, n ] data fields ... in that case
> the de-serialization of the stream should probably be stream-driven
> rather than structure-driven.
> 
>    Stefan

Yes I've been struggling with that exactly.
Anthony, any thoughts?


-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
                   ` (8 preceding siblings ...)
  2011-10-02 20:21 ` [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Stefan Berger
@ 2011-10-03  6:46 ` Michael S. Tsirkin
  2011-10-03 12:51   ` Anthony Liguori
  9 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03  6:46 UTC (permalink / raw)
  To: Michael Roth; +Cc: aliguori, qemu-devel

On Mon, Sep 19, 2011 at 09:41:41AM -0500, Michael Roth wrote:
> OVERVIEW
> 
> This patch series implements a QEMUFile Visitor class that's intended to abstract away direct calls to qemu_put_*/qemu_get_* for save/load functions. Currently this is done by always creating a QEMUFileInputVisitor/QEMUFileOutputVisitor pair with each call to qemu_fopen_ops() and maintaining a QEMUFile->I/O Visitor mapping. save/load functions that are to be converted would them simply use lookup routines to get a Visitor based on their QEMUFile arguments. Once these save/load functions are all converted, we can then change the interfaces in bulk and switch over to passing in the Visitor directly.
> 
> An example conversion of Slirp, which still uses old-style save/load routines, is included as part of this series. Anthony I believe has VMState converted over to using Visitors, so theoretically all VMStatified devices are good to go (Anthony: if that's the case feel free to rebase on this or point me to the repo to pull in, and I'll work off the resulting branch).
> 
> PLANS
> 
> Ultimately, the goal is to implement a new migration protocol that is self-describing, such that incompatibilities or migration errors can be more easilly detected. Currently, a simple change in data types for a particular device can introduce subtle bugs that won't be detected by the target, since the target interprets the data according to it's own expectation of what those data types are. Currently the plan is to use ASN.1 BER in place of QEMUFile.
> 
> By using a Visitor interface for migration, we also gain the ability to generate a migration schema (via, say, a JSONSchemaVisitor). This is similar to the VMState schema generator in Anthony's "Add live migration unit tests" series (http://thread.gmane.org/gmane.comp.emulators.qemu/97754/focus=97754), except that it is applicable to both VMState and old-style save/load functions.
> 
> There is still quite a bit a work to get to that point. Anthony addressed some of the issues on the VMState side of things in the aforementioned series, and I believe he already has some patches that convert VMState over to using visitors insteads of qemu_put_*/qemu_get_* directly. That being the case, what's left is:
> 
> 1) Convert old-style save/load functions over to using visitors. I'm not sure what the best approach is for this. We basically have 2 options: either by converting them to VMState, or converting the functions over to using visitors, such as with the example slirp conversion. Even if we do option 2 it would likely need to be done in 2 phases:
> 
> phase 1: a mechanical conversion where we basically replace each qemu_put*/qemu_get* with a visit_*. This allows use to plop in a new, say BERVisitor to switch to using the new migration protocol. It should also be possible to do this without breaking migration compatibility with older versions. It does not however result in a well-specified schema if we were to plop in, say, a JSONSchemaVisitor. This is where we need phase 2.
> 
> phase 2: Currently, runtime state modifies our "schema" the way things are currently done. Slirp instance and virtqueue enumeration, for instance:
> 
> slirp/slirp.c:
>     for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
>         if (ex_ptr->ex_pty == 3) {
>             struct socket *so;
>             so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr,
>                                        ntohs(ex_ptr->ex_fport));
>             if (!so)
>                 continue;
> 
>             qemu_put_byte(f, 42);
>             slirp_socket_save(f, so);
>         }
> 
> hw/virtio.c:
>     for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
>         if (vdev->vq[i].vring.num == 0)
>             break;
> 
>         qemu_put_be32(f, vdev->vq[i].vring.num);
>         qemu_put_be64(f, vdev->vq[i].pa);
>         qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
>         if (vdev->binding->save_queue)
>             vdev->binding->save_queue(vdev->binding_opaque, i, f);
>     }
> 
> 
> To be able to build a schema around this we'd need to do a visit_start_array() or visit_start_list(), to denote a list of entries, and ideally we'd also describe structure of these entries. But often there is no structure of these entries...they may just be values taken from various data structures. So, either we do hacky things like using visitor intefaces to create "fake" structs (we say we're dealing with a struct when we're really not), or we actually put these into an intermediate struct which we then use to assign to store/load migration fields from.
> 
> phase 2 sounds a whole lot like converting over to VMState. So I think we'll only want to go with option 2 in places where converting to VMState is not practical/planned. Or we can hold off on the phase 2 stuff and just focus on getting the new protocol in place ASAP. After which, we can rework things to support generating a well-specified schema.
> 
> 2) Once the conversion stuff is done, we can modify the save/load interfaces in bulk to accept a Visitor in place of a QEMUFile (but continue using the old protocol/QEMUFile*Visitor for the time being)
> 
> 3) Implement migration capabilities negotiation. This was discussed to some extent in the XBZRLE v4 thread (http://lists.gnu.org/archive/html/qemu-devel/2011-08/msg01155.html), basically having a set of migration capabilities that can be queried via QMP. Management tools would then choose the optimal migration settings from the intersection of these.
> 
> 4) Implement the BERVisitor and make this the default migration protocol.
> 
> Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
> 
> Thoughts?

Yes, I think this creates much more work than an upfront conversion
of the protocol to BER, then cleaning up interfaces, would be.

Here are some suggestions:

- Let's make the protocol be BER directly.
  As a first step, use a single octet string for
  the whole of data. Next, start splitting this up.

- Don't try to use arrays at all. They don't make sense
  in either jason or asn. Instead, use sets for unordered
  data and sequences for ordered data.

- Most data should be unordered: we don't normally care
  in which order we get device attributes.
  So we would have set of sequences, and
  each sequence is attribute name (ascii string) + attribute data 

- This does not discuss how we will handle cross version
  migration in a lot of detail. Presumably you expect capability magic to
  work for this. However this does not work for migration to file.
  A self describing format makes a much simpler solution possible:
  separating attributes which destination must parse from those
  it can skip safely. If destination sees an attribute
  that was marked as must parse and does not recognize it,
  migration fails. If not, just skip the attribute.

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03  6:46 ` Michael S. Tsirkin
@ 2011-10-03 12:51   ` Anthony Liguori
  2011-10-03 13:24     ` Michael S. Tsirkin
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 12:51 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: aliguori, Michael Roth, qemu-devel

On 10/03/2011 01:46 AM, Michael S. Tsirkin wrote:
> On Mon, Sep 19, 2011 at 09:41:41AM -0500, Michael Roth wrote:
>> OVERVIEW
>>
>> This patch series implements a QEMUFile Visitor class that's intended to abstract away direct calls to qemu_put_*/qemu_get_* for save/load functions. Currently this is done by always creating a QEMUFileInputVisitor/QEMUFileOutputVisitor pair with each call to qemu_fopen_ops() and maintaining a QEMUFile->I/O Visitor mapping. save/load functions that are to be converted would them simply use lookup routines to get a Visitor based on their QEMUFile arguments. Once these save/load functions are all converted, we can then change the interfaces in bulk and switch over to passing in the Visitor directly.
>>
>> An example conversion of Slirp, which still uses old-style save/load routines, is included as part of this series. Anthony I believe has VMState converted over to using Visitors, so theoretically all VMStatified devices are good to go (Anthony: if that's the case feel free to rebase on this or point me to the repo to pull in, and I'll work off the resulting branch).
>>
>> PLANS
>>
>> Ultimately, the goal is to implement a new migration protocol that is self-describing, such that incompatibilities or migration errors can be more easilly detected. Currently, a simple change in data types for a particular device can introduce subtle bugs that won't be detected by the target, since the target interprets the data according to it's own expectation of what those data types are. Currently the plan is to use ASN.1 BER in place of QEMUFile.
>>
>> By using a Visitor interface for migration, we also gain the ability to generate a migration schema (via, say, a JSONSchemaVisitor). This is similar to the VMState schema generator in Anthony's "Add live migration unit tests" series (http://thread.gmane.org/gmane.comp.emulators.qemu/97754/focus=97754), except that it is applicable to both VMState and old-style save/load functions.
>>
>> There is still quite a bit a work to get to that point. Anthony addressed some of the issues on the VMState side of things in the aforementioned series, and I believe he already has some patches that convert VMState over to using visitors insteads of qemu_put_*/qemu_get_* directly. That being the case, what's left is:
>>
>> 1) Convert old-style save/load functions over to using visitors. I'm not sure what the best approach is for this. We basically have 2 options: either by converting them to VMState, or converting the functions over to using visitors, such as with the example slirp conversion. Even if we do option 2 it would likely need to be done in 2 phases:
>>
>> phase 1: a mechanical conversion where we basically replace each qemu_put*/qemu_get* with a visit_*. This allows use to plop in a new, say BERVisitor to switch to using the new migration protocol. It should also be possible to do this without breaking migration compatibility with older versions. It does not however result in a well-specified schema if we were to plop in, say, a JSONSchemaVisitor. This is where we need phase 2.
>>
>> phase 2: Currently, runtime state modifies our "schema" the way things are currently done. Slirp instance and virtqueue enumeration, for instance:
>>
>> slirp/slirp.c:
>>      for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
>>          if (ex_ptr->ex_pty == 3) {
>>              struct socket *so;
>>              so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr,
>>                                         ntohs(ex_ptr->ex_fport));
>>              if (!so)
>>                  continue;
>>
>>              qemu_put_byte(f, 42);
>>              slirp_socket_save(f, so);
>>          }
>>
>> hw/virtio.c:
>>      for (i = 0; i<  VIRTIO_PCI_QUEUE_MAX; i++) {
>>          if (vdev->vq[i].vring.num == 0)
>>              break;
>>
>>          qemu_put_be32(f, vdev->vq[i].vring.num);
>>          qemu_put_be64(f, vdev->vq[i].pa);
>>          qemu_put_be16s(f,&vdev->vq[i].last_avail_idx);
>>          if (vdev->binding->save_queue)
>>              vdev->binding->save_queue(vdev->binding_opaque, i, f);
>>      }
>>
>>
>> To be able to build a schema around this we'd need to do a visit_start_array() or visit_start_list(), to denote a list of entries, and ideally we'd also describe structure of these entries. But often there is no structure of these entries...they may just be values taken from various data structures. So, either we do hacky things like using visitor intefaces to create "fake" structs (we say we're dealing with a struct when we're really not), or we actually put these into an intermediate struct which we then use to assign to store/load migration fields from.
>>
>> phase 2 sounds a whole lot like converting over to VMState. So I think we'll only want to go with option 2 in places where converting to VMState is not practical/planned. Or we can hold off on the phase 2 stuff and just focus on getting the new protocol in place ASAP. After which, we can rework things to support generating a well-specified schema.
>>
>> 2) Once the conversion stuff is done, we can modify the save/load interfaces in bulk to accept a Visitor in place of a QEMUFile (but continue using the old protocol/QEMUFile*Visitor for the time being)
>>
>> 3) Implement migration capabilities negotiation. This was discussed to some extent in the XBZRLE v4 thread (http://lists.gnu.org/archive/html/qemu-devel/2011-08/msg01155.html), basically having a set of migration capabilities that can be queried via QMP. Management tools would then choose the optimal migration settings from the intersection of these.
>>
>> 4) Implement the BERVisitor and make this the default migration protocol.
>>
>> Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
>>
>> Thoughts?
>
> Yes, I think this creates much more work than an upfront conversion
> of the protocol to BER, then cleaning up interfaces, would be.
>
> Here are some suggestions:
>
> - Let's make the protocol be BER directly.
>    As a first step, use a single octet string for
>    the whole of data. Next, start splitting this up.

This can't be done without breaking the old style migration protocol.  I don't 
have a problem with that but I do have a problem with repeatedly breaking 
migration protocol.

The Visitor interface is very little abstraction over a native BER interface. 
It lets us introduce BER incrementally while still supporting the old protocol.

>
> - Don't try to use arrays at all. They don't make sense
>    in either jason or asn. Instead, use sets for unordered
>    data and sequences for ordered data.

sequences map to visit_start_array() pretty well. You would do something like:

visit_start_array(v, "entries", errp);
for (int i = 0; i < s->size; i++) {
     visit_type_int(v, NULL, &s->entry[i], errp);
}
visit_end_array(v, errp);

> - Most data should be unordered: we don't normally care
>    in which order we get device attributes.
>    So we would have set of sequences, and
>    each sequence is attribute name (ascii string) + attribute data

I'd actually be inclined to make everything ordered by default and not encode 
explicit field names.  I'm afraid that encoding field names on the wire is going 
to easily increase the amount of device state by an order of magnitude.

> - This does not discuss how we will handle cross version
>    migration in a lot of detail. Presumably you expect capability magic to
>    work for this. However this does not work for migration to file.
>    A self describing format makes a much simpler solution possible:
>    separating attributes which destination must parse from those
>    it can skip safely. If destination sees an attribute
>    that was marked as must parse and does not recognize it,
>    migration fails. If not, just skip the attribute.

Compatibility and the wire format are more or less orthogonal.  A self 
describing format helps eliminate a class of bugs but getting compatibility 
right requires quite a bit more.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-02 21:08   ` Michael S. Tsirkin
@ 2011-10-03 12:55     ` Anthony Liguori
  2011-10-03 13:10       ` Stefan Berger
  2011-10-03 13:38       ` Michael S. Tsirkin
  0 siblings, 2 replies; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 12:55 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel, aliguori, Michael Roth, Stefan Berger

On 10/02/2011 04:08 PM, Michael S. Tsirkin wrote:
> On Sun, Oct 02, 2011 at 04:21:47PM -0400, Stefan Berger wrote:
>>
>>> 4) Implement the BERVisitor and make this the default migration protocol.
>>>
>>> Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
>>>
>>> Thoughts?
>> Is anyone working on this? If not I may give it a shot (tomorrow++)
>> for at least some of the primitives... for enabling vNVRAM metadata
>> of course. Indefinite length encoding of constructed data types I
>> suppose won't be used otherwise the visitor interface seems wrong
>> for parsing and skipping of extra data towards the end of a
>> structure if version n wrote the stream and appended some of its
>> version n data and now version m<  n is trying to read the struct
>> and needs to skip the version [m+1, n ] data fields ... in that case
>> the de-serialization of the stream should probably be stream-driven
>> rather than structure-driven.
>>
>>     Stefan
>
> Yes I've been struggling with that exactly.
> Anthony, any thoughts?

It just depends on how you write your visitor.  If you used sequences, you'd 
probably do something like this:

start_struct ->
   check for sequence tag, push starting offset and size onto stack
   increment offset to next tag

type_int (et al) ->
   check for explicit type, parse data
   increment offset to next tag

end_struct ->
   pop starting offset and size to temp variables
   set offset to starting offset + size

This is roughly how the QMP input marshaller works FWIW.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 12:55     ` Anthony Liguori
@ 2011-10-03 13:10       ` Stefan Berger
  2011-10-03 13:18         ` Anthony Liguori
  2011-10-03 13:38       ` Michael S. Tsirkin
  1 sibling, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2011-10-03 13:10 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, aliguori, Michael Roth, Michael S. Tsirkin

On 10/03/2011 08:55 AM, Anthony Liguori wrote:
> On 10/02/2011 04:08 PM, Michael S. Tsirkin wrote:
>> On Sun, Oct 02, 2011 at 04:21:47PM -0400, Stefan Berger wrote:
>>>
>>>> 4) Implement the BERVisitor and make this the default migration 
>>>> protocol.
>>>>
>>>> Most of the work will be in 1), though with the implementation in 
>>>> this series we should be able to do it incrementally. I'm not sure 
>>>> if the best approach is doing the mechanical phase 1 conversion, 
>>>> then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 
>>>> 1), or just doing VMState conversions which gives basically the 
>>>> same capabilities as phase 1 + 2.
>>>>
>>>> Thoughts?
>>> Is anyone working on this? If not I may give it a shot (tomorrow++)
>>> for at least some of the primitives... for enabling vNVRAM metadata
>>> of course. Indefinite length encoding of constructed data types I
>>> suppose won't be used otherwise the visitor interface seems wrong
>>> for parsing and skipping of extra data towards the end of a
>>> structure if version n wrote the stream and appended some of its
>>> version n data and now version m<  n is trying to read the struct
>>> and needs to skip the version [m+1, n ] data fields ... in that case
>>> the de-serialization of the stream should probably be stream-driven
>>> rather than structure-driven.
>>>
>>>     Stefan
>>
>> Yes I've been struggling with that exactly.
>> Anthony, any thoughts?
>
> It just depends on how you write your visitor.  If you used sequences, 
> you'd probably do something like this:
>
> start_struct ->
>   check for sequence tag, push starting offset and size onto stack
>   increment offset to next tag
>
> type_int (et al) ->
>   check for explicit type, parse data
>   increment offset to next tag
>
> end_struct ->
>   pop starting offset and size to temp variables
>   set offset to starting offset + size
>
> This is roughly how the QMP input marshaller works FWIW.
>
I am doing that. Indefinite length encoding *would* be a problem because 
you cannot push the size onto the stack so that you could skip to the 
end of the structure.

    Stefan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:10       ` Stefan Berger
@ 2011-10-03 13:18         ` Anthony Liguori
  2011-10-03 13:30           ` Michael S. Tsirkin
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 13:18 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, aliguori, Michael Roth, Michael S. Tsirkin

On 10/03/2011 08:10 AM, Stefan Berger wrote:
> I am doing that. Indefinite length encoding *would* be a problem because you
> cannot push the size onto the stack so that you could skip to the end of the
> structure.

For an indefinite length encoding, you just have to keep reading the stream at 
end_struct until you hit the canary tag ignoring anything you encounter.

Regards,

Anthony Liguori

>
> Stefan
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 12:51   ` Anthony Liguori
@ 2011-10-03 13:24     ` Michael S. Tsirkin
  2011-10-03 13:43       ` Anthony Liguori
  0 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 13:24 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: aliguori, Michael Roth, qemu-devel

On Mon, Oct 03, 2011 at 07:51:00AM -0500, Anthony Liguori wrote:
> >Here are some suggestions:
> >
> >- Let's make the protocol be BER directly.
> >   As a first step, use a single octet string for
> >   the whole of data. Next, start splitting this up.
> 
> This can't be done without breaking the old style migration
> protocol.  I don't have a problem with that but I do have a problem
> with repeatedly breaking migration protocol.

As long as this is within a release cycle, is this a real problem?

I think switching to BER then imediately releasing will be
a mistake. Let's switch, iron out bugs and add features.

> The Visitor interface is very little abstraction over a native BER
> interface. It lets us introduce BER incrementally while still
> supporting the old protocol.
> 
> >
> >- Don't try to use arrays at all. They don't make sense
> >   in either jason or asn. Instead, use sets for unordered
> >   data and sequences for ordered data.
> 
> sequences map to visit_start_array() pretty well. You would do something like:
> 
> visit_start_array(v, "entries", errp);
> for (int i = 0; i < s->size; i++) {
>     visit_type_int(v, NULL, &s->entry[i], errp);
> }
> visit_end_array(v, errp);

Sequences can encode structures not just arrays.
How would you encode this for example:

SEQUENCE OF { VQN: INTEGER, SEQUENCE { OPTIONAL VECTOR: INTEGER}  }

This is a natural description of a virtio device.

Yes we can unwrap this to a structure of arrays but
this is tweaking protocol to match implementation.


> >- Most data should be unordered: we don't normally care
> >   in which order we get device attributes.
> >   So we would have set of sequences, and
> >   each sequence is attribute name (ascii string) + attribute data
> 
> I'd actually be inclined to make everything ordered by default and
> not encode explicit field names.  I'm afraid that encoding field
> names on the wire is going to easily increase the amount of device
> state by an order of magnitude.

Assuming we worry about space used by name tags, let's use
content-specific tags.

> >- This does not discuss how we will handle cross version
> >   migration in a lot of detail. Presumably you expect capability magic to
> >   work for this. However this does not work for migration to file.
> >   A self describing format makes a much simpler solution possible:
> >   separating attributes which destination must parse from those
> >   it can skip safely. If destination sees an attribute
> >   that was marked as must parse and does not recognize it,
> >   migration fails. If not, just skip the attribute.
> 
> Compatibility and the wire format are more or less orthogonal.  A
> self describing format helps eliminate a class of bugs but getting
> compatibility right requires quite a bit more.
> 
> Regards,
> 
> Anthony Liguori

So I think we do care about compatibility.
I think this is by far the most interesting part of the problem.
And making that robust IMO pretty much requires not relying
on field order whenever we are not absolutely sure
no other order makes sense.

Content-specific tags as suggested above let us do unordered.
We lose the ability to easily examine
input by external tools but without field names
they won't be able to parse it so it's not all that valuable anyway.


-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:18         ` Anthony Liguori
@ 2011-10-03 13:30           ` Michael S. Tsirkin
  2011-10-03 13:48             ` Anthony Liguori
  0 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 13:30 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, aliguori, Michael Roth, Stefan Berger

On Mon, Oct 03, 2011 at 08:18:31AM -0500, Anthony Liguori wrote:
> On 10/03/2011 08:10 AM, Stefan Berger wrote:
> >I am doing that. Indefinite length encoding *would* be a problem because you
> >cannot push the size onto the stack so that you could skip to the end of the
> >structure.
> 
> For an indefinite length encoding, you just have to keep reading the
> stream at end_struct until you hit the canary tag ignoring anything
> you encounter.
> 
> Regards,
> 
> Anthony Liguori

That's not exactly right: one indefinite length encoding can be nested
within the other. So what we must do is keep reading, read out,
decode and skip regular length encodings, and count the
nesting of indefinite length encodings. When we see bit 7 set,
we increase nesting. When we see end of content, we descrease nesting.
Stop when nesting reaches 0.

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 12:55     ` Anthony Liguori
  2011-10-03 13:10       ` Stefan Berger
@ 2011-10-03 13:38       ` Michael S. Tsirkin
  2011-10-03 13:51         ` Anthony Liguori
  1 sibling, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 13:38 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, aliguori, Michael Roth, Stefan Berger

On Mon, Oct 03, 2011 at 07:55:48AM -0500, Anthony Liguori wrote:
> On 10/02/2011 04:08 PM, Michael S. Tsirkin wrote:
> >On Sun, Oct 02, 2011 at 04:21:47PM -0400, Stefan Berger wrote:
> >>
> >>>4) Implement the BERVisitor and make this the default migration protocol.
> >>>
> >>>Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
> >>>
> >>>Thoughts?
> >>Is anyone working on this? If not I may give it a shot (tomorrow++)
> >>for at least some of the primitives... for enabling vNVRAM metadata
> >>of course. Indefinite length encoding of constructed data types I
> >>suppose won't be used otherwise the visitor interface seems wrong
> >>for parsing and skipping of extra data towards the end of a
> >>structure if version n wrote the stream and appended some of its
> >>version n data and now version m<  n is trying to read the struct
> >>and needs to skip the version [m+1, n ] data fields ... in that case
> >>the de-serialization of the stream should probably be stream-driven
> >>rather than structure-driven.
> >>
> >>    Stefan
> >
> >Yes I've been struggling with that exactly.
> >Anthony, any thoughts?
> 
> It just depends on how you write your visitor.  If you used
> sequences, you'd probably do something like this:
> 
> start_struct ->
>   check for sequence tag, push starting offset and size onto stack
>   increment offset to next tag
> 
> type_int (et al) ->
>   check for explicit type, parse data
>   increment offset to next tag
> 
> end_struct ->
>   pop starting offset and size to temp variables
>   set offset to starting offset + size
> 
> This is roughly how the QMP input marshaller works FWIW.
> 
> Regards,
> 
> Anthony Liguori

One thing I worry about is enabling zero copy for
large string types (e.g. memory migration).

So we need to be able to see a tag for memory page + address,
read that from socket directly at the correct virtual address.

Probably, we can avoid using visitors for memory, and hope
everything else can stand an extra copy since it's small.

But then, why do we worry about the size of
encoded device state as Anthony seems to do?

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:24     ` Michael S. Tsirkin
@ 2011-10-03 13:43       ` Anthony Liguori
  2011-10-03 14:11         ` Michael S. Tsirkin
                           ` (2 more replies)
  0 siblings, 3 replies; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 13:43 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: aliguori, Anthony Liguori, Michael Roth, qemu-devel

On 10/03/2011 08:24 AM, Michael S. Tsirkin wrote:
> On Mon, Oct 03, 2011 at 07:51:00AM -0500, Anthony Liguori wrote:
>>> Here are some suggestions:
>>>
>>> - Let's make the protocol be BER directly.
>>>    As a first step, use a single octet string for
>>>    the whole of data. Next, start splitting this up.
>>
>> This can't be done without breaking the old style migration
>> protocol.  I don't have a problem with that but I do have a problem
>> with repeatedly breaking migration protocol.
>
> As long as this is within a release cycle, is this a real problem?

I think if we try to fit it within a release we'll either end up with a 2 year 
long release or a half-broken conversion.

I'd rather buy ourselves time by supporting both formats.  That way we can 
remove the old format when we're satisfied with the ASN.1 encoding.

>> The Visitor interface is very little abstraction over a native BER
>> interface. It lets us introduce BER incrementally while still
>> supporting the old protocol.
>>
>>>
>>> - Don't try to use arrays at all. They don't make sense
>>>    in either jason or asn. Instead, use sets for unordered
>>>    data and sequences for ordered data.
>>
>> sequences map to visit_start_array() pretty well. You would do something like:
>>
>> visit_start_array(v, "entries", errp);
>> for (int i = 0; i<  s->size; i++) {
>>      visit_type_int(v, NULL,&s->entry[i], errp);
>> }
>> visit_end_array(v, errp);
>
> Sequences can encode structures not just arrays.
> How would you encode this for example:
>
> SEQUENCE OF { VQN: INTEGER, SEQUENCE { OPTIONAL VECTOR: INTEGER}  }

visit_start_array(v, "vqs", errp);
for (i = 0; i < s->n_vqs; i++) {
     // Array elements never have a name, hence NULL name
     visit_start_struct(v, "VirtQueue", NULL, errp);
     visit_type_int(v, &s->vq[i].num, "vqn", errp);

     // Given this sub-struct an arbitrary name.  It could also be anonymous.
     visit_start_struct(v, "MsixInfo", "msix_info", errp);
     if (s->vq[i].msix_enabled) {
         visit_type_int(v, &s->vq[i].vector, "vector", errp);
     }
     visit_end_struct(v, errp);

     visit_end_struct(v, errp);
}
visit_end_array(v, errp);

This would also generate JSON of:

'vqs': [ { 'vqn': 2, 'msix_info': { 'vector': 3 } } ]

> This is a natural description of a virtio device.
>
> Yes we can unwrap this to a structure of arrays but
> this is tweaking protocol to match implementation.

There's no need to from a Visitor perspective.  It doesn't make it any easier.

>
>>> - Most data should be unordered: we don't normally care
>>>    in which order we get device attributes.
>>>    So we would have set of sequences, and
>>>    each sequence is attribute name (ascii string) + attribute data
>>
>> I'd actually be inclined to make everything ordered by default and
>> not encode explicit field names.  I'm afraid that encoding field
>> names on the wire is going to easily increase the amount of device
>> state by an order of magnitude.
>
> Assuming we worry about space used by name tags, let's use
> content-specific tags.
>
>>> - This does not discuss how we will handle cross version
>>>    migration in a lot of detail. Presumably you expect capability magic to
>>>    work for this. However this does not work for migration to file.
>>>    A self describing format makes a much simpler solution possible:
>>>    separating attributes which destination must parse from those
>>>    it can skip safely. If destination sees an attribute
>>>    that was marked as must parse and does not recognize it,
>>>    migration fails. If not, just skip the attribute.
>>
>> Compatibility and the wire format are more or less orthogonal.  A
>> self describing format helps eliminate a class of bugs but getting
>> compatibility right requires quite a bit more.
>>
>> Regards,
>>
>> Anthony Liguori
>
> So I think we do care about compatibility.
> I think this is by far the most interesting part of the problem.
> And making that robust IMO pretty much requires not relying
> on field order whenever we are not absolutely sure
> no other order makes sense.

There are multiple things to consider with compatibility:

1) Creating compatible device models.  This is a qdev problem and can't be 
solved in the protocol.

2) Ensuring we are sending all the data we need to.  I think we solve this 
problem by autogenerating Visitors from the C definitions of the device structures.

3) Having the flexibility to morph what's sent over the wire into something that 
we matches our internal state.  I don't think the wire format solves this.  I 
think what solves this the ability to read the protocol into a data structure 
such that we can manipulate the data structure in memory.

Having the ability to ignore some fields is not enough.  We need to also be able 
to split a single field into multiple fields, and event split a single device 
into multiple devices.  If we're dealing with a tree data structure in memory, 
we have limitless ability to fudge things.

Visitors buy us this.  Right now, we're talking about:

void virtio_marshal(QEMUFile *f, VirtioDevice *vdev);

If you just do BER encoding, that doesn't help us.  We still can only read from 
the wire to a VirtioDevice.   If we do:

void virtio_marshal(Visitor *v, VirtioDevice *vdev);

We can still do BER encoding.  We can also do QEMUFile encoding in the current 
format.

But we also can Visit from a QObject.  That means we can write something that 
reads in BER off the wire and generates QObjects corresponding to whatever is 
sent.  We can then manipulate those QObjects in memory as much as we'd like 
before handing a QObject Visitor to virtio_marshal.

Separating parsing off the wire from handing something to the device models and 
allowing transformation in between is the key to addressing compatibility in a 
robust fashion.

Regards,

Anthony Liguori

> Content-specific tags as suggested above let us do unordered.
> We lose the ability to easily examine
> input by external tools but without field names
> they won't be able to parse it so it's not all that valuable anyway.
>
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:30           ` Michael S. Tsirkin
@ 2011-10-03 13:48             ` Anthony Liguori
  2011-10-03 14:18               ` Michael S. Tsirkin
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 13:48 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: aliguori, Anthony Liguori, Stefan Berger, qemu-devel, Michael Roth

On 10/03/2011 08:30 AM, Michael S. Tsirkin wrote:
> On Mon, Oct 03, 2011 at 08:18:31AM -0500, Anthony Liguori wrote:
>> On 10/03/2011 08:10 AM, Stefan Berger wrote:
>>> I am doing that. Indefinite length encoding *would* be a problem because you
>>> cannot push the size onto the stack so that you could skip to the end of the
>>> structure.
>>
>> For an indefinite length encoding, you just have to keep reading the
>> stream at end_struct until you hit the canary tag ignoring anything
>> you encounter.
>>
>> Regards,
>>
>> Anthony Liguori
>
> That's not exactly right: one indefinite length encoding can be nested
> within the other. So what we must do is keep reading, read out,
> decode and skip regular length encodings, and count the
> nesting of indefinite length encodings. When we see bit 7 set,
> we increase nesting. When we see end of content, we descrease nesting.
> Stop when nesting reaches 0.

Yes, you basically have a skip field in the Visitor.  If skip is set, then don't 
actually marshal to anything.  So:

type_int (et al.)
   if (!skip) {
      store to passed in int
   }
   increment offset

Then when you want to skip to the end of the indefinite, you do something like:

skip_indefinite:
   while tag != CANARY:
     if tag == INT:
       visit_type_int(v, NULL, NULL, errp);
     elif tag == STRING:
       visit_type_str(v, NULL, NULL, errp);
     elif tag == INDEFINITE:
       visit_start_struct(v, NULL, NULL, errp);
       skip_indefinite(v, errp);
       visit_end_struct(v, NULL, NULL, errp);

end_struct:
   v->skip = true;
   skip_indefinite(v, errp);
   v->skip = false;

Regards,

Anthony Liguori

>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:38       ` Michael S. Tsirkin
@ 2011-10-03 13:51         ` Anthony Liguori
  2011-10-03 14:41           ` Michael S. Tsirkin
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 13:51 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: aliguori, Anthony Liguori, Stefan Berger, qemu-devel, Michael Roth

On 10/03/2011 08:38 AM, Michael S. Tsirkin wrote:
> On Mon, Oct 03, 2011 at 07:55:48AM -0500, Anthony Liguori wrote:
>> On 10/02/2011 04:08 PM, Michael S. Tsirkin wrote:
>>> On Sun, Oct 02, 2011 at 04:21:47PM -0400, Stefan Berger wrote:
>>>>
>>>>> 4) Implement the BERVisitor and make this the default migration protocol.
>>>>>
>>>>> Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
>>>>>
>>>>> Thoughts?
>>>> Is anyone working on this? If not I may give it a shot (tomorrow++)
>>>> for at least some of the primitives... for enabling vNVRAM metadata
>>>> of course. Indefinite length encoding of constructed data types I
>>>> suppose won't be used otherwise the visitor interface seems wrong
>>>> for parsing and skipping of extra data towards the end of a
>>>> structure if version n wrote the stream and appended some of its
>>>> version n data and now version m<   n is trying to read the struct
>>>> and needs to skip the version [m+1, n ] data fields ... in that case
>>>> the de-serialization of the stream should probably be stream-driven
>>>> rather than structure-driven.
>>>>
>>>>     Stefan
>>>
>>> Yes I've been struggling with that exactly.
>>> Anthony, any thoughts?
>>
>> It just depends on how you write your visitor.  If you used
>> sequences, you'd probably do something like this:
>>
>> start_struct ->
>>    check for sequence tag, push starting offset and size onto stack
>>    increment offset to next tag
>>
>> type_int (et al) ->
>>    check for explicit type, parse data
>>    increment offset to next tag
>>
>> end_struct ->
>>    pop starting offset and size to temp variables
>>    set offset to starting offset + size
>>
>> This is roughly how the QMP input marshaller works FWIW.
>>
>> Regards,
>>
>> Anthony Liguori
>
> One thing I worry about is enabling zero copy for
> large string types (e.g. memory migration).

Memory shouldn't be done through Visitors.  It should be handled as a special case.

> So we need to be able to see a tag for memory page + address,
> read that from socket directly at the correct virtual address.
>
> Probably, we can avoid using visitors for memory, and hope
> everything else can stand an extra copy since it's small.
>
> But then, why do we worry about the size of
> encoded device state as Anthony seems to do?

There's a significant difference between the cost of something on the wire and 
the cost of doing a memcpy.  The cost of the data on the wire is directly 
proportional to downtime.  So if we increase the size of the device state by a 
factor of 10, we increase the minimum downtime by a factor of 10.

Of course, *if* the size of device state is already negligible with respect to 
the minimum downtime, then it doesn't matter.  This is easy to quantify though. 
  For a normal migration session today, what's the total size of the device 
state in relation to the calculated bandwidth of the minimum downtime?

If it's very small, then we can add names and not worry about it.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:43       ` Anthony Liguori
@ 2011-10-03 14:11         ` Michael S. Tsirkin
  2011-10-03 14:42           ` Anthony Liguori
  2011-10-03 14:15         ` Michael S. Tsirkin
  2011-10-05  2:05         ` Stefan Berger
  2 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 14:11 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: aliguori, Anthony Liguori, Michael Roth, qemu-devel

On Mon, Oct 03, 2011 at 08:43:54AM -0500, Anthony Liguori wrote:
> >>visit_start_array(v, "entries", errp);
> >>for (int i = 0; i<  s->size; i++) {
> >>     visit_type_int(v, NULL,&s->entry[i], errp);
> >>}
> >>visit_end_array(v, errp);
> >
> >Sequences can encode structures not just arrays.
> >How would you encode this for example:
> >
> >SEQUENCE OF { VQN: INTEGER, SEQUENCE { OPTIONAL VECTOR: INTEGER}  }
> 
> visit_start_array(v, "vqs", errp);
> for (i = 0; i < s->n_vqs; i++) {
>     // Array elements never have a name, hence NULL name
>     visit_start_struct(v, "VirtQueue", NULL, errp);
>     visit_type_int(v, &s->vq[i].num, "vqn", errp);
> 
>     // Given this sub-struct an arbitrary name.  It could also be anonymous.
>     visit_start_struct(v, "MsixInfo", "msix_info", errp);
>     if (s->vq[i].msix_enabled) {
>         visit_type_int(v, &s->vq[i].vector, "vector", errp);

Why is this a pointer to vector, btw?

>     }
>     visit_end_struct(v, errp);
> 
>     visit_end_struct(v, errp);
> }
> visit_end_array(v, errp);
> 
> This would also generate JSON of:
> 
> 'vqs': [ { 'vqn': 2, 'msix_info': { 'vector': 3 } } ]

How would optional fields be handled? Specifically
the case where first field in a sequence tells
you the meaning of the following ones?

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:43       ` Anthony Liguori
  2011-10-03 14:11         ` Michael S. Tsirkin
@ 2011-10-03 14:15         ` Michael S. Tsirkin
  2011-10-03 14:55           ` Anthony Liguori
  2011-10-05  2:05         ` Stefan Berger
  2 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 14:15 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: aliguori, Anthony Liguori, Michael Roth, qemu-devel

On Mon, Oct 03, 2011 at 08:43:54AM -0500, Anthony Liguori wrote:
> Having the ability to ignore some fields is not enough.

But it is also really required.

> We need to
> also be able to split a single field into multiple fields, and event
> split a single device into multiple devices.  If we're dealing with
> a tree data structure in memory, we have limitless ability to fudge
> things.
> 
> Visitors buy us this.  Right now, we're talking about:
> 
> void virtio_marshal(QEMUFile *f, VirtioDevice *vdev);
> 
> If you just do BER encoding, that doesn't help us.  We still can
> only read from the wire to a VirtioDevice.   If we do:
> 
> void virtio_marshal(Visitor *v, VirtioDevice *vdev);
> 
> We can still do BER encoding.  We can also do QEMUFile encoding in
> the current format.
> 
> But we also can Visit from a QObject.  That means we can write
> something that reads in BER off the wire and generates QObjects
> corresponding to whatever is sent.  We can then manipulate those
> QObjects in memory as much as we'd like before handing a QObject
> Visitor to virtio_marshal.
> 
> Separating parsing off the wire from handing something to the device
> models and allowing transformation in between is the key to
> addressing compatibility in a robust fashion.

Yes. I agree it is required, and doing this with visitors
does looks like a good way to handle that.

However the ability to skip fields at the protocol level
is also required, and it needs to be handled at the
protocol level. For example, I am worried about
invalid input with a huge array size making us allocate
and read a gigabyte into memory on the assumption that we will want to
manipulate it later. Yes we could introduce artificial
buffer size limits but these might then come to bite us.
If instead we can see that no one needs the array
up front, it will be easy to discard.

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:48             ` Anthony Liguori
@ 2011-10-03 14:18               ` Michael S. Tsirkin
  2011-10-03 14:56                 ` Anthony Liguori
  0 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 14:18 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: aliguori, Anthony Liguori, Stefan Berger, qemu-devel, Michael Roth

On Mon, Oct 03, 2011 at 08:48:05AM -0500, Anthony Liguori wrote:
> On 10/03/2011 08:30 AM, Michael S. Tsirkin wrote:
> >On Mon, Oct 03, 2011 at 08:18:31AM -0500, Anthony Liguori wrote:
> >>On 10/03/2011 08:10 AM, Stefan Berger wrote:
> >>>I am doing that. Indefinite length encoding *would* be a problem because you
> >>>cannot push the size onto the stack so that you could skip to the end of the
> >>>structure.
> >>
> >>For an indefinite length encoding, you just have to keep reading the
> >>stream at end_struct until you hit the canary tag ignoring anything
> >>you encounter.
> >>
> >>Regards,
> >>
> >>Anthony Liguori
> >
> >That's not exactly right: one indefinite length encoding can be nested
> >within the other. So what we must do is keep reading, read out,
> >decode and skip regular length encodings, and count the
> >nesting of indefinite length encodings. When we see bit 7 set,
> >we increase nesting. When we see end of content, we descrease nesting.
> >Stop when nesting reaches 0.
> 
> Yes, you basically have a skip field in the Visitor.  If skip is
> set, then don't actually marshal to anything.  So:
> 
> type_int (et al.)
>   if (!skip) {
>      store to passed in int
>   }
>   increment offset
> 
> Then when you want to skip to the end of the indefinite, you do something like:
> 
> skip_indefinite:
>   while tag != CANARY:
>     if tag == INT:
>       visit_type_int(v, NULL, NULL, errp);
>     elif tag == STRING:
>       visit_type_str(v, NULL, NULL, errp);
>     elif tag == INDEFINITE:
>       visit_start_struct(v, NULL, NULL, errp);
>       skip_indefinite(v, errp);
>       visit_end_struct(v, NULL, NULL, errp);
> 
> end_struct:
>   v->skip = true;
>   skip_indefinite(v, errp);
>   v->skip = false;
> 
> Regards,
> 
> Anthony Liguori

This implementation is recursive, which has the problem
of wasting unlimited resources to skip bad input.
And we really do not need to look at tag at all. Just
the length.

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:51         ` Anthony Liguori
@ 2011-10-03 14:41           ` Michael S. Tsirkin
  2011-10-03 15:00             ` Anthony Liguori
  0 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 14:41 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: aliguori, Anthony Liguori, Stefan Berger, qemu-devel, Michael Roth

On Mon, Oct 03, 2011 at 08:51:10AM -0500, Anthony Liguori wrote:
> On 10/03/2011 08:38 AM, Michael S. Tsirkin wrote:
> >On Mon, Oct 03, 2011 at 07:55:48AM -0500, Anthony Liguori wrote:
> >>On 10/02/2011 04:08 PM, Michael S. Tsirkin wrote:
> >>>On Sun, Oct 02, 2011 at 04:21:47PM -0400, Stefan Berger wrote:
> >>>>
> >>>>>4) Implement the BERVisitor and make this the default migration protocol.
> >>>>>
> >>>>>Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
> >>>>>
> >>>>>Thoughts?
> >>>>Is anyone working on this? If not I may give it a shot (tomorrow++)
> >>>>for at least some of the primitives... for enabling vNVRAM metadata
> >>>>of course. Indefinite length encoding of constructed data types I
> >>>>suppose won't be used otherwise the visitor interface seems wrong
> >>>>for parsing and skipping of extra data towards the end of a
> >>>>structure if version n wrote the stream and appended some of its
> >>>>version n data and now version m<   n is trying to read the struct
> >>>>and needs to skip the version [m+1, n ] data fields ... in that case
> >>>>the de-serialization of the stream should probably be stream-driven
> >>>>rather than structure-driven.
> >>>>
> >>>>    Stefan
> >>>
> >>>Yes I've been struggling with that exactly.
> >>>Anthony, any thoughts?
> >>
> >>It just depends on how you write your visitor.  If you used
> >>sequences, you'd probably do something like this:
> >>
> >>start_struct ->
> >>   check for sequence tag, push starting offset and size onto stack
> >>   increment offset to next tag
> >>
> >>type_int (et al) ->
> >>   check for explicit type, parse data
> >>   increment offset to next tag
> >>
> >>end_struct ->
> >>   pop starting offset and size to temp variables
> >>   set offset to starting offset + size
> >>
> >>This is roughly how the QMP input marshaller works FWIW.
> >>
> >>Regards,
> >>
> >>Anthony Liguori
> >
> >One thing I worry about is enabling zero copy for
> >large string types (e.g. memory migration).
> 
> Memory shouldn't be done through Visitors.  It should be handled as a special case.

OK, that's fine then.

> >So we need to be able to see a tag for memory page + address,
> >read that from socket directly at the correct virtual address.
> >
> >Probably, we can avoid using visitors for memory, and hope
> >everything else can stand an extra copy since it's small.
> >
> >But then, why do we worry about the size of
> >encoded device state as Anthony seems to do?
> 
> There's a significant difference between the cost of something on
> the wire and the cost of doing a memcpy.  The cost of the data on
> the wire is directly proportional to downtime.  So if we increase
> the size of the device state by a factor of 10, we increase the
> minimum downtime by a factor of 10.
> 
> Of course, *if* the size of device state is already negligible with
> respect to the minimum downtime, then it doesn't matter.  This is
> easy to quantify though.  For a normal migration session today,
> what's the total size of the device state in relation to the
> calculated bandwidth of the minimum downtime?
> 
> If it's very small, then we can add names and not worry about it.
> 
> Regards,
> 
> Anthony Liguori

Yes, it's easy to quantify. I think the following gives us
the offset before and after, so the difference is the size
we seek, right?


diff --git a/savevm.c b/savevm.c
index 1feaa70..dbbbcc6 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1543,6 +1543,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
 int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
 {
     SaveStateEntry *se;
+    unsigned long long vm_state_size;
 
     cpu_synchronize_all_states();
 
@@ -1557,6 +1558,8 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
         se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque);
     }
 
+    vm_state_size = qemu_ftell(f);
+    fprintf(stderr, "start size: %lld\n", vm_state_size);
     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
         int len;
 
@@ -1577,6 +1580,8 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
 
         vmstate_save(f, se);
     }
+    vm_state_size = qemu_ftell(f);
+    fprintf(stderr, "end size: %lld\n", vm_state_size);
 
     qemu_put_byte(f, QEMU_VM_EOF);
 

-- 
MST

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 14:11         ` Michael S. Tsirkin
@ 2011-10-03 14:42           ` Anthony Liguori
  2011-10-03 15:29             ` Michael S. Tsirkin
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 14:42 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: aliguori, Michael Roth, qemu-devel

On 10/03/2011 09:11 AM, Michael S. Tsirkin wrote:
> On Mon, Oct 03, 2011 at 08:43:54AM -0500, Anthony Liguori wrote:
>>>> visit_start_array(v, "entries", errp);
>>>> for (int i = 0; i<   s->size; i++) {
>>>>      visit_type_int(v, NULL,&s->entry[i], errp);
>>>> }
>>>> visit_end_array(v, errp);
>>>
>>> Sequences can encode structures not just arrays.
>>> How would you encode this for example:
>>>
>>> SEQUENCE OF { VQN: INTEGER, SEQUENCE { OPTIONAL VECTOR: INTEGER}  }
>>
>> visit_start_array(v, "vqs", errp);
>> for (i = 0; i<  s->n_vqs; i++) {
>>      // Array elements never have a name, hence NULL name
>>      visit_start_struct(v, "VirtQueue", NULL, errp);
>>      visit_type_int(v,&s->vq[i].num, "vqn", errp);
>>
>>      // Given this sub-struct an arbitrary name.  It could also be anonymous.
>>      visit_start_struct(v, "MsixInfo", "msix_info", errp);
>>      if (s->vq[i].msix_enabled) {
>>          visit_type_int(v,&s->vq[i].vector, "vector", errp);
>
> Why is this a pointer to vector, btw?

So you can write a single visit function that works for input or output.

Think of the simple case like:

void visit_simple_type(Visitor *v, SimpleType *t, const char *name, Error **errp)
{
    visit_start_struct(v, "SimpleType", name, errp);
    visit_type_int(v, &t->a, "a", errp);
    visit_type_int(v, &t->b, "b", errp);
    visit_end_struct(v, errp);
}

For complex types like Virtio, you need to do a bit more.  You wouldn't do a 
simple for () {} loop but instead use the Visitor list mechanism.  That would 
eliminate the need to have to marshal n_vqs.

>
>>      }
>>      visit_end_struct(v, errp);
>>
>>      visit_end_struct(v, errp);
>> }
>> visit_end_array(v, errp);
>>
>> This would also generate JSON of:
>>
>> 'vqs': [ { 'vqn': 2, 'msix_info': { 'vector': 3 } } ]
>
> How would optional fields be handled?

As far as the Visitor goes, if something is optional you just don't encode it. 
If you need to key off the presence of a field, presumably you could just check 
to see whether it succeeded or failed to visit that field.  I'm not 100% sure if 
you can do a single input/output visitor when you have optional fields.

My rough thinking is that each device would have a input/output visitor callback 
that took the same signature.  That gives the flexibility of having two separate 
interfaces but in the common case, you just pass the same function for both.

> Specifically
> the case where first field in a sequence tells
> you the meaning of the following ones?

Can you give me the example in ASN.1?

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 14:15         ` Michael S. Tsirkin
@ 2011-10-03 14:55           ` Anthony Liguori
  2011-10-03 15:41             ` Michael S. Tsirkin
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 14:55 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Michael Roth, qemu-devel

On 10/03/2011 09:15 AM, Michael S. Tsirkin wrote:
> On Mon, Oct 03, 2011 at 08:43:54AM -0500, Anthony Liguori wrote:
>> Having the ability to ignore some fields is not enough.
>
> But it is also really required.

I agree.  It's the principle of being conservative in what you send and liberal 
in what you accept.

>> We need to
>> also be able to split a single field into multiple fields, and event
>> split a single device into multiple devices.  If we're dealing with
>> a tree data structure in memory, we have limitless ability to fudge
>> things.
>>
>> Visitors buy us this.  Right now, we're talking about:
>>
>> void virtio_marshal(QEMUFile *f, VirtioDevice *vdev);
>>
>> If you just do BER encoding, that doesn't help us.  We still can
>> only read from the wire to a VirtioDevice.   If we do:
>>
>> void virtio_marshal(Visitor *v, VirtioDevice *vdev);
>>
>> We can still do BER encoding.  We can also do QEMUFile encoding in
>> the current format.
>>
>> But we also can Visit from a QObject.  That means we can write
>> something that reads in BER off the wire and generates QObjects
>> corresponding to whatever is sent.  We can then manipulate those
>> QObjects in memory as much as we'd like before handing a QObject
>> Visitor to virtio_marshal.
>>
>> Separating parsing off the wire from handing something to the device
>> models and allowing transformation in between is the key to
>> addressing compatibility in a robust fashion.
>
> Yes. I agree it is required, and doing this with visitors
> does looks like a good way to handle that.
>
> However the ability to skip fields at the protocol level
> is also required, and it needs to be handled at the
> protocol level. For example, I am worried about
> invalid input with a huge array size making us allocate
> and read a gigabyte into memory on the assumption that we will want to
> manipulate it later. Yes we could introduce artificial
> buffer size limits but these might then come to bite us.
> If instead we can see that no one needs the array
> up front, it will be easy to discard.

Yes, we just were covering in the other parts of the thread.  When you 
end_struct, end_array, or end_list, the visitor can discard whatevers left.

How I see this all evolving in the future is that we would have a formal 
protocol specification.  From that spec, we would generate Visitors.  This would 
handle taking what's on the wire and building an in-memory tree.  If an ASN.1 
sequence that isn't part of the spec is passed, we should just silently discard it.

I think the scenario above is that we have an array in the spec, but the 
implementation no longer uses that array.  Your concern is that its inefficient 
to keep it in memory?

I think there are two cases.  The first is that that array is required in the 
spec, but optional in our implementation and that decision is made at run time. 
  In this case, it needs to get entered into memory.

If the array is no longer needed at all in our implementation, we can certainly 
annotate the protocol spec to skip it and not bring it into memory.  Maybe it's 
an internal spec annotation or something.

All that said, this doesn't seem to be a terribly important problem to solve IMHO.

Regards,

Anthony Liguori

>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 14:18               ` Michael S. Tsirkin
@ 2011-10-03 14:56                 ` Anthony Liguori
  2011-10-03 15:42                   ` Michael S. Tsirkin
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 14:56 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: aliguori, Stefan Berger, qemu-devel, Michael Roth

On 10/03/2011 09:18 AM, Michael S. Tsirkin wrote:
>> skip_indefinite:
>>    while tag != CANARY:
>>      if tag == INT:
>>        visit_type_int(v, NULL, NULL, errp);
>>      elif tag == STRING:
>>        visit_type_str(v, NULL, NULL, errp);
>>      elif tag == INDEFINITE:
>>        visit_start_struct(v, NULL, NULL, errp);
>>        skip_indefinite(v, errp);
>>        visit_end_struct(v, NULL, NULL, errp);
>>
>> end_struct:
>>    v->skip = true;
>>    skip_indefinite(v, errp);
>>    v->skip = false;
>>
>> Regards,
>>
>> Anthony Liguori
>
> This implementation is recursive, which has the problem
> of wasting unlimited resources to skip bad input.
> And we really do not need to look at tag at all. Just
> the length.

That's just one implementation.  You could certainly do a non-recursive 
implementation.

Regards,

Anthony Liguori

>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 14:41           ` Michael S. Tsirkin
@ 2011-10-03 15:00             ` Anthony Liguori
  2011-10-03 15:45               ` Michael S. Tsirkin
  2011-10-05 11:28               ` Michael S. Tsirkin
  0 siblings, 2 replies; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 15:00 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: Michael Roth, aliguori, Anthony Liguori, qemu-devel, Stefan Berger

On 10/03/2011 09:41 AM, Michael S. Tsirkin wrote:
> On Mon, Oct 03, 2011 at 08:51:10AM -0500, Anthony Liguori wrote:
>> On 10/03/2011 08:38 AM, Michael S. Tsirkin wrote:
>>> On Mon, Oct 03, 2011 at 07:55:48AM -0500, Anthony Liguori wrote:
>>>> On 10/02/2011 04:08 PM, Michael S. Tsirkin wrote:
>>>>> On Sun, Oct 02, 2011 at 04:21:47PM -0400, Stefan Berger wrote:
>>>>>>
>>>>>>> 4) Implement the BERVisitor and make this the default migration protocol.
>>>>>>>
>>>>>>> Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
>>>>>>>
>>>>>>> Thoughts?
>>>>>> Is anyone working on this? If not I may give it a shot (tomorrow++)
>>>>>> for at least some of the primitives... for enabling vNVRAM metadata
>>>>>> of course. Indefinite length encoding of constructed data types I
>>>>>> suppose won't be used otherwise the visitor interface seems wrong
>>>>>> for parsing and skipping of extra data towards the end of a
>>>>>> structure if version n wrote the stream and appended some of its
>>>>>> version n data and now version m<    n is trying to read the struct
>>>>>> and needs to skip the version [m+1, n ] data fields ... in that case
>>>>>> the de-serialization of the stream should probably be stream-driven
>>>>>> rather than structure-driven.
>>>>>>
>>>>>>     Stefan
>>>>>
>>>>> Yes I've been struggling with that exactly.
>>>>> Anthony, any thoughts?
>>>>
>>>> It just depends on how you write your visitor.  If you used
>>>> sequences, you'd probably do something like this:
>>>>
>>>> start_struct ->
>>>>    check for sequence tag, push starting offset and size onto stack
>>>>    increment offset to next tag
>>>>
>>>> type_int (et al) ->
>>>>    check for explicit type, parse data
>>>>    increment offset to next tag
>>>>
>>>> end_struct ->
>>>>    pop starting offset and size to temp variables
>>>>    set offset to starting offset + size
>>>>
>>>> This is roughly how the QMP input marshaller works FWIW.
>>>>
>>>> Regards,
>>>>
>>>> Anthony Liguori
>>>
>>> One thing I worry about is enabling zero copy for
>>> large string types (e.g. memory migration).
>>
>> Memory shouldn't be done through Visitors.  It should be handled as a special case.
>
> OK, that's fine then.
>
>>> So we need to be able to see a tag for memory page + address,
>>> read that from socket directly at the correct virtual address.
>>>
>>> Probably, we can avoid using visitors for memory, and hope
>>> everything else can stand an extra copy since it's small.
>>>
>>> But then, why do we worry about the size of
>>> encoded device state as Anthony seems to do?
>>
>> There's a significant difference between the cost of something on
>> the wire and the cost of doing a memcpy.  The cost of the data on
>> the wire is directly proportional to downtime.  So if we increase
>> the size of the device state by a factor of 10, we increase the
>> minimum downtime by a factor of 10.
>>
>> Of course, *if* the size of device state is already negligible with
>> respect to the minimum downtime, then it doesn't matter.  This is
>> easy to quantify though.  For a normal migration session today,
>> what's the total size of the device state in relation to the
>> calculated bandwidth of the minimum downtime?
>>
>> If it's very small, then we can add names and not worry about it.
>>
>> Regards,
>>
>> Anthony Liguori
>
> Yes, it's easy to quantify. I think the following gives us
> the offset before and after, so the difference is the size
> we seek, right?

Yeah, you'll also want:

diff --git a/arch_init.c b/arch_init.c
index a6c69c7..0d64200 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -334,6 +334,10 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, voi

      expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;

+    if (stage == 2 && expected_time <= migrate_max_downtime()) {
+        fprintf(stderr, "max bwidth: %lld\n", (long)(expected_time * bwidth));
+    }
+
      return (stage == 2) && (expected_time <= migrate_max_downtime());
  }

You'll want to compare the size to max bwidth.

BTW, putting this info properly into migration stats would probably be pretty 
useful.

Regards,

Anthony Liguori

^ permalink raw reply related	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 14:42           ` Anthony Liguori
@ 2011-10-03 15:29             ` Michael S. Tsirkin
  2011-10-03 15:44               ` Anthony Liguori
  0 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 15:29 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: aliguori, Michael Roth, qemu-devel

On Mon, Oct 03, 2011 at 09:42:02AM -0500, Anthony Liguori wrote:
> On 10/03/2011 09:11 AM, Michael S. Tsirkin wrote:
> >On Mon, Oct 03, 2011 at 08:43:54AM -0500, Anthony Liguori wrote:
> >>>>visit_start_array(v, "entries", errp);
> >>>>for (int i = 0; i<   s->size; i++) {
> >>>>     visit_type_int(v, NULL,&s->entry[i], errp);
> >>>>}
> >>>>visit_end_array(v, errp);
> >>>
> >>>Sequences can encode structures not just arrays.
> >>>How would you encode this for example:
> >>>
> >>>SEQUENCE OF { VQN: INTEGER, SEQUENCE { OPTIONAL VECTOR: INTEGER}  }
> >>
> >>visit_start_array(v, "vqs", errp);
> >>for (i = 0; i<  s->n_vqs; i++) {
> >>     // Array elements never have a name, hence NULL name
> >>     visit_start_struct(v, "VirtQueue", NULL, errp);
> >>     visit_type_int(v,&s->vq[i].num, "vqn", errp);
> >>
> >>     // Given this sub-struct an arbitrary name.  It could also be anonymous.
> >>     visit_start_struct(v, "MsixInfo", "msix_info", errp);
> >>     if (s->vq[i].msix_enabled) {
> >>         visit_type_int(v,&s->vq[i].vector, "vector", errp);
> >
> >Why is this a pointer to vector, btw?
> 
> So you can write a single visit function that works for input or output.
> 
> Think of the simple case like:
> 
> void visit_simple_type(Visitor *v, SimpleType *t, const char *name, Error **errp)
> {
>    visit_start_struct(v, "SimpleType", name, errp);
>    visit_type_int(v, &t->a, "a", errp);
>    visit_type_int(v, &t->b, "b", errp);
>    visit_end_struct(v, errp);
> }

Okay, so this actually stores the pointer to the integer somewhere?
So what is the lifetime requirement for this memory?
For how long must it stay around?

> For complex types like Virtio, you need to do a bit more.  You
> wouldn't do a simple for () {} loop but instead use the Visitor list
> mechanism.  That would eliminate the need to have to marshal n_vqs.

Example?

> >
> >>     }
> >>     visit_end_struct(v, errp);
> >>
> >>     visit_end_struct(v, errp);
> >>}
> >>visit_end_array(v, errp);
> >>
> >>This would also generate JSON of:
> >>
> >>'vqs': [ { 'vqn': 2, 'msix_info': { 'vector': 3 } } ]
> >
> >How would optional fields be handled?
> 
> As far as the Visitor goes, if something is optional you just don't
> encode it. If you need to key off the presence of a field,
> presumably you could just check to see whether it succeeded or
> failed to visit that field.

It would typically depend on the value.


> I'm not 100% sure if you can do a
> single input/output visitor when you have optional fields.
> 
> My rough thinking is that each device would have a input/output
> visitor callback that took the same signature.  That gives the
> flexibility of having two separate interfaces but in the common
> case, you just pass the same function for both.
> 
> >Specifically
> >the case where first field in a sequence tells
> >you the meaning of the following ones?
> 
> Can you give me the example in ASN.1?
> 
> Regards,
> 
> Anthony Liguori

That would be a selection from CHOICE.
Note that CHOICE doesn't affect encoding on the wire:
BER just uses the underlying type.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 14:55           ` Anthony Liguori
@ 2011-10-03 15:41             ` Michael S. Tsirkin
  0 siblings, 0 replies; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 15:41 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: Michael Roth, qemu-devel

On Mon, Oct 03, 2011 at 09:55:45AM -0500, Anthony Liguori wrote:
> How I see this all evolving in the future is that we would have a
> formal protocol specification.  From that spec, we would generate
> Visitors.  This would handle taking what's on the wire and building
> an in-memory tree.  If an ASN.1 sequence that isn't part of the spec
> is passed, we should just silently discard it.
> 
> I think the scenario above is that we have an array in the spec, but
> the implementation no longer uses that array.  Your concern is that
> its inefficient to keep it in memory?
> 
> I think there are two cases.  The first is that that array is
> required in the spec, but optional in our implementation and that
> decision is made at run time.  In this case, it needs to get entered
> into memory.
> 
> If the array is no longer needed at all in our implementation, we
> can certainly annotate the protocol spec to skip it and not bring it
> into memory.  Maybe it's an internal spec annotation or something.
> 
> All that said, this doesn't seem to be a terribly important problem to solve IMHO.

This is not exactly the concern.
Consider that when we see an optional field we do not recognize,
we need to discard it.

Now, the reason we get a field we do not recognize might be
because someone is feeding us malformed data (to DOS
us, or by mistake). If we just trust input we will
allocate insane amounts of memory.

This is why we should not rely on fields to have reasonable size in the
input, we need to validate them.


-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 14:56                 ` Anthony Liguori
@ 2011-10-03 15:42                   ` Michael S. Tsirkin
  0 siblings, 0 replies; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 15:42 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: aliguori, Stefan Berger, qemu-devel, Michael Roth

On Mon, Oct 03, 2011 at 09:56:47AM -0500, Anthony Liguori wrote:
> On 10/03/2011 09:18 AM, Michael S. Tsirkin wrote:
> >>skip_indefinite:
> >>   while tag != CANARY:
> >>     if tag == INT:
> >>       visit_type_int(v, NULL, NULL, errp);
> >>     elif tag == STRING:
> >>       visit_type_str(v, NULL, NULL, errp);
> >>     elif tag == INDEFINITE:
> >>       visit_start_struct(v, NULL, NULL, errp);
> >>       skip_indefinite(v, errp);
> >>       visit_end_struct(v, NULL, NULL, errp);
> >>
> >>end_struct:
> >>   v->skip = true;
> >>   skip_indefinite(v, errp);
> >>   v->skip = false;
> >>
> >>Regards,
> >>
> >>Anthony Liguori
> >
> >This implementation is recursive, which has the problem
> >of wasting unlimited resources to skip bad input.
> >And we really do not need to look at tag at all. Just
> >the length.
> 
> That's just one implementation.  You could certainly do a
> non-recursive implementation.
> 
> Regards,
> 
> Anthony Liguori

What I was saying exactly: we do not need to
maintain a stack to skip an indefinite length field,
just a nesting counter will do.

> >

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 15:29             ` Michael S. Tsirkin
@ 2011-10-03 15:44               ` Anthony Liguori
  2011-10-03 15:58                 ` Michael S. Tsirkin
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 15:44 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Michael Roth, qemu-devel

On 10/03/2011 10:29 AM, Michael S. Tsirkin wrote:
> On Mon, Oct 03, 2011 at 09:42:02AM -0500, Anthony Liguori wrote:
>> On 10/03/2011 09:11 AM, Michael S. Tsirkin wrote:
>>> On Mon, Oct 03, 2011 at 08:43:54AM -0500, Anthony Liguori wrote:
>>>>>> visit_start_array(v, "entries", errp);
>>>>>> for (int i = 0; i<    s->size; i++) {
>>>>>>      visit_type_int(v, NULL,&s->entry[i], errp);
>>>>>> }
>>>>>> visit_end_array(v, errp);
>>>>>
>>>>> Sequences can encode structures not just arrays.
>>>>> How would you encode this for example:
>>>>>
>>>>> SEQUENCE OF { VQN: INTEGER, SEQUENCE { OPTIONAL VECTOR: INTEGER}  }
>>>>
>>>> visit_start_array(v, "vqs", errp);
>>>> for (i = 0; i<   s->n_vqs; i++) {
>>>>      // Array elements never have a name, hence NULL name
>>>>      visit_start_struct(v, "VirtQueue", NULL, errp);
>>>>      visit_type_int(v,&s->vq[i].num, "vqn", errp);
>>>>
>>>>      // Given this sub-struct an arbitrary name.  It could also be anonymous.
>>>>      visit_start_struct(v, "MsixInfo", "msix_info", errp);
>>>>      if (s->vq[i].msix_enabled) {
>>>>          visit_type_int(v,&s->vq[i].vector, "vector", errp);
>>>
>>> Why is this a pointer to vector, btw?
>>
>> So you can write a single visit function that works for input or output.
>>
>> Think of the simple case like:
>>
>> void visit_simple_type(Visitor *v, SimpleType *t, const char *name, Error **errp)
>> {
>>     visit_start_struct(v, "SimpleType", name, errp);
>>     visit_type_int(v,&t->a, "a", errp);
>>     visit_type_int(v,&t->b, "b", errp);
>>     visit_end_struct(v, errp);
>> }
>
> Okay, so this actually stores the pointer to the integer somewhere?
> So what is the lifetime requirement for this memory?

typedef struct SimpleType
{
    int a;
    int b;
} SimpleType;

So the Input visitor (reading off of the wire), essentially is:

type_int(Visitor *v, int *value, Error **errp)
{
    *value = read_int_off_wire;
}

The Output visitor (writing to the wire), is:

type_int(Visitor *v, int *value, Error **errp)
{
    write_int_to_write(*value);
}

> For how long must it stay around?

There are two interest cases where life cycle matters: pointers to structs and 
lists.

Right now, Visitors model all structures as pointers to structs.  I've been 
thinking of having two separate interfaces, but for now, it takes:

start_struct(Visitor *v, void **value, size_t size, const char *name, Error **errp)
{
}

The output visitor ignores size, but the input visitor essentially does:

{
    *value = g_malloc0(size);
}

For QAPI, we actually provide automatic free functions that can free structures 
that were allocated by a Visitor.

>> For complex types like Virtio, you need to do a bit more.  You
>> wouldn't do a simple for () {} loop but instead use the Visitor list
>> mechanism.  That would eliminate the need to have to marshal n_vqs.
>
> Example?

For a list, you need to make your list type compatible with GenericList.  Once 
you've done that, you can do:

     GenericList *i;

     visit_start_list(m, name, errp);

     for (i = visit_next_list(m, (GenericList **)obj, errp);
          i;
          i = visit_next_list(m, &i, errp)) {
         TestStructList *native_i = (TestStructList *)i;
         visit_type_TestStruct(m, &native_i->value, NULL, errp);
     }

     visit_end_list(m, errp);

I won't rewrite the virtio code, but hopefully it's clear how you would modify 
it to work this way.

>
>>>
>>>>      }
>>>>      visit_end_struct(v, errp);
>>>>
>>>>      visit_end_struct(v, errp);
>>>> }
>>>> visit_end_array(v, errp);
>>>>
>>>> This would also generate JSON of:
>>>>
>>>> 'vqs': [ { 'vqn': 2, 'msix_info': { 'vector': 3 } } ]
>>>
>>> How would optional fields be handled?
>>
>> As far as the Visitor goes, if something is optional you just don't
>> encode it. If you need to key off the presence of a field,
>> presumably you could just check to see whether it succeeded or
>> failed to visit that field.
>
> It would typically depend on the value.
>
>
>> I'm not 100% sure if you can do a
>> single input/output visitor when you have optional fields.
>>
>> My rough thinking is that each device would have a input/output
>> visitor callback that took the same signature.  That gives the
>> flexibility of having two separate interfaces but in the common
>> case, you just pass the same function for both.
>>
>>> Specifically
>>> the case where first field in a sequence tells
>>> you the meaning of the following ones?
>>
>> Can you give me the example in ASN.1?
>>
>> Regards,
>>
>> Anthony Liguori
>
> That would be a selection from CHOICE.
> Note that CHOICE doesn't affect encoding on the wire:
> BER just uses the underlying type.

Ah, so that's a union value.  I think we would have to decide how we wanted to 
model unions.  Presumably, the selected CHOICE is indicated by some other field? 
  I would think it's a good idea to use the type information to identify which 
selection of a CHOICE was chosen.

I had implemented a union type in the old glib branch but that was based on a 
struct with an enum element and then the appropriate value element.

Regards,

Anthony Liguori

>
>
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 15:00             ` Anthony Liguori
@ 2011-10-03 15:45               ` Michael S. Tsirkin
  2011-10-03 16:05                 ` Anthony Liguori
  2011-10-05 11:28               ` Michael S. Tsirkin
  1 sibling, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 15:45 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Michael Roth, aliguori, Anthony Liguori, qemu-devel, Stefan Berger

On Mon, Oct 03, 2011 at 10:00:48AM -0500, Anthony Liguori wrote:
> On 10/03/2011 09:41 AM, Michael S. Tsirkin wrote:
> >On Mon, Oct 03, 2011 at 08:51:10AM -0500, Anthony Liguori wrote:
> >>On 10/03/2011 08:38 AM, Michael S. Tsirkin wrote:
> >>>On Mon, Oct 03, 2011 at 07:55:48AM -0500, Anthony Liguori wrote:
> >>>>On 10/02/2011 04:08 PM, Michael S. Tsirkin wrote:
> >>>>>On Sun, Oct 02, 2011 at 04:21:47PM -0400, Stefan Berger wrote:
> >>>>>>
> >>>>>>>4) Implement the BERVisitor and make this the default migration protocol.
> >>>>>>>
> >>>>>>>Most of the work will be in 1), though with the implementation in this series we should be able to do it incrementally. I'm not sure if the best approach is doing the mechanical phase 1 conversion, then doing phase 2 sometime after 4), doing phase 1 + 2 as part of 1), or just doing VMState conversions which gives basically the same capabilities as phase 1 + 2.
> >>>>>>>
> >>>>>>>Thoughts?
> >>>>>>Is anyone working on this? If not I may give it a shot (tomorrow++)
> >>>>>>for at least some of the primitives... for enabling vNVRAM metadata
> >>>>>>of course. Indefinite length encoding of constructed data types I
> >>>>>>suppose won't be used otherwise the visitor interface seems wrong
> >>>>>>for parsing and skipping of extra data towards the end of a
> >>>>>>structure if version n wrote the stream and appended some of its
> >>>>>>version n data and now version m<    n is trying to read the struct
> >>>>>>and needs to skip the version [m+1, n ] data fields ... in that case
> >>>>>>the de-serialization of the stream should probably be stream-driven
> >>>>>>rather than structure-driven.
> >>>>>>
> >>>>>>    Stefan
> >>>>>
> >>>>>Yes I've been struggling with that exactly.
> >>>>>Anthony, any thoughts?
> >>>>
> >>>>It just depends on how you write your visitor.  If you used
> >>>>sequences, you'd probably do something like this:
> >>>>
> >>>>start_struct ->
> >>>>   check for sequence tag, push starting offset and size onto stack
> >>>>   increment offset to next tag
> >>>>
> >>>>type_int (et al) ->
> >>>>   check for explicit type, parse data
> >>>>   increment offset to next tag
> >>>>
> >>>>end_struct ->
> >>>>   pop starting offset and size to temp variables
> >>>>   set offset to starting offset + size
> >>>>
> >>>>This is roughly how the QMP input marshaller works FWIW.
> >>>>
> >>>>Regards,
> >>>>
> >>>>Anthony Liguori
> >>>
> >>>One thing I worry about is enabling zero copy for
> >>>large string types (e.g. memory migration).
> >>
> >>Memory shouldn't be done through Visitors.  It should be handled as a special case.
> >
> >OK, that's fine then.
> >
> >>>So we need to be able to see a tag for memory page + address,
> >>>read that from socket directly at the correct virtual address.
> >>>
> >>>Probably, we can avoid using visitors for memory, and hope
> >>>everything else can stand an extra copy since it's small.
> >>>
> >>>But then, why do we worry about the size of
> >>>encoded device state as Anthony seems to do?
> >>
> >>There's a significant difference between the cost of something on
> >>the wire and the cost of doing a memcpy.  The cost of the data on
> >>the wire is directly proportional to downtime.  So if we increase
> >>the size of the device state by a factor of 10, we increase the
> >>minimum downtime by a factor of 10.
> >>
> >>Of course, *if* the size of device state is already negligible with
> >>respect to the minimum downtime, then it doesn't matter.  This is
> >>easy to quantify though.  For a normal migration session today,
> >>what's the total size of the device state in relation to the
> >>calculated bandwidth of the minimum downtime?
> >>
> >>If it's very small, then we can add names and not worry about it.
> >>
> >>Regards,
> >>
> >>Anthony Liguori
> >
> >Yes, it's easy to quantify. I think the following gives us
> >the offset before and after, so the difference is the size
> >we seek, right?
> 
> Yeah, you'll also want:
> 
> diff --git a/arch_init.c b/arch_init.c
> index a6c69c7..0d64200 100644
> --- a/arch_init.c
> +++ b/arch_init.c
> @@ -334,6 +334,10 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, voi
> 
>      expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
> 
> +    if (stage == 2 && expected_time <= migrate_max_downtime()) {
> +        fprintf(stderr, "max bwidth: %lld\n", (long)(expected_time * bwidth));
> +    }
> +
>      return (stage == 2) && (expected_time <= migrate_max_downtime());
>  }
> 
> You'll want to compare the size to max bwidth.

Well that depends on how guest behaves etc. I'm guessing
just full memory size is a sane thing to compare against.

I don't have a problem sticking this fprintf in as well.

> BTW, putting this info properly into migration stats would probably
> be pretty useful.
> 
> Regards,
> 
> Anthony Liguori

Problem is adding anything to monitor makes me worry
about future compatibility so much I usually just give up.
IMO we really need a namespace for in-development experimental
commands, like "unsupported-XXX", this would belong.

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 15:44               ` Anthony Liguori
@ 2011-10-03 15:58                 ` Michael S. Tsirkin
  2011-10-03 16:02                   ` Anthony Liguori
  0 siblings, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 15:58 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: Michael Roth, qemu-devel

On Mon, Oct 03, 2011 at 10:44:45AM -0500, Anthony Liguori wrote:
> >>>Specifically
> >>>the case where first field in a sequence tells
> >>>you the meaning of the following ones?
> >>
> >>Can you give me the example in ASN.1?
> >>
> >>Regards,
> >>
> >>Anthony Liguori
> >
> >That would be a selection from CHOICE.
> >Note that CHOICE doesn't affect encoding on the wire:
> >BER just uses the underlying type.
> 
> Ah, so that's a union value.  I think we would have to decide how we
> wanted to model unions.

IMHO it's easier to use if a visitor for the right type
is called, not a top level one for the union.

>  Presumably, the selected CHOICE is
> indicated by some other field?

Exactly. Some function of some other field :)

> I would think it's a good idea to
> use the type information to identify which selection of a CHOICE was
> chosen.

Not sure what this means. We really need to look at the
serialized field to know which selection was chose.
In theory the field that dictates the meaning might come
after the choice, but I think we don't limit outselves
too much if we require it to come before in a sequence.


> I had implemented a union type in the old glib branch but that was
> based on a struct with an enum element and then the appropriate
> value element.
> 
> Regards,
> 
> Anthony Liguori

Yes, this seems limiting.

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 15:58                 ` Michael S. Tsirkin
@ 2011-10-03 16:02                   ` Anthony Liguori
  0 siblings, 0 replies; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 16:02 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Michael Roth, qemu-devel

On 10/03/2011 10:58 AM, Michael S. Tsirkin wrote:
> On Mon, Oct 03, 2011 at 10:44:45AM -0500, Anthony Liguori wrote:
>>>>> Specifically
>>>>> the case where first field in a sequence tells
>>>>> you the meaning of the following ones?
>>>>
>>>> Can you give me the example in ASN.1?
>>>>
>>>> Regards,
>>>>
>>>> Anthony Liguori
>>>
>>> That would be a selection from CHOICE.
>>> Note that CHOICE doesn't affect encoding on the wire:
>>> BER just uses the underlying type.
>>
>> Ah, so that's a union value.  I think we would have to decide how we
>> wanted to model unions.
>
> IMHO it's easier to use if a visitor for the right type
> is called, not a top level one for the union.
>
>>   Presumably, the selected CHOICE is
>> indicated by some other field?
>
> Exactly. Some function of some other field :)
>
>> I would think it's a good idea to
>> use the type information to identify which selection of a CHOICE was
>> chosen.
>
> Not sure what this means. We really need to look at the
> serialized field to know which selection was chose.
> In theory the field that dictates the meaning might come
> after the choice, but I think we don't limit outselves
> too much if we require it to come before in a sequence.

Yeah, you can certainly open code it..

if (s->some_field == FOO) {
    visit_type_int(...);
} else if (s->some_field == BAR) {
    visit_type_str(...);
}

Personally, I'm inclined to just go with that strategy unless it becomes 
pervasive.  If so, I think it's worth abstracting to a higher level concept.

What that means for having a multi-Visitor system is that the protocol layer 
would accept any valid choice and the Device layer would validate that the 
selected choice is valid based on the field contents.

Regards,

Anthony Liguori

>
>
>> I had implemented a union type in the old glib branch but that was
>> based on a struct with an enum element and then the appropriate
>> value element.
>>
>> Regards,
>>
>> Anthony Liguori
>
> Yes, this seems limiting.
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 15:45               ` Michael S. Tsirkin
@ 2011-10-03 16:05                 ` Anthony Liguori
  2011-10-03 16:24                   ` Daniel P. Berrange
  2011-10-03 16:51                   ` Michael S. Tsirkin
  0 siblings, 2 replies; 50+ messages in thread
From: Anthony Liguori @ 2011-10-03 16:05 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Stefan Berger, Michael Roth, qemu-devel

On 10/03/2011 10:45 AM, Michael S. Tsirkin wrote:
>> BTW, putting this info properly into migration stats would probably
>> be pretty useful.
>>
>> Regards,
>>
>> Anthony Liguori
>
> Problem is adding anything to monitor makes me worry
> about future compatibility so much I usually just give up.
> IMO we really need a namespace for in-development experimental
> commands, like "unsupported-XXX", this would belong.

Or just make all of QMP unsupported across any given version.  I'm not kidding 
about that actually.

If we document what the protocol is for any given version, then a layer like 
libvirt can deal with providing a consistent interface.

I often wonder who we're trying to preserve compatibility for.  Part of 
libvirt's mission statement is providing a stable API so why not leverage that 
mission and get us out of the compatibility business.

Regards,

Anthony Liguori

>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 16:05                 ` Anthony Liguori
@ 2011-10-03 16:24                   ` Daniel P. Berrange
  2011-10-03 16:51                   ` Michael S. Tsirkin
  1 sibling, 0 replies; 50+ messages in thread
From: Daniel P. Berrange @ 2011-10-03 16:24 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: qemu-devel, Stefan Berger, Michael Roth, Michael S. Tsirkin

On Mon, Oct 03, 2011 at 11:05:02AM -0500, Anthony Liguori wrote:
> On 10/03/2011 10:45 AM, Michael S. Tsirkin wrote:
> >>BTW, putting this info properly into migration stats would probably
> >>be pretty useful.
> >>
> >>Regards,
> >>
> >>Anthony Liguori
> >
> >Problem is adding anything to monitor makes me worry
> >about future compatibility so much I usually just give up.
> >IMO we really need a namespace for in-development experimental
> >commands, like "unsupported-XXX", this would belong.
> 
> Or just make all of QMP unsupported across any given version.  I'm
> not kidding about that actually.
> 
> If we document what the protocol is for any given version, then a
> layer like libvirt can deal with providing a consistent interface.
> 
> I often wonder who we're trying to preserve compatibility for.  Part
> of libvirt's mission statement is providing a stable API so why not
> leverage that mission and get us out of the compatibility business.

You are correct, that ultimately libvirt should be isolating applications
from any changes in QEMU's monitor protocol that take place across
versions. We just ask that QEMU try not to make too many gratuitous
changes, if it is reasonable to avoid them without negative impact on
QEMU. The more time we have to spend adding support for different
commands across each QEMU release, the less time we have to spend on
adding useful features elsewhere.

To me the biggest benefit of QMP is not that the commands are to be
supported long term, but rather than it is a sanely parsable format
with detectable error reporting and asynchronous events.

The important thing to us, is not to make semantic changes to any
*existing* commands. If an existing command is flawed, either leave
it alone & introduce a new one, or if compatibility can't be maintained
for some reason remove the old one & introduce a new one with a *new*
name to avoid any confusion.

So if you have to deprecate some existing QMP commands and introduce
new ones to replace them across releases we can & will cope with that
in libvirt.

Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 16:05                 ` Anthony Liguori
  2011-10-03 16:24                   ` Daniel P. Berrange
@ 2011-10-03 16:51                   ` Michael S. Tsirkin
  1 sibling, 0 replies; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-03 16:51 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: Stefan Berger, Michael Roth, qemu-devel

On Mon, Oct 03, 2011 at 11:05:02AM -0500, Anthony Liguori wrote:
> On 10/03/2011 10:45 AM, Michael S. Tsirkin wrote:
> >>BTW, putting this info properly into migration stats would probably
> >>be pretty useful.
> >>
> >>Regards,
> >>
> >>Anthony Liguori
> >
> >Problem is adding anything to monitor makes me worry
> >about future compatibility so much I usually just give up.
> >IMO we really need a namespace for in-development experimental
> >commands, like "unsupported-XXX", this would belong.
> 
> Or just make all of QMP unsupported across any given version.  I'm
> not kidding about that actually.
> 
> If we document what the protocol is for any given version, then a
> layer like libvirt can deal with providing a consistent interface.

Once distros ship qemu, people will use it. Once they use it
in some way for long, it becomes the supposted interface.
No one reads documentation, obviously :) - do you expect
people to re-read qemu documentation *with every release*?

> 
> I often wonder who we're trying to preserve compatibility for.  Part
> of libvirt's mission statement is providing a stable API so why not
> leverage that mission and get us out of the compatibility business.
> 
> Regards,
> 
> Anthony Liguori

you can't force everyone to use libvirt.
Part of QMP mission statement was a stable interface.
If it's not that I don't know what it's for.

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 13:43       ` Anthony Liguori
  2011-10-03 14:11         ` Michael S. Tsirkin
  2011-10-03 14:15         ` Michael S. Tsirkin
@ 2011-10-05  2:05         ` Stefan Berger
  2011-10-05 12:54           ` Anthony Liguori
  2 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2011-10-05  2:05 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: qemu-devel, aliguori, Anthony Liguori, Michael Roth, Michael S. Tsirkin

On 10/03/2011 09:43 AM, Anthony Liguori wrote:
> On 10/03/2011 08:24 AM, Michael S. Tsirkin wrote:
>> On Mon, Oct 03, 2011 at 07:51:00AM -0500, Anthony Liguori wrote:
>>>> Here are some suggestions:
>>>>
>>>> - Let's make the protocol be BER directly.
>>>>    As a first step, use a single octet string for
>>>>    the whole of data. Next, start splitting this up.
>>>
>>> This can't be done without breaking the old style migration
>>> protocol.  I don't have a problem with that but I do have a problem
>>> with repeatedly breaking migration protocol.
>>
>> As long as this is within a release cycle, is this a real problem?
>
> I think if we try to fit it within a release we'll either end up with 
> a 2 year long release or a half-broken conversion.
>
> I'd rather buy ourselves time by supporting both formats.  That way we 
> can remove the old format when we're satisfied with the ASN.1 encoding.
Hm, if backwards compatibility is what we want to achieve (migrating 
from Qemu 1.1 to Qemu 1.0) then at least the ASN.1 decoder / encoder 
should be all done in 1.0, no? Otherwise what would it mean to if 1.0 
just skipped types 1.1 sends and doesn't understand?
> There are multiple things to consider with compatibility:
>
> 1) Creating compatible device models.  This is a qdev problem and 
> can't be solved in the protocol.
>
> 2) Ensuring we are sending all the data we need to.  I think we solve 
> this problem by autogenerating Visitors from the C definitions of the 
> device structures.
>
I would have thought that we would write a function that takes the 
VMStateDescription as an argument and write ASN.1 BER or CER comprising:
- a header containing the version of the device data
- the minimum version required to read the device data
- walk the array of VMStateFields and encode the the device data

and similarly a function for walking the fields for decoding of each 
device state.

So at least I am surprised to hear 'autogeneration' for this particular 
case...

    Stefan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-03 15:00             ` Anthony Liguori
  2011-10-03 15:45               ` Michael S. Tsirkin
@ 2011-10-05 11:28               ` Michael S. Tsirkin
  2011-10-05 12:46                 ` Anthony Liguori
  1 sibling, 1 reply; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-05 11:28 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: Anthony Liguori, owasserm, Stefan Berger, Michael Roth,
	qemu-devel, aliguori

On Mon, Oct 03, 2011 at 10:00:48AM -0500, Anthony Liguori wrote:
> >Yes, it's easy to quantify. I think the following gives us
> >the offset before and after, so the difference is the size
> >we seek, right?

OK, Orit (Cc'd) did some research - this is a booting
while still in grub, size probably does not get much less than that.
windows is said to be much more aggressive in allocating memory.

 start offset: 9600673
 end offset: 9614933

So we get 15K out of 9M.
By the way, most of the memory here is pretty much
all uniform I guess, because compressing it gets us:
gzip: 1934169
bzip2 -9: 1462551

So even with aggressive compression, we probably won't be able to get
below 1.5M for memory, two orders of magnitude above device state.

Sounds convincing?

-- 
MST

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-05 11:28               ` Michael S. Tsirkin
@ 2011-10-05 12:46                 ` Anthony Liguori
  0 siblings, 0 replies; 50+ messages in thread
From: Anthony Liguori @ 2011-10-05 12:46 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: owasserm, Stefan Berger, Michael Roth, qemu-devel, aliguori

On 10/05/2011 06:28 AM, Michael S. Tsirkin wrote:
> On Mon, Oct 03, 2011 at 10:00:48AM -0500, Anthony Liguori wrote:
>>> Yes, it's easy to quantify. I think the following gives us
>>> the offset before and after, so the difference is the size
>>> we seek, right?
>
> OK, Orit (Cc'd) did some research - this is a booting
> while still in grub, size probably does not get much less than that.
> windows is said to be much more aggressive in allocating memory.
>
>   start offset: 9600673
>   end offset: 9614933
>
> So we get 15K out of 9M.

So, let's do some napkin math.

Assume that it works out that most of that 15k are 4 byte integers.  If we 
assume an average name length of 6 characters, a string should be encoded in 8 
bytes.  That means for every 4 bytes, we add 8 bytes which means we're 
increasing by 200%.

That means 45k.

A 1gbit link can xmit at max, 128k in 1ms.  So that extra 30k is going to cost 
~250us in transmit time if we can get 1gbit.  A the default rate limit, it 
should cost us right around 1ms.

I guess it's liveable although with 30 network cards, I suspect it gets a heck 
of a lot worse.

Regards,

Anthony Liguori

> By the way, most of the memory here is pretty much
> all uniform I guess, because compressing it gets us:
> gzip: 1934169
> bzip2 -9: 1462551
>
> So even with aggressive compression, we probably won't be able to get
> below 1.5M for memory, two orders of magnitude above device state.
>
> Sounds convincing?
>

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-05  2:05         ` Stefan Berger
@ 2011-10-05 12:54           ` Anthony Liguori
  2011-10-05 19:06             ` Michael S. Tsirkin
  0 siblings, 1 reply; 50+ messages in thread
From: Anthony Liguori @ 2011-10-05 12:54 UTC (permalink / raw)
  To: Stefan Berger; +Cc: qemu-devel, aliguori, Michael Roth, Michael S. Tsirkin

On 10/04/2011 09:05 PM, Stefan Berger wrote:
> On 10/03/2011 09:43 AM, Anthony Liguori wrote:
>> On 10/03/2011 08:24 AM, Michael S. Tsirkin wrote:
>>> On Mon, Oct 03, 2011 at 07:51:00AM -0500, Anthony Liguori wrote:
>>>>> Here are some suggestions:
>>>>>
>>>>> - Let's make the protocol be BER directly.
>>>>> As a first step, use a single octet string for
>>>>> the whole of data. Next, start splitting this up.
>>>>
>>>> This can't be done without breaking the old style migration
>>>> protocol. I don't have a problem with that but I do have a problem
>>>> with repeatedly breaking migration protocol.
>>>
>>> As long as this is within a release cycle, is this a real problem?
>>
>> I think if we try to fit it within a release we'll either end up with a 2 year
>> long release or a half-broken conversion.
>>
>> I'd rather buy ourselves time by supporting both formats. That way we can
>> remove the old format when we're satisfied with the ASN.1 encoding.
> Hm, if backwards compatibility is what we want to achieve (migrating from Qemu
> 1.1 to Qemu 1.0) then at least the ASN.1 decoder / encoder should be all done in
> 1.0, no? Otherwise what would it mean to if 1.0 just skipped types 1.1 sends and
> doesn't understand?

Before we introduce ASN1, we ought to introduce migration capabilities. 
Migration capabilities would be used to negotation ASN.1 over the wire.  That 
means that 1.1 would use the existing protocol to talk to 1.0.

>> There are multiple things to consider with compatibility:
>>
>> 1) Creating compatible device models. This is a qdev problem and can't be
>> solved in the protocol.
>>
>> 2) Ensuring we are sending all the data we need to. I think we solve this
>> problem by autogenerating Visitors from the C definitions of the device
>> structures.
>>
> I would have thought that we would write a function that takes the
> VMStateDescription as an argument and write ASN.1 BER or CER comprising:
> - a header containing the version of the device data
> - the minimum version required to read the device data
> - walk the array of VMStateFields and encode the the device data

Sort of.  You modify VMStateInfo to accept a visitor and name parameter in load 
and put.  Then you write an ASN.1 BER Visitor and pass that visitor to 
VMStateInfo->load/put.

Regards,

Anthony Liguori

>
> and similarly a function for walking the fields for decoding of each device state.
>
> So at least I am surprised to hear 'autogeneration' for this particular case...
>
> Stefan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface
  2011-10-05 12:54           ` Anthony Liguori
@ 2011-10-05 19:06             ` Michael S. Tsirkin
  0 siblings, 0 replies; 50+ messages in thread
From: Michael S. Tsirkin @ 2011-10-05 19:06 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, aliguori, Michael Roth, Stefan Berger

On Wed, Oct 05, 2011 at 07:54:21AM -0500, Anthony Liguori wrote:
> On 10/04/2011 09:05 PM, Stefan Berger wrote:
> >On 10/03/2011 09:43 AM, Anthony Liguori wrote:
> >>On 10/03/2011 08:24 AM, Michael S. Tsirkin wrote:
> >>>On Mon, Oct 03, 2011 at 07:51:00AM -0500, Anthony Liguori wrote:
> >>>>>Here are some suggestions:
> >>>>>
> >>>>>- Let's make the protocol be BER directly.
> >>>>>As a first step, use a single octet string for
> >>>>>the whole of data. Next, start splitting this up.
> >>>>
> >>>>This can't be done without breaking the old style migration
> >>>>protocol. I don't have a problem with that but I do have a problem
> >>>>with repeatedly breaking migration protocol.
> >>>
> >>>As long as this is within a release cycle, is this a real problem?
> >>
> >>I think if we try to fit it within a release we'll either end up with a 2 year
> >>long release or a half-broken conversion.
> >>
> >>I'd rather buy ourselves time by supporting both formats. That way we can
> >>remove the old format when we're satisfied with the ASN.1 encoding.
> >Hm, if backwards compatibility is what we want to achieve (migrating from Qemu
> >1.1 to Qemu 1.0) then at least the ASN.1 decoder / encoder should be all done in
> >1.0, no? Otherwise what would it mean to if 1.0 just skipped types 1.1 sends and
> >doesn't understand?
> 
> Before we introduce ASN1, we ought to introduce migration
> capabilities.

FWIW we introduce it for tpm as first step.

> Migration capabilities would be used to negotation
> ASN.1 over the wire.

I'm not yet really convinced we need capabilities at all.
Would be sad to make asn depend on that.

> That means that 1.1 would use the existing
> protocol to talk to 1.0.

We basically never had qemu that can talk
across versions 100%. The compability we carry around
is so much dead code. And no wonder: there's no way
to parse the protocol without being bug for bug compatible.

IMO let's just switch to a sane protocol first.
Being compatible with that will be much easier.

> >>There are multiple things to consider with compatibility:
> >>
> >>1) Creating compatible device models. This is a qdev problem and can't be
> >>solved in the protocol.
> >>
> >>2) Ensuring we are sending all the data we need to. I think we solve this
> >>problem by autogenerating Visitors from the C definitions of the device
> >>structures.
> >>
> >I would have thought that we would write a function that takes the
> >VMStateDescription as an argument and write ASN.1 BER or CER comprising:
> >- a header containing the version of the device data
> >- the minimum version required to read the device data
> >- walk the array of VMStateFields and encode the the device data
> 
> Sort of.  You modify VMStateInfo to accept a visitor and name
> parameter in load and put.  Then you write an ASN.1 BER Visitor and
> pass that visitor to VMStateInfo->load/put.
> 
> Regards,
> 
> Anthony Liguori
> 
> >
> >and similarly a function for walking the fields for decoding of each device state.
> >
> >So at least I am surprised to hear 'autogeneration' for this particular case...
> >
> >Stefan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [Qemu-devel] [RFC 3/8] qapi: add QemuFileInputVisitor
  2011-09-19 14:41 ` [Qemu-devel] [RFC 3/8] qapi: add QemuFileInputVisitor Michael Roth
@ 2011-10-24 23:59   ` Chris Krumme
  0 siblings, 0 replies; 50+ messages in thread
From: Chris Krumme @ 2011-10-24 23:59 UTC (permalink / raw)
  To: qemu-devel

On 09/19/2011 09:41 AM, Michael Roth wrote:
> Visitor interfaces to read values from a QEMUFile
>
> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
> ---
>   Makefile.objs                  |    1 +
>   qapi/qemu-file-input-visitor.c |  350 ++++++++++++++++++++++++++++++++++++++++
>   qapi/qemu-file-input-visitor.h |   26 +++
>   3 files changed, 377 insertions(+), 0 deletions(-)
>   create mode 100644 qapi/qemu-file-input-visitor.c
>   create mode 100644 qapi/qemu-file-input-visitor.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 48fe0c4..6bc8555 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -82,6 +82,7 @@ fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y))
>   common-obj-y = $(block-obj-y) blockdev.o
>   common-obj-y += $(qapi-obj-y)
>   common-obj-y += qapi/qemu-file-output-visitor.o
> +common-obj-y += qapi/qemu-file-input-visitor.o
>   common-obj-y += $(net-obj-y)
>   common-obj-y += $(qobject-obj-y)
>   common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
> diff --git a/qapi/qemu-file-input-visitor.c b/qapi/qemu-file-input-visitor.c
> new file mode 100644
> index 0000000..7217125
> --- /dev/null
> +++ b/qapi/qemu-file-input-visitor.c
> @@ -0,0 +1,350 @@
> +/*
> + * QEMUFile Output 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.
> + *
> + */
> +
> +#include "qemu-file-input-visitor.h"
> +#include "qemu-queue.h"
> +#include "qemu-common.h"
> +#include "qemu-objects.h"
> +#include "hw/hw.h"
> +#include "qerror.h"
> +
> +typedef struct {
> +    size_t elem_count;
> +    size_t elem_size;
> +    size_t pos;
> +} ArrayInfo;
> +
> +typedef struct StackEntry
> +{
> +    enum {
> +        QFIV_ARRAY,
> +        QFIV_LIST,
> +        QFIV_STRUCT,
> +    } type;
> +    ArrayInfo array_info;
> +    QTAILQ_ENTRY(StackEntry) node;
> +} StackEntry;
> +
> +struct QemuFileInputVisitor
> +{
> +    Visitor visitor;
> +    QTAILQ_HEAD(, StackEntry) stack;
> +    QEMUFile *file;
> +};
> +
> +static QemuFileInputVisitor *to_iv(Visitor *v)
> +{
> +    return container_of(v, QemuFileInputVisitor, visitor);
> +}
> +
> +static void qemu_file_input_push(QemuFileInputVisitor *iv, StackEntry *e)
> +{
> +    QTAILQ_INSERT_HEAD(&iv->stack, e, node);
> +}
> +
> +static void qemu_file_input_push_array(QemuFileInputVisitor *iv, ArrayInfo ai)
> +{
> +    StackEntry *e = g_malloc0(sizeof(*e));
> +    e->type = QFIV_ARRAY;
> +    e->array_info = ai;
> +    qemu_file_input_push(iv, e);
> +}
> +
> +static void qemu_file_input_push_list(QemuFileInputVisitor *iv)
> +{
> +    StackEntry *e = g_malloc0(sizeof(*e));
> +    e->type = QFIV_LIST;
> +    qemu_file_input_push(iv, e);
> +}
> +
> +static void qemu_file_input_push_struct(QemuFileInputVisitor *iv)
> +{
> +    StackEntry *e = g_malloc0(sizeof(*e));
> +    e->type = QFIV_STRUCT;
> +    qemu_file_input_push(iv, e);
> +}
> +
> +static void *qemu_file_input_pop(QemuFileInputVisitor *iv)
> +{
> +    StackEntry *e = QTAILQ_FIRST(&iv->stack);
> +    QTAILQ_REMOVE(&iv->stack, e, node);
> +    return e;
> +}
> +
> +static bool qemu_file_input_is_array(QemuFileInputVisitor *iv)
> +{
> +    StackEntry *e = QTAILQ_FIRST(&iv->stack);
> +    return e->type == QFIV_ARRAY;
> +}
> +
> +static bool qemu_file_input_is_list(QemuFileInputVisitor *ov)
> +{
> +    StackEntry *e = QTAILQ_FIRST(&ov->stack);
> +    return e&&  e->type == QFIV_LIST;
> +}
> +
> +static void qemu_file_input_start_struct(Visitor *v, void **obj,
> +                                         const char *kind,
> +                                         const char *name, size_t size,
> +                                         Error **errp)
> +{
> +    QemuFileInputVisitor *iv = to_iv(v);
> +
> +    if (obj&&  *obj == NULL) {
> +        *obj = g_malloc0(size);
> +    }
> +    qemu_file_input_push_struct(iv);
> +}
> +
> +static void qemu_file_input_end_struct(Visitor *v, Error **errp)
> +{
> +    QemuFileInputVisitor *iv = to_iv(v);
> +    StackEntry *e = qemu_file_input_pop(iv);
> +
> +    if (!e || e->type != QFIV_STRUCT) {
> +        error_set(errp, QERR_UNDEFINED_ERROR);
> +        return;
> +    }
> +    g_free(e);

Hello Michael,

I was looking at the code again to see what (private) comment I had made 
the first time I read the code, and now I see additional issues.

The error test above and below will leak the e pointer when the type is 
wrong.

> +}
> +
> +static void qemu_file_input_start_list(Visitor *v, const char *name,
> +                                       Error **errp)
> +{
> +    QemuFileInputVisitor *iv = to_iv(v);
> +    qemu_file_input_push_list(iv);
> +}
> +
> +static GenericList *qemu_file_input_next_list(Visitor *v, GenericList **list,
> +                                           Error **errp)
> +{
> +    QemuFileInputVisitor *iv = to_iv(v);
> +    GenericList *entry;
> +
> +    if (!qemu_file_input_is_list(iv)) {
> +        error_set(errp, QERR_UNDEFINED_ERROR);
> +    }
> +
> +    entry = g_malloc0(sizeof(*entry));
> +    if (*list) {
> +        (*list)->next = entry;
> +    }
> +
> +    *list = entry;
> +    return entry;
> +}
> +
> +static void qemu_file_input_end_list(Visitor *v, Error **errp)
> +{
> +    QemuFileInputVisitor *iv = to_iv(v);
> +    StackEntry *e = qemu_file_input_pop(iv);
> +    if (!e || e->type != QFIV_LIST) {
> +        error_set(errp, QERR_UNDEFINED_ERROR);

and here

> +        return;
> +    }
> +    g_free(e);
> +}
> +
> +static void qemu_file_input_start_array(Visitor *v, void **obj,
> +                                        const char *name,
> +                                        size_t elem_count,
> +                                        size_t elem_size,
> +                                        Error **errp)
> +{
> +    QemuFileInputVisitor *iv = to_iv(v);
> +    ArrayInfo ai = {
> +        .elem_count = elem_count,
> +        .elem_size = elem_size,
> +        .pos = 0
> +    };
> +    if (*obj == NULL) {
> +        *obj = g_malloc0(elem_count * elem_size);
> +    }
> +    qemu_file_input_push_array(iv, ai);
> +}
> +
> +static void qemu_file_input_next_array(Visitor *v, Error **errp)
> +{
> +    QemuFileInputVisitor *iv = to_iv(v);
> +    StackEntry *e = QTAILQ_FIRST(&iv->stack);
> +
> +    if (!qemu_file_input_is_array(iv) ||
> +        e->array_info.pos>= e->array_info.elem_count) {
> +        error_set(errp, QERR_UNDEFINED_ERROR);
> +    }
> +
> +    e->array_info.pos++;
> +}
> +
> +static void qemu_file_input_end_array(Visitor *v, Error **errp)
> +{
> +    QemuFileInputVisitor *iv = to_iv(v);
> +    StackEntry *e = qemu_file_input_pop(iv);
> +    if (!e || e->type != QFIV_ARRAY) {
> +        error_set(errp, QERR_UNDEFINED_ERROR);

and here.

Thanks

Chris

> +        return;
> +    }
> +    g_free(e);
> +}
> +
> +static void qemu_file_input_type_str(Visitor *v, char **obj, const char *name,
> +                                  Error **errp)
> +{
> +    if (obj) {
> +        g_free(*obj);
> +    }
> +}
> +
> +static void qemu_file_input_type_uint8_t(Visitor *v, uint8_t *obj,
> +                                         const char *name, Error **errp)
> +{
> +    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
> +    *obj = qemu_get_byte(ov->file);
> +}
> +
> +static void qemu_file_input_type_uint16_t(Visitor *v, uint16_t *obj,
> +                                          const char *name, Error **errp)
> +{
> +    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
> +    *obj = qemu_get_byte(ov->file)<<  8;
> +    *obj |= qemu_get_byte(ov->file);
> +}
> +
> +static void qemu_file_input_type_uint32_t(Visitor *v, uint32_t *obj,
> +                                          const char *name, Error **errp)
> +{
> +    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
> +    *obj = qemu_get_byte(ov->file)<<  24;
> +    *obj |= qemu_get_byte(ov->file)<<  16;
> +    *obj |= qemu_get_byte(ov->file)<<  8;
> +    *obj |= qemu_get_byte(ov->file);
> +}
> +
> +static void qemu_file_input_type_uint64_t(Visitor *v, uint64_t *obj,
> +                                          const char *name, Error **errp)
> +{
> +    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
> +    *obj = (uint64_t)qemu_get_byte(ov->file)<<  56;
> +    *obj |= (uint64_t)qemu_get_byte(ov->file)<<  48;
> +    *obj |= (uint64_t)qemu_get_byte(ov->file)<<  40;
> +    *obj |= (uint64_t)qemu_get_byte(ov->file)<<  32;
> +    *obj |= qemu_get_byte(ov->file)<<  24;
> +    *obj |= qemu_get_byte(ov->file)<<  16;
> +    *obj |= qemu_get_byte(ov->file)<<  8;
> +    *obj |= qemu_get_byte(ov->file);
> +}
> +
> +static void qemu_file_input_type_int8_t(Visitor *v, int8_t *obj,
> +                                        const char *name, Error **errp)
> +{
> +    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
> +    *obj = qemu_get_sbyte(ov->file);
> +}
> +
> +static void qemu_file_input_type_int16_t(Visitor *v, int16_t *obj,
> +                                         const char *name, Error **errp)
> +{
> +    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
> +    *obj = qemu_get_sbyte(ov->file)<<  8;
> +    *obj |= qemu_get_sbyte(ov->file);
> +}
> +
> +static void qemu_file_input_type_int32_t(Visitor *v, int32_t *obj,
> +                                         const char *name, Error **errp)
> +{
> +    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
> +    *obj = qemu_get_sbyte(ov->file)<<  24;
> +    *obj |= qemu_get_sbyte(ov->file)<<  16;
> +    *obj |= qemu_get_sbyte(ov->file)<<  8;
> +    *obj |= qemu_get_sbyte(ov->file);
> +}
> +
> +static void qemu_file_input_type_int64_t(Visitor *v, int64_t *obj,
> +                                         const char *name, Error **errp)
> +{
> +    QemuFileInputVisitor *ov = container_of(v, QemuFileInputVisitor, visitor);
> +    *obj = (int64_t)qemu_get_sbyte(ov->file)<<  56;
> +    *obj |= (int64_t)qemu_get_sbyte(ov->file)<<  48;
> +    *obj |= (int64_t)qemu_get_sbyte(ov->file)<<  40;
> +    *obj |= (int64_t)qemu_get_sbyte(ov->file)<<  32;
> +    *obj |= qemu_get_sbyte(ov->file)<<  24;
> +    *obj |= qemu_get_sbyte(ov->file)<<  16;
> +    *obj |= qemu_get_sbyte(ov->file)<<  8;
> +    *obj |= qemu_get_sbyte(ov->file);
> +}
> +
> +static void qemu_file_input_type_bool(Visitor *v, bool *obj, const char *name,
> +                                     Error **errp)
> +{
> +    uint8_t val;
> +    qemu_file_input_type_uint8_t(v,&val, name, errp);
> +    *obj = val;
> +}
> +
> +static void qemu_file_input_type_number(Visitor *v, double *obj,
> +                                        const char *name, Error **errp)
> +{
> +    uint64_t *val = (uint64_t *)obj;
> +    qemu_file_input_type_uint64_t(v, val, name, errp);
> +}
> +
> +static void qemu_file_input_type_enum(Visitor *v, int *obj,
> +                                      const char *strings[], const char *kind,
> +                                      const char *name, Error **errp)
> +{
> +}
> +
> +Visitor *qemu_file_input_get_visitor(QemuFileInputVisitor *ov)
> +{
> +    return&ov->visitor;
> +}
> +
> +void qemu_file_input_visitor_cleanup(QemuFileInputVisitor *ov)
> +{
> +    g_free(ov);
> +}
> +
> +QemuFileInputVisitor *qemu_file_input_visitor_new(QEMUFile *f)
> +{
> +    QemuFileInputVisitor *v;
> +
> +    v = g_malloc0(sizeof(*v));
> +
> +    v->file = f;
> +
> +    v->visitor.start_struct = qemu_file_input_start_struct;
> +    v->visitor.end_struct = qemu_file_input_end_struct;
> +    v->visitor.start_list = qemu_file_input_start_list;
> +    v->visitor.next_list = qemu_file_input_next_list;
> +    v->visitor.end_list = qemu_file_input_end_list;
> +    v->visitor.start_array = qemu_file_input_start_array;
> +    v->visitor.next_array = qemu_file_input_next_array;
> +    v->visitor.end_array = qemu_file_input_end_array;
> +    v->visitor.type_enum = qemu_file_input_type_enum;
> +    v->visitor.type_int = qemu_file_input_type_int64_t;
> +    v->visitor.type_uint8_t = qemu_file_input_type_uint8_t;
> +    v->visitor.type_uint16_t = qemu_file_input_type_uint16_t;
> +    v->visitor.type_uint32_t = qemu_file_input_type_uint32_t;
> +    v->visitor.type_uint64_t = qemu_file_input_type_uint64_t;
> +    v->visitor.type_int8_t = qemu_file_input_type_int8_t;
> +    v->visitor.type_int16_t = qemu_file_input_type_int16_t;
> +    v->visitor.type_int32_t = qemu_file_input_type_int32_t;
> +    v->visitor.type_int64_t = qemu_file_input_type_int64_t;
> +    v->visitor.type_bool = qemu_file_input_type_bool;
> +    v->visitor.type_str = qemu_file_input_type_str;
> +    v->visitor.type_number = qemu_file_input_type_number;
> +
> +    QTAILQ_INIT(&v->stack);
> +
> +    return v;
> +}
> diff --git a/qapi/qemu-file-input-visitor.h b/qapi/qemu-file-input-visitor.h
> new file mode 100644
> index 0000000..67b5554
> --- /dev/null
> +++ b/qapi/qemu-file-input-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_INPUT_VISITOR_H
> +#define QEMU_FILE_INPUT_VISITOR_H
> +
> +#include "qapi-visit-core.h"
> +
> +typedef struct QemuFileInputVisitor QemuFileInputVisitor;
> +
> +QemuFileInputVisitor *qemu_file_input_visitor_new(QEMUFile *f);
> +void qemu_file_input_visitor_cleanup(QemuFileInputVisitor *d);
> +
> +Visitor *qemu_file_input_get_visitor(QemuFileInputVisitor *v);
> +
> +#endif

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2011-10-24 23:59 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-09-19 14:41 [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Michael Roth
2011-09-19 14:41 ` [Qemu-devel] [RFC 1/8] qapi: add Visitor interfaces for uint*_t and int*_t Michael Roth
2011-09-19 14:41 ` [Qemu-devel] [RFC 2/8] qapi: add QemuFileOutputVisitor Michael Roth
2011-09-19 14:41 ` [Qemu-devel] [RFC 3/8] qapi: add QemuFileInputVisitor Michael Roth
2011-10-24 23:59   ` Chris Krumme
2011-09-19 14:41 ` [Qemu-devel] [RFC 4/8] savevm: move QEMUFile interfaces into qemu-file.c Michael Roth
2011-09-24  7:23   ` Blue Swirl
2011-09-19 14:41 ` [Qemu-devel] [RFC 5/8] qapi: test cases for QEMUFile input/output visitors Michael Roth
2011-09-19 14:41 ` [Qemu-devel] [RFC 6/8] savevm: add QEMUFile->visitor lookup routines Michael Roth
2011-09-19 14:41 ` [Qemu-devel] [RFC 7/8] cutil: add strocat(), to concat a string to an offset in another Michael Roth
2011-09-20 10:43   ` Paolo Bonzini
2011-09-19 14:41 ` [Qemu-devel] [RFC 8/8] slirp: convert save/load function to visitor interface Michael Roth
2011-09-30 13:39   ` Anthony Liguori
2011-09-30 14:08     ` Michael Roth
2011-10-02 20:21 ` [Qemu-devel] [RFC] New Migration Protocol using Visitor Interface Stefan Berger
2011-10-02 21:08   ` Michael S. Tsirkin
2011-10-03 12:55     ` Anthony Liguori
2011-10-03 13:10       ` Stefan Berger
2011-10-03 13:18         ` Anthony Liguori
2011-10-03 13:30           ` Michael S. Tsirkin
2011-10-03 13:48             ` Anthony Liguori
2011-10-03 14:18               ` Michael S. Tsirkin
2011-10-03 14:56                 ` Anthony Liguori
2011-10-03 15:42                   ` Michael S. Tsirkin
2011-10-03 13:38       ` Michael S. Tsirkin
2011-10-03 13:51         ` Anthony Liguori
2011-10-03 14:41           ` Michael S. Tsirkin
2011-10-03 15:00             ` Anthony Liguori
2011-10-03 15:45               ` Michael S. Tsirkin
2011-10-03 16:05                 ` Anthony Liguori
2011-10-03 16:24                   ` Daniel P. Berrange
2011-10-03 16:51                   ` Michael S. Tsirkin
2011-10-05 11:28               ` Michael S. Tsirkin
2011-10-05 12:46                 ` Anthony Liguori
2011-10-03  6:46 ` Michael S. Tsirkin
2011-10-03 12:51   ` Anthony Liguori
2011-10-03 13:24     ` Michael S. Tsirkin
2011-10-03 13:43       ` Anthony Liguori
2011-10-03 14:11         ` Michael S. Tsirkin
2011-10-03 14:42           ` Anthony Liguori
2011-10-03 15:29             ` Michael S. Tsirkin
2011-10-03 15:44               ` Anthony Liguori
2011-10-03 15:58                 ` Michael S. Tsirkin
2011-10-03 16:02                   ` Anthony Liguori
2011-10-03 14:15         ` Michael S. Tsirkin
2011-10-03 14:55           ` Anthony Liguori
2011-10-03 15:41             ` Michael S. Tsirkin
2011-10-05  2:05         ` Stefan Berger
2011-10-05 12:54           ` Anthony Liguori
2011-10-05 19:06             ` Michael S. Tsirkin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.