]> git.proxmox.com Git - pve-qemu.git/blob - debian/patches/pve/0045-savevm-async-move-more-code-to-cleanup-and-rename-to.patch
fix vmstate-snapshots with iothread=1
[pve-qemu.git] / debian / patches / pve / 0045-savevm-async-move-more-code-to-cleanup-and-rename-to.patch
1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2 From: Stefan Reiter <s.reiter@proxmox.com>
3 Date: Wed, 27 May 2020 11:33:19 +0200
4 Subject: [PATCH] savevm-async: move more code to cleanup and rename to
5 finalize
6
7 process_savevm_cleanup is renamed to process_savevm_finalize to
8 accomodate more code that is not all cleanup related.
9
10 The benefit of this is that it allows us to call functions which need to
11 run in the main AIOContext directly. It doesn't majorly affect snapshot
12 performance, since the first instruction that is moved stops the VM,
13 so the downtime stays about the same.
14
15 The target bdrv is additionally moved to the IOHandler context before
16 process_savevm_co to make sure the coroutine can call functions that
17 require it to own the bdrv's context. process_savevm_finalize then moves
18 it back to the main context to run its part.
19
20 Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
21 ---
22 savevm-async.c | 87 +++++++++++++++++++++++++++++---------------------
23 1 file changed, 51 insertions(+), 36 deletions(-)
24
25 diff --git a/savevm-async.c b/savevm-async.c
26 index c3fe741c38..2894c94233 100644
27 --- a/savevm-async.c
28 +++ b/savevm-async.c
29 @@ -50,7 +50,7 @@ static struct SnapshotState {
30 int saved_vm_running;
31 QEMUFile *file;
32 int64_t total_time;
33 - QEMUBH *cleanup_bh;
34 + QEMUBH *finalize_bh;
35 Coroutine *co;
36 } snap_state;
37
38 @@ -196,12 +196,42 @@ static const QEMUFileOps block_file_ops = {
39 .close = block_state_close,
40 };
41
42 -static void process_savevm_cleanup(void *opaque)
43 +static void process_savevm_finalize(void *opaque)
44 {
45 int ret;
46 - qemu_bh_delete(snap_state.cleanup_bh);
47 - snap_state.cleanup_bh = NULL;
48 + AioContext *iohandler_ctx = iohandler_get_aio_context();
49 + MigrationState *ms = migrate_get_current();
50 +
51 + qemu_bh_delete(snap_state.finalize_bh);
52 + snap_state.finalize_bh = NULL;
53 snap_state.co = NULL;
54 +
55 + /* We need to own the target bdrv's context for the following functions,
56 + * so move it back. It can stay in the main context and live out its live
57 + * there, since we're done with it after this method ends anyway.
58 + */
59 + aio_context_acquire(iohandler_ctx);
60 + blk_set_aio_context(snap_state.target, qemu_get_aio_context(), NULL);
61 + aio_context_release(iohandler_ctx);
62 +
63 + ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
64 + if (ret < 0) {
65 + save_snapshot_error("vm_stop_force_state error %d", ret);
66 + }
67 +
68 + (void)qemu_savevm_state_complete_precopy(snap_state.file, false, false);
69 + ret = qemu_file_get_error(snap_state.file);
70 + if (ret < 0) {
71 + save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
72 + }
73 +
74 + DPRINTF("state saving complete\n");
75 +
76 + /* clear migration state */
77 + migrate_set_state(&ms->state, MIGRATION_STATUS_SETUP,
78 + ret ? MIGRATION_STATUS_FAILED : MIGRATION_STATUS_COMPLETED);
79 + ms->to_dst_file = NULL;
80 +
81 qemu_savevm_state_cleanup();
82
83 ret = save_snapshot_cleanup();
84 @@ -219,16 +249,15 @@ static void process_savevm_cleanup(void *opaque)
85 }
86 }
87
88 -static void process_savevm_coro(void *opaque)
89 +static void coroutine_fn process_savevm_co(void *opaque)
90 {
91 int ret;
92 int64_t maxlen;
93 - MigrationState *ms = migrate_get_current();
94
95 ret = qemu_file_get_error(snap_state.file);
96 if (ret < 0) {
97 save_snapshot_error("qemu_savevm_state_setup failed");
98 - goto out;
99 + return;
100 }
101
102 while (snap_state.state == SAVE_STATE_ACTIVE) {
103 @@ -245,7 +274,7 @@ static void process_savevm_coro(void *opaque)
104 save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
105 break;
106 }
107 - DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
108 + DPRINTF("savevm iterate pending size %lu ret %d\n", pending_size, ret);
109 } else {
110 qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL);
111 ret = global_state_store();
112 @@ -253,40 +282,20 @@ static void process_savevm_coro(void *opaque)
113 save_snapshot_error("global_state_store error %d", ret);
114 break;
115 }
116 - ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
117 - if (ret < 0) {
118 - save_snapshot_error("vm_stop_force_state error %d", ret);
119 - break;
120 - }
121 - DPRINTF("savevm inerate finished\n");
122 - /* upstream made the return value here inconsistent
123 - * (-1 instead of 'ret' in one case and 0 after flush which can
124 - * still set a file error...)
125 - */
126 - (void)qemu_savevm_state_complete_precopy(snap_state.file, false, false);
127 - ret = qemu_file_get_error(snap_state.file);
128 - if (ret < 0) {
129 - save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
130 - break;
131 - }
132 - DPRINTF("save complete\n");
133 +
134 + DPRINTF("savevm iterate complete\n");
135 break;
136 }
137 }
138
139 - qemu_bh_schedule(snap_state.cleanup_bh);
140 -
141 -out:
142 - /* set migration state accordingly and clear soon-to-be stale file */
143 - migrate_set_state(&ms->state, MIGRATION_STATUS_SETUP,
144 - ret ? MIGRATION_STATUS_FAILED : MIGRATION_STATUS_COMPLETED);
145 - ms->to_dst_file = NULL;
146 + qemu_bh_schedule(snap_state.finalize_bh);
147 }
148
149 void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
150 {
151 Error *local_err = NULL;
152 MigrationState *ms = migrate_get_current();
153 + AioContext *iohandler_ctx = iohandler_get_aio_context();
154
155 int bdrv_oflags = BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH;
156
157 @@ -347,7 +356,7 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
158
159 /*
160 * qemu_savevm_* paths use migration code and expect a migration state.
161 - * State is cleared in process_savevm_thread, but has to be initialized
162 + * State is cleared in process_savevm_co, but has to be initialized
163 * here (blocking main thread, from QMP) to avoid race conditions.
164 */
165 migrate_init(ms);
166 @@ -358,13 +367,19 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
167 blk_op_block_all(snap_state.target, snap_state.blocker);
168
169 snap_state.state = SAVE_STATE_ACTIVE;
170 - snap_state.cleanup_bh = qemu_bh_new(process_savevm_cleanup, &snap_state);
171 - snap_state.co = qemu_coroutine_create(&process_savevm_coro, NULL);
172 + snap_state.finalize_bh = qemu_bh_new(process_savevm_finalize, &snap_state);
173 + snap_state.co = qemu_coroutine_create(&process_savevm_co, NULL);
174 qemu_mutex_unlock_iothread();
175 qemu_savevm_state_header(snap_state.file);
176 qemu_savevm_state_setup(snap_state.file);
177 qemu_mutex_lock_iothread();
178 - aio_co_schedule(iohandler_get_aio_context(), snap_state.co);
179 +
180 + /* Async processing from here on out happens in iohandler context, so let
181 + * the target bdrv have its home there.
182 + */
183 + blk_set_aio_context(snap_state.target, iohandler_ctx, &local_err);
184 +
185 + aio_co_schedule(iohandler_ctx, snap_state.co);
186
187 return;
188