1
CI: https://gitlab.com/peterx/qemu/-/pipelines/1577280033
2
(note: it's a pipeline of two patchsets, to save CI credits and time)
3
4
v1: https://lore.kernel.org/r/20241204005138.702289-1-peterx@redhat.com
5
6
This is v2 of the series, removing RFC tag, because my goal is to have them
7
(or some newer version) merged.
8
9
The major change is I merged last three patches, and did quite some changes
10
here and there, to make sure the global disk activation status is always
11
consistent. The whole idea is still the same. I say changelog won't help.
12
13
I also temporarily dropped Fabiano's ping-pong test cases to avoid
14
different versions floating on the list (as I know a new version is coming
15
at some point. Fabiano: you're taking over the 10.0 pulls, so I assume
16
you're aware so there's no concern on order of merges). I'll review the
17
test cases separately when they're ready, but this series is still tested
18
with that pingpong test and it keeps working.
19
1
I started looking at this problem as a whole when reviewing Fabiano's
20
I started looking at this problem as a whole when reviewing Fabiano's
2
series, especially the patch (for a QEMU crash [1]):
21
series, especially the patch (for a QEMU crash [1]):
3
22
4
https://lore.kernel.org/r/20241125144612.16194-5-farosas@suse.de
23
https://lore.kernel.org/r/20241125144612.16194-5-farosas@suse.de
5
24
6
The proposed patch could work, but it's unwanted to add such side effect to
25
The proposed patch could work, but it's unwanted to add such side effect to
7
migration. So I start to think about whether we can provide a cleaner
26
migration. So I start to think about whether we can provide a cleaner
8
approach, at least remove that "we must active the disk for migration"
27
approach, because migration doesn't need the disks to be active to work at
9
dependency, because migration really don't need the disks to be active..
28
all. Hence we should try to avoid adding a migration ABI (which doesn't
29
matter now, but may matter some day) into prepare phase on disk activation
30
status. Migration should happen with disks inactivated.
10
31
11
It's also a pure wish that, if bdrv_inactivate_all() could be benign to be
32
It's also a pure wish that, if bdrv_inactivate_all() could be benign to be
12
called even if all disks are already inactive. Then problem also gone.
33
called even if all disks are already inactive. Then the bug is also gone.
13
After all, similar call on bdrv_activate_all() upon all-active disks is all
34
After all, similar call on bdrv_activate_all() upon all-active disks is all
14
fine. I hope that wish could still be fair.
35
fine. I hope that wish could still be fair. But I don't know well on
36
block layer to say anything meaningful.
15
37
16
And when I was looking at that, I found more things spread all over the
38
And when I was looking at that, I found more things spread all over the
17
place on disk activation. I decided to clean all of them up, while
39
place on disk activation. I decided to clean all of them up, while
18
hopefully fixing the QEMU crash [1] too.
40
hopefully fixing the QEMU crash [1] too.
19
41
20
So this is what I came up with as of today. Marking RFC as of now, just to
42
For this v2, I did some more tests, I want to make sure all the past paths
21
collect some feedbacks first. At least I'd like to go with one more patch
43
keep working at least on failure or cancel races, also in postcopy failure
22
to deprecate late-block-active - not deprecating its function, but make it
44
cases. So I did below and they all run pass (when I said "emulated" below,
23
always happen (which is the default as of now for Libvirt), which should
45
I meant I hacked something to trigger those race / rare failures, because
24
hopefully be migration-ABI-safe.
46
they aren't easy to trigger with vanilla binary):
25
47
26
With the help of Fabiano's test cases, I at least am sure this series works
48
- Tested generic migrate_cancel during precopy, disk activation won't be
27
for the ping pong migrations, and all existing qtests.
49
affected. Disk status reports correct values in tracepoints.
28
50
29
Let me know, thanks.
51
- Test Fabiano's ping-pong migration tests on PAUSED state VM.
52
53
- Emulated precopy failure before sending non-iterable, disk inactivation
54
won't happen, and also activation won't trigger after migration cleanups
55
(even if activation on top of activate disk is benign, I checked traces
56
to make sure it'll provide consistent disk status, skipping activation).
57
58
- Emulated precopy failure right after sending non-iterable. Disks will be
59
inactivated, but then can be reactivated properly before VM starts.
60
61
- Emulated postcopy failure when sending the packed data (which is after
62
disk invalidated), and making sure src VM will get back to live properly,
63
re-activate the disks before starting.
64
65
- Emulated concurrent migrate_cancel at the end of migration_completion()
66
of precopy, after disk inactivated. Disks can be reactivated properly.
67
68
NOTE: here if dest QEMU didn't quit before migrate_cancel,
69
bdrv_activate_all() can crash src QEMU. This behavior should be the same
70
before/after this patch.
71
72
Comments welcomed, thanks.
30
73
31
[1] https://gitlab.com/qemu-project/qemu/-/issues/2395
74
[1] https://gitlab.com/qemu-project/qemu/-/issues/2395
32
75
33
Fabiano Rosas (4):
76
Peter Xu (6):
34
tests/qtest/migration: Move more code under only_target
35
tests/qtest/migration: Don't use hardcoded strings for -serial
36
tests/qtest/migration: Support cleaning up only one side of migration
37
tests/qtest/migration: Test successive migrations
38
39
Peter Xu (7):
40
migration: Add helper to get target runstate
77
migration: Add helper to get target runstate
78
qmp/cont: Only activate disks if migration completed
41
migration/block: Make late-block-active the default
79
migration/block: Make late-block-active the default
42
migration/block: Apply late-block-active behavior to postcopy
80
migration/block: Apply late-block-active behavior to postcopy
43
migration/block: Fix possible race with block_inactive
81
migration/block: Fix possible race with block_inactive
44
migration/block: Merge block reactivations for fail/cancel
82
migration/block: Rewrite disk activation
45
migration/block: Extend the migration_block_* API to dest side
46
migration/block: Apply the migration_block_* API to postcopy
47
83
48
migration/migration.h | 33 ++++-
84
include/migration/misc.h | 4 ++
49
tests/qtest/migration-helpers.h | 2 +
85
migration/migration.h | 6 +-
50
migration/migration.c | 177 +++++++++++-----------
86
migration/block-active.c | 94 +++++++++++++++++++++++++++
51
migration/savevm.c | 32 ++--
87
migration/colo.c | 2 +-
52
tests/qtest/migration-helpers.c | 8 +
88
migration/migration.c | 136 +++++++++++++++------------------------
53
tests/qtest/migration-test.c | 252 +++++++++++++++++++++++++-------
89
migration/savevm.c | 46 ++++++-------
54
6 files changed, 344 insertions(+), 160 deletions(-)
90
monitor/qmp-cmds.c | 22 +++----
91
migration/meson.build | 1 +
92
migration/trace-events | 3 +
93
9 files changed, 188 insertions(+), 126 deletions(-)
94
create mode 100644 migration/block-active.c
55
95
56
--
96
--
57
2.47.0
97
2.47.0
diff view generated by jsdifflib
1
In 99% cases, after QEMU migrates to dest host, it tries to detect the
1
In 99% cases, after QEMU migrates to dest host, it tries to detect the
2
target VM runstate using global_state_get_runstate().
2
target VM runstate using global_state_get_runstate().
3
3
4
There's one outlier so far which is Xen that won't send global state.
4
There's one outlier so far which is Xen that won't send global state.
5
That's the major reason why global_state_received() check was always there
5
That's the major reason why global_state_received() check was always there
6
together with global_state_get_runstate().
6
together with global_state_get_runstate().
7
7
8
However it's utterly confusing why global_state_received() has anything to
8
However it's utterly confusing why global_state_received() has anything to
9
do with "let's start VM or not".
9
do with "let's start VM or not".
10
10
11
Provide a helper to explain it, then we have an unified entry for getting
11
Provide a helper to explain it, then we have an unified entry for getting
12
the target dest QEMU runstate after migration.
12
the target dest QEMU runstate after migration.
13
13
14
Suggested-by: Fabiano Rosas <farosas@suse.de>
14
Suggested-by: Fabiano Rosas <farosas@suse.de>
15
Signed-off-by: Peter Xu <peterx@redhat.com>
15
Signed-off-by: Peter Xu <peterx@redhat.com>
16
---
16
---
17
migration/migration.c | 21 +++++++++++++++++----
17
migration/migration.c | 21 +++++++++++++++++----
18
1 file changed, 17 insertions(+), 4 deletions(-)
18
1 file changed, 17 insertions(+), 4 deletions(-)
19
19
20
diff --git a/migration/migration.c b/migration/migration.c
20
diff --git a/migration/migration.c b/migration/migration.c
21
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
22
--- a/migration/migration.c
22
--- a/migration/migration.c
23
+++ b/migration/migration.c
23
+++ b/migration/migration.c
24
@@ -XXX,XX +XXX,XX @@ static bool migration_needs_multiple_sockets(void)
24
@@ -XXX,XX +XXX,XX @@ static bool migration_needs_multiple_sockets(void)
25
return migrate_multifd() || migrate_postcopy_preempt();
25
return migrate_multifd() || migrate_postcopy_preempt();
26
}
26
}
27
27
28
+static RunState migration_get_target_runstate(void)
28
+static RunState migration_get_target_runstate(void)
29
+{
29
+{
30
+ /*
30
+ /*
31
+ * When the global state is not migrated, it means we don't know the
31
+ * When the global state is not migrated, it means we don't know the
32
+ * runstate of the src QEMU. We don't have much choice but assuming
32
+ * runstate of the src QEMU. We don't have much choice but assuming
33
+ * the VM is running. NOTE: this is pretty rare case, so far only Xen
33
+ * the VM is running. NOTE: this is pretty rare case, so far only Xen
34
+ * uses it.
34
+ * uses it.
35
+ */
35
+ */
36
+ if (!global_state_received()) {
36
+ if (!global_state_received()) {
37
+ return RUN_STATE_RUNNING;
37
+ return RUN_STATE_RUNNING;
38
+ }
38
+ }
39
+
39
+
40
+ return global_state_get_runstate();
40
+ return global_state_get_runstate();
41
+}
41
+}
42
+
42
+
43
static bool transport_supports_multi_channels(MigrationAddress *addr)
43
static bool transport_supports_multi_channels(MigrationAddress *addr)
44
{
44
{
45
if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
45
if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
46
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
46
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
47
* unless we really are starting the VM.
47
* unless we really are starting the VM.
48
*/
48
*/
49
if (!migrate_late_block_activate() ||
49
if (!migrate_late_block_activate() ||
50
- (autostart && (!global_state_received() ||
50
- (autostart && (!global_state_received() ||
51
- runstate_is_live(global_state_get_runstate())))) {
51
- runstate_is_live(global_state_get_runstate())))) {
52
+ (autostart && runstate_is_live(migration_get_target_runstate()))) {
52
+ (autostart && runstate_is_live(migration_get_target_runstate()))) {
53
/* Make sure all file formats throw away their mutable metadata.
53
/* Make sure all file formats throw away their mutable metadata.
54
* If we get an error here, just don't restart the VM yet. */
54
* If we get an error here, just don't restart the VM yet. */
55
bdrv_activate_all(&local_err);
55
bdrv_activate_all(&local_err);
56
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
56
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
57
57
58
dirty_bitmap_mig_before_vm_start();
58
dirty_bitmap_mig_before_vm_start();
59
59
60
- if (!global_state_received() ||
60
- if (!global_state_received() ||
61
- runstate_is_live(global_state_get_runstate())) {
61
- runstate_is_live(global_state_get_runstate())) {
62
+ if (runstate_is_live(migration_get_target_runstate())) {
62
+ if (runstate_is_live(migration_get_target_runstate())) {
63
if (autostart) {
63
if (autostart) {
64
vm_start();
64
vm_start();
65
} else {
65
} else {
66
--
66
--
67
2.47.0
67
2.47.0
diff view generated by jsdifflib
1
From: Fabiano Rosas <farosas@suse.de>
1
As the comment says, the activation of disks is for the case where
2
migration has completed, rather than when QEMU is still during
3
migration (RUN_STATE_INMIGRATE).
2
4
3
The only_target option's purpose is to make sure only the destination
5
Move the code over to reflect what the comment is describing.
4
QTestState machine is initialized. This allows the test code to retain
5
an already initialized source machine (e.g. for doing ping pong
6
migration).
7
6
8
We have drifted from that a bit when adding new code, so move some
7
Cc: Kevin Wolf <kwolf@redhat.com>
9
lines under only_target to restore the functionality.
8
Cc: Markus Armbruster <armbru@redhat.com>
10
11
Signed-off-by: Fabiano Rosas <farosas@suse.de>
12
Link: https://lore.kernel.org/r/20241125144612.16194-2-farosas@suse.de
13
Signed-off-by: Peter Xu <peterx@redhat.com>
9
Signed-off-by: Peter Xu <peterx@redhat.com>
14
---
10
---
15
tests/qtest/migration-test.c | 44 ++++++++++++++++++++----------------
11
monitor/qmp-cmds.c | 26 ++++++++++++++------------
16
1 file changed, 25 insertions(+), 19 deletions(-)
12
1 file changed, 14 insertions(+), 12 deletions(-)
17
13
18
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
14
diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
19
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
20
--- a/tests/qtest/migration-test.c
16
--- a/monitor/qmp-cmds.c
21
+++ b/tests/qtest/migration-test.c
17
+++ b/monitor/qmp-cmds.c
22
@@ -XXX,XX +XXX,XX @@ static int test_migrate_start(QTestState **from, QTestState **to,
18
@@ -XXX,XX +XXX,XX @@ void qmp_cont(Error **errp)
23
g_autofree gchar *arch_target = NULL;
24
/* options for source and target */
25
g_autofree gchar *arch_opts = NULL;
26
- g_autofree gchar *cmd_source = NULL;
27
g_autofree gchar *cmd_target = NULL;
28
const gchar *ignore_stderr;
29
g_autofree char *shmem_opts = NULL;
30
@@ -XXX,XX +XXX,XX @@ static int test_migrate_start(QTestState **from, QTestState **to,
31
}
19
}
32
}
20
}
33
21
34
- dst_state = (QTestMigrationState) { };
22
- /* Continuing after completed migration. Images have been inactivated to
35
- src_state = (QTestMigrationState) { };
23
- * allow the destination to take control. Need to get control back now.
36
bootfile_create(tmpfs, args->suspend_me);
24
- *
37
- src_state.suspend_me = args->suspend_me;
25
- * If there are no inactive block nodes (e.g. because the VM was just
38
26
- * paused rather than completing a migration), bdrv_inactivate_all() simply
39
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
27
- * doesn't do anything. */
40
memory_size = "150M";
28
- bdrv_activate_all(&local_err);
41
@@ -XXX,XX +XXX,XX @@ static int test_migrate_start(QTestState **from, QTestState **to,
29
- if (local_err) {
42
30
- error_propagate(errp, local_err);
43
g_test_message("Using machine type: %s", machine);
31
- return;
44
32
- }
45
- cmd_source = g_strdup_printf("-accel kvm%s -accel tcg "
33
-
46
- "-machine %s,%s "
34
if (runstate_check(RUN_STATE_INMIGRATE)) {
47
- "-name source,debug-threads=on "
35
autostart = 1;
48
- "-m %s "
36
} else {
49
- "-serial file:%s/src_serial "
37
+ /*
50
- "%s %s %s %s %s",
38
+ * Continuing after completed migration. Images have been
51
- kvm_opts ? kvm_opts : "",
39
+ * inactivated to allow the destination to take control. Need to
52
- machine, machine_opts,
40
+ * get control back now.
53
- memory_size, tmpfs,
41
+ *
54
- arch_opts ? arch_opts : "",
42
+ * If there are no inactive block nodes (e.g. because the VM was
55
- arch_source ? arch_source : "",
43
+ * just paused rather than completing a migration),
56
- shmem_opts ? shmem_opts : "",
44
+ * bdrv_inactivate_all() simply doesn't do anything.
57
- args->opts_source ? args->opts_source : "",
45
+ */
58
- ignore_stderr);
46
+ bdrv_activate_all(&local_err);
59
if (!args->only_target) {
47
+ if (local_err) {
60
+ g_autofree gchar *cmd_source = NULL;
48
+ error_propagate(errp, local_err);
61
+
49
+ return;
62
+ src_state = (QTestMigrationState) { };
50
+ }
63
+ src_state.suspend_me = args->suspend_me;
51
vm_start();
64
+
65
+ cmd_source = g_strdup_printf("-accel kvm%s -accel tcg "
66
+ "-machine %s,%s "
67
+ "-name source,debug-threads=on "
68
+ "-m %s "
69
+ "-serial file:%s/src_serial "
70
+ "%s %s %s %s %s",
71
+ kvm_opts ? kvm_opts : "",
72
+ machine, machine_opts,
73
+ memory_size, tmpfs,
74
+ arch_opts ? arch_opts : "",
75
+ arch_source ? arch_source : "",
76
+ shmem_opts ? shmem_opts : "",
77
+ args->opts_source ? args->opts_source : "",
78
+ ignore_stderr);
79
+
80
*from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source);
81
qtest_qmp_set_event_callback(*from,
82
migrate_watch_for_events,
83
&src_state);
84
}
52
}
85
53
}
86
+ dst_state = (QTestMigrationState) { };
87
+
88
cmd_target = g_strdup_printf("-accel kvm%s -accel tcg "
89
"-machine %s,%s "
90
"-name target,debug-threads=on "
91
@@ -XXX,XX +XXX,XX @@ static int test_migrate_start(QTestState **from, QTestState **to,
92
* Always enable migration events. Libvirt always uses it, let's try
93
* to mimic as closer as that.
94
*/
95
- migrate_set_capability(*from, "events", true);
96
+ if (!args->only_target) {
97
+ migrate_set_capability(*from, "events", true);
98
+ }
99
migrate_set_capability(*to, "events", true);
100
101
return 0;
102
--
54
--
103
2.47.0
55
2.47.0
diff view generated by jsdifflib
1
Migration capability 'late-block-active' controls when the block drives
1
Migration capability 'late-block-active' controls when the block drives
2
will be activated. If enabled, block drives will only be activated until
2
will be activated. If enabled, block drives will only be activated until
3
VM starts, either src runstate was "live" (RUNNING, or SUSPENDED), or it'll
3
VM starts, either src runstate was "live" (RUNNING, or SUSPENDED), or it'll
4
be postponed until qmp_cont().
4
be postponed until qmp_cont().
5
5
6
Let's do this unconditionally. There's no harm to delay activation of
6
Let's do this unconditionally. There's no harm to delay activation of
7
block drives. Meanwhile there's no ABI breakage if dest does it, because
7
block drives. Meanwhile there's no ABI breakage if dest does it, because
8
src QEMU has nothing to do with it, so it's no concern on ABI breakage.
8
src QEMU has nothing to do with it, so it's no concern on ABI breakage.
9
9
10
IIUC we could avoid introducing this cap when introducing it before, but
10
IIUC we could avoid introducing this cap when introducing it before, but
11
now it's still not too late to just always do it. Cap now prone to
11
now it's still not too late to just always do it. Cap now prone to
12
removal, but it'll be for later patches.
12
removal, but it'll be for later patches.
13
13
14
Signed-off-by: Peter Xu <peterx@redhat.com>
14
Signed-off-by: Peter Xu <peterx@redhat.com>
15
---
15
---
16
migration/migration.c | 38 +++++++++++++++++++-------------------
16
migration/migration.c | 38 +++++++++++++++++++-------------------
17
1 file changed, 19 insertions(+), 19 deletions(-)
17
1 file changed, 19 insertions(+), 19 deletions(-)
18
18
19
diff --git a/migration/migration.c b/migration/migration.c
19
diff --git a/migration/migration.c b/migration/migration.c
20
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
21
--- a/migration/migration.c
21
--- a/migration/migration.c
22
+++ b/migration/migration.c
22
+++ b/migration/migration.c
23
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
23
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
24
24
25
trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter");
25
trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter");
26
26
27
- /* If capability late_block_activate is set:
27
- /* If capability late_block_activate is set:
28
- * Only fire up the block code now if we're going to restart the
28
- * Only fire up the block code now if we're going to restart the
29
- * VM, else 'cont' will do it.
29
- * VM, else 'cont' will do it.
30
- * This causes file locking to happen; so we don't want it to happen
30
- * This causes file locking to happen; so we don't want it to happen
31
- * unless we really are starting the VM.
31
- * unless we really are starting the VM.
32
- */
32
- */
33
- if (!migrate_late_block_activate() ||
33
- if (!migrate_late_block_activate() ||
34
- (autostart && runstate_is_live(migration_get_target_runstate()))) {
34
- (autostart && runstate_is_live(migration_get_target_runstate()))) {
35
- /* Make sure all file formats throw away their mutable metadata.
35
- /* Make sure all file formats throw away their mutable metadata.
36
- * If we get an error here, just don't restart the VM yet. */
36
- * If we get an error here, just don't restart the VM yet. */
37
- bdrv_activate_all(&local_err);
37
- bdrv_activate_all(&local_err);
38
- if (local_err) {
38
- if (local_err) {
39
- error_report_err(local_err);
39
- error_report_err(local_err);
40
- local_err = NULL;
40
- local_err = NULL;
41
- autostart = false;
41
- autostart = false;
42
- }
42
- }
43
- }
43
- }
44
-
44
-
45
/*
45
/*
46
* This must happen after all error conditions are dealt with and
46
* This must happen after all error conditions are dealt with and
47
* we're sure the VM is going to be running on this host.
47
* we're sure the VM is going to be running on this host.
48
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
48
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
49
49
50
if (runstate_is_live(migration_get_target_runstate())) {
50
if (runstate_is_live(migration_get_target_runstate())) {
51
if (autostart) {
51
if (autostart) {
52
- vm_start();
52
- vm_start();
53
+ /*
53
+ /*
54
+ * Block activation is always delayed until VM starts, either
54
+ * Block activation is always delayed until VM starts, either
55
+ * here (which means we need to start the dest VM right now..),
55
+ * here (which means we need to start the dest VM right now..),
56
+ * or until qmp_cont() later.
56
+ * or until qmp_cont() later.
57
+ *
57
+ *
58
+ * We used to have cap 'late-block-activate' but now we do this
58
+ * We used to have cap 'late-block-activate' but now we do this
59
+ * unconditionally, as it has no harm but only benefit. E.g.,
59
+ * unconditionally, as it has no harm but only benefit. E.g.,
60
+ * it's not part of migration ABI on the time of disk activation.
60
+ * it's not part of migration ABI on the time of disk activation.
61
+ *
61
+ *
62
+ * Make sure all file formats throw away their mutable
62
+ * Make sure all file formats throw away their mutable
63
+ * metadata. If error, don't restart the VM yet.
63
+ * metadata. If error, don't restart the VM yet.
64
+ */
64
+ */
65
+ bdrv_activate_all(&local_err);
65
+ bdrv_activate_all(&local_err);
66
+ if (local_err) {
66
+ if (local_err) {
67
+ error_report_err(local_err);
67
+ error_report_err(local_err);
68
+ local_err = NULL;
68
+ local_err = NULL;
69
+ } else {
69
+ } else {
70
+ vm_start();
70
+ vm_start();
71
+ }
71
+ }
72
} else {
72
} else {
73
runstate_set(RUN_STATE_PAUSED);
73
runstate_set(RUN_STATE_PAUSED);
74
}
74
}
75
--
75
--
76
2.47.0
76
2.47.0
diff view generated by jsdifflib
...
...
5
for postcopy unconditionally, just like precopy. After this patch, we
5
for postcopy unconditionally, just like precopy. After this patch, we
6
should have unified the behavior across all.
6
should have unified the behavior across all.
7
7
8
Signed-off-by: Peter Xu <peterx@redhat.com>
8
Signed-off-by: Peter Xu <peterx@redhat.com>
9
---
9
---
10
migration/savevm.c | 23 ++++++++++-------------
10
migration/savevm.c | 25 ++++++++++++-------------
11
1 file changed, 10 insertions(+), 13 deletions(-)
11
1 file changed, 12 insertions(+), 13 deletions(-)
12
12
13
diff --git a/migration/savevm.c b/migration/savevm.c
13
diff --git a/migration/savevm.c b/migration/savevm.c
14
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
15
--- a/migration/savevm.c
15
--- a/migration/savevm.c
16
+++ b/migration/savevm.c
16
+++ b/migration/savevm.c
...
...
32
dirty_bitmap_mig_before_vm_start();
32
dirty_bitmap_mig_before_vm_start();
33
33
34
if (autostart) {
34
if (autostart) {
35
- /* Hold onto your hats, starting the CPU */
35
- /* Hold onto your hats, starting the CPU */
36
- vm_start();
36
- vm_start();
37
+ /* Make sure all file formats throw away their mutable metadata.
37
+ /*
38
+ * If we get an error here, just don't restart the VM yet. */
38
+ * Make sure all file formats throw away their mutable metadata.
39
+ * If we get an error here, just don't restart the VM yet.
40
+ */
39
+ bdrv_activate_all(&local_err);
41
+ bdrv_activate_all(&local_err);
40
+ trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated");
42
+ trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated");
41
+ if (local_err) {
43
+ if (local_err) {
42
+ error_report_err(local_err);
44
+ error_report_err(local_err);
43
+ local_err = NULL;
45
+ local_err = NULL;
...
...
diff view generated by jsdifflib
1
Src QEMU sets block_inactive=true very early before the invalidation takes
1
Src QEMU sets block_inactive=true very early before the invalidation takes
2
place. It means if something wrong happened during setting the flag but
2
place. It means if something wrong happened during setting the flag but
3
before reaching qemu_savevm_state_complete_precopy_non_iterable() where it
3
before reaching qemu_savevm_state_complete_precopy_non_iterable() where it
4
did the invalidation work, it'll make block_inactive flag inconsistent.
4
did the invalidation work, it'll make block_inactive flag inconsistent.
5
5
6
For example, think about when qemu_savevm_state_complete_precopy_iterable()
6
For example, think about when qemu_savevm_state_complete_precopy_iterable()
7
can fail: it will have block_inactive set to true even if all block drives
7
can fail: it will have block_inactive set to true even if all block drives
8
are active.
8
are active.
9
9
10
Fix that by only update the flag after the invalidation is done.
10
Fix that by only update the flag after the invalidation is done.
11
11
12
No Fixes for any commit, because it's not an issue if bdrv_activate_all()
12
No Fixes for any commit, because it's not an issue if bdrv_activate_all()
13
is re-entrant upon all-active disks - false positive block_inactive can
13
is re-entrant upon all-active disks - false positive block_inactive can
14
bring nothing more than "trying to active the blocks but they're already
14
bring nothing more than "trying to active the blocks but they're already
15
active". However let's still do it right to avoid the inconsistent flag
15
active". However let's still do it right to avoid the inconsistent flag
16
v.s. reality.
16
v.s. reality.
17
17
18
Signed-off-by: Peter Xu <peterx@redhat.com>
18
Signed-off-by: Peter Xu <peterx@redhat.com>
19
---
19
---
20
migration/migration.c | 9 +++------
20
migration/migration.c | 9 +++------
21
migration/savevm.c | 2 ++
21
migration/savevm.c | 2 ++
22
2 files changed, 5 insertions(+), 6 deletions(-)
22
2 files changed, 5 insertions(+), 6 deletions(-)
23
23
24
diff --git a/migration/migration.c b/migration/migration.c
24
diff --git a/migration/migration.c b/migration/migration.c
25
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
26
--- a/migration/migration.c
26
--- a/migration/migration.c
27
+++ b/migration/migration.c
27
+++ b/migration/migration.c
28
@@ -XXX,XX +XXX,XX @@ static int migration_completion_precopy(MigrationState *s,
28
@@ -XXX,XX +XXX,XX @@ static int migration_completion_precopy(MigrationState *s,
29
goto out_unlock;
29
goto out_unlock;
30
}
30
}
31
31
32
- /*
32
- /*
33
- * Inactivate disks except in COLO, and track that we have done so in order
33
- * Inactivate disks except in COLO, and track that we have done so in order
34
- * to remember to reactivate them if migration fails or is cancelled.
34
- * to remember to reactivate them if migration fails or is cancelled.
35
- */
35
- */
36
- s->block_inactive = !migrate_colo();
36
- s->block_inactive = !migrate_colo();
37
migration_rate_set(RATE_LIMIT_DISABLED);
37
migration_rate_set(RATE_LIMIT_DISABLED);
38
+
38
+
39
+ /* Inactivate disks except in COLO */
39
+ /* Inactivate disks except in COLO */
40
ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false,
40
ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false,
41
- s->block_inactive);
41
- s->block_inactive);
42
+ !migrate_colo());
42
+ !migrate_colo());
43
out_unlock:
43
out_unlock:
44
bql_unlock();
44
bql_unlock();
45
return ret;
45
return ret;
46
diff --git a/migration/savevm.c b/migration/savevm.c
46
diff --git a/migration/savevm.c b/migration/savevm.c
47
index XXXXXXX..XXXXXXX 100644
47
index XXXXXXX..XXXXXXX 100644
48
--- a/migration/savevm.c
48
--- a/migration/savevm.c
49
+++ b/migration/savevm.c
49
+++ b/migration/savevm.c
50
@@ -XXX,XX +XXX,XX @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
50
@@ -XXX,XX +XXX,XX @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
51
qemu_file_set_error(f, ret);
51
qemu_file_set_error(f, ret);
52
return ret;
52
return ret;
53
}
53
}
54
+ /* Remember that we did this */
54
+ /* Remember that we did this */
55
+ s->block_inactive = true;
55
+ s->block_inactive = true;
56
}
56
}
57
if (!in_postcopy) {
57
if (!in_postcopy) {
58
/* Postcopy stream will still be going */
58
/* Postcopy stream will still be going */
59
--
59
--
60
2.47.0
60
2.47.0
diff view generated by jsdifflib
1
This patch proposes a flag to maintain disk activation status globally. It
2
mostly rewrites disk activation mgmt for QEMU, including COLO and QMP
3
command xen_save_devices_state.
4
5
Backgrounds
6
===========
7
8
We have two problems on disk activations, one resolved, one not.
9
10
Problem 1: disk activation recover (for switchover interruptions)
11
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12
1
When migration is either cancelled or failed during switchover, especially
13
When migration is either cancelled or failed during switchover, especially
2
when after the disks are inactivated, QEMU needs to remember re-activate
14
when after the disks are inactivated, QEMU needs to remember re-activate
3
the disks again before vm starts.
15
the disks again before vm starts.
4
16
5
It used to be done separately in two paths: one in qmp_migrate_cancel(),
17
It used to be done separately in two paths: one in qmp_migrate_cancel(),
6
the other one in the failure path of migration_completion().
18
the other one in the failure path of migration_completion().
7
19
8
It used to be fixed in different commits, all over the places in QEMU. So
20
It used to be fixed in different commits, all over the places in QEMU. So
9
these are the ones I see at least:
21
these are the relevant changes I saw, I'm not sure if it's complete list:
10
22
11
- In 2016, commit fe904ea824 ("migration: regain control of images when
23
- In 2016, commit fe904ea824 ("migration: regain control of images when
12
migration fails to complete")
24
migration fails to complete")
13
25
14
- In 2017, commit 1d2acc3162 ("migration: re-active images while migration
26
- In 2017, commit 1d2acc3162 ("migration: re-active images while migration
15
been canceled after inactive them")
27
been canceled after inactive them")
16
28
17
- In 2023, commit 6dab4c93ec ("migration: Attempt disk reactivation in
29
- In 2023, commit 6dab4c93ec ("migration: Attempt disk reactivation in
18
more failure scenarios")
30
more failure scenarios")
19
31
20
We could have done better on trying to solve this once and for all. Now it
32
Now since we have a slightly better picture maybe we can unify the
21
might be a good chance because we have a better whole picture now.
33
reactivation in a single path.
22
34
23
Put both of the error cases together now into migration_iteration_finish(),
35
One side benefit of doing so is, we can move the disk operation outside QMP
24
which will be invoked for either of the scenario.
36
command "migrate_cancel". It's possible that in the future we may want to
25
37
make "migrate_cancel" be OOB-compatible, while that requires the command
26
At the meantime, cleanup block_inactive in a few ways:
38
doesn't need BQL in the first place. This will already do that and make
27
39
migrate_cancel command lightweight.
28
- Rename it to block_active, which is easier to follow..
40
29
41
Problem 2: disk invalidation on top of invalidated disks
30
- Maintain the flag for the whole lifecycle of QEMU. Otherwise it's not
42
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31
clear on when the flag is valid or not.
43
32
44
This is an unresolved bug for current QEMU. Link in "Resolves:" at the
33
- Put it together with the operations, rather than updating the flag
45
end. It turns out besides the src switchover phase (problem 1 above), QEMU
34
separately.
46
also needs to remember block activation on destination.
35
47
36
Now we set the flag to TRUE from the start, showing block ownership of a
48
Consider two continuous migration in a row, where the VM was always paused.
37
fresh started QEMU (but so far only used in migration module). Then it can
49
In that scenario, the disks are not activated even until migration
38
be modified by migration switchover code if invalidation happened. With
50
completed in the 1st round. When the 2nd round starts, if QEMU doesn't
39
that, the flag always reflects the correct block ownership.
51
know the status of the disks, it needs to try inactivate the disk again.
40
52
41
NOTE: it can always be racy if there's concurrent operation to change the
53
Here the issue is the block layer API bdrv_inactivate_all() will crash a
42
block activation status (e.g., qmp_cont() right after migration failure but
54
QEMU if invoked on already inactive disks for the 2nd migration. For
43
right before block re-activate happens), but that's not avoidable even
55
detail, see the bug link at the end.
44
before this patch, so it's not part of the goal that this patch would like
56
45
to achieve, so put aside as of now.
57
Implementation
46
58
==============
59
60
This patch proposes to maintain disk activation with a global flag, so we
61
know:
62
63
- If we used to inactivate disks for migration, but migration got
64
cancelled, or failed, QEMU will know it should reactivate the disks.
65
66
- On incoming side, if the disks are never activated but then another
67
migration is triggered, QEMU should be able to tell that inactivate is
68
not needed for the 2nd migration.
69
70
We used to have disk_inactive, but it only solves the 1st issue, not the
71
2nd. Also, it's done in completely separate paths so it's extremely hard
72
to follow either how the flag changes, or the duration that the flag is
73
valid, and when we will reactivate the disks.
74
75
Convert the existing disk_inactive flag into that global flag (also invert
76
its naming), and maintain the disk activation status for the whole
77
lifecycle of qemu. That includes the incoming QEMU.
78
79
Put both of the error cases of source migration (failure, cancelled)
80
together into migration_iteration_finish(), which will be invoked for
81
either of the scenario. So from that part QEMU should behave the same as
82
before. However with such global maintenance on disk activation status, we
83
not only cleanup quite a few temporary paths that we try to maintain the
84
disk activation status (e.g. in postcopy code), meanwhile it fixes the
85
crash for problem 2 in one shot.
86
87
For freshly started QEMU, the flag is initialized to TRUE showing that the
88
QEMU owns the disks by default.
89
90
For incoming migrated QEMU, the flag will be initialized to FALSE once and
91
for all showing that the dest QEMU doesn't own the disks until switchover.
92
That is guaranteed by the "once" variable.
93
94
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2395
47
Signed-off-by: Peter Xu <peterx@redhat.com>
95
Signed-off-by: Peter Xu <peterx@redhat.com>
48
---
96
---
49
migration/migration.h | 20 +++++++++-
97
include/migration/misc.h | 4 ++
50
migration/migration.c | 86 +++++++++++++++++++++++++------------------
98
migration/migration.h | 6 +--
51
migration/savevm.c | 11 ++----
99
migration/block-active.c | 94 ++++++++++++++++++++++++++++++++++++++++
52
3 files changed, 72 insertions(+), 45 deletions(-)
100
migration/colo.c | 2 +-
53
101
migration/migration.c | 80 ++++++++--------------------------
102
migration/savevm.c | 33 ++++++--------
103
monitor/qmp-cmds.c | 8 +---
104
migration/meson.build | 1 +
105
migration/trace-events | 3 ++
106
9 files changed, 140 insertions(+), 91 deletions(-)
107
create mode 100644 migration/block-active.c
108
109
diff --git a/include/migration/misc.h b/include/migration/misc.h
110
index XXXXXXX..XXXXXXX 100644
111
--- a/include/migration/misc.h
112
+++ b/include/migration/misc.h
113
@@ -XXX,XX +XXX,XX @@ bool migration_incoming_postcopy_advised(void);
114
/* True if background snapshot is active */
115
bool migration_in_bg_snapshot(void);
116
117
+/* Wrapper for block active/inactive operations */
118
+bool migration_block_activate(Error **errp);
119
+bool migration_block_inactivate(void);
120
+
121
#endif
54
diff --git a/migration/migration.h b/migration/migration.h
122
diff --git a/migration/migration.h b/migration/migration.h
55
index XXXXXXX..XXXXXXX 100644
123
index XXXXXXX..XXXXXXX 100644
56
--- a/migration/migration.h
124
--- a/migration/migration.h
57
+++ b/migration/migration.h
125
+++ b/migration/migration.h
58
@@ -XXX,XX +XXX,XX @@ struct MigrationState {
126
@@ -XXX,XX +XXX,XX @@ struct MigrationState {
59
/* Flag set once the migration thread is running (and needs joining) */
127
/* Flag set once the migration thread is running (and needs joining) */
60
bool migration_thread_running;
128
bool migration_thread_running;
61
129
62
- /* Flag set once the migration thread called bdrv_inactivate_all */
130
- /* Flag set once the migration thread called bdrv_inactivate_all */
63
- bool block_inactive;
131
- bool block_inactive;
64
+ /*
132
-
65
+ * Migration-only cache to remember the block layer activation status.
66
+ * Protected by BQL.
67
+ *
68
+ * We need this because..
69
+ *
70
+ * - Migration can fail after block devices are invalidated (during
71
+ * switchover phase). When that happens, we need to be able to
72
+ * recover the block drive status by re-activating them.
73
+ *
74
+ * For freshly started QEMU, block_active is initialized to TRUE
75
+ * reflecting the scenario where QEMU owns block device ownerships.
76
+ */
77
+ bool block_active;
78
79
/* Migration is waiting for guest to unplug device */
133
/* Migration is waiting for guest to unplug device */
80
QemuSemaphore wait_unplug_sem;
134
QemuSemaphore wait_unplug_sem;
135
81
@@ -XXX,XX +XXX,XX @@ void migration_bitmap_sync_precopy(bool last_stage);
136
@@ -XXX,XX +XXX,XX @@ void migration_bitmap_sync_precopy(bool last_stage);
82
/* migration/block-dirty-bitmap.c */
137
/* migration/block-dirty-bitmap.c */
83
void dirty_bitmap_mig_init(void);
138
void dirty_bitmap_mig_init(void);
84
139
85
+/* Wrapper for block active/inactive operations */
140
+/* migration/block-active.c */
86
+bool migration_block_activate(MigrationState *s);
141
+void migration_block_active_setup(bool active);
87
+bool migration_block_inactivate(MigrationState *s);
88
+
142
+
89
#endif
143
#endif
90
diff --git a/migration/migration.c b/migration/migration.c
144
diff --git a/migration/block-active.c b/migration/block-active.c
91
index XXXXXXX..XXXXXXX 100644
145
new file mode 100644
92
--- a/migration/migration.c
146
index XXXXXXX..XXXXXXX
93
+++ b/migration/migration.c
147
--- /dev/null
94
@@ -XXX,XX +XXX,XX @@ static void migration_downtime_end(MigrationState *s)
148
+++ b/migration/block-active.c
95
trace_vmstate_downtime_checkpoint("src-downtime-end");
149
@@ -XXX,XX +XXX,XX @@
96
}
150
+/*
97
151
+ * Block activation tracking for migration purpose
98
+bool migration_block_activate(MigrationState *s)
152
+ *
153
+ * SPDX-License-Identifier: GPL-2.0-or-later
154
+ *
155
+ * Copyright (C) 2024 Red Hat, Inc.
156
+ */
157
+#include "qemu/osdep.h"
158
+#include "block/block.h"
159
+#include "qapi/error.h"
160
+#include "migration/migration.h"
161
+#include "qemu/error-report.h"
162
+#include "trace.h"
163
+
164
+/*
165
+ * Migration-only cache to remember the block layer activation status.
166
+ * Protected by BQL.
167
+ *
168
+ * We need this because..
169
+ *
170
+ * - Migration can fail after block devices are invalidated (during
171
+ * switchover phase). When that happens, we need to be able to recover
172
+ * the block drive status by re-activating them.
173
+ *
174
+ * - Currently bdrv_inactivate_all() is not safe to be invoked on top of
175
+ * invalidated drives (even if bdrv_activate_all() is actually safe to be
176
+ * called any time!). It means remembering this could help migration to
177
+ * make sure it won't invalidate twice in a row, crashing QEMU. It can
178
+ * happen when we migrate a PAUSED VM from host1 to host2, then migrate
179
+ * again to host3 without starting it. TODO: a cleaner solution is to
180
+ * allow safe invoke of bdrv_inactivate_all() at anytime, like
181
+ * bdrv_activate_all().
182
+ *
183
+ * For freshly started QEMU, the flag is initialized to TRUE reflecting the
184
+ * scenario where QEMU owns block device ownerships.
185
+ *
186
+ * For incoming QEMU taking a migration stream, the flag is initialized to
187
+ * FALSE reflecting that the incoming side doesn't own the block devices,
188
+ * not until switchover happens.
189
+ */
190
+static bool migration_block_active;
191
+
192
+/* Setup the disk activation status */
193
+void migration_block_active_setup(bool active)
99
+{
194
+{
100
+ Error *local_err = NULL;
195
+ migration_block_active = active;
196
+}
197
+
198
+bool migration_block_activate(Error **errp)
199
+{
200
+ ERRP_GUARD();
101
+
201
+
102
+ assert(bql_locked());
202
+ assert(bql_locked());
103
+
203
+
104
+ if (s->block_active) {
204
+ if (migration_block_active) {
205
+ trace_migration_block_activation("active-skipped");
105
+ return true;
206
+ return true;
106
+ }
207
+ }
107
+
208
+
108
+ bdrv_activate_all(&local_err);
209
+ trace_migration_block_activation("active");
109
+ if (local_err) {
210
+
110
+ error_report_err(local_err);
211
+ bdrv_activate_all(errp);
212
+ if (*errp) {
213
+ error_report_err(error_copy(*errp));
111
+ return false;
214
+ return false;
112
+ }
215
+ }
113
+
216
+
114
+ s->block_active = true;
217
+ migration_block_active = true;
115
+ return true;
218
+ return true;
116
+}
219
+}
117
+
220
+
118
+bool migration_block_inactivate(MigrationState *s)
221
+bool migration_block_inactivate(void)
119
+{
222
+{
120
+ int ret;
223
+ int ret;
121
+
224
+
122
+ assert(bql_locked());
225
+ assert(bql_locked());
123
+
226
+
124
+ if (!s->block_active) {
227
+ if (!migration_block_active) {
228
+ trace_migration_block_activation("inactive-skipped");
125
+ return true;
229
+ return true;
126
+ }
230
+ }
127
+
231
+
232
+ trace_migration_block_activation("inactive");
233
+
128
+ ret = bdrv_inactivate_all();
234
+ ret = bdrv_inactivate_all();
129
+ if (ret) {
235
+ if (ret) {
130
+ error_report("%s: bdrv_inactivate_all() failed: %d\n",
236
+ error_report("%s: bdrv_inactivate_all() failed: %d",
131
+ __func__, ret);
237
+ __func__, ret);
132
+ return false;
238
+ return false;
133
+ }
239
+ }
134
+
240
+
135
+ s->block_active = false;
241
+ migration_block_active = false;
136
+ return true;
242
+ return true;
137
+}
243
+}
138
+
244
diff --git a/migration/colo.c b/migration/colo.c
139
static bool migration_needs_multiple_sockets(void)
245
index XXXXXXX..XXXXXXX 100644
246
--- a/migration/colo.c
247
+++ b/migration/colo.c
248
@@ -XXX,XX +XXX,XX @@ static void *colo_process_incoming_thread(void *opaque)
249
250
/* Make sure all file formats throw away their mutable metadata */
251
bql_lock();
252
- bdrv_activate_all(&local_err);
253
+ migration_block_activate(&local_err);
254
bql_unlock();
255
if (local_err) {
256
error_report_err(local_err);
257
diff --git a/migration/migration.c b/migration/migration.c
258
index XXXXXXX..XXXXXXX 100644
259
--- a/migration/migration.c
260
+++ b/migration/migration.c
261
@@ -XXX,XX +XXX,XX @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels,
262
263
static void process_incoming_migration_bh(void *opaque)
140
{
264
{
141
return migrate_multifd() || migrate_postcopy_preempt();
265
- Error *local_err = NULL;
266
MigrationIncomingState *mis = opaque;
267
268
trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter");
269
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
270
* Make sure all file formats throw away their mutable
271
* metadata. If error, don't restart the VM yet.
272
*/
273
- bdrv_activate_all(&local_err);
274
- if (local_err) {
275
- error_report_err(local_err);
276
- local_err = NULL;
277
- } else {
278
+ if (migration_block_activate(NULL)) {
279
vm_start();
280
}
281
} else {
142
@@ -XXX,XX +XXX,XX @@ static void migrate_fd_cancel(MigrationState *s)
282
@@ -XXX,XX +XXX,XX @@ static void migrate_fd_cancel(MigrationState *s)
143
}
283
}
144
}
284
}
145
}
285
}
146
- if (s->state == MIGRATION_STATUS_CANCELLING && s->block_inactive) {
286
- if (s->state == MIGRATION_STATUS_CANCELLING && s->block_inactive) {
...
...
154
- }
294
- }
155
- }
295
- }
156
}
296
}
157
297
158
void migration_add_notifier_mode(NotifierWithReturn *notify,
298
void migration_add_notifier_mode(NotifierWithReturn *notify,
299
@@ -XXX,XX +XXX,XX @@ void qmp_migrate_incoming(const char *uri, bool has_channels,
300
return;
301
}
302
303
+ /*
304
+ * Newly setup incoming QEMU. Mark the block active state to reflect
305
+ * that the src currently owns the disks.
306
+ */
307
+ migration_block_active_setup(false);
308
+
309
once = false;
310
}
311
312
@@ -XXX,XX +XXX,XX @@ static int postcopy_start(MigrationState *ms, Error **errp)
313
QIOChannelBuffer *bioc;
314
QEMUFile *fb;
315
uint64_t bandwidth = migrate_max_postcopy_bandwidth();
316
- bool restart_block = false;
317
int cur_state = MIGRATION_STATUS_ACTIVE;
318
319
if (migrate_postcopy_preempt()) {
320
@@ -XXX,XX +XXX,XX @@ static int postcopy_start(MigrationState *ms, Error **errp)
321
goto fail;
322
}
323
324
- ret = bdrv_inactivate_all();
325
- if (ret < 0) {
326
- error_setg_errno(errp, -ret, "%s: Failed in bdrv_inactivate_all()",
327
- __func__);
328
+ if (!migration_block_inactivate()) {
329
+ error_setg(errp, "%s: Failed in bdrv_inactivate_all()", __func__);
330
goto fail;
331
}
332
- restart_block = true;
333
334
/*
335
* Cause any non-postcopiable, but iterative devices to
336
@@ -XXX,XX +XXX,XX @@ static int postcopy_start(MigrationState *ms, Error **errp)
337
goto fail_closefb;
338
}
339
340
- restart_block = false;
341
-
342
/* Now send that blob */
343
if (qemu_savevm_send_packaged(ms->to_dst_file, bioc->data, bioc->usage)) {
344
error_setg(errp, "%s: Failed to send packaged data", __func__);
345
@@ -XXX,XX +XXX,XX @@ fail_closefb:
346
fail:
347
migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
348
MIGRATION_STATUS_FAILED);
349
- if (restart_block) {
350
- /* A failure happened early enough that we know the destination hasn't
351
- * accessed block devices, so we're safe to recover.
352
- */
353
- Error *local_err = NULL;
354
-
355
- bdrv_activate_all(&local_err);
356
- if (local_err) {
357
- error_report_err(local_err);
358
- }
359
- }
360
+ migration_block_activate(NULL);
361
migration_call_notifiers(ms, MIG_EVENT_PRECOPY_FAILED, NULL);
362
bql_unlock();
363
return -1;
159
@@ -XXX,XX +XXX,XX @@ static void migration_completion_postcopy(MigrationState *s)
364
@@ -XXX,XX +XXX,XX @@ static void migration_completion_postcopy(MigrationState *s)
160
trace_migration_completion_postcopy_end_after_complete();
365
trace_migration_completion_postcopy_end_after_complete();
161
}
366
}
162
367
163
-static void migration_completion_failed(MigrationState *s,
368
-static void migration_completion_failed(MigrationState *s,
...
...
204
case MIGRATION_STATUS_CANCELLING:
409
case MIGRATION_STATUS_CANCELLING:
205
+ /*
410
+ /*
206
+ * Re-activate the block drives if they're inactivated. Note, COLO
411
+ * Re-activate the block drives if they're inactivated. Note, COLO
207
+ * shouldn't use block_active at all, so it should be no-op there.
412
+ * shouldn't use block_active at all, so it should be no-op there.
208
+ */
413
+ */
209
+ migration_block_activate(s);
414
+ migration_block_activate(NULL);
210
if (runstate_is_live(s->vm_old_state)) {
415
if (runstate_is_live(s->vm_old_state)) {
211
if (!runstate_check(RUN_STATE_SHUTDOWN)) {
416
if (!runstate_check(RUN_STATE_SHUTDOWN)) {
212
vm_start();
417
vm_start();
213
@@ -XXX,XX +XXX,XX @@ static void migration_instance_init(Object *obj)
418
@@ -XXX,XX +XXX,XX @@ static void migration_instance_init(Object *obj)
214
ms->state = MIGRATION_STATUS_NONE;
419
ms->state = MIGRATION_STATUS_NONE;
215
ms->mbps = -1;
420
ms->mbps = -1;
216
ms->pages_per_second = -1;
421
ms->pages_per_second = -1;
217
+ /* Freshly started QEMU owns all the block devices */
422
+ /* Freshly started QEMU owns all the block devices */
218
+ ms->block_active = true;
423
+ migration_block_active_setup(true);
219
qemu_sem_init(&ms->pause_sem, 0);
424
qemu_sem_init(&ms->pause_sem, 0);
220
qemu_mutex_init(&ms->error_mutex);
425
qemu_mutex_init(&ms->error_mutex);
221
426
222
diff --git a/migration/savevm.c b/migration/savevm.c
427
diff --git a/migration/savevm.c b/migration/savevm.c
223
index XXXXXXX..XXXXXXX 100644
428
index XXXXXXX..XXXXXXX 100644
224
--- a/migration/savevm.c
429
--- a/migration/savevm.c
225
+++ b/migration/savevm.c
430
+++ b/migration/savevm.c
226
@@ -XXX,XX +XXX,XX @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
431
@@ -XXX,XX +XXX,XX @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
432
}
433
227
if (inactivate_disks) {
434
if (inactivate_disks) {
228
/* Inactivate before sending QEMU_VM_EOF so that the
435
- /* Inactivate before sending QEMU_VM_EOF so that the
229
* bdrv_activate_all() on the other end won't fail. */
436
- * bdrv_activate_all() on the other end won't fail. */
230
- ret = bdrv_inactivate_all();
437
- ret = bdrv_inactivate_all();
231
- if (ret) {
438
- if (ret) {
232
- error_setg(&local_err, "%s: bdrv_inactivate_all() failed (%d)",
439
- error_setg(&local_err, "%s: bdrv_inactivate_all() failed (%d)",
233
- __func__, ret);
440
- __func__, ret);
234
+ if (!migration_block_inactivate(ms)) {
441
+ /*
442
+ * Inactivate before sending QEMU_VM_EOF so that the
443
+ * bdrv_activate_all() on the other end won't fail.
444
+ */
445
+ if (!migration_block_inactivate()) {
235
+ error_setg(&local_err, "%s: bdrv_inactivate_all() failed",
446
+ error_setg(&local_err, "%s: bdrv_inactivate_all() failed",
236
+ __func__);
447
+ __func__);
237
migrate_set_error(ms, local_err);
448
migrate_set_error(ms, local_err);
238
error_report_err(local_err);
449
error_report_err(local_err);
239
- qemu_file_set_error(f, ret);
450
- qemu_file_set_error(f, ret);
...
...
243
- /* Remember that we did this */
454
- /* Remember that we did this */
244
- s->block_inactive = true;
455
- s->block_inactive = true;
245
}
456
}
246
if (!in_postcopy) {
457
if (!in_postcopy) {
247
/* Postcopy stream will still be going */
458
/* Postcopy stream will still be going */
459
@@ -XXX,XX +XXX,XX @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
460
461
static void loadvm_postcopy_handle_run_bh(void *opaque)
462
{
463
- Error *local_err = NULL;
464
MigrationIncomingState *mis = opaque;
465
466
trace_vmstate_downtime_checkpoint("dst-postcopy-bh-enter");
467
@@ -XXX,XX +XXX,XX @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
468
* Make sure all file formats throw away their mutable metadata.
469
* If we get an error here, just don't restart the VM yet.
470
*/
471
- bdrv_activate_all(&local_err);
472
+ bool success = migration_block_activate(NULL);
473
+
474
trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated");
475
- if (local_err) {
476
- error_report_err(local_err);
477
- local_err = NULL;
478
- } else {
479
+
480
+ if (success) {
481
vm_start();
482
}
483
} else {
484
@@ -XXX,XX +XXX,XX @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live,
485
* side of the migration take control of the images.
486
*/
487
if (live && !saved_vm_running) {
488
- ret = bdrv_inactivate_all();
489
- if (ret) {
490
- error_setg(errp, "%s: bdrv_inactivate_all() failed (%d)",
491
- __func__, ret);
492
- }
493
+ migration_block_inactivate();
494
}
495
}
496
497
diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
498
index XXXXXXX..XXXXXXX 100644
499
--- a/monitor/qmp-cmds.c
500
+++ b/monitor/qmp-cmds.c
501
@@ -XXX,XX +XXX,XX @@
502
#include "qapi/type-helpers.h"
503
#include "hw/mem/memory-device.h"
504
#include "hw/intc/intc.h"
505
+#include "migration/misc.h"
506
507
NameInfo *qmp_query_name(Error **errp)
508
{
509
@@ -XXX,XX +XXX,XX @@ void qmp_cont(Error **errp)
510
* Continuing after completed migration. Images have been
511
* inactivated to allow the destination to take control. Need to
512
* get control back now.
513
- *
514
- * If there are no inactive block nodes (e.g. because the VM was
515
- * just paused rather than completing a migration),
516
- * bdrv_inactivate_all() simply doesn't do anything.
517
*/
518
- bdrv_activate_all(&local_err);
519
- if (local_err) {
520
+ if (!migration_block_activate(&local_err)) {
521
error_propagate(errp, local_err);
522
return;
523
}
524
diff --git a/migration/meson.build b/migration/meson.build
525
index XXXXXXX..XXXXXXX 100644
526
--- a/migration/meson.build
527
+++ b/migration/meson.build
528
@@ -XXX,XX +XXX,XX @@ migration_files = files(
529
530
system_ss.add(files(
531
'block-dirty-bitmap.c',
532
+ 'block-active.c',
533
'channel.c',
534
'channel-block.c',
535
'cpu-throttle.c',
536
diff --git a/migration/trace-events b/migration/trace-events
537
index XXXXXXX..XXXXXXX 100644
538
--- a/migration/trace-events
539
+++ b/migration/trace-events
540
@@ -XXX,XX +XXX,XX @@ migration_pagecache_insert(void) "Error allocating page"
541
# cpu-throttle.c
542
cpu_throttle_set(int new_throttle_pct) "set guest CPU throttled by %d%%"
543
cpu_throttle_dirty_sync(void) ""
544
+
545
+# block-active.c
546
+migration_block_activation(const char *name) "%s"
248
--
547
--
249
2.47.0
548
2.47.0
diff view generated by jsdifflib
Deleted patch
1
Extend the usage of such API to the receiver side, because such status
2
maintenance is also necessary there.
3
1
4
It's needed to avoid bdrv_inactivate_all() not being invoked when the
5
drives are already inactive. When invoked, it can crash QEMU. See the
6
issue link for more information.
7
8
So it means we need to start remember the block activation status not only
9
on src, but also on dest.
10
11
One thing tricky here is, when we need to consider incoming QEMU, we need
12
to also consider its initial status as incoming side. Due to the fact that
13
before switchover the incoming QEMU doesn't own the disks, we need to set
14
initial state to FALSE for a incoming QEMU. In this case, postcopy recover
15
doesn't count.
16
17
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2395
18
Signed-off-by: Peter Xu <peterx@redhat.com>
19
---
20
migration/migration.h | 13 +++++++++++++
21
migration/migration.c | 13 +++++++------
22
2 files changed, 20 insertions(+), 6 deletions(-)
23
24
diff --git a/migration/migration.h b/migration/migration.h
25
index XXXXXXX..XXXXXXX 100644
26
--- a/migration/migration.h
27
+++ b/migration/migration.h
28
@@ -XXX,XX +XXX,XX @@ struct MigrationState {
29
* switchover phase). When that happens, we need to be able to
30
* recover the block drive status by re-activating them.
31
*
32
+ * - Currently bdrv_inactivate_all() is not safe to be invoked on top
33
+ * of invalidated drives (even if bdrv_activate_all() is actually
34
+ * safe to be called any time!). It means remembering this could
35
+ * help migration to make sure it won't invalidate twice in a row,
36
+ * crashing QEMU. It can happen when we migrate a PAUSED VM from
37
+ * host1 to host2, then migrate again to host3 without starting it.
38
+ * TODO: a cleaner solution is to allow safe invoke of
39
+ * bdrv_inactivate_all() at anytime, like bdrv_activate_all().
40
+ *
41
* For freshly started QEMU, block_active is initialized to TRUE
42
* reflecting the scenario where QEMU owns block device ownerships.
43
+ *
44
+ * For incoming QEMU taking a migration stream, block_active is set to
45
+ * FALSE reflecting that the incoming side doesn't own the block
46
+ * devices, not until switchover happens.
47
*/
48
bool block_active;
49
50
diff --git a/migration/migration.c b/migration/migration.c
51
index XXXXXXX..XXXXXXX 100644
52
--- a/migration/migration.c
53
+++ b/migration/migration.c
54
@@ -XXX,XX +XXX,XX @@ migration_incoming_state_setup(MigrationIncomingState *mis, Error **errp)
55
return false;
56
}
57
58
+ /*
59
+ * Newly setup QEMU, prepared for incoming migration. Mark the block
60
+ * active state to reflect that the src currently owns the disks.
61
+ */
62
+ migrate_get_current()->block_active = false;
63
+
64
migrate_set_state(&mis->state, current, MIGRATION_STATUS_SETUP);
65
return true;
66
}
67
@@ -XXX,XX +XXX,XX @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels,
68
69
static void process_incoming_migration_bh(void *opaque)
70
{
71
- Error *local_err = NULL;
72
MigrationIncomingState *mis = opaque;
73
74
trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter");
75
@@ -XXX,XX +XXX,XX @@ static void process_incoming_migration_bh(void *opaque)
76
* Make sure all file formats throw away their mutable
77
* metadata. If error, don't restart the VM yet.
78
*/
79
- bdrv_activate_all(&local_err);
80
- if (local_err) {
81
- error_report_err(local_err);
82
- local_err = NULL;
83
- } else {
84
+ if (migration_block_activate(migrate_get_current())) {
85
vm_start();
86
}
87
} else {
88
--
89
2.47.0
diff view generated by jsdifflib
Deleted patch
1
Postcopy also has similar error handling for re-activation of block
2
devices. Use the same API as precopy to do the re-activation, for both src
3
& dst sides.
4
1
5
Signed-off-by: Peter Xu <peterx@redhat.com>
6
---
7
migration/migration.c | 22 +++-------------------
8
migration/savevm.c | 10 ++++------
9
2 files changed, 7 insertions(+), 25 deletions(-)
10
11
diff --git a/migration/migration.c b/migration/migration.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/migration/migration.c
14
+++ b/migration/migration.c
15
@@ -XXX,XX +XXX,XX @@ static int postcopy_start(MigrationState *ms, Error **errp)
16
QIOChannelBuffer *bioc;
17
QEMUFile *fb;
18
uint64_t bandwidth = migrate_max_postcopy_bandwidth();
19
- bool restart_block = false;
20
int cur_state = MIGRATION_STATUS_ACTIVE;
21
22
if (migrate_postcopy_preempt()) {
23
@@ -XXX,XX +XXX,XX @@ static int postcopy_start(MigrationState *ms, Error **errp)
24
goto fail;
25
}
26
27
- ret = bdrv_inactivate_all();
28
- if (ret < 0) {
29
- error_setg_errno(errp, -ret, "%s: Failed in bdrv_inactivate_all()",
30
- __func__);
31
+ if (!migration_block_inactivate(ms)) {
32
+ error_setg(errp, "%s: Failed in bdrv_inactivate_all()", __func__);
33
goto fail;
34
}
35
- restart_block = true;
36
37
/*
38
* Cause any non-postcopiable, but iterative devices to
39
@@ -XXX,XX +XXX,XX @@ static int postcopy_start(MigrationState *ms, Error **errp)
40
goto fail_closefb;
41
}
42
43
- restart_block = false;
44
-
45
/* Now send that blob */
46
if (qemu_savevm_send_packaged(ms->to_dst_file, bioc->data, bioc->usage)) {
47
error_setg(errp, "%s: Failed to send packaged data", __func__);
48
@@ -XXX,XX +XXX,XX @@ fail_closefb:
49
fail:
50
migrate_set_state(&ms->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
51
MIGRATION_STATUS_FAILED);
52
- if (restart_block) {
53
- /* A failure happened early enough that we know the destination hasn't
54
- * accessed block devices, so we're safe to recover.
55
- */
56
- Error *local_err = NULL;
57
-
58
- bdrv_activate_all(&local_err);
59
- if (local_err) {
60
- error_report_err(local_err);
61
- }
62
- }
63
+ migration_block_activate(ms);
64
migration_call_notifiers(ms, MIG_EVENT_PRECOPY_FAILED, NULL);
65
bql_unlock();
66
return -1;
67
diff --git a/migration/savevm.c b/migration/savevm.c
68
index XXXXXXX..XXXXXXX 100644
69
--- a/migration/savevm.c
70
+++ b/migration/savevm.c
71
@@ -XXX,XX +XXX,XX @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
72
73
static void loadvm_postcopy_handle_run_bh(void *opaque)
74
{
75
- Error *local_err = NULL;
76
MigrationIncomingState *mis = opaque;
77
78
trace_vmstate_downtime_checkpoint("dst-postcopy-bh-enter");
79
@@ -XXX,XX +XXX,XX @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
80
if (autostart) {
81
/* Make sure all file formats throw away their mutable metadata.
82
* If we get an error here, just don't restart the VM yet. */
83
- bdrv_activate_all(&local_err);
84
+ bool success = migration_block_activate(migrate_get_current());
85
+
86
trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated");
87
- if (local_err) {
88
- error_report_err(local_err);
89
- local_err = NULL;
90
- } else {
91
+
92
+ if (success) {
93
vm_start();
94
}
95
} else {
96
--
97
2.47.0
diff view generated by jsdifflib
Deleted patch
1
From: Fabiano Rosas <farosas@suse.de>
2
1
3
Stop using hardcoded strings for -serial so we can in the next patches
4
perform more than one migration in a row. Having the serial path
5
hardcoded means we cannot reuse the code when dst becomes the new src.
6
7
Signed-off-by: Fabiano Rosas <farosas@suse.de>
8
Link: https://lore.kernel.org/r/20241125144612.16194-3-farosas@suse.de
9
Signed-off-by: Peter Xu <peterx@redhat.com>
10
---
11
tests/qtest/migration-helpers.h | 2 +
12
tests/qtest/migration-helpers.c | 8 ++++
13
tests/qtest/migration-test.c | 68 ++++++++++++++++++---------------
14
3 files changed, 48 insertions(+), 30 deletions(-)
15
16
diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h
17
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qtest/migration-helpers.h
19
+++ b/tests/qtest/migration-helpers.h
20
@@ -XXX,XX +XXX,XX @@ typedef struct QTestMigrationState {
21
bool resume_seen;
22
bool suspend_seen;
23
bool suspend_me;
24
+ char *serial;
25
} QTestMigrationState;
26
27
bool migrate_watch_for_events(QTestState *who, const char *name,
28
@@ -XXX,XX +XXX,XX @@ static inline bool probe_o_direct_support(const char *tmpfs)
29
#endif
30
void migration_test_add(const char *path, void (*fn)(void));
31
void migration_event_wait(QTestState *s, const char *target);
32
+char *migrate_get_unique_serial(const char *tmpfs);
33
34
#endif /* MIGRATION_HELPERS_H */
35
diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c
36
index XXXXXXX..XXXXXXX 100644
37
--- a/tests/qtest/migration-helpers.c
38
+++ b/tests/qtest/migration-helpers.c
39
@@ -XXX,XX +XXX,XX @@ void migration_event_wait(QTestState *s, const char *target)
40
qobject_unref(response);
41
} while (!found);
42
}
43
+
44
+char *migrate_get_unique_serial(const char *tmpfs)
45
+{
46
+ static int i;
47
+
48
+ assert(i < INT_MAX);
49
+ return g_strdup_printf("%s/serial_%d", tmpfs, i++);
50
+}
51
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
52
index XXXXXXX..XXXXXXX 100644
53
--- a/tests/qtest/migration-test.c
54
+++ b/tests/qtest/migration-test.c
55
@@ -XXX,XX +XXX,XX @@ static void bootfile_create(char *dir, bool suspend_me)
56
* we get an 'A' followed by an endless string of 'B's
57
* but on the destination we won't have the A (unless we enabled suspend/resume)
58
*/
59
-static void wait_for_serial(const char *side)
60
+static void wait_for_serial(const char *serialpath)
61
{
62
- g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side);
63
FILE *serialfile = fopen(serialpath, "r");
64
65
do {
66
@@ -XXX,XX +XXX,XX @@ static void wait_for_serial(const char *side)
67
break;
68
69
default:
70
- fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side);
71
+ fprintf(stderr, "Unexpected %d on %s\n", readvalue, serialpath);
72
g_assert_not_reached();
73
}
74
} while (true);
75
@@ -XXX,XX +XXX,XX @@ static int test_migrate_start(QTestState **from, QTestState **to,
76
77
src_state = (QTestMigrationState) { };
78
src_state.suspend_me = args->suspend_me;
79
+ src_state.serial = migrate_get_unique_serial(tmpfs);
80
81
cmd_source = g_strdup_printf("-accel kvm%s -accel tcg "
82
"-machine %s,%s "
83
"-name source,debug-threads=on "
84
"-m %s "
85
- "-serial file:%s/src_serial "
86
+ "-serial file:%s "
87
"%s %s %s %s %s",
88
kvm_opts ? kvm_opts : "",
89
machine, machine_opts,
90
- memory_size, tmpfs,
91
+ memory_size, src_state.serial,
92
arch_opts ? arch_opts : "",
93
arch_source ? arch_source : "",
94
shmem_opts ? shmem_opts : "",
95
@@ -XXX,XX +XXX,XX @@ static int test_migrate_start(QTestState **from, QTestState **to,
96
}
97
98
dst_state = (QTestMigrationState) { };
99
+ dst_state.serial = migrate_get_unique_serial(tmpfs);
100
101
cmd_target = g_strdup_printf("-accel kvm%s -accel tcg "
102
"-machine %s,%s "
103
"-name target,debug-threads=on "
104
"-m %s "
105
- "-serial file:%s/dest_serial "
106
+ "-serial file:%s "
107
"-incoming %s "
108
"%s %s %s %s %s",
109
kvm_opts ? kvm_opts : "",
110
machine, machine_opts,
111
- memory_size, tmpfs, uri,
112
+ memory_size, dst_state.serial, uri,
113
arch_opts ? arch_opts : "",
114
arch_target ? arch_target : "",
115
shmem_opts ? shmem_opts : "",
116
@@ -XXX,XX +XXX,XX @@ static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest)
117
qtest_quit(to);
118
119
cleanup("migsocket");
120
- cleanup("src_serial");
121
- cleanup("dest_serial");
122
+ unlink(src_state.serial);
123
+ g_free(src_state.serial);
124
+ unlink(dst_state.serial);
125
+ g_free(dst_state.serial);
126
cleanup(FILE_TEST_FILENAME);
127
}
128
129
@@ -XXX,XX +XXX,XX @@ static int migrate_postcopy_prepare(QTestState **from_ptr,
130
" 'port': '0' } } ] } }");
131
132
/* Wait for the first serial output from the source */
133
- wait_for_serial("src_serial");
134
+ wait_for_serial(src_state.serial);
135
wait_for_suspend(from, &src_state);
136
137
migrate_qmp(from, to, NULL, NULL, "{}");
138
@@ -XXX,XX +XXX,XX @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to,
139
}
140
141
/* Make sure we get at least one "B" on destination */
142
- wait_for_serial("dest_serial");
143
+ wait_for_serial(dst_state.serial);
144
145
if (uffd_feature_thread_id) {
146
read_blocktime(to);
147
@@ -XXX,XX +XXX,XX @@ static void test_precopy_common(MigrateCommon *args)
148
149
/* Wait for the first serial output from the source */
150
if (args->result == MIG_TEST_SUCCEED) {
151
- wait_for_serial("src_serial");
152
+ wait_for_serial(src_state.serial);
153
wait_for_suspend(from, &src_state);
154
}
155
156
@@ -XXX,XX +XXX,XX @@ static void test_precopy_common(MigrateCommon *args)
157
qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}");
158
}
159
160
- wait_for_serial("dest_serial");
161
+ wait_for_serial(dst_state.serial);
162
}
163
164
finish:
165
@@ -XXX,XX +XXX,XX @@ static void test_file_common(MigrateCommon *args, bool stop_src)
166
}
167
168
migrate_ensure_converge(from);
169
- wait_for_serial("src_serial");
170
+ wait_for_serial(src_state.serial);
171
172
if (stop_src) {
173
qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}");
174
@@ -XXX,XX +XXX,XX @@ static void test_file_common(MigrateCommon *args, bool stop_src)
175
}
176
wait_for_resume(to, &dst_state);
177
178
- wait_for_serial("dest_serial");
179
+ wait_for_serial(dst_state.serial);
180
181
if (check_offset) {
182
file_check_offset_region();
183
@@ -XXX,XX +XXX,XX @@ static void test_ignore_shared(void)
184
migrate_set_capability(to, "x-ignore-shared", true);
185
186
/* Wait for the first serial output from the source */
187
- wait_for_serial("src_serial");
188
+ wait_for_serial(src_state.serial);
189
190
migrate_qmp(from, to, uri, NULL, "{}");
191
192
@@ -XXX,XX +XXX,XX @@ static void test_ignore_shared(void)
193
194
qtest_qmp_eventwait(to, "RESUME");
195
196
- wait_for_serial("dest_serial");
197
+ wait_for_serial(dst_state.serial);
198
wait_for_migration_complete(from);
199
200
/* Check whether shared RAM has been really skipped */
201
@@ -XXX,XX +XXX,XX @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail)
202
migrate_set_capability(from, "validate-uuid", true);
203
204
/* Wait for the first serial output from the source */
205
- wait_for_serial("src_serial");
206
+ wait_for_serial(src_state.serial);
207
208
migrate_qmp(from, to, uri, NULL, "{}");
209
210
@@ -XXX,XX +XXX,XX @@ static void do_test_validate_uri_channel(MigrateCommon *args)
211
}
212
213
/* Wait for the first serial output from the source */
214
- wait_for_serial("src_serial");
215
+ wait_for_serial(src_state.serial);
216
217
/*
218
* 'uri' and 'channels' validation is checked even before the migration
219
@@ -XXX,XX +XXX,XX @@ static void test_migrate_auto_converge(void)
220
migrate_set_capability(from, "pause-before-switchover", true);
221
222
/* Wait for the first serial output from the source */
223
- wait_for_serial("src_serial");
224
+ wait_for_serial(src_state.serial);
225
226
migrate_qmp(from, to, uri, NULL, "{}");
227
228
@@ -XXX,XX +XXX,XX @@ static void test_migrate_auto_converge(void)
229
230
qtest_qmp_eventwait(to, "RESUME");
231
232
- wait_for_serial("dest_serial");
233
+ wait_for_serial(dst_state.serial);
234
wait_for_migration_complete(from);
235
236
test_migrate_end(from, to, true);
237
@@ -XXX,XX +XXX,XX @@ static void test_multifd_tcp_cancel(void)
238
migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}");
239
240
/* Wait for the first serial output from the source */
241
- wait_for_serial("src_serial");
242
+ wait_for_serial(src_state.serial);
243
244
migrate_qmp(from, to, NULL, NULL, "{}");
245
246
@@ -XXX,XX +XXX,XX @@ static void test_multifd_tcp_cancel(void)
247
/* Make sure QEMU process "to" exited */
248
qtest_set_expected_status(to, EXIT_FAILURE);
249
qtest_wait_qemu(to);
250
- qtest_quit(to);
251
+ unlink(dst_state.serial);
252
+ g_free(dst_state.serial);
253
254
/*
255
* Ensure the source QEMU finishes its cancellation process before we
256
@@ -XXX,XX +XXX,XX @@ static void test_multifd_tcp_cancel(void)
257
wait_for_stop(from, &src_state);
258
qtest_qmp_eventwait(to2, "RESUME");
259
260
- wait_for_serial("dest_serial");
261
+ wait_for_serial(dst_state.serial);
262
wait_for_migration_complete(from);
263
test_migrate_end(from, to2, true);
264
}
265
@@ -XXX,XX +XXX,XX @@ static QTestState *dirtylimit_start_vm(void)
266
QTestState *vm = NULL;
267
g_autofree gchar *cmd = NULL;
268
269
+ src_state = (QTestMigrationState) { };
270
+ src_state.serial = migrate_get_unique_serial(tmpfs);
271
+
272
bootfile_create(tmpfs, false);
273
cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 "
274
"-name dirtylimit-test,debug-threads=on "
275
"-m 150M -smp 1 "
276
- "-serial file:%s/vm_serial "
277
+ "-serial file:%s "
278
"-drive file=%s,format=raw ",
279
- tmpfs, bootpath);
280
+ src_state.serial, bootpath);
281
282
vm = qtest_init(cmd);
283
return vm;
284
@@ -XXX,XX +XXX,XX @@ static QTestState *dirtylimit_start_vm(void)
285
static void dirtylimit_stop_vm(QTestState *vm)
286
{
287
qtest_quit(vm);
288
- cleanup("vm_serial");
289
+ unlink(src_state.serial);
290
+ g_free(src_state.serial);
291
}
292
293
static void test_vcpu_dirty_limit(void)
294
@@ -XXX,XX +XXX,XX @@ static void test_vcpu_dirty_limit(void)
295
vm = dirtylimit_start_vm();
296
297
/* Wait for the first serial output from the vm*/
298
- wait_for_serial("vm_serial");
299
+ wait_for_serial(src_state.serial);
300
301
/* Do dirtyrate measurement with calc time equals 1s */
302
calc_dirty_rate(vm, 1);
303
@@ -XXX,XX +XXX,XX @@ static void migrate_dirty_limit_wait_showup(QTestState *from,
304
migrate_set_capability(from, "pause-before-switchover", true);
305
306
/* Wait for the serial output from the source */
307
- wait_for_serial("src_serial");
308
+ wait_for_serial(src_state.serial);
309
}
310
311
/*
312
@@ -XXX,XX +XXX,XX @@ static void test_migrate_dirty_limit(void)
313
314
qtest_qmp_eventwait(to, "RESUME");
315
316
- wait_for_serial("dest_serial");
317
+ wait_for_serial(dst_state.serial);
318
wait_for_migration_complete(from);
319
320
test_migrate_end(from, to, true);
321
--
322
2.47.0
diff view generated by jsdifflib
Deleted patch
1
From: Fabiano Rosas <farosas@suse.de>
2
1
3
We don't always want to cleanup both VMs at the same time. One example
4
is the multifd cancel test, where there's a second migration reusing
5
the source VM. The next patches will add another instance, keeping the
6
destination VM instead.
7
8
Extract the cleanup routine from test_migrate_end() into another
9
function so it can be reused.
10
11
Signed-off-by: Fabiano Rosas <farosas@suse.de>
12
Link: https://lore.kernel.org/r/20241125144612.16194-4-farosas@suse.de
13
Signed-off-by: Peter Xu <peterx@redhat.com>
14
---
15
tests/qtest/migration-test.c | 35 +++++++++++++++++++++--------------
16
1 file changed, 21 insertions(+), 14 deletions(-)
17
18
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/tests/qtest/migration-test.c
21
+++ b/tests/qtest/migration-test.c
22
@@ -XXX,XX +XXX,XX @@ static int test_migrate_start(QTestState **from, QTestState **to,
23
return 0;
24
}
25
26
+static void migrate_cleanup(QTestState *from, QTestState *to)
27
+{
28
+ if (from) {
29
+ qtest_quit(from);
30
+ unlink(src_state.serial);
31
+ g_free(src_state.serial);
32
+ }
33
+
34
+ if (to) {
35
+ qtest_quit(to);
36
+ unlink(dst_state.serial);
37
+ g_free(dst_state.serial);
38
+ }
39
+
40
+ cleanup("migsocket");
41
+ cleanup(FILE_TEST_FILENAME);
42
+}
43
+
44
static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest)
45
{
46
unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d;
47
48
- qtest_quit(from);
49
-
50
- if (test_dest) {
51
+ if (to && test_dest) {
52
qtest_memread(to, start_address, &dest_byte_a, 1);
53
54
/* Destination still running, wait for a byte to change */
55
@@ -XXX,XX +XXX,XX @@ static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest)
56
check_guests_ram(to);
57
}
58
59
- qtest_quit(to);
60
-
61
- cleanup("migsocket");
62
- unlink(src_state.serial);
63
- g_free(src_state.serial);
64
- unlink(dst_state.serial);
65
- g_free(dst_state.serial);
66
- cleanup(FILE_TEST_FILENAME);
67
+ migrate_cleanup(from, to);
68
}
69
70
#ifdef CONFIG_GNUTLS
71
@@ -XXX,XX +XXX,XX @@ static void test_multifd_tcp_cancel(void)
72
73
/* Make sure QEMU process "to" exited */
74
qtest_set_expected_status(to, EXIT_FAILURE);
75
- qtest_wait_qemu(to);
76
- unlink(dst_state.serial);
77
- g_free(dst_state.serial);
78
+ migrate_cleanup(NULL, to);
79
80
/*
81
* Ensure the source QEMU finishes its cancellation process before we
82
--
83
2.47.0
diff view generated by jsdifflib
Deleted patch
1
From: Fabiano Rosas <farosas@suse.de>
2
1
3
Add a framework for running migrations back and forth between two
4
guests (aka ping-pong migration). We have seen a couple of bugs that
5
only reproduce when a guest is migrated and the destination machine is
6
used as the source of a new migration.
7
8
Add a simple test that does 2 migrations and another test that uses
9
the late-block-activate capability, which is one area where a bug has
10
been found.
11
12
Note that this does not reuse any of the existing tests
13
(e.g. test_precopy_common), because there is too much ambiguity
14
regarding how to handle the hooks, args->result, stop/continue, etc.
15
16
Signed-off-by: Fabiano Rosas <farosas@suse.de>
17
Link: https://lore.kernel.org/r/20241125144612.16194-6-farosas@suse.de
18
Signed-off-by: Peter Xu <peterx@redhat.com>
19
---
20
tests/qtest/migration-test.c | 121 +++++++++++++++++++++++++++++++++++
21
1 file changed, 121 insertions(+)
22
23
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/tests/qtest/migration-test.c
26
+++ b/tests/qtest/migration-test.c
27
@@ -XXX,XX +XXX,XX @@ finish:
28
test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED);
29
}
30
31
+static void migrate_dst_becomes_src(QTestState **from, QTestState **to,
32
+ QTestMigrationState *src_state,
33
+ QTestMigrationState *dst_state)
34
+{
35
+ *from = *to;
36
+
37
+ *src_state = (QTestMigrationState) { };
38
+ src_state->serial = dst_state->serial;
39
+ qtest_qmp_set_event_callback(*from, migrate_watch_for_events, src_state);
40
+
41
+ src_state->stop_seen = dst_state->stop_seen;
42
+}
43
+
44
+static void test_precopy_ping_pong_common(MigrateCommon *args, int n,
45
+ bool late_block_activate)
46
+{
47
+ QTestState *from, *to;
48
+ bool keep_paused = false;
49
+
50
+ g_assert(!args->live);
51
+
52
+ g_test_message("Migrating back and forth %d times", n);
53
+
54
+ for (int i = 0; i < n; i++) {
55
+ g_test_message("%s (%d/%d)", i % 2 ? "pong" : "ping", i + 1, n);
56
+
57
+ if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) {
58
+ return;
59
+ }
60
+
61
+ if (late_block_activate) {
62
+ migrate_set_capability(from, "late-block-activate", true);
63
+ migrate_set_capability(to, "late-block-activate", true);
64
+
65
+ /*
66
+ * The late-block-activate capability expects that the
67
+ * management layer will issue a qmp_continue command on
68
+ * the destination once the migration finishes. In order
69
+ * to test the capability properly, make sure the test is
70
+ * not issuing spurious continue commands.
71
+ */
72
+ keep_paused = true;
73
+ }
74
+
75
+ if (!src_state.stop_seen) {
76
+ wait_for_serial(src_state.serial);
77
+ }
78
+
79
+ /* because of !args->live */
80
+ qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}");
81
+ wait_for_stop(from, &src_state);
82
+
83
+ migrate_ensure_converge(from);
84
+ migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}");
85
+
86
+ wait_for_migration_complete(from);
87
+ wait_for_migration_complete(to);
88
+
89
+ /* note check_guests_ram() requires a stopped guest */
90
+ check_guests_ram(to);
91
+
92
+ if (keep_paused) {
93
+ /*
94
+ * Nothing issued continue on the destination, reflect
95
+ * that the guest is paused in the dst_state.
96
+ */
97
+ dst_state.stop_seen = true;
98
+ } else {
99
+ qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}");
100
+ wait_for_serial(dst_state.serial);
101
+ dst_state.stop_seen = false;
102
+ }
103
+
104
+ /*
105
+ * Last iteration, let the guest run to make sure there's no
106
+ * memory corruption. The test_migrate_end() below will check
107
+ * the memory one last time.
108
+ */
109
+ if (i == n - 1) {
110
+ qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}");
111
+ wait_for_serial(dst_state.serial);
112
+ dst_state.stop_seen = false;
113
+ break;
114
+ }
115
+
116
+ /*
117
+ * Prepare for migrating back: clear the original source and
118
+ * switch source & destination.
119
+ */
120
+ migrate_cleanup(from, NULL);
121
+ migrate_dst_becomes_src(&from, &to, &src_state, &dst_state);
122
+
123
+ args->start.only_target = true;
124
+ }
125
+
126
+ test_migrate_end(from, to, true);
127
+}
128
+
129
static void file_dirty_offset_region(void)
130
{
131
g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME);
132
@@ -XXX,XX +XXX,XX @@ static void test_migrate_dirty_limit(void)
133
test_migrate_end(from, to, true);
134
}
135
136
+static void test_precopy_ping_pong(void)
137
+{
138
+ MigrateCommon args = {
139
+ .listen_uri = "tcp:127.0.0.1:0",
140
+ };
141
+
142
+ test_precopy_ping_pong_common(&args, 2, false);
143
+}
144
+
145
+static void test_precopy_ping_pong_late_block(void)
146
+{
147
+ MigrateCommon args = {
148
+ .listen_uri = "tcp:127.0.0.1:0",
149
+ };
150
+
151
+ test_precopy_ping_pong_common(&args, 2, true);
152
+}
153
+
154
static bool kvm_dirty_ring_supported(void)
155
{
156
#if defined(__linux__) && defined(HOST_X86_64)
157
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
158
}
159
}
160
161
+ migration_test_add("/migration/precopy/ping-pong/plain",
162
+ test_precopy_ping_pong);
163
+ migration_test_add("/migration/precopy/ping-pong/late-block-activate",
164
+ test_precopy_ping_pong_late_block);
165
+
166
ret = g_test_run();
167
168
g_assert_cmpint(ret, ==, 0);
169
--
170
2.47.0
diff view generated by jsdifflib