Patchwork [2/4] savevm: save vmstate with seekable file

login
register
mail settings
Submitter Wayne Xia
Date Feb. 28, 2013, 8:09 a.m.
Message ID <1362038985-19008-3-git-send-email-xiawenc@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/223810/
State New
Headers show

Comments

Wayne Xia - Feb. 28, 2013, 8:09 a.m.
This patch added logic in qemu_savevm level which can predict the space
used in iteration, when qemu file is seekable

Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
---
 include/migration/vmstate.h |   10 +++++
 savevm.c                    |   87 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 95 insertions(+), 2 deletions(-)

Patch

diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index f27276c..a1bb8b3 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -39,6 +39,16 @@  typedef struct SaveVMHandlers {
     void (*cancel)(void *opaque);
     LoadStateHandler *load_state;
     bool (*is_active)(void *opaque);
+    /* seekable file */
+    int (*save_live_iterate_seekable)(QEMUFile *f, int64_t base,
+                                      int64_t max, void *opaque);
+    int64_t (*save_live_iterate_seekable_get_size)(void *opaque);
+    /* after return, complete_seekable must make sure qemu_ftell(f) will
+       report the correct value */
+    int (*save_live_complete_seekable)(QEMUFile *f, int64_t iterate_base,
+                                       int64_t iterate_max,
+                                       int64_t complete_base,
+                                       void *opaque);
 } SaveVMHandlers;
 
 int register_savevm(DeviceState *dev,
diff --git a/savevm.c b/savevm.c
index 54dc090..57dbf09 100644
--- a/savevm.c
+++ b/savevm.c
@@ -125,6 +125,11 @@  struct QEMUFile {
     int buf_size; /* 0 when writing */
     uint8_t buf[IO_BUF_SIZE];
 
+    /* seekable file support */
+    bool file_seekable; /* write to seekable file instead of stream */
+    int64_t iterate_base;
+    int64_t iterate_size;
+
     int last_error;
 };
 
@@ -1220,6 +1225,12 @@  typedef struct SaveStateEntry {
     CompatEntry *compat;
     int no_migrate;
     int is_ram;
+    /* seekable file live save */
+    /* for iterate */
+    int64_t iterate_base; /* base start, include head QEMU_VM_SECTION_PART */
+    int64_t iterate_base1; /* without head, used by device's function */
+    int64_t iterate_size; /* total iterate size, include 5 byte head */
+    int64_t iterate_size1; /* without head, used by device's function */
 } SaveStateEntry;
 
 
@@ -1597,6 +1608,42 @@  bool qemu_savevm_state_blocked(Error **errp)
     return false;
 }
 
+#define VMSTATE_ITERATE_PART_HEAD_SIZE (5)
+/* This function is closely related with qemu_savevm_state_iterate(), it should
+   be called before iterate, to arrange the device's space to write.
+   it returns the total size, negative on error. */
+static int64_t qemu_savevm_state_iterate_calculate_space(int64_t begin)
+{
+    int64_t base_offset;
+    SaveStateEntry *se;
+
+    base_offset = begin;
+
+    QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+        if (!se->ops || !se->ops->save_live_iterate) {
+            continue;
+        }
+        if (se->ops && se->ops->is_active) {
+            if (!se->ops->is_active(se->opaque)) {
+                continue;
+            }
+        }
+        if ((!se->ops->save_live_iterate_seekable) ||
+            (!se->ops->save_live_iterate_seekable_get_size) ||
+            (!se->ops->save_live_complete_seekable)) {
+            return -1;
+        }
+        se->iterate_base = base_offset;
+        se->iterate_base1 = base_offset + VMSTATE_ITERATE_PART_HEAD_SIZE;
+        se->iterate_size1 =
+                     se->ops->save_live_iterate_seekable_get_size(se->opaque);
+        se->iterate_size = se->iterate_size1 + VMSTATE_ITERATE_PART_HEAD_SIZE;
+        base_offset += se->iterate_size;
+    }
+
+    return base_offset - begin;
+}
+
 int qemu_savevm_state_begin(QEMUFile *f,
                             const MigrationParams *params)
 {
@@ -1642,6 +1689,18 @@  int qemu_savevm_state_begin(QEMUFile *f,
             return ret;
         }
     }
+
+    if (f->file_seekable) {
+        int64_t size, begin = qemu_ftell(f);
+        size = qemu_savevm_state_iterate_calculate_space(begin);
+        if (size < 0) {
+            qemu_savevm_state_cancel();
+            return size;
+        }
+        f->iterate_size = size;
+        f->iterate_base = begin;
+    }
+
     ret = qemu_file_get_error(f);
     if (ret != 0) {
         qemu_savevm_state_cancel();
@@ -1675,11 +1734,22 @@  int qemu_savevm_state_iterate(QEMUFile *f)
             return 0;
         }
         trace_savevm_section_start();
+        if (f->file_seekable) {
+            ret = qemu_fseek(f, se->iterate_base);
+            if (ret < 0) {
+                break;
+            }
+        }
         /* Section type */
         qemu_put_byte(f, QEMU_VM_SECTION_PART);
         qemu_put_be32(f, se->section_id);
 
-        ret = se->ops->save_live_iterate(f, se->opaque);
+        if (f->file_seekable) {
+            ret = se->ops->save_live_iterate_seekable(f,
+                             se->iterate_base1, se->iterate_size1, se->opaque);
+        } else {
+            ret = se->ops->save_live_iterate(f, se->opaque);
+        }
         trace_savevm_section_end(se->section_id);
 
         if (ret <= 0) {
@@ -1707,6 +1777,13 @@  int qemu_savevm_state_complete(QEMUFile *f)
 
     cpu_synchronize_all_states();
 
+    if (f->file_seekable) {
+        ret = qemu_fseek(f, f->iterate_base + f->iterate_size);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
         if (!se->ops || !se->ops->save_live_complete) {
             continue;
@@ -1721,7 +1798,13 @@  int qemu_savevm_state_complete(QEMUFile *f)
         qemu_put_byte(f, QEMU_VM_SECTION_END);
         qemu_put_be32(f, se->section_id);
 
-        ret = se->ops->save_live_complete(f, se->opaque);
+        if (f->file_seekable) {
+            int64_t complete_base = qemu_ftell(f);
+            ret = se->ops->save_live_complete_seekable(f, se->iterate_base1,
+                                 se->iterate_size1, complete_base, se->opaque);
+        } else {
+            ret = se->ops->save_live_complete(f, se->opaque);
+        }
         trace_savevm_section_end(se->section_id);
         if (ret < 0) {
             return ret;