@@ -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,
@@ -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;
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(-)