summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Wolf <kwolf@redhat.com>2011-11-16 11:35:54 +0100
committerKevin Wolf <kwolf@redhat.com>2011-12-15 12:40:33 +0100
commitc2c9a46609164a36b477f6cff1d10ed27a6b53fc (patch)
tree7cf3cf48bcc3e87a93457b5bb5ef9a7cc468ca2e
parent3763f26f2fdff156fec6868b651fe7853fc64de5 (diff)
downloadqemu-c2c9a46609164a36b477f6cff1d10ed27a6b53fc.tar.gz
qcow2: Allow >4 GB VM state
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>
-rw-r--r--block.h2
-rw-r--r--block/qcow2-snapshot.c34
-rw-r--r--block/qcow2.h2
-rw-r--r--docs/specs/qcow2.txt8
-rw-r--r--savevm.c2
5 files changed, 42 insertions, 6 deletions
diff --git a/block.h b/block.h
index 0e3ff9f233..3bd4398609 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 chosen 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 c3112bf71a..7d3fde5a8a 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 4e44eea5ef..99e45361f5 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 e792953c8f..b6adcaddb7 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 b72f6c0d17..f153c25419 100644
--- a/savevm.c
+++ b/savevm.c
@@ -2002,7 +2002,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;