switch (s->state) {
case MIGRATION_STATUS_ACTIVE:
+ case MIGRATION_STATUS_POSTCOPY_DEVICE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP:
MigrationState *s = current_migration;
return (s->state == MIGRATION_STATUS_ACTIVE ||
+ s->state == MIGRATION_STATUS_POSTCOPY_DEVICE ||
s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
}
break;
case MIGRATION_STATUS_ACTIVE:
case MIGRATION_STATUS_CANCELLING:
+ case MIGRATION_STATUS_POSTCOPY_DEVICE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_PRE_SWITCHOVER:
case MIGRATION_STATUS_DEVICE:
case MIGRATION_STATUS_CANCELLING:
case MIGRATION_STATUS_CANCELLED:
case MIGRATION_STATUS_ACTIVE:
+ case MIGRATION_STATUS_POSTCOPY_DEVICE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER:
MigrationState *s = migrate_get_current();
switch (s->state) {
+ case MIGRATION_STATUS_POSTCOPY_DEVICE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_PAUSED:
case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP:
memset(&mig_stats, 0, sizeof(mig_stats));
migration_reset_vfio_bytes_transferred();
+ s->postcopy_package_loaded = false;
+ qemu_event_reset(&s->postcopy_package_loaded_event);
+
return 0;
}
tmp32 = ldl_be_p(buf);
trace_source_return_path_thread_pong(tmp32);
qemu_sem_post(&ms->rp_state.rp_pong_acks);
+ if (tmp32 == QEMU_VM_PING_PACKAGED_LOADED) {
+ trace_source_return_path_thread_postcopy_package_loaded();
+ ms->postcopy_package_loaded = true;
+ qemu_event_set(&ms->postcopy_package_loaded_event);
+ }
break;
case MIG_RP_MSG_REQ_PAGES:
if (migrate_postcopy_ram()) {
qemu_savevm_send_ping(fb, 3);
}
+ if (ms->rp_state.rp_thread_created) {
+ /*
+ * This ping will tell us that all non-postcopiable device state has been
+ * successfully loaded and the destination is about to start. When
+ * response is received, it will trigger transition from POSTCOPY_DEVICE
+ * to POSTCOPY_ACTIVE state.
+ */
+ qemu_savevm_send_ping(fb, QEMU_VM_PING_PACKAGED_LOADED);
+ }
qemu_savevm_send_postcopy_run(fb);
*/
migration_rate_set(migrate_max_postcopy_bandwidth());
- /* Now, switchover looks all fine, switching to postcopy-active */
+ /*
+ * Now, switchover looks all fine, switching to POSTCOPY_DEVICE, or
+ * directly to POSTCOPY_ACTIVE if there is no return path.
+ */
migrate_set_state(&ms->state, MIGRATION_STATUS_DEVICE,
+ ms->rp_state.rp_thread_created ?
+ MIGRATION_STATUS_POSTCOPY_DEVICE :
MIGRATION_STATUS_POSTCOPY_ACTIVE);
bql_unlock();
return postcopy_pause(s);
} else {
/*
- * For precopy (or postcopy with error outside IO), we fail
- * with no time.
+ * For precopy (or postcopy with error outside IO, or before dest
+ * starts), we fail with no time.
*/
migrate_set_state(&s->state, state, MIGRATION_STATUS_FAILED);
trace_migration_thread_file_err();
{
uint64_t must_precopy, can_postcopy, pending_size;
Error *local_err = NULL;
- bool in_postcopy = s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE;
+ bool in_postcopy = (s->state == MIGRATION_STATUS_POSTCOPY_DEVICE ||
+ s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
bool can_switchover = migration_can_switchover(s);
bool complete_ready;
* POSTCOPY_ACTIVE it means switchover already happened.
*/
complete_ready = !pending_size;
+ if (s->state == MIGRATION_STATUS_POSTCOPY_DEVICE &&
+ (s->postcopy_package_loaded || complete_ready)) {
+ /*
+ * If package has been loaded, the event is set and we will
+ * immediatelly transition to POSTCOPY_ACTIVE. If we are ready for
+ * completion, we need to wait for destination to load the postcopy
+ * package before actually completing.
+ */
+ qemu_event_wait(&s->postcopy_package_loaded_event);
+ migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_DEVICE,
+ MIGRATION_STATUS_POSTCOPY_ACTIVE);
+ }
} else {
/*
* Exact pending reporting is only needed for precopy. Taking RAM
qemu_sem_destroy(&ms->rp_state.rp_pong_acks);
qemu_sem_destroy(&ms->postcopy_qemufile_src_sem);
error_free(ms->error);
+ qemu_event_destroy(&ms->postcopy_package_loaded_event);
}
static void migration_instance_init(Object *obj)
qemu_sem_init(&ms->wait_unplug_sem, 0);
qemu_sem_init(&ms->postcopy_qemufile_src_sem, 0);
qemu_mutex_init(&ms->qemu_file_lock);
+ qemu_event_init(&ms->postcopy_package_loaded_event, 0);
}
/*
object_ref(OBJECT(migr));
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
- MIGRATION_STATUS_POSTCOPY_ACTIVE);
+ mis->to_src_file ? MIGRATION_STATUS_POSTCOPY_DEVICE :
+ MIGRATION_STATUS_POSTCOPY_ACTIVE);
qemu_event_set(&mis->thread_sync_event);
trace_postcopy_ram_listen_thread_start();
"loadvm failed during postcopy: %d: ", load_res);
migrate_set_error(migr, local_err);
g_clear_pointer(&local_err, error_report_err);
- migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
- MIGRATION_STATUS_FAILED);
+ migrate_set_state(&mis->state, mis->state, MIGRATION_STATUS_FAILED);
goto out;
}
}
*/
qemu_event_wait(&mis->main_thread_load_event);
+ /*
+ * Device load in the main thread has finished, we should be in
+ * POSTCOPY_ACTIVE now.
+ */
migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
MIGRATION_STATUS_COMPLETED);
# @postcopy-active: like active, but now in postcopy mode.
# (since 2.5)
#
+# @postcopy-device: like postcopy-active, but the destination is still
+# loading device state and is not running yet. If migration fails
+# during this state, the source side will resume. If there is no
+# return-path from destination to source, this state is skipped.
+# (since 10.2)
+#
# @postcopy-paused: during postcopy but paused. (since 3.0)
#
# @postcopy-recover-setup: setup phase for a postcopy recovery
##
{ 'enum': 'MigrationStatus',
'data': [ 'none', 'setup', 'cancelling', 'cancelled',
- 'active', 'postcopy-active', 'postcopy-paused',
- 'postcopy-recover-setup',
+ 'active', 'postcopy-device', 'postcopy-active',
+ 'postcopy-paused', 'postcopy-recover-setup',
'postcopy-recover', 'completed', 'failed', 'colo',
'pre-switchover', 'device', 'wait-unplug' ] }
##