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:
parent
3763f26f2f
commit
c2c9a46609
2
block.h
2
block.h
@ -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 */
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
2
savevm.c
2
savevm.c
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user