]>
Commit | Line | Data |
---|---|---|
4dec8413 | 1 | From c209554171466e2aa8b1eca92d952429faca5fc6 Mon Sep 17 00:00:00 2001 |
5ad5891c DM |
2 | From: Dietmar Maurer <dietmar@proxmox.com> |
3 | Date: Thu, 29 Nov 2012 10:46:49 +0100 | |
884c5e9f | 4 | Subject: [PATCH v5 6/6] add vm state to backups |
5ad5891c DM |
5 | |
6 | ||
7 | Signed-off-by: Dietmar Maurer <dietmar@proxmox.com> | |
8 | --- | |
9 | blockdev.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++- | |
309874bd | 10 | hmp.c | 3 +- |
89af8a77 DM |
11 | qapi-schema.json | 5 +- |
12 | 3 files changed, 200 insertions(+), 4 deletions(-) | |
5ad5891c DM |
13 | |
14 | diff --git a/blockdev.c b/blockdev.c | |
884c5e9f | 15 | index 683f7da..dd20631 100644 |
5ad5891c DM |
16 | --- a/blockdev.c |
17 | +++ b/blockdev.c | |
18 | @@ -22,6 +22,8 @@ | |
92bf040c | 19 | #include "sysemu/arch_init.h" |
5ad5891c DM |
20 | #include "backup.h" |
21 | #include "vma.h" | |
89af8a77 DM |
22 | +#include "migration/qemu-file.h" |
23 | +#include "migration/migration.h" | |
5ad5891c DM |
24 | |
25 | static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); | |
26 | ||
92bf040c | 27 | @@ -1355,6 +1357,10 @@ static struct GenericBackupState { |
5ad5891c DM |
28 | size_t total; |
29 | size_t transferred; | |
30 | size_t zero_bytes; | |
31 | + unsigned char buf[BACKUP_CLUSTER_SIZE]; | |
32 | + int buf_index; | |
33 | + size_t buf_cluster_num; | |
34 | + guint8 vmstate_dev_id; | |
35 | } backup_state; | |
36 | ||
37 | typedef struct BackupCB { | |
92bf040c | 38 | @@ -1510,10 +1516,170 @@ static void backup_start_jobs(void) |
ddfd618f | 39 | backup_run_next_job(); |
5ad5891c DM |
40 | } |
41 | ||
42 | +static int backup_state_close(void *opaque) | |
43 | +{ | |
44 | + if (!backup_state.buf_index) { | |
45 | + return 0; | |
46 | + } | |
47 | + | |
48 | + size_t zero_bytes = 0; | |
49 | + if (backup_state.buf_index < BACKUP_CLUSTER_SIZE) { | |
50 | + memset(backup_state.buf + backup_state.buf_index, 0, | |
51 | + BACKUP_CLUSTER_SIZE - backup_state.buf_index); | |
52 | + } | |
884c5e9f | 53 | + int bytes = backup_state.driver->dump( |
5ad5891c DM |
54 | + backup_state.writer, backup_state.vmstate_dev_id, |
55 | + backup_state.buf_cluster_num, | |
56 | + backup_state.buf, &zero_bytes); | |
57 | + backup_state.buf_index = 0; | |
58 | + | |
59 | + return bytes < 0 ? -1 : 0; | |
60 | +} | |
61 | + | |
62 | +static int backup_state_put_buffer(void *opaque, const uint8_t *buf, | |
63 | + int64_t pos, int size) | |
64 | +{ | |
65 | + assert(backup_state.driver); | |
66 | + assert(backup_state.writer); | |
884c5e9f | 67 | + assert(backup_state.driver->dump); |
5ad5891c DM |
68 | + |
69 | + /* Note: our backup driver expects to get whole clusters (64KB) */ | |
70 | + | |
71 | + int ret = size; | |
72 | + | |
73 | + while (size > 0) { | |
74 | + int l = BACKUP_CLUSTER_SIZE - backup_state.buf_index; | |
75 | + l = l > size ? size : l; | |
76 | + memcpy(backup_state.buf + backup_state.buf_index, buf, l); | |
77 | + backup_state.buf_index += l; | |
78 | + buf += l; | |
79 | + size -= l; | |
80 | + if (backup_state.buf_index == BACKUP_CLUSTER_SIZE) { | |
81 | + size_t zero_bytes = 0; | |
884c5e9f | 82 | + int bytes = backup_state.driver->dump( |
5ad5891c DM |
83 | + backup_state.writer, backup_state.vmstate_dev_id, |
84 | + backup_state.buf_cluster_num++, | |
85 | + backup_state.buf, &zero_bytes); | |
86 | + backup_state.buf_index = 0; | |
87 | + if (bytes < 0) { | |
88 | + return -1; | |
89 | + } | |
90 | + } | |
91 | + } | |
92 | + | |
93 | + return ret; | |
94 | +} | |
95 | + | |
96 | +static const QEMUFileOps backup_file_ops = { | |
97 | + .put_buffer = backup_state_put_buffer, | |
98 | + .close = backup_state_close, | |
99 | +}; | |
100 | + | |
101 | +static void coroutine_fn backup_start_savevm(void *opaque) | |
102 | +{ | |
103 | + assert(backup_state.driver); | |
104 | + assert(backup_state.writer); | |
105 | + int ret; | |
106 | + char *err = NULL; | |
107 | + uint64_t remaining; | |
108 | + int64_t maxlen; | |
109 | + MigrationParams params = { | |
110 | + .blk = 0, | |
111 | + .shared = 0 | |
112 | + }; | |
113 | + | |
114 | + int restart = 0; | |
115 | + | |
116 | + QEMUFile *file = qemu_fopen_ops(NULL, &backup_file_ops); | |
117 | + | |
118 | + ret = qemu_savevm_state_begin(file, ¶ms); | |
119 | + if (ret < 0) { | |
120 | + qemu_fclose(file); | |
121 | + err = g_strdup("qemu_savevm_state_begin failed"); | |
122 | + goto abort; | |
123 | + } | |
124 | + | |
125 | + while (1) { | |
126 | + ret = qemu_savevm_state_iterate(file); | |
127 | + remaining = ram_bytes_remaining(); | |
128 | + | |
129 | + if (ret < 0) { | |
130 | + qemu_fclose(file); | |
131 | + err = g_strdup_printf("qemu_savevm_state_iterate error %d", ret); | |
132 | + goto abort; | |
133 | + } | |
134 | + | |
135 | + /* stop the VM if we use too much space, | |
136 | + * or if remaining is just a few MB | |
137 | + */ | |
138 | + maxlen = ram_bytes_total(); | |
139 | + size_t cpos = backup_state.buf_cluster_num * BACKUP_CLUSTER_SIZE; | |
140 | + if ((remaining < 100000) || ((cpos + remaining) >= maxlen)) { | |
141 | + if (runstate_is_running()) { | |
142 | + restart = 1; | |
143 | + vm_stop(RUN_STATE_SAVE_VM); | |
144 | + } | |
145 | + } | |
146 | + | |
147 | + if (ret == 1) { /* finished */ | |
148 | + if (runstate_is_running()) { | |
149 | + restart = 1; | |
150 | + vm_stop(RUN_STATE_SAVE_VM); | |
151 | + } | |
152 | + | |
153 | + ret = qemu_savevm_state_complete(file); | |
154 | + if (ret < 0) { | |
155 | + qemu_fclose(file); | |
156 | + err = g_strdup("qemu_savevm_state_complete error"); | |
157 | + goto abort; | |
158 | + | |
159 | + } else { | |
160 | + if (qemu_fclose(file) < 0) { | |
161 | + error_setg(&backup_state.error, | |
162 | + "backup_start_savevm: qemu_fclose failed"); | |
163 | + goto abort; | |
164 | + } | |
884c5e9f | 165 | + if (backup_state.driver->complete(backup_state.writer, |
5ad5891c | 166 | + backup_state.vmstate_dev_id, 0) < 0) { |
884c5e9f | 167 | + err = g_strdup("backup_start_savevm: complete failed"); |
5ad5891c DM |
168 | + goto abort; |
169 | + } | |
309874bd | 170 | + backup_run_next_job(); |
5ad5891c DM |
171 | + goto out; |
172 | + } | |
173 | + } | |
174 | + } | |
175 | + | |
176 | +out: | |
177 | + if (restart) { | |
178 | + vm_start(); | |
179 | + } | |
180 | + return; | |
181 | + | |
182 | +abort: | |
183 | + backup_state.end_time = time(NULL); | |
184 | + | |
185 | + Error *local_err = NULL; | |
884c5e9f | 186 | + backup_state.driver->close(backup_state.writer, &local_err); |
5ad5891c DM |
187 | + backup_state.writer = NULL; |
188 | + | |
189 | + error_propagate(&backup_state.error, local_err); | |
190 | + | |
191 | + if (err) { | |
192 | + if (!backup_state.error) { | |
193 | + error_setg(&backup_state.error, "%s", err); | |
194 | + } | |
195 | + g_free(err); | |
196 | + } | |
197 | + | |
198 | + goto out; | |
199 | +} | |
200 | + | |
89af8a77 DM |
201 | char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, |
202 | bool has_config_file, const char *config_file, | |
5ad5891c DM |
203 | bool has_devlist, const char *devlist, |
204 | - bool has_speed, int64_t speed, Error **errp) | |
205 | + bool has_speed, int64_t speed, | |
206 | + bool has_state, bool state, Error **errp) | |
207 | { | |
208 | BlockDriverState *bs; | |
209 | Error *local_err = NULL; | |
89af8a77 | 210 | @@ -1528,6 +1694,8 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, |
359f03dc DM |
211 | return NULL; |
212 | } | |
5ad5891c DM |
213 | |
214 | + bool save_state = has_state ? state : false; | |
215 | + | |
216 | /* Todo: try to auto-detect format based on file name */ | |
3055eeb4 | 217 | format = has_format ? format : BACKUP_FORMAT_VMA; |
5ad5891c | 218 | |
89af8a77 | 219 | @@ -1608,6 +1776,22 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, |
5ad5891c DM |
220 | size_t total = 0; |
221 | ||
222 | /* register all devices for vma writer */ | |
223 | + | |
224 | + guint8 vmstate_dev_id = 0; | |
225 | + if (save_state) { | |
226 | + /* Note: we pass ram_bytes_total() for vmstate size | |
227 | + * The backup driver needs to be aware of the fact | |
228 | + * that the real stream size can be different (we do | |
229 | + * not know that size in advance). | |
230 | + */ | |
231 | + size_t ramsize = ram_bytes_total(); | |
884c5e9f | 232 | + vmstate_dev_id = driver->register_stream(writer, "vmstate", ramsize); |
5ad5891c DM |
233 | + if (vmstate_dev_id <= 0) { |
234 | + error_setg(errp, "register vmstate stream failed"); | |
235 | + goto err; | |
236 | + } | |
237 | + } | |
238 | + | |
239 | l = bcblist; | |
240 | while (l) { | |
241 | BackupCB *bcb = l->data; | |
89af8a77 | 242 | @@ -1675,6 +1859,9 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, |
5ad5891c DM |
243 | backup_state.total = total; |
244 | backup_state.transferred = 0; | |
245 | backup_state.zero_bytes = 0; | |
246 | + backup_state.buf_index = 0; | |
247 | + backup_state.buf_cluster_num = 0; | |
248 | + backup_state.vmstate_dev_id = vmstate_dev_id; | |
249 | ||
309874bd DM |
250 | /* Grab a reference so hotplug does not delete the |
251 | * BlockDriverState from underneath us. | |
89af8a77 | 252 | @@ -1686,7 +1873,12 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, |
309874bd DM |
253 | drive_get_ref(drive_get_by_blockdev(bcb->bs)); |
254 | } | |
255 | ||
5ad5891c DM |
256 | - backup_start_jobs(); |
257 | + if (save_state) { | |
258 | + Coroutine *co = qemu_coroutine_create(backup_start_savevm); | |
259 | + qemu_coroutine_enter(co, NULL); | |
260 | + } else { | |
89af8a77 | 261 | + backup_start_jobs(); |
5ad5891c DM |
262 | + } |
263 | ||
264 | return g_strdup(backup_state.uuid_str); | |
265 | ||
266 | diff --git a/hmp.c b/hmp.c | |
89af8a77 | 267 | index b2c1f23..370cdf8 100644 |
5ad5891c DM |
268 | --- a/hmp.c |
269 | +++ b/hmp.c | |
92bf040c | 270 | @@ -1052,7 +1052,8 @@ void hmp_backup(Monitor *mon, const QDict *qdict) |
5ad5891c DM |
271 | Error *errp = NULL; |
272 | ||
89af8a77 | 273 | qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist, |
309874bd DM |
274 | - devlist, qdict_haskey(qdict, "speed"), speed, &errp); |
275 | + devlist, qdict_haskey(qdict, "speed"), speed, false, false, | |
3055eeb4 | 276 | + &errp); |
309874bd | 277 | |
5ad5891c DM |
278 | if (error_is_set(&errp)) { |
279 | monitor_printf(mon, "%s\n", error_get_pretty(errp)); | |
280 | diff --git a/qapi-schema.json b/qapi-schema.json | |
89af8a77 | 281 | index 09ca8ef..1fabb67 100644 |
5ad5891c DM |
282 | --- a/qapi-schema.json |
283 | +++ b/qapi-schema.json | |
89af8a77 DM |
284 | @@ -1885,6 +1885,8 @@ |
285 | # @devlist: #optional list of block device names (separated by ',', ';' | |
286 | # or ':'). By default the backup includes all writable block devices. | |
5ad5891c DM |
287 | # |
288 | +# @state: #optional flag to include vm state | |
289 | +# | |
290 | # Returns: the uuid of the backup job | |
291 | # | |
89af8a77 DM |
292 | # Since: 1.5.0 |
293 | @@ -1892,7 +1894,8 @@ | |
294 | { 'command': 'backup', 'data': { 'backup-file': 'str', | |
295 | '*format': 'BackupFormat', | |
296 | '*config-file': 'str', | |
5ad5891c | 297 | - '*devlist': 'str', '*speed': 'int' }, |
89af8a77 DM |
298 | + '*devlist': 'str', '*speed': 'int', |
299 | + '*state': 'bool' }, | |
5ad5891c DM |
300 | 'returns': 'str' } |
301 | ||
302 | ## | |
303 | -- | |
304 | 1.7.2.5 | |
305 |