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>
This commit is contained in:
Kevin Wolf 2011-11-16 11:35:54 +01:00
parent 3763f26f2f
commit c2c9a46609
5 changed files with 42 additions and 6 deletions

View File

@ -22,7 +22,7 @@ typedef struct QEMUSnapshotInfo {
/* the following fields are informative. They are not needed for /* the following fields are informative. They are not needed for
the consistency of the snapshot */ the consistency of the snapshot */
char name[256]; /* user chosen name */ 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_sec; /* UTC date of the snapshot */
uint32_t date_nsec; uint32_t date_nsec;
uint64_t vm_clock_nsec; /* VM clock relative to boot */ uint64_t vm_clock_nsec; /* VM clock relative to boot */

View File

@ -46,6 +46,10 @@ typedef struct QEMU_PACKED QCowSnapshotHeader {
/* name follows */ /* name follows */
} QCowSnapshotHeader; } QCowSnapshotHeader;
typedef struct QEMU_PACKED QCowSnapshotExtraData {
uint64_t vm_state_size_large;
} QCowSnapshotExtraData;
void qcow2_free_snapshots(BlockDriverState *bs) void qcow2_free_snapshots(BlockDriverState *bs)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
@ -64,6 +68,7 @@ int qcow2_read_snapshots(BlockDriverState *bs)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
QCowSnapshotHeader h; QCowSnapshotHeader h;
QCowSnapshotExtraData extra;
QCowSnapshot *sn; QCowSnapshot *sn;
int i, id_str_size, name_size; int i, id_str_size, name_size;
int64_t offset; int64_t offset;
@ -100,9 +105,18 @@ int qcow2_read_snapshots(BlockDriverState *bs)
id_str_size = be16_to_cpu(h.id_str_size); id_str_size = be16_to_cpu(h.id_str_size);
name_size = be16_to_cpu(h.name_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; offset += extra_data_size;
if (extra_data_size >= 8) {
sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large);
}
/* Read snapshot ID */ /* Read snapshot ID */
sn->id_str = g_malloc(id_str_size + 1); sn->id_str = g_malloc(id_str_size + 1);
ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); 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; BDRVQcowState *s = bs->opaque;
QCowSnapshot *sn; QCowSnapshot *sn;
QCowSnapshotHeader h; QCowSnapshotHeader h;
QCowSnapshotExtraData extra;
int i, name_size, id_str_size, snapshots_size; int i, name_size, id_str_size, snapshots_size;
struct { struct {
uint32_t nb_snapshots; uint32_t nb_snapshots;
@ -150,6 +165,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
sn = s->snapshots + i; sn = s->snapshots + i;
offset = align_offset(offset, 8); offset = align_offset(offset, 8);
offset += sizeof(h); offset += sizeof(h);
offset += sizeof(extra);
offset += strlen(sn->id_str); offset += strlen(sn->id_str);
offset += strlen(sn->name); offset += strlen(sn->name);
} }
@ -169,10 +185,18 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
memset(&h, 0, sizeof(h)); memset(&h, 0, sizeof(h));
h.l1_table_offset = cpu_to_be64(sn->l1_table_offset); h.l1_table_offset = cpu_to_be64(sn->l1_table_offset);
h.l1_size = cpu_to_be32(sn->l1_size); 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_sec = cpu_to_be32(sn->date_sec);
h.date_nsec = cpu_to_be32(sn->date_nsec); h.date_nsec = cpu_to_be32(sn->date_nsec);
h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_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); id_str_size = strlen(sn->id_str);
name_size = strlen(sn->name); name_size = strlen(sn->name);
@ -186,6 +210,12 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
} }
offset += sizeof(h); 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); ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;

View File

@ -78,7 +78,7 @@ typedef struct QCowSnapshot {
uint32_t l1_size; uint32_t l1_size;
char *id_str; char *id_str;
char *name; char *name;
uint32_t vm_state_size; uint64_t vm_state_size;
uint32_t date_sec; uint32_t date_sec;
uint32_t date_nsec; uint32_t date_nsec;
uint64_t vm_clock_nsec; uint64_t vm_clock_nsec;

View File

@ -253,7 +253,13 @@ Snapshot table entry:
36 - 39: Size of extra data in the table entry (used for future 36 - 39: Size of extra data in the table entry (used for future
extensions of the format) 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) variable: Unique ID string for the snapshot (not null terminated)

View File

@ -2002,7 +2002,7 @@ void do_savevm(Monitor *mon, const QDict *qdict)
int ret; int ret;
QEMUFile *f; QEMUFile *f;
int saved_vm_running; int saved_vm_running;
uint32_t vm_state_size; uint64_t vm_state_size;
#ifdef _WIN32 #ifdef _WIN32
struct _timeb tb; struct _timeb tb;
struct tm *ptm; struct tm *ptm;