aboutsummaryrefslogtreecommitdiff
path: root/migration
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-01-05 13:35:25 +0000
committerPeter Maydell <peter.maydell@linaro.org>2024-01-05 13:35:25 +0000
commitc8193acc078e297fd46b6229e02b819b65c6702e (patch)
tree91b0e8af5e2947331c733b252bc99f8c08efd185 /migration
parent05470c3979d5485003e129ff4b0c2ef98af91d86 (diff)
parentb12635ff08ab2e5a6a955c6866ef4525fb3deb5d (diff)
downloadqemu-c8193acc078e297fd46b6229e02b819b65c6702e.zip
qemu-c8193acc078e297fd46b6229e02b819b65c6702e.tar.gz
qemu-c8193acc078e297fd46b6229e02b819b65c6702e.tar.bz2
Merge tag 'migration-20240104-pull-request' of https://gitlab.com/peterx/qemu into staging
migration 1st pull for 9.0 - We lost Juan and Leo in the maintainers file - Steven's suspend state fix - Steven's fix for coverity on migrate_mode - Avihai's migration cleanup series # -----BEGIN PGP SIGNATURE----- # # iIgEABYKADAWIQS5GE3CDMRX2s990ak7X8zN86vXBgUCZZY0TxIccGV0ZXJ4QHJl # ZGhhdC5jb20ACgkQO1/MzfOr1wbSxgEAoM5g3wkc22lpAlRpU+hJUqT9NVOVQSK+ # Fk7XJYTdSgABAKzykA6hAmU5Kj+yVI6jI874SVZbs2FWpFs4osvsKk4D # =sfuM # -----END PGP SIGNATURE----- # gpg: Signature made Thu 04 Jan 2024 04:30:07 GMT # gpg: using EDDSA key B9184DC20CC457DACF7DD1A93B5FCCCDF3ABD706 # gpg: issuer "peterx@redhat.com" # gpg: Good signature from "Peter Xu <xzpeter@gmail.com>" [unknown] # gpg: aka "Peter Xu <peterx@redhat.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: B918 4DC2 0CC4 57DA CF7D D1A9 3B5F CCCD F3AB D706 * tag 'migration-20240104-pull-request' of https://gitlab.com/peterx/qemu: (26 commits) migration: fix coverity migrate_mode finding migration/multifd: Remove unnecessary usage of local Error migration: Remove unnecessary usage of local Error migration: Fix migration_channel_read_peek() error path migration/multifd: Remove error_setg() in migration_ioc_process_incoming() migration/multifd: Fix leaking of Error in TLS error flow migration/multifd: Simplify multifd_channel_connect() if else statement migration/multifd: Fix error message in multifd_recv_initial_packet() migration: Remove errp parameter in migration_fd_process_incoming() migration: Refactor migration_incoming_setup() migration: Remove nulling of hostname in migrate_init() migration: Remove migrate_max_downtime() declaration tests/qtest: postcopy migration with suspend tests/qtest: precopy migration with suspend tests/qtest: option to suspend during migration tests/qtest: migration events migration: preserve suspended for bg_migration migration: preserve suspended for snapshot migration: preserve suspended runstate migration: propagate suspended runstate ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'migration')
-rw-r--r--migration/channel.c9
-rw-r--r--migration/global_state.c47
-rw-r--r--migration/migration-hmp-cmds.c8
-rw-r--r--migration/migration.c42
-rw-r--r--migration/migration.h4
-rw-r--r--migration/multifd.c27
-rw-r--r--migration/options.c4
-rw-r--r--migration/rdma.c6
-rw-r--r--migration/savevm.c23
9 files changed, 78 insertions, 92 deletions
diff --git a/migration/channel.c b/migration/channel.c
index ca3319a..f9de064 100644
--- a/migration/channel.c
+++ b/migration/channel.c
@@ -117,9 +117,12 @@ int migration_channel_read_peek(QIOChannel *ioc,
len = qio_channel_readv_full(ioc, &iov, 1, NULL, NULL,
QIO_CHANNEL_READ_FLAG_MSG_PEEK, errp);
- if (len <= 0 && len != QIO_CHANNEL_ERR_BLOCK) {
- error_setg(errp,
- "Failed to peek at channel");
+ if (len < 0 && len != QIO_CHANNEL_ERR_BLOCK) {
+ return -1;
+ }
+
+ if (len == 0) {
+ error_setg(errp, "Failed to peek at channel");
return -1;
}
diff --git a/migration/global_state.c b/migration/global_state.c
index 8ee15db..3a9796c 100644
--- a/migration/global_state.c
+++ b/migration/global_state.c
@@ -22,7 +22,16 @@
typedef struct {
uint32_t size;
- uint8_t runstate[100];
+
+ /*
+ * runstate was 100 bytes, zero padded, but we trimmed it to add a
+ * few fields and maintain backwards compatibility.
+ */
+ uint8_t runstate[32];
+ uint8_t has_vm_was_suspended;
+ uint8_t vm_was_suspended;
+ uint8_t unused[66];
+
RunState state;
bool received;
} GlobalState;
@@ -35,6 +44,10 @@ static void global_state_do_store(RunState state)
assert(strlen(state_str) < sizeof(global_state.runstate));
strpadcpy((char *)global_state.runstate, sizeof(global_state.runstate),
state_str, '\0');
+ global_state.has_vm_was_suspended = true;
+ global_state.vm_was_suspended = vm_get_suspended();
+
+ memset(global_state.unused, 0, sizeof(global_state.unused));
}
void global_state_store(void)
@@ -59,24 +72,7 @@ RunState global_state_get_runstate(void)
static bool global_state_needed(void *opaque)
{
- GlobalState *s = opaque;
- char *runstate = (char *)s->runstate;
-
- /* If it is not optional, it is mandatory */
-
- if (migrate_get_current()->store_global_state) {
- return true;
- }
-
- /* If state is running or paused, it is not needed */
-
- if (strcmp(runstate, "running") == 0 ||
- strcmp(runstate, "paused") == 0) {
- return false;
- }
-
- /* for any other state it is needed */
- return true;
+ return migrate_get_current()->store_global_state;
}
static int global_state_post_load(void *opaque, int version_id)
@@ -93,7 +89,7 @@ static int global_state_post_load(void *opaque, int version_id)
sizeof(s->runstate)) == sizeof(s->runstate)) {
/*
* This condition should never happen during migration, because
- * all runstate names are shorter than 100 bytes (the size of
+ * all runstate names are shorter than 32 bytes (the size of
* s->runstate). However, a malicious stream could overflow
* the qapi_enum_parse() call, so we force the last character
* to a NUL byte.
@@ -110,6 +106,14 @@ static int global_state_post_load(void *opaque, int version_id)
}
s->state = r;
+ /*
+ * global_state is saved on the outgoing side before forcing a stopped
+ * state, so it may have saved state=suspended and vm_was_suspended=0.
+ * Now we are in a paused state, and when we later call vm_start, it must
+ * restore the suspended state, so we must set vm_was_suspended=1 here.
+ */
+ vm_set_suspended(s->vm_was_suspended || r == RUN_STATE_SUSPENDED);
+
return 0;
}
@@ -134,6 +138,9 @@ static const VMStateDescription vmstate_globalstate = {
.fields = (const VMStateField[]) {
VMSTATE_UINT32(size, GlobalState),
VMSTATE_BUFFER(runstate, GlobalState),
+ VMSTATE_UINT8(has_vm_was_suspended, GlobalState),
+ VMSTATE_UINT8(vm_was_suspended, GlobalState),
+ VMSTATE_BUFFER(unused, GlobalState),
VMSTATE_END_OF_LIST()
},
};
diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 99710c8..740a219 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -399,15 +399,17 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
void hmp_loadvm(Monitor *mon, const QDict *qdict)
{
- int saved_vm_running = runstate_is_running();
+ RunState saved_state = runstate_get();
+
const char *name = qdict_get_str(qdict, "name");
Error *err = NULL;
vm_stop(RUN_STATE_RESTORE_VM);
- if (load_snapshot(name, NULL, false, NULL, &err) && saved_vm_running) {
- vm_start();
+ if (load_snapshot(name, NULL, false, NULL, &err)) {
+ load_snapshot_resume(saved_state);
}
+
hmp_handle_error(mon, err);
}
diff --git a/migration/migration.c b/migration/migration.c
index 3ce04b2..454cd4e 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -604,7 +604,7 @@ static void process_incoming_migration_bh(void *opaque)
*/
if (!migrate_late_block_activate() ||
(autostart && (!global_state_received() ||
- global_state_get_runstate() == RUN_STATE_RUNNING))) {
+ runstate_is_live(global_state_get_runstate())))) {
/* Make sure all file formats throw away their mutable metadata.
* If we get an error here, just don't restart the VM yet. */
bdrv_activate_all(&local_err);
@@ -628,7 +628,7 @@ static void process_incoming_migration_bh(void *opaque)
dirty_bitmap_mig_before_vm_start();
if (!global_state_received() ||
- global_state_get_runstate() == RUN_STATE_RUNNING) {
+ runstate_is_live(global_state_get_runstate())) {
if (autostart) {
vm_start();
} else {
@@ -724,11 +724,8 @@ fail:
/**
* migration_incoming_setup: Setup incoming migration
* @f: file for main migration channel
- * @errp: where to put errors
- *
- * Returns: %true on success, %false on error.
*/
-static bool migration_incoming_setup(QEMUFile *f, Error **errp)
+static void migration_incoming_setup(QEMUFile *f)
{
MigrationIncomingState *mis = migration_incoming_get_current();
@@ -736,7 +733,6 @@ static bool migration_incoming_setup(QEMUFile *f, Error **errp)
mis->from_src_file = f;
}
qemu_file_set_blocking(f, false);
- return true;
}
void migration_incoming_process(void)
@@ -778,11 +774,9 @@ static bool postcopy_try_recover(void)
return false;
}
-void migration_fd_process_incoming(QEMUFile *f, Error **errp)
+void migration_fd_process_incoming(QEMUFile *f)
{
- if (!migration_incoming_setup(f, errp)) {
- return;
- }
+ migration_incoming_setup(f);
if (postcopy_try_recover()) {
return;
}
@@ -836,10 +830,9 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
* issue is not possible.
*/
ret = migration_channel_read_peek(ioc, (void *)&channel_magic,
- sizeof(channel_magic), &local_err);
+ sizeof(channel_magic), errp);
if (ret != 0) {
- error_propagate(errp, local_err);
return;
}
@@ -849,16 +842,12 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
}
if (multifd_load_setup(errp) != 0) {
- error_setg(errp, "Failed to setup multifd channels");
return;
}
if (default_channel) {
f = qemu_file_new_input(ioc);
-
- if (!migration_incoming_setup(f, errp)) {
- return;
- }
+ migration_incoming_setup(f);
} else {
/* Multiple connections */
assert(migration_needs_multiple_sockets());
@@ -1588,7 +1577,6 @@ int migrate_init(MigrationState *s, Error **errp)
s->migration_thread_running = false;
error_free(s->error);
s->error = NULL;
- s->hostname = NULL;
s->vmdesc = NULL;
migrate_set_state(&s->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP);
@@ -1836,8 +1824,6 @@ bool migration_is_blocked(Error **errp)
static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc,
bool resume, Error **errp)
{
- Error *local_err = NULL;
-
if (blk_inc) {
warn_report("parameter 'inc' is deprecated;"
" use blockdev-mirror with NBD instead");
@@ -1907,8 +1893,7 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc,
"current migration capabilities");
return false;
}
- if (!migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, true, &local_err)) {
- error_propagate(errp, local_err);
+ if (!migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, true, errp)) {
return false;
}
s->must_remove_block_options = true;
@@ -2416,7 +2401,6 @@ static int postcopy_start(MigrationState *ms, Error **errp)
migration_downtime_start(ms);
- qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
global_state_store();
ret = migration_stop_vm(RUN_STATE_FINISH_MIGRATE);
if (ret < 0) {
@@ -2615,7 +2599,6 @@ static int migration_completion_precopy(MigrationState *s,
qemu_mutex_lock_iothread();
migration_downtime_start(s);
- qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
s->vm_old_state = runstate_get();
global_state_store();
@@ -3136,7 +3119,7 @@ static void migration_iteration_finish(MigrationState *s)
case MIGRATION_STATUS_FAILED:
case MIGRATION_STATUS_CANCELLED:
case MIGRATION_STATUS_CANCELLING:
- if (s->vm_old_state == RUN_STATE_RUNNING) {
+ if (runstate_is_live(s->vm_old_state)) {
if (!runstate_check(RUN_STATE_SHUTDOWN)) {
vm_start();
}
@@ -3392,7 +3375,7 @@ static void bg_migration_vm_start_bh(void *opaque)
qemu_bh_delete(s->vm_start_bh);
s->vm_start_bh = NULL;
- vm_start();
+ vm_resume(s->vm_old_state);
migration_downtime_end(s);
}
@@ -3464,11 +3447,6 @@ static void *bg_migration_thread(void *opaque)
qemu_mutex_lock_iothread();
- /*
- * If VM is currently in suspended state, then, to make a valid runstate
- * transition in vm_stop_force_state() we need to wakeup it up.
- */
- qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
s->vm_old_state = runstate_get();
global_state_store();
diff --git a/migration/migration.h b/migration/migration.h
index cf2c9c8..17972da 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -474,14 +474,12 @@ struct MigrationState {
void migrate_set_state(int *state, int old_state, int new_state);
-void migration_fd_process_incoming(QEMUFile *f, Error **errp);
+void migration_fd_process_incoming(QEMUFile *f);
void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp);
void migration_incoming_process(void);
bool migration_has_all_channels(void);
-uint64_t migrate_max_downtime(void);
-
void migrate_set_error(MigrationState *s, const Error *error);
bool migrate_has_error(MigrationState *s);
diff --git a/migration/multifd.c b/migration/multifd.c
index 4094606..9f353ae 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -228,8 +228,8 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp)
}
if (msg.id > migrate_multifd_channels()) {
- error_setg(errp, "multifd: received channel version %u "
- "expected %u", msg.version, MULTIFD_VERSION);
+ error_setg(errp, "multifd: received channel id %u is greater than "
+ "number of channels %u", msg.id, migrate_multifd_channels());
return -1;
}
@@ -787,6 +787,7 @@ static void multifd_tls_outgoing_handshake(QIOTask *task,
trace_multifd_tls_outgoing_handshake_error(ioc, error_get_pretty(err));
+ migrate_set_error(migrate_get_current(), err);
/*
* Error happen, mark multifd_send_thread status as 'quit' although it
* is not created, and then tell who pay attention to me.
@@ -794,6 +795,7 @@ static void multifd_tls_outgoing_handshake(QIOTask *task,
p->quit = true;
qemu_sem_post(&multifd_send_state->channels_ready);
qemu_sem_post(&p->sem_sync);
+ error_free(err);
}
static void *multifd_tls_handshake_thread(void *opaque)
@@ -847,14 +849,13 @@ static bool multifd_channel_connect(MultiFDSendParams *p,
* so we mustn't call multifd_send_thread until then
*/
return multifd_tls_channel_connect(p, ioc, errp);
-
- } else {
- migration_ioc_register_yank(ioc);
- p->registered_yank = true;
- p->c = ioc;
- qemu_thread_create(&p->thread, p->name, multifd_send_thread, p,
- QEMU_THREAD_JOINABLE);
}
+
+ migration_ioc_register_yank(ioc);
+ p->registered_yank = true;
+ p->c = ioc;
+ qemu_thread_create(&p->thread, p->name, multifd_send_thread, p,
+ QEMU_THREAD_JOINABLE);
return true;
}
@@ -950,12 +951,10 @@ int multifd_save_setup(Error **errp)
for (i = 0; i < thread_count; i++) {
MultiFDSendParams *p = &multifd_send_state->params[i];
- Error *local_err = NULL;
int ret;
- ret = multifd_send_state->ops->send_setup(p, &local_err);
+ ret = multifd_send_state->ops->send_setup(p, errp);
if (ret) {
- error_propagate(errp, local_err);
return ret;
}
}
@@ -1194,12 +1193,10 @@ int multifd_load_setup(Error **errp)
for (i = 0; i < thread_count; i++) {
MultiFDRecvParams *p = &multifd_recv_state->params[i];
- Error *local_err = NULL;
int ret;
- ret = multifd_recv_state->ops->recv_setup(p, &local_err);
+ ret = multifd_recv_state->ops->recv_setup(p, errp);
if (ret) {
- error_propagate(errp, local_err);
return ret;
}
}
diff --git a/migration/options.c b/migration/options.c
index 8d8ec73..3e3e0b9 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -833,8 +833,10 @@ uint64_t migrate_max_postcopy_bandwidth(void)
MigMode migrate_mode(void)
{
MigrationState *s = migrate_get_current();
+ MigMode mode = s->parameters.mode;
- return s->parameters.mode;
+ assert(mode >= 0 && mode < MIG_MODE__MAX);
+ return mode;
}
int migrate_multifd_channels(void)
diff --git a/migration/rdma.c b/migration/rdma.c
index 04debab..94c0f87 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -4035,7 +4035,6 @@ static void rdma_accept_incoming_migration(void *opaque)
{
RDMAContext *rdma = opaque;
QEMUFile *f;
- Error *local_err = NULL;
trace_qemu_rdma_accept_incoming_migration();
if (qemu_rdma_accept(rdma) < 0) {
@@ -4057,10 +4056,7 @@ static void rdma_accept_incoming_migration(void *opaque)
}
rdma->migration_started_on_destination = 1;
- migration_fd_process_incoming(f, &local_err);
- if (local_err) {
- error_reportf_err(local_err, "RDMA ERROR:");
- }
+ migration_fd_process_incoming(f);
}
void rdma_start_incoming_migration(InetSocketAddress *host_port,
diff --git a/migration/savevm.c b/migration/savevm.c
index c8d08fa..6410705 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -3046,7 +3046,7 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate,
QEMUSnapshotInfo sn1, *sn = &sn1;
int ret = -1, ret2;
QEMUFile *f;
- int saved_vm_running;
+ RunState saved_state = runstate_get();
uint64_t vm_state_size;
g_autoptr(GDateTime) now = g_date_time_new_now_local();
@@ -3092,8 +3092,6 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate,
return false;
}
- saved_vm_running = runstate_is_running();
-
global_state_store();
vm_stop(RUN_STATE_SAVE_VM);
@@ -3147,9 +3145,7 @@ bool save_snapshot(const char *name, bool overwrite, const char *vmstate,
the_end:
bdrv_drain_all_end();
- if (saved_vm_running) {
- vm_start();
- }
+ vm_resume(saved_state);
return ret == 0;
}
@@ -3317,6 +3313,14 @@ err_drain:
return false;
}
+void load_snapshot_resume(RunState state)
+{
+ vm_resume(state);
+ if (state == RUN_STATE_RUNNING && runstate_get() == RUN_STATE_SUSPENDED) {
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, &error_abort);
+ }
+}
+
bool delete_snapshot(const char *name, bool has_devices,
strList *devices, Error **errp)
{
@@ -3381,16 +3385,15 @@ static void snapshot_load_job_bh(void *opaque)
{
Job *job = opaque;
SnapshotJob *s = container_of(job, SnapshotJob, common);
- int orig_vm_running;
+ RunState orig_state = runstate_get();
job_progress_set_remaining(&s->common, 1);
- orig_vm_running = runstate_is_running();
vm_stop(RUN_STATE_RESTORE_VM);
s->ret = load_snapshot(s->tag, s->vmstate, true, s->devices, s->errp);
- if (s->ret && orig_vm_running) {
- vm_start();
+ if (s->ret) {
+ load_snapshot_resume(orig_state);
}
job_progress_update(&s->common, 1);