@@ -189,7 +189,6 @@ static struct {
.cache = NULL,
};
-
int64_t xbzrle_cache_resize(int64_t new_size)
{
if (XBZRLE.cache != NULL) {
@@ -591,6 +590,7 @@ static void reset_ram_globals(void)
static int ram_save_setup(QEMUFile *f, void *opaque)
{
RAMBlock *block;
+ const MigrationParams *params = &migrate_get_current()->params;
migration_bitmap_init();
qemu_mutex_lock_ramlist();
@@ -610,8 +610,10 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
acct_clear();
}
- memory_global_dirty_log_start();
- migration_bitmap_sync();
+ if (!params->postcopy) {
+ memory_global_dirty_log_start();
+ migration_bitmap_sync();
+ }
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
@@ -916,7 +918,21 @@ done:
return ret;
}
+static void ram_save_set_params(const MigrationParams *params, void *opaque)
+{
+ if (params->postcopy) {
+ savevm_ram_handlers.save_live_iterate =
+ postcopy_outgoing_ram_save_iterate;
+ savevm_ram_handlers.save_live_complete =
+ postcopy_outgoing_ram_save_complete;
+ } else {
+ savevm_ram_handlers.save_live_iterate = ram_save_iterate;
+ savevm_ram_handlers.save_live_complete = ram_save_complete;
+ }
+}
+
SaveVMHandlers savevm_ram_handlers = {
+ .set_params = ram_save_set_params,
.save_live_setup = ram_save_setup,
.save_live_iterate = ram_save_iterate,
.save_live_complete = ram_save_complete,
@@ -64,6 +64,10 @@ int exec_start_outgoing_migration(MigrationState *s, const char *command)
{
FILE *f;
+ if (s->params.postcopy) {
+ return -ENOSYS;
+ }
+
f = popen(command, "w");
if (f == NULL) {
DPRINTF("Unable to popen exec target\n");
@@ -90,6 +90,23 @@ int fd_start_outgoing_migration(MigrationState *s, const char *fdname)
s->write = fd_write;
s->close = fd_close;
+ if (s->params.postcopy) {
+ int flags = fcntl(s->fd, F_GETFL);
+ if ((flags & O_ACCMODE) != O_RDWR) {
+ goto err_after_open;
+ }
+
+ s->fd_read = dup(s->fd);
+ if (s->fd_read == -1) {
+ goto err_after_open;
+ }
+ s->file_read = qemu_fopen_fd(s->fd_read, "rb");
+ if (s->file_read == NULL) {
+ close(s->fd_read);
+ goto err_after_open;
+ }
+ }
+
migrate_fd_connect(s);
return 0;
@@ -167,6 +167,107 @@ static void postcopy_incoming_send_req(QEMUFile *f,
}
}
+static int postcopy_outgoing_recv_req_idstr(QEMUFile *f,
+ struct qemu_umem_req *req,
+ size_t *offset)
+{
+ int ret;
+
+ req->len = qemu_peek_byte(f, *offset);
+ *offset += 1;
+ if (req->len == 0) {
+ return -EAGAIN;
+ }
+ req->idstr = g_malloc((int)req->len + 1);
+ ret = qemu_peek_buffer(f, (uint8_t*)req->idstr, req->len, *offset);
+ *offset += ret;
+ if (ret != req->len) {
+ g_free(req->idstr);
+ req->idstr = NULL;
+ return -EAGAIN;
+ }
+ req->idstr[req->len] = 0;
+ return 0;
+}
+
+static int postcopy_outgoing_recv_req_pgoffs(QEMUFile *f,
+ struct qemu_umem_req *req,
+ size_t *offset)
+{
+ int ret;
+ uint32_t be32;
+ uint32_t i;
+
+ ret = qemu_peek_buffer(f, (uint8_t*)&be32, sizeof(be32), *offset);
+ *offset += sizeof(be32);
+ if (ret != sizeof(be32)) {
+ return -EAGAIN;
+ }
+
+ req->nr = be32_to_cpu(be32);
+ req->pgoffs = g_new(uint64_t, req->nr);
+ for (i = 0; i < req->nr; i++) {
+ uint64_t be64;
+ ret = qemu_peek_buffer(f, (uint8_t*)&be64, sizeof(be64), *offset);
+ *offset += sizeof(be64);
+ if (ret != sizeof(be64)) {
+ g_free(req->pgoffs);
+ req->pgoffs = NULL;
+ return -EAGAIN;
+ }
+ req->pgoffs[i] = be64_to_cpu(be64);
+ }
+ return 0;
+}
+
+static int postcopy_outgoing_recv_req(QEMUFile *f, struct qemu_umem_req *req)
+{
+ int size;
+ int ret;
+ size_t offset = 0;
+
+ size = qemu_peek_buffer(f, (uint8_t*)&req->cmd, 1, offset);
+ if (size <= 0) {
+ return -EAGAIN;
+ }
+ offset += 1;
+
+ switch (req->cmd) {
+ case QEMU_UMEM_REQ_INIT:
+ case QEMU_UMEM_REQ_EOC:
+ /* nothing */
+ break;
+ case QEMU_UMEM_REQ_PAGE:
+ ret = postcopy_outgoing_recv_req_idstr(f, req, &offset);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = postcopy_outgoing_recv_req_pgoffs(f, req, &offset);
+ if (ret < 0) {
+ return ret;
+ }
+ break;
+ case QEMU_UMEM_REQ_PAGE_CONT:
+ ret = postcopy_outgoing_recv_req_pgoffs(f, req, &offset);
+ if (ret < 0) {
+ return ret;
+ }
+ break;
+ default:
+ abort();
+ break;
+ }
+ qemu_file_skip(f, offset);
+ DPRINTF("cmd %d\n", req->cmd);
+ return 0;
+}
+
+static void postcopy_outgoing_free_req(struct qemu_umem_req *req)
+{
+ g_free(req->idstr);
+ g_free(req->pgoffs);
+}
+
/***************************************************************************
* QEMU_VM_POSTCOPY section subtype
*/
@@ -174,6 +275,328 @@ static void postcopy_incoming_send_req(QEMUFile *f,
#define QEMU_VM_POSTCOPY_SECTION_FULL 1
/***************************************************************************
+ * outgoing part
+ */
+
+enum POState {
+ PO_STATE_ERROR_RECEIVE,
+ PO_STATE_ACTIVE,
+ PO_STATE_EOC_RECEIVED,
+ PO_STATE_ALL_PAGES_SENT,
+ PO_STATE_COMPLETED,
+};
+typedef enum POState POState;
+
+struct PostcopyOutgoingState {
+ POState state;
+ QEMUFile *mig_read;
+ int fd_read;
+ RAMBlock *last_block_read;
+
+ QEMUFile *mig_buffered_write;
+ MigrationState *ms;
+};
+
+int postcopy_outgoing_create_read_socket(MigrationState *s)
+{
+ if (!s->params.postcopy) {
+ return 0;
+ }
+
+ s->fd_read = dup(s->fd);
+ if (s->fd_read == -1) {
+ int ret = -errno;
+ perror("dup");
+ return ret;
+ }
+ s->file_read = qemu_fopen_socket(s->fd_read);
+ if (s->file_read == NULL) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void postcopy_outgoing_state_begin(QEMUFile *f)
+{
+ uint64_t options = 0;
+ qemu_put_ubyte(f, QEMU_VM_POSTCOPY_INIT);
+ qemu_put_be32(f, sizeof(options));
+ qemu_put_be64(f, options);
+}
+
+void postcopy_outgoing_state_complete(
+ QEMUFile *f, const uint8_t *buffer, size_t buffer_size)
+{
+ qemu_put_ubyte(f, QEMU_VM_POSTCOPY_SECTION_FULL);
+ qemu_put_be32(f, buffer_size);
+ qemu_put_buffer(f, buffer, buffer_size);
+}
+
+int postcopy_outgoing_ram_save_iterate(QEMUFile *f, void *opaque)
+{
+ qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+ return 1;
+}
+
+int postcopy_outgoing_ram_save_complete(QEMUFile *f, void *opaque)
+{
+ qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+ return 0;
+}
+
+/*
+ * return value
+ * 0: continue postcopy mode
+ * > 0: completed postcopy mode.
+ * < 0: error
+ */
+static int postcopy_outgoing_handle_req(PostcopyOutgoingState *s,
+ const struct qemu_umem_req *req,
+ bool *written)
+{
+ int i;
+ RAMBlock *block;
+
+ DPRINTF("cmd %d state %d\n", req->cmd, s->state);
+ switch(req->cmd) {
+ case QEMU_UMEM_REQ_INIT:
+ /* nothing */
+ break;
+ case QEMU_UMEM_REQ_EOC:
+ /* tell to finish migration. */
+ if (s->state == PO_STATE_ALL_PAGES_SENT) {
+ s->state = PO_STATE_COMPLETED;
+ DPRINTF("-> PO_STATE_COMPLETED\n");
+ } else {
+ s->state = PO_STATE_EOC_RECEIVED;
+ DPRINTF("-> PO_STATE_EOC_RECEIVED\n");
+ }
+ return 1;
+ case QEMU_UMEM_REQ_PAGE:
+ DPRINTF("idstr: %s\n", req->idstr);
+ block = ram_find_block(req->idstr, strlen(req->idstr));
+ if (block == NULL) {
+ return -EINVAL;
+ }
+ s->last_block_read = block;
+ /* fall through */
+ case QEMU_UMEM_REQ_PAGE_CONT:
+ DPRINTF("nr %d\n", req->nr);
+ if (s->mig_buffered_write == NULL) {
+ assert(s->state == PO_STATE_ALL_PAGES_SENT);
+ break;
+ }
+ for (i = 0; i < req->nr; i++) {
+ DPRINTF("offs[%d] 0x%"PRIx64"\n", i, req->pgoffs[i]);
+ int ret = ram_save_page(s->mig_buffered_write, s->last_block_read,
+ req->pgoffs[i] << TARGET_PAGE_BITS, false);
+ if (ret > 0) {
+ *written = true;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void postcopy_outgoing_close_mig_read(PostcopyOutgoingState *s)
+{
+ if (s->mig_read != NULL) {
+ qemu_set_fd_handler(s->fd_read, NULL, NULL, NULL);
+ qemu_fclose(s->mig_read);
+ s->mig_read = NULL;
+ fd_close(&s->fd_read);
+
+ s->ms->file_read = NULL;
+ s->ms->fd_read = -1;
+ }
+}
+
+static void postcopy_outgoing_completed(PostcopyOutgoingState *s)
+{
+ postcopy_outgoing_close_mig_read(s);
+ s->ms->postcopy = NULL;
+ g_free(s);
+}
+
+static void postcopy_outgoing_recv_handler(void *opaque)
+{
+ PostcopyOutgoingState *s = opaque;
+ bool written = false;
+ int ret = 0;
+
+ assert(s->state == PO_STATE_ACTIVE ||
+ s->state == PO_STATE_ALL_PAGES_SENT);
+
+ do {
+ struct qemu_umem_req req = {.idstr = NULL,
+ .pgoffs = NULL};
+
+ ret = postcopy_outgoing_recv_req(s->mig_read, &req);
+ if (ret < 0) {
+ if (ret == -EAGAIN) {
+ ret = 0;
+ }
+ break;
+ }
+
+ /* Even when s->state == PO_STATE_ALL_PAGES_SENT,
+ some request can be received like QEMU_UMEM_REQ_EOC */
+ ret = postcopy_outgoing_handle_req(s, &req, &written);
+ postcopy_outgoing_free_req(&req);
+ } while (ret == 0);
+
+ /*
+ * flush buffered_file.
+ * Although mig_write is rate-limited buffered file, those written pages
+ * are requested on demand by the destination. So forcibly push
+ * those pages ignoring rate limiting
+ */
+ if (written) {
+ qemu_buffered_file_drain(s->mig_buffered_write);
+ }
+
+ if (ret < 0) {
+ switch (s->state) {
+ case PO_STATE_ACTIVE:
+ s->state = PO_STATE_ERROR_RECEIVE;
+ DPRINTF("-> PO_STATE_ERROR_RECEIVE\n");
+ break;
+ case PO_STATE_ALL_PAGES_SENT:
+ s->state = PO_STATE_COMPLETED;
+ DPRINTF("-> PO_STATE_ALL_PAGES_SENT\n");
+ break;
+ default:
+ abort();
+ }
+ }
+ if (s->state == PO_STATE_ERROR_RECEIVE || s->state == PO_STATE_COMPLETED) {
+ postcopy_outgoing_close_mig_read(s);
+ }
+ if (s->state == PO_STATE_COMPLETED) {
+ DPRINTF("PO_STATE_COMPLETED\n");
+ MigrationState *ms = s->ms;
+ postcopy_outgoing_completed(s);
+ migrate_fd_completed(ms);
+ }
+}
+
+PostcopyOutgoingState *postcopy_outgoing_begin(MigrationState *ms)
+{
+ PostcopyOutgoingState *s = g_new(PostcopyOutgoingState, 1);
+ DPRINTF("outgoing begin\n");
+ qemu_buffered_file_drain(ms->file);
+
+ s->ms = ms;
+ s->state = PO_STATE_ACTIVE;
+ s->fd_read = ms->fd_read;
+ s->mig_read = ms->file_read;
+ s->mig_buffered_write = ms->file;
+
+ /* Make sure all dirty bits are set */
+ memory_global_dirty_log_stop();
+ migration_bitmap_init();
+
+ qemu_set_fd_handler(s->fd_read,
+ &postcopy_outgoing_recv_handler, NULL, s);
+ postcopy_outgoing_recv_handler(s);
+ return s;
+}
+
+static void postcopy_outgoing_ram_all_sent(QEMUFile *f,
+ PostcopyOutgoingState *s)
+{
+ assert(s->state == PO_STATE_ACTIVE);
+
+ s->state = PO_STATE_ALL_PAGES_SENT;
+ /* tell incoming side that all pages are sent */
+ qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+ qemu_buffered_file_drain(f);
+ DPRINTF("sent RAM_SAVE_FLAG_EOS\n");
+ migrate_fd_cleanup(s->ms);
+
+ /* Later migrate_fd_complete() will be called which calls
+ * migrate_fd_cleanup() again. So dummy file is created
+ * for qemu monitor to keep working.
+ */
+ s->ms->file = qemu_fopen_ops(NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL);
+ s->mig_buffered_write = NULL;
+
+ migration_bitmap_free();
+}
+
+int postcopy_outgoing_ram_save_background(QEMUFile *f, void *postcopy)
+{
+ PostcopyOutgoingState *s = postcopy;
+#define MAX_WAIT 50 /* stolen from ram_save_iterate() */
+ double t0;
+ int i;
+
+ assert(s->state == PO_STATE_ACTIVE ||
+ s->state == PO_STATE_EOC_RECEIVED ||
+ s->state == PO_STATE_ERROR_RECEIVE);
+
+ switch (s->state) {
+ case PO_STATE_ACTIVE:
+ /* nothing. processed below */
+ break;
+ case PO_STATE_EOC_RECEIVED:
+ qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+ s->state = PO_STATE_COMPLETED;
+ postcopy_outgoing_completed(s);
+ DPRINTF("PO_STATE_COMPLETED\n");
+ return 1;
+ case PO_STATE_ERROR_RECEIVE:
+ postcopy_outgoing_completed(s);
+ DPRINTF("PO_STATE_ERROR_RECEIVE\n");
+ return -1;
+ default:
+ abort();
+ }
+
+ DPRINTF("outgoing background state: %d\n", s->state);
+ i = 0;
+ t0 = qemu_get_clock_ns(rt_clock);
+ while (qemu_file_rate_limit(f) == 0) {
+ int nfds = -1;
+ fd_set readfds;
+ struct timeval timeout = {.tv_sec = 0, .tv_usec = 0};
+ int ret;
+
+ if (ram_save_block(f, false) == 0) { /* no more blocks */
+ DPRINTF("outgoing background all sent\n");
+ assert(s->state == PO_STATE_ACTIVE);
+ postcopy_outgoing_ram_all_sent(f, s);
+ return 0;
+ }
+
+ FD_ZERO(&readfds);
+ set_fd(s->fd_read, &readfds, &nfds);
+ ret = select(nfds + 1, &readfds, NULL, NULL, &timeout);
+ if (ret >= 0 && FD_ISSET(s->fd_read, &readfds)) {
+ /* page request is pending */
+ DPRINTF("pending request\n");
+ break;
+ }
+
+ /* stolen from ram_save_iterate() */
+ if ((i & 63) == 0) {
+ int64_t t1 = (qemu_get_clock_ns(rt_clock) - t0) / 1000000;
+ if (t1 > MAX_WAIT) {
+ DPRINTF("too long %"PRIu64"\n", t1);
+ break;
+ }
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+/***************************************************************************
* incoming part
*/
@@ -64,7 +64,11 @@ static void tcp_wait_for_connect(int fd, void *opaque)
} else {
DPRINTF("migrate connect success\n");
s->fd = fd;
- migrate_fd_connect(s);
+ if (postcopy_outgoing_create_read_socket(s) < 0) {
+ migrate_fd_error(s);
+ } else {
+ migrate_fd_connect(s);
+ }
}
}
@@ -71,12 +71,20 @@ static void unix_wait_for_connect(void *opaque)
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
- if (val == 0)
+ if (val == 0) {
+ ret = postcopy_outgoing_create_read_socket(s);
+ if (ret < 0) {
+ goto error_out;
+ }
migrate_fd_connect(s);
- else {
+ } else {
DPRINTF("error connecting %d\n", val);
- migrate_fd_error(s);
+ goto error_out;
}
+ return;
+
+error_out:
+ migrate_fd_error(s);
}
int unix_start_outgoing_migration(MigrationState *s, const char *path)
@@ -111,11 +119,19 @@ int unix_start_outgoing_migration(MigrationState *s, const char *path)
if (ret < 0) {
DPRINTF("connect failed\n");
- migrate_fd_error(s);
- return ret;
+ goto error_out;
+ }
+
+ ret = postcopy_outgoing_create_read_socket(s);
+ if (ret < 0) {
+ goto error_out;
}
migrate_fd_connect(s);
return 0;
+
+error_out:
+ migrate_fd_error(s);
+ return ret;
}
static void unix_accept_incoming_migration(void *opaque)
@@ -41,6 +41,11 @@ enum {
MIG_STATE_COMPLETED,
};
+enum {
+ MIG_SUBSTATE_PRECOPY,
+ MIG_SUBSTATE_POSTCOPY,
+};
+
#define MAX_THROTTLE (32 << 20) /* Migration speed throttling */
/* Migration XBZRLE default cache size */
@@ -328,6 +333,17 @@ void migrate_fd_put_ready(MigrationState *s)
return;
}
+ if (s->substate == MIG_SUBSTATE_POSTCOPY) {
+ /* PRINTF("postcopy background\n"); */
+ ret = postcopy_outgoing_ram_save_background(s->file, s->postcopy);
+ if (ret > 0) {
+ migrate_fd_completed(s);
+ } else if (ret < 0) {
+ migrate_fd_error(s);
+ }
+ return;
+ }
+
DPRINTF("iterate\n");
ret = qemu_savevm_state_iterate(s->file);
if (ret < 0) {
@@ -341,7 +357,20 @@ void migrate_fd_put_ready(MigrationState *s)
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
- if (qemu_savevm_state_complete(s->file) < 0) {
+ if (s->params.postcopy) {
+ if (qemu_savevm_state_complete(s->file, &s->params) < 0) {
+ migrate_fd_error(s);
+ if (old_vm_running) {
+ vm_start();
+ }
+ return;
+ }
+ s->substate = MIG_SUBSTATE_POSTCOPY;
+ s->postcopy = postcopy_outgoing_begin(s);
+ return;
+ }
+
+ if (qemu_savevm_state_complete(s->file, &s->params) < 0) {
migrate_fd_error(s);
} else {
migrate_fd_completed(s);
@@ -431,6 +460,7 @@ void migrate_fd_connect(MigrationState *s)
int ret;
s->state = MIG_STATE_ACTIVE;
+ s->substate = MIG_SUBSTATE_PRECOPY;
s->file = qemu_fopen_ops_buffered(s);
DPRINTF("beginning savevm\n");
@@ -28,6 +28,7 @@ struct MigrationParams {
};
typedef struct MigrationState MigrationState;
+typedef struct PostcopyOutgoingState PostcopyOutgoingState;
struct MigrationState
{
@@ -46,6 +47,12 @@ struct MigrationState
int64_t dirty_pages_rate;
bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
int64_t xbzrle_cache_size;
+
+ /* for postcopy */
+ int substate; /* precopy or postcopy */
+ int fd_read;
+ QEMUFile *file_read; /* connection from the detination */
+ PostcopyOutgoingState *postcopy;
};
void process_incoming_migration(QEMUFile *f);
@@ -135,6 +142,17 @@ int64_t migrate_xbzrle_cache_size(void);
int64_t xbzrle_cache_resize(int64_t new_size);
+/* For outgoing postcopy */
+int postcopy_outgoing_create_read_socket(MigrationState *s);
+void postcopy_outgoing_state_begin(QEMUFile *f);
+void postcopy_outgoing_state_complete(
+ QEMUFile *f, const uint8_t *buffer, size_t buffer_size);
+int postcopy_outgoing_ram_save_iterate(QEMUFile *f, void *opaque);
+int postcopy_outgoing_ram_save_complete(QEMUFile *f, void *opaque);
+
+PostcopyOutgoingState *postcopy_outgoing_begin(MigrationState *s);
+int postcopy_outgoing_ram_save_background(QEMUFile *f, void *postcopy);
+
/* For incoming postcopy */
extern bool incoming_postcopy;
@@ -1647,6 +1647,12 @@ int qemu_savevm_state_begin(QEMUFile *f,
qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+ if (params->postcopy) {
+ /* tell this is postcopy */
+ qemu_put_byte(f, QEMU_VM_POSTCOPY);
+ postcopy_outgoing_state_begin(f);
+ }
+
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
int len;
@@ -1734,8 +1740,10 @@ int qemu_savevm_state_iterate(QEMUFile *f)
return ret;
}
-int qemu_savevm_state_complete(QEMUFile *f)
+int qemu_savevm_state_complete(QEMUFile *f, const MigrationParams *params)
{
+ QEMUFile *orig_f = NULL;
+ QEMUFileBuf *buf_file = NULL;
SaveStateEntry *se;
int ret;
@@ -1762,6 +1770,20 @@ int qemu_savevm_state_complete(QEMUFile *f)
}
}
+ if (params->postcopy) {
+ /* VMStateDescription:pre/post_load and
+ * cpu_sychronize_all_post_init() may fault on guest RAM.
+ * (MSR_KVM_WALL_CLOCK, MSR_KVM_SYSTEM_TIME)
+ * postcopy threads needs to be created before the fault.
+ *
+ * This is hacky, but it's because size of section/state structure
+ * can't be easily determined without actual loading.
+ */
+ orig_f = f;
+ buf_file = qemu_fopen_buf_write();
+ f = buf_file->file;
+ }
+
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
int len;
@@ -1787,6 +1809,15 @@ int qemu_savevm_state_complete(QEMUFile *f)
qemu_put_byte(f, QEMU_VM_EOF);
+ if (params->postcopy) {
+ qemu_fflush(f);
+ qemu_put_byte(orig_f, QEMU_VM_POSTCOPY);
+ postcopy_outgoing_state_complete(
+ orig_f, buf_file->buffer, buf_file->buffer_size);
+ qemu_fclose(f);
+ f = orig_f;
+ }
+
return qemu_file_get_error(f);
}
@@ -1825,7 +1856,7 @@ static int qemu_savevm_state(QEMUFile *f)
goto out;
} while (ret == 0);
- ret = qemu_savevm_state_complete(f);
+ ret = qemu_savevm_state_complete(f, ¶ms);
out:
if (ret == 0) {
@@ -81,7 +81,7 @@ bool qemu_savevm_state_blocked(Error **errp);
int qemu_savevm_state_begin(QEMUFile *f,
const MigrationParams *params);
int qemu_savevm_state_iterate(QEMUFile *f);
-int qemu_savevm_state_complete(QEMUFile *f);
+int qemu_savevm_state_complete(QEMUFile *f, const MigrationParams *params);
void qemu_savevm_state_cancel(QEMUFile *f);
int qemu_loadvm_state(QEMUFile *f);
This patch implements postcopy live migration for outgoing part Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> --- Changes v2 -> v3: - modify savevm_ram_handlers instead of if (postcopy) - code simplification Changes v1 -> v2: - fix parameter to qemu_fdopen() - handle QEMU_UMEM_REQ_EOC properly when PO_STATE_ALL_PAGES_SENT, QEMU_UMEM_REQ_EOC request was ignored. handle properly it. - flush on-demand page unconditionally - improve postcopy_outgoing_ram_save_live and postcopy_outgoing_begin() - use qemu_fopen_fd - use memory api instead of obsolete api - segv in postcopy_outgoing_check_all_ram_sent() - catch up qapi change --- arch_init.c | 22 ++- migration-exec.c | 4 + migration-fd.c | 17 ++ migration-postcopy.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++ migration-tcp.c | 6 +- migration-unix.c | 26 +++- migration.c | 32 +++- migration.h | 18 +++ savevm.c | 35 ++++- sysemu.h | 2 +- 10 files changed, 572 insertions(+), 13 deletions(-)