Patchwork qcow2: Allow >4 GB VM state

login
register
mail settings
Submitter Kevin Wolf
Date Nov. 25, 2011, 12:25 p.m.
Message ID <1322223920-4377-1-git-send-email-kwolf@redhat.com>
Download mbox | patch
Permalink /patch/127676/
State New
Headers show

Comments

Kevin Wolf - Nov. 25, 2011, 12:25 p.m.
This is a compatible extension to the snapshot header format that allows
saving a 64 bit VM state size.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.h                |    2 +-
 block/qcow2-snapshot.c |   34 ++++++++++++++++++++++++++++++++--
 block/qcow2.h          |    2 +-
 docs/specs/qcow2.txt   |    8 +++++++-
 savevm.c               |    2 +-
 5 files changed, 42 insertions(+), 6 deletions(-)

Patch

diff --git a/block.h b/block.h
index 1d28e85..924451b 100644
--- a/block.h
+++ b/block.h
@@ -22,7 +22,7 @@  typedef struct QEMUSnapshotInfo {
     /* the following fields are informative. They are not needed for
        the consistency of the snapshot */
     char name[256]; /* user choosen name */
-    uint32_t vm_state_size; /* VM state info size */
+    uint64_t vm_state_size; /* VM state info size */
     uint32_t date_sec; /* UTC date of the snapshot */
     uint32_t date_nsec;
     uint64_t vm_clock_nsec; /* VM clock relative to boot */
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index c3112bf..7d3fde5 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -46,6 +46,10 @@  typedef struct QEMU_PACKED QCowSnapshotHeader {
     /* name follows  */
 } QCowSnapshotHeader;
 
+typedef struct QEMU_PACKED QCowSnapshotExtraData {
+    uint64_t vm_state_size_large;
+} QCowSnapshotExtraData;
+
 void qcow2_free_snapshots(BlockDriverState *bs)
 {
     BDRVQcowState *s = bs->opaque;
@@ -64,6 +68,7 @@  int qcow2_read_snapshots(BlockDriverState *bs)
 {
     BDRVQcowState *s = bs->opaque;
     QCowSnapshotHeader h;
+    QCowSnapshotExtraData extra;
     QCowSnapshot *sn;
     int i, id_str_size, name_size;
     int64_t offset;
@@ -100,9 +105,18 @@  int qcow2_read_snapshots(BlockDriverState *bs)
         id_str_size = be16_to_cpu(h.id_str_size);
         name_size = be16_to_cpu(h.name_size);
 
-        /* Skip extra data */
+        /* Read extra data */
+        ret = bdrv_pread(bs->file, offset, &extra,
+                         MIN(sizeof(extra), extra_data_size));
+        if (ret < 0) {
+            goto fail;
+        }
         offset += extra_data_size;
 
+        if (extra_data_size >= 8) {
+            sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large);
+        }
+
         /* Read snapshot ID */
         sn->id_str = g_malloc(id_str_size + 1);
         ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size);
@@ -136,6 +150,7 @@  static int qcow2_write_snapshots(BlockDriverState *bs)
     BDRVQcowState *s = bs->opaque;
     QCowSnapshot *sn;
     QCowSnapshotHeader h;
+    QCowSnapshotExtraData extra;
     int i, name_size, id_str_size, snapshots_size;
     struct {
         uint32_t nb_snapshots;
@@ -150,6 +165,7 @@  static int qcow2_write_snapshots(BlockDriverState *bs)
         sn = s->snapshots + i;
         offset = align_offset(offset, 8);
         offset += sizeof(h);
+        offset += sizeof(extra);
         offset += strlen(sn->id_str);
         offset += strlen(sn->name);
     }
@@ -169,10 +185,18 @@  static int qcow2_write_snapshots(BlockDriverState *bs)
         memset(&h, 0, sizeof(h));
         h.l1_table_offset = cpu_to_be64(sn->l1_table_offset);
         h.l1_size = cpu_to_be32(sn->l1_size);
-        h.vm_state_size = cpu_to_be32(sn->vm_state_size);
+        /* If it doesn't fit in 32 bit, older implementations should treat it
+         * as a disk-only snapshot rather than truncate the VM state */
+        if (sn->vm_state_size <= 0xffffffff) {
+            h.vm_state_size = cpu_to_be32(sn->vm_state_size);
+        }
         h.date_sec = cpu_to_be32(sn->date_sec);
         h.date_nsec = cpu_to_be32(sn->date_nsec);
         h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec);
+        h.extra_data_size = cpu_to_be32(sizeof(extra));
+
+        memset(&extra, 0, sizeof(extra));
+        extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size);
 
         id_str_size = strlen(sn->id_str);
         name_size = strlen(sn->name);
@@ -186,6 +210,12 @@  static int qcow2_write_snapshots(BlockDriverState *bs)
         }
         offset += sizeof(h);
 
+        ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra));
+        if (ret < 0) {
+            goto fail;
+        }
+        offset += sizeof(extra);
+
         ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size);
         if (ret < 0) {
             goto fail;
diff --git a/block/qcow2.h b/block/qcow2.h
index 4e44eea..99e4536 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -78,7 +78,7 @@  typedef struct QCowSnapshot {
     uint32_t l1_size;
     char *id_str;
     char *name;
-    uint32_t vm_state_size;
+    uint64_t vm_state_size;
     uint32_t date_sec;
     uint32_t date_nsec;
     uint64_t vm_clock_nsec;
diff --git a/docs/specs/qcow2.txt b/docs/specs/qcow2.txt
index e792953..b6adcad 100644
--- a/docs/specs/qcow2.txt
+++ b/docs/specs/qcow2.txt
@@ -253,7 +253,13 @@  Snapshot table entry:
         36 - 39:    Size of extra data in the table entry (used for future
                     extensions of the format)
 
-        variable:   Extra data for future extensions. Must be ignored.
+        variable:   Extra data for future extensions. Unknown fields must be
+                    ignored. Currently defined are (offset relative to snapshot
+                    table entry):
+
+                    Byte 40 - 47:   Size of the VM state in bytes. 0 if no VM
+                                    state is saved. If this field is present,
+                                    the 32-bit value in bytes 32-35 is ignored.
 
         variable:   Unique ID string for the snapshot (not null terminated)
 
diff --git a/savevm.c b/savevm.c
index f53cd4c..83d1025 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1951,7 +1951,7 @@  void do_savevm(Monitor *mon, const QDict *qdict)
     int ret;
     QEMUFile *f;
     int saved_vm_running;
-    uint32_t vm_state_size;
+    uint64_t vm_state_size;
 #ifdef _WIN32
     struct _timeb tb;
     struct tm *ptm;