]> git.proxmox.com Git - pve-qemu-kvm.git/blob - debian/patches/pve/0046-convert-savevm-async-to-threads.patch
Fix #796: convert savevm-async to threads
[pve-qemu-kvm.git] / debian / patches / pve / 0046-convert-savevm-async-to-threads.patch
1 From f18d2aee91bca252a6b90583784f49236ec17d58 Mon Sep 17 00:00:00 2001
2 From: Wolfgang Bumiller <w.bumiller@proxmox.com>
3 Date: Tue, 8 Nov 2016 11:13:06 +0100
4 Subject: [PATCH 46/46] convert savevm-async to threads
5
6 ---
7 savevm-async.c | 149 +++++++++++++++++++++++++++++++++++----------------------
8 1 file changed, 93 insertions(+), 56 deletions(-)
9
10 diff --git a/savevm-async.c b/savevm-async.c
11 index 05b5b19..e293a00 100644
12 --- a/savevm-async.c
13 +++ b/savevm-async.c
14 @@ -48,6 +48,8 @@ static struct SnapshotState {
15 int saved_vm_running;
16 QEMUFile *file;
17 int64_t total_time;
18 + QEMUBH *cleanup_bh;
19 + QemuThread thread;
20 } snap_state;
21
22 SaveVMInfo *qmp_query_savevm(Error **errp)
23 @@ -135,19 +137,6 @@ static void save_snapshot_error(const char *fmt, ...)
24 g_free (msg);
25
26 snap_state.state = SAVE_STATE_ERROR;
27 -
28 - save_snapshot_cleanup();
29 -}
30 -
31 -static void save_snapshot_completed(void)
32 -{
33 - DPRINTF("save_snapshot_completed\n");
34 -
35 - if (save_snapshot_cleanup() < 0) {
36 - snap_state.state = SAVE_STATE_ERROR;
37 - } else {
38 - snap_state.state = SAVE_STATE_COMPLETED;
39 - }
40 }
41
42 static int block_state_close(void *opaque)
43 @@ -156,36 +145,76 @@ static int block_state_close(void *opaque)
44 return blk_flush(snap_state.target);
45 }
46
47 +typedef struct BlkRwCo {
48 + int64_t offset;
49 + QEMUIOVector *qiov;
50 + int ret;
51 +} BlkRwCo;
52 +
53 +static void block_state_write_entry(void *opaque) {
54 + BlkRwCo *rwco = opaque;
55 + rwco->ret = blk_co_pwritev(snap_state.target, rwco->offset, rwco->qiov->size,
56 + rwco->qiov, 0);
57 +}
58 +
59 static ssize_t block_state_writev_buffer(void *opaque, struct iovec *iov,
60 int iovcnt, int64_t pos)
61 {
62 - int ret;
63 QEMUIOVector qiov;
64 + AioContext *aio_context;
65 + Coroutine *co;
66 + BlkRwCo rwco;
67 +
68 + assert(pos == snap_state.bs_pos);
69 + rwco = (BlkRwCo) {
70 + .offset = pos,
71 + .qiov = &qiov,
72 + .ret = NOT_DONE,
73 + };
74
75 qemu_iovec_init_external(&qiov, iov, iovcnt);
76 - ret = blk_co_pwritev(snap_state.target, pos, qiov.size, &qiov, 0);
77 - if (ret < 0) {
78 - return ret;
79 +
80 + co = qemu_coroutine_create(&block_state_write_entry, &rwco);
81 + qemu_coroutine_enter(co);
82 +
83 + aio_context = blk_get_aio_context(snap_state.target);
84 + while (rwco.ret == NOT_DONE) {
85 + aio_poll(aio_context, true);
86 }
87 +
88 snap_state.bs_pos += qiov.size;
89 return qiov.size;
90 }
91
92 -static int store_and_stop(void) {
93 - if (global_state_store()) {
94 - save_snapshot_error("Error saving global state");
95 - return 1;
96 +static void process_savevm_cleanup(void *opaque)
97 +{
98 + int ret;
99 + qemu_bh_delete(snap_state.cleanup_bh);
100 + snap_state.cleanup_bh = NULL;
101 + qemu_mutex_unlock_iothread();
102 + qemu_thread_join(&snap_state.thread);
103 + qemu_mutex_lock_iothread();
104 + ret = save_snapshot_cleanup();
105 + if (ret < 0) {
106 + save_snapshot_error("save_snapshot_cleanup error %d", ret);
107 + } else if (snap_state.state == SAVE_STATE_ACTIVE) {
108 + snap_state.state = SAVE_STATE_COMPLETED;
109 + } else {
110 + save_snapshot_error("process_savevm_cleanup: invalid state: %d",
111 + snap_state.state);
112 }
113 - if (runstate_is_running()) {
114 - vm_stop(RUN_STATE_SAVE_VM);
115 + if (snap_state.saved_vm_running) {
116 + vm_start();
117 + snap_state.saved_vm_running = false;
118 }
119 - return 0;
120 }
121
122 -static void process_savevm_co(void *opaque)
123 +static void *process_savevm_thread(void *opaque)
124 {
125 int ret;
126 int64_t maxlen;
127 + AioContext *aio_context;
128 +
129 MigrationParams params = {
130 .blk = 0,
131 .shared = 0
132 @@ -193,57 +222,64 @@ static void process_savevm_co(void *opaque)
133
134 snap_state.state = SAVE_STATE_ACTIVE;
135
136 - qemu_mutex_unlock_iothread();
137 + rcu_register_thread();
138 +
139 qemu_savevm_state_header(snap_state.file);
140 ret = qemu_savevm_state_begin(snap_state.file, &params);
141 - qemu_mutex_lock_iothread();
142
143 if (ret < 0) {
144 save_snapshot_error("qemu_savevm_state_begin failed");
145 - return;
146 + rcu_unregister_thread();
147 + return NULL;
148 }
149
150 + aio_context = bdrv_get_aio_context(blk_bs(snap_state.target));
151 + aio_context_acquire(aio_context);
152 +
153 while (snap_state.state == SAVE_STATE_ACTIVE) {
154 uint64_t pending_size, pend_post, pend_nonpost;
155
156 qemu_savevm_state_pending(snap_state.file, 0, &pend_nonpost, &pend_post);
157 pending_size = pend_post + pend_nonpost;
158
159 - if (pending_size) {
160 - ret = qemu_savevm_state_iterate(snap_state.file, false);
161 - if (ret < 0) {
162 - save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
163 - break;
164 - }
165 - DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
166 + maxlen = blk_getlength(snap_state.target) - 30*1024*1024;
167 +
168 + if (pending_size > 400000 && snap_state.bs_pos + pending_size < maxlen) {
169 + ret = qemu_savevm_state_iterate(snap_state.file, false);
170 + if (ret < 0) {
171 + aio_context_release(aio_context);
172 + save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
173 + break;
174 + }
175 + DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
176 } else {
177 - DPRINTF("done iterating\n");
178 - if (store_and_stop())
179 + aio_context_release(aio_context);
180 + qemu_mutex_lock_iothread();
181 + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
182 + ret = global_state_store();
183 + if (ret) {
184 + qemu_mutex_unlock_iothread();
185 + save_snapshot_error("global_state_store error %d", ret);
186 + break;
187 + }
188 + ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
189 + if (ret < 0) {
190 + qemu_mutex_unlock_iothread();
191 + save_snapshot_error("vm_stop_force_state error %d", ret);
192 break;
193 + }
194 DPRINTF("savevm inerate finished\n");
195 qemu_savevm_state_complete_precopy(snap_state.file, false);
196 + qemu_savevm_state_cleanup();
197 DPRINTF("save complete\n");
198 - save_snapshot_completed();
199 + qemu_mutex_unlock_iothread();
200 break;
201 }
202 -
203 - /* stop the VM if we get to the end of available space,
204 - * or if pending_size is just a few MB
205 - */
206 - maxlen = blk_getlength(snap_state.target) - 30*1024*1024;
207 - if ((pending_size < 100000) ||
208 - ((snap_state.bs_pos + pending_size) >= maxlen)) {
209 - if (store_and_stop())
210 - break;
211 - }
212 - }
213 -
214 - if(snap_state.state == SAVE_STATE_CANCELLED) {
215 - save_snapshot_completed();
216 - Error *errp = NULL;
217 - qmp_savevm_end(&errp);
218 }
219
220 + qemu_bh_schedule(snap_state.cleanup_bh);
221 + rcu_unregister_thread();
222 + return NULL;
223 }
224
225 static const QEMUFileOps block_file_ops = {
226 @@ -306,8 +342,9 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
227 error_setg(&snap_state.blocker, "block device is in use by savevm");
228 blk_op_block_all(snap_state.target, snap_state.blocker);
229
230 - Coroutine *co = qemu_coroutine_create(process_savevm_co, NULL);
231 - qemu_coroutine_enter(co);
232 + snap_state.cleanup_bh = qemu_bh_new(process_savevm_cleanup, &snap_state);
233 + qemu_thread_create(&snap_state.thread, "savevm-async", process_savevm_thread,
234 + NULL, QEMU_THREAD_JOINABLE);
235
236 return;
237
238 --
239 2.1.4
240