aboutsummaryrefslogtreecommitdiff
path: root/migration/migration.c
diff options
context:
space:
mode:
Diffstat (limited to 'migration/migration.c')
-rw-r--r--migration/migration.c129
1 files changed, 108 insertions, 21 deletions
diff --git a/migration/migration.c b/migration/migration.c
index 98429dc843..62761d5705 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -104,6 +104,9 @@ enum mig_rp_message_type {
static MigrationState *current_migration;
static bool migration_object_check(MigrationState *ms, Error **errp);
+static int migration_maybe_pause(MigrationState *s,
+ int *current_active_state,
+ int new_state);
void migration_object_init(void)
{
@@ -526,6 +529,8 @@ static bool migration_is_setup_or_active(int state)
case MIGRATION_STATUS_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_SETUP:
+ case MIGRATION_STATUS_PRE_SWITCHOVER:
+ case MIGRATION_STATUS_DEVICE:
return true;
default:
@@ -600,6 +605,8 @@ MigrationInfo *qmp_query_migrate(Error **errp)
case MIGRATION_STATUS_ACTIVE:
case MIGRATION_STATUS_CANCELLING:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
+ case MIGRATION_STATUS_PRE_SWITCHOVER:
+ case MIGRATION_STATUS_DEVICE:
/* TODO add some postcopy stats */
info->has_status = true;
info->has_total_time = true;
@@ -865,6 +872,12 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
if (params->has_block_incremental) {
dest->block_incremental = params->block_incremental;
}
+ if (params->has_x_multifd_channels) {
+ dest->x_multifd_channels = params->x_multifd_channels;
+ }
+ if (params->has_x_multifd_page_count) {
+ dest->x_multifd_page_count = params->x_multifd_page_count;
+ }
}
static void migrate_params_apply(MigrateSetParameters *params)
@@ -1071,19 +1084,30 @@ static void migrate_fd_cleanup(void *opaque)
MIGRATION_STATUS_CANCELLED);
}
+ if (s->error) {
+ /* It is used on info migrate. We can't free it */
+ error_report_err(error_copy(s->error));
+ }
notifier_list_notify(&migration_state_notifiers, s);
block_cleanup_parameters(s);
}
+void migrate_set_error(MigrationState *s, const Error *error)
+{
+ qemu_mutex_lock(&s->error_mutex);
+ if (!s->error) {
+ s->error = error_copy(error);
+ }
+ qemu_mutex_unlock(&s->error_mutex);
+}
+
void migrate_fd_error(MigrationState *s, const Error *error)
{
trace_migrate_fd_error(error_get_pretty(error));
assert(s->to_dst_file == NULL);
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
MIGRATION_STATUS_FAILED);
- if (!s->error) {
- s->error = error_copy(error);
- }
+ migrate_set_error(s, error);
notifier_list_notify(&migration_state_notifiers, s);
block_cleanup_parameters(s);
}
@@ -1104,6 +1128,10 @@ static void migrate_fd_cancel(MigrationState *s)
if (!migration_is_setup_or_active(old_state)) {
break;
}
+ /* If the migration is paused, kick it out of the pause */
+ if (old_state == MIGRATION_STATUS_PRE_SWITCHOVER) {
+ qemu_sem_post(&s->pause_sem);
+ }
migrate_set_state(&s->state, old_state, MIGRATION_STATUS_CANCELLING);
} while (s->state != MIGRATION_STATUS_CANCELLING);
@@ -1183,6 +1211,8 @@ bool migration_is_idle(void)
case MIGRATION_STATUS_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_COLO:
+ case MIGRATION_STATUS_PRE_SWITCHOVER:
+ case MIGRATION_STATUS_DEVICE:
return false;
case MIGRATION_STATUS__MAX:
g_assert_not_reached();
@@ -1362,29 +1392,24 @@ void qmp_migrate_cancel(Error **errp)
migrate_fd_cancel(migrate_get_current());
}
-void qmp_migrate_set_cache_size(int64_t value, Error **errp)
+void qmp_migrate_continue(MigrationStatus state, Error **errp)
{
MigrationState *s = migrate_get_current();
- int64_t new_size;
-
- /* Check for truncation */
- if (value != (size_t)value) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
- "exceeding address space");
+ if (s->state != state) {
+ error_setg(errp, "Migration not in expected state: %s",
+ MigrationStatus_str(s->state));
return;
}
+ qemu_sem_post(&s->pause_sem);
+}
- /* Cache should not be larger than guest ram size */
- if (value > ram_bytes_total()) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
- "exceeds guest ram size ");
- return;
- }
+void qmp_migrate_set_cache_size(int64_t value, Error **errp)
+{
+ MigrationState *s = migrate_get_current();
+ int64_t new_size;
- new_size = xbzrle_cache_resize(value);
+ new_size = xbzrle_cache_resize(value, errp);
if (new_size < 0) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
- "is smaller than page size");
return;
}
@@ -1521,6 +1546,16 @@ bool migrate_use_multifd(void)
return s->enabled_capabilities[MIGRATION_CAPABILITY_X_MULTIFD];
}
+bool migrate_pause_before_switchover(void)
+{
+ MigrationState *s;
+
+ s = migrate_get_current();
+
+ return s->enabled_capabilities[
+ MIGRATION_CAPABILITY_PAUSE_BEFORE_SWITCHOVER];
+}
+
int migrate_multifd_channels(void)
{
MigrationState *s;
@@ -1799,8 +1834,11 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
QEMUFile *fb;
int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
bool restart_block = false;
- migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE,
- MIGRATION_STATUS_POSTCOPY_ACTIVE);
+ int cur_state = MIGRATION_STATUS_ACTIVE;
+ if (!migrate_pause_before_switchover()) {
+ migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE,
+ MIGRATION_STATUS_POSTCOPY_ACTIVE);
+ }
trace_postcopy_start();
qemu_mutex_lock_iothread();
@@ -1814,6 +1852,12 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
goto fail;
}
+ ret = migration_maybe_pause(ms, &cur_state,
+ MIGRATION_STATUS_POSTCOPY_ACTIVE);
+ if (ret < 0) {
+ goto fail;
+ }
+
ret = bdrv_inactivate_all();
if (ret < 0) {
goto fail;
@@ -1952,6 +1996,41 @@ fail:
}
/**
+ * migration_maybe_pause: Pause if required to by
+ * migrate_pause_before_switchover called with the iothread locked
+ * Returns: 0 on success
+ */
+static int migration_maybe_pause(MigrationState *s,
+ int *current_active_state,
+ int new_state)
+{
+ if (!migrate_pause_before_switchover()) {
+ return 0;
+ }
+
+ /* Since leaving this state is not atomic with posting the semaphore
+ * it's possible that someone could have issued multiple migrate_continue
+ * and the semaphore is incorrectly positive at this point;
+ * the docs say it's undefined to reinit a semaphore that's already
+ * init'd, so use timedwait to eat up any existing posts.
+ */
+ while (qemu_sem_timedwait(&s->pause_sem, 1) == 0) {
+ /* This block intentionally left blank */
+ }
+
+ qemu_mutex_unlock_iothread();
+ migrate_set_state(&s->state, *current_active_state,
+ MIGRATION_STATUS_PRE_SWITCHOVER);
+ qemu_sem_wait(&s->pause_sem);
+ migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER,
+ new_state);
+ *current_active_state = new_state;
+ qemu_mutex_lock_iothread();
+
+ return s->state == new_state ? 0 : -EINVAL;
+}
+
+/**
* migration_completion: Used by migration_thread when there's not much left.
* The caller 'breaks' the loop when this returns.
*
@@ -1977,6 +2056,10 @@ static void migration_completion(MigrationState *s, int current_active_state,
bool inactivate = !migrate_colo_enabled();
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
if (ret >= 0) {
+ ret = migration_maybe_pause(s, &current_active_state,
+ MIGRATION_STATUS_DEVICE);
+ }
+ if (ret >= 0) {
qemu_file_set_rate_limit(s->to_dst_file, INT64_MAX);
ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false,
inactivate);
@@ -2355,8 +2438,10 @@ static void migration_instance_finalize(Object *obj)
MigrationState *ms = MIGRATION_OBJ(obj);
MigrationParameters *params = &ms->parameters;
+ qemu_mutex_destroy(&ms->error_mutex);
g_free(params->tls_hostname);
g_free(params->tls_creds);
+ qemu_sem_destroy(&ms->pause_sem);
}
static void migration_instance_init(Object *obj)
@@ -2367,6 +2452,8 @@ static void migration_instance_init(Object *obj)
ms->state = MIGRATION_STATUS_NONE;
ms->xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE;
ms->mbps = -1;
+ qemu_sem_init(&ms->pause_sem, 0);
+ qemu_mutex_init(&ms->error_mutex);
params->tls_hostname = g_strdup("");
params->tls_creds = g_strdup("");