]>
Commit | Line | Data |
---|---|---|
7378b84a DM |
1 | Index: new/qapi-schema.json |
2 | =================================================================== | |
5db17e8c DM |
3 | --- new.orig/qapi-schema.json 2012-09-07 07:41:45.000000000 +0200 |
4 | +++ new/qapi-schema.json 2012-09-13 09:22:06.000000000 +0200 | |
71788d62 | 5 | @@ -2493,3 +2493,12 @@ |
7378b84a DM |
6 | # Since: 1.2.0 |
7 | ## | |
8 | { 'command': 'query-target', 'returns': 'TargetInfo' } | |
9 | + | |
10 | + | |
71788d62 | 11 | +{ 'command': 'snapshot-start' 'data': { '*statefile': 'str' } } |
7378b84a DM |
12 | + |
13 | +{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } } | |
14 | + | |
71788d62 DM |
15 | +{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } } |
16 | + | |
7378b84a DM |
17 | +{ 'command': 'snapshot-end' } |
18 | Index: new/qmp.c | |
19 | =================================================================== | |
5db17e8c | 20 | --- new.orig/qmp.c 2012-09-07 07:41:45.000000000 +0200 |
49adae7d DM |
21 | +++ new/qmp.c 2012-09-14 14:06:56.000000000 +0200 |
22 | @@ -479,3 +479,224 @@ | |
7378b84a DM |
23 | return arch_query_cpu_definitions(errp); |
24 | } | |
25 | ||
26 | +static struct SnapshotState { | |
27 | + int in_progress; | |
28 | + int saved_vm_running; | |
29 | +} snap_state; | |
30 | + | |
49adae7d DM |
31 | +static int block_put_buffer(void *opaque, const uint8_t *buf, |
32 | + int64_t pos, int size) | |
33 | +{ | |
34 | + bdrv_pwrite(opaque, pos, buf, size); | |
35 | + return size; | |
36 | +} | |
37 | + | |
38 | +static int bdrv_fclose(void *opaque) | |
39 | +{ | |
40 | + return bdrv_flush(opaque); | |
41 | +} | |
42 | + | |
71788d62 | 43 | +void qmp_snapshot_start(bool has_statefile, const char *statefile, Error **errp) |
7378b84a | 44 | +{ |
49adae7d DM |
45 | + BlockDriverState *bs = NULL; |
46 | + BlockDriver *drv = NULL; | |
47 | + int bdrv_oflags = BDRV_O_NOCACHE | BDRV_O_RDWR; | |
7378b84a DM |
48 | + QEMUFile *f; |
49 | + int ret; | |
50 | + | |
51 | + if (snap_state.in_progress) { | |
52 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
53 | + "VM snapshot already started\n"); | |
54 | + return; | |
55 | + } | |
e3a98889 | 56 | + |
7378b84a DM |
57 | + snap_state.in_progress = 1; |
58 | + | |
59 | + snap_state.saved_vm_running = runstate_is_running(); | |
60 | + | |
61 | + vm_stop(RUN_STATE_SAVE_VM); | |
62 | + | |
71788d62 DM |
63 | + if (!has_statefile) |
64 | + return; | |
65 | + | |
49adae7d DM |
66 | + /* Open the image */ |
67 | + bs = bdrv_new("vmstate"); | |
68 | + ret = bdrv_open(bs, statefile, bdrv_oflags, drv); | |
69 | + if (ret < 0) { | |
70 | + error_set(errp, QERR_OPEN_FILE_FAILED, statefile); | |
71 | + goto restart; | |
72 | + } | |
73 | + | |
74 | + f = qemu_fopen_ops(bs, block_put_buffer, NULL, bdrv_fclose, | |
75 | + NULL, NULL, NULL); | |
7378b84a DM |
76 | + if (!f) { |
77 | + error_set(errp, QERR_OPEN_FILE_FAILED, statefile); | |
e3a98889 | 78 | + goto restart; |
7378b84a DM |
79 | + } |
80 | + | |
e3a98889 | 81 | + ret = qemu_savevm_state(f); |
49adae7d DM |
82 | + |
83 | + bdrv_truncate(bs, qemu_ftell(f)); // ignore errors | |
84 | + | |
7378b84a | 85 | + qemu_fclose(f); |
49adae7d | 86 | + |
7378b84a DM |
87 | + if (ret < 0) { |
88 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
89 | + "Error %d while writing VM state\n", ret); | |
49adae7d | 90 | + goto restart; |
e3a98889 | 91 | + } |
49adae7d DM |
92 | +end: |
93 | + if (bs) | |
94 | + bdrv_delete(bs); | |
e3a98889 DM |
95 | + |
96 | + return; | |
97 | + | |
98 | +restart: | |
99 | + | |
100 | + snap_state.in_progress = 0; | |
101 | + | |
102 | + if (snap_state.saved_vm_running) { | |
103 | + vm_start(); | |
7378b84a | 104 | + } |
49adae7d DM |
105 | + |
106 | + goto end; | |
7378b84a DM |
107 | +} |
108 | + | |
109 | +void qmp_snapshot_end(Error **errp) | |
110 | +{ | |
111 | + if (!snap_state.in_progress) { | |
112 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
113 | + "VM snapshot not started\n"); | |
114 | + return; | |
115 | + } | |
116 | + snap_state.in_progress = 0; | |
117 | + | |
118 | + if (snap_state.saved_vm_running) { | |
119 | + vm_start(); | |
120 | + } | |
121 | +} | |
122 | + | |
123 | +/* Fixme: Copied from savevm */ | |
124 | +static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, | |
125 | + const char *name) | |
126 | +{ | |
127 | + QEMUSnapshotInfo *sn_tab, *sn; | |
128 | + int nb_sns, i, ret; | |
129 | + | |
130 | + ret = -ENOENT; | |
131 | + nb_sns = bdrv_snapshot_list(bs, &sn_tab); | |
132 | + if (nb_sns < 0) | |
133 | + return ret; | |
134 | + for(i = 0; i < nb_sns; i++) { | |
135 | + sn = &sn_tab[i]; | |
136 | + if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { | |
137 | + *sn_info = *sn; | |
138 | + ret = 0; | |
139 | + break; | |
140 | + } | |
141 | + } | |
142 | + g_free(sn_tab); | |
143 | + return ret; | |
144 | +} | |
145 | + | |
146 | +void qmp_snapshot_drive(const char *device, const char *name, Error **errp) | |
147 | +{ | |
148 | + BlockDriverState *bs; | |
149 | + QEMUSnapshotInfo sn1, *sn = &sn1; | |
150 | + int ret; | |
151 | +#ifdef _WIN32 | |
152 | + struct _timeb tb; | |
153 | +#else | |
154 | + struct timeval tv; | |
155 | +#endif | |
156 | + | |
157 | + if (!snap_state.in_progress) { | |
158 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
159 | + "VM snapshot not started\n"); | |
160 | + return; | |
161 | + } | |
162 | + | |
163 | + bs = bdrv_find(device); | |
164 | + if (!bs) { | |
165 | + error_set(errp, QERR_DEVICE_NOT_FOUND, device); | |
166 | + return; | |
167 | + } | |
168 | + | |
169 | + if (!bdrv_is_inserted(bs)) { | |
170 | + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); | |
171 | + return; | |
172 | + } | |
173 | + | |
174 | + if (bdrv_is_read_only(bs)) { | |
175 | + error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); | |
176 | + return; | |
177 | + } | |
178 | + | |
179 | + if (!bdrv_can_snapshot(bs)) { | |
180 | + error_set(errp, QERR_NOT_SUPPORTED); | |
181 | + return; | |
182 | + } | |
183 | + | |
184 | + if (bdrv_snapshot_find(bs, sn, name) >= 0) { | |
185 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, | |
186 | + "snapshot '%s' already exists", name); | |
187 | + return; | |
188 | + } | |
189 | + | |
190 | + sn = &sn1; | |
191 | + memset(sn, 0, sizeof(*sn)); | |
192 | + | |
193 | +#ifdef _WIN32 | |
194 | + _ftime(&tb); | |
195 | + sn->date_sec = tb.time; | |
196 | + sn->date_nsec = tb.millitm * 1000000; | |
197 | +#else | |
198 | + gettimeofday(&tv, NULL); | |
199 | + sn->date_sec = tv.tv_sec; | |
200 | + sn->date_nsec = tv.tv_usec * 1000; | |
201 | +#endif | |
202 | + sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock); | |
203 | + | |
204 | + pstrcpy(sn->name, sizeof(sn->name), name); | |
205 | + | |
206 | + sn->vm_state_size = 0; /* do not save state */ | |
207 | + | |
208 | + ret = bdrv_snapshot_create(bs, sn); | |
209 | + if (ret < 0) { | |
210 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "Error while creating snapshot on '%s'\n", device); | |
211 | + return; | |
212 | + } | |
213 | +} | |
71788d62 DM |
214 | + |
215 | +void qmp_delete_drive_snapshot(const char *device, const char *name, Error **errp) | |
216 | +{ | |
217 | + BlockDriverState *bs; | |
218 | + QEMUSnapshotInfo sn1, *sn = &sn1; | |
219 | + int ret; | |
220 | + | |
221 | + bs = bdrv_find(device); | |
222 | + if (!bs) { | |
223 | + error_set(errp, QERR_DEVICE_NOT_FOUND, device); | |
224 | + return; | |
225 | + } | |
226 | + if (bdrv_is_read_only(bs)) { | |
227 | + error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); | |
228 | + return; | |
229 | + } | |
230 | + | |
231 | + if (!bdrv_can_snapshot(bs)) { | |
232 | + error_set(errp, QERR_NOT_SUPPORTED); | |
233 | + return; | |
234 | + } | |
235 | + | |
236 | + if (bdrv_snapshot_find(bs, sn, name) < 0) { | |
237 | + /* return success if snapshot does not exists */ | |
238 | + return; | |
239 | + } | |
240 | + | |
241 | + ret = bdrv_snapshot_delete(bs, name); | |
242 | + if (ret < 0) { | |
243 | + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "Error while deleting snapshot on '%s'\n", device); | |
244 | + return; | |
245 | + } | |
246 | +} | |
7378b84a DM |
247 | Index: new/qmp-commands.hx |
248 | =================================================================== | |
5db17e8c DM |
249 | --- new.orig/qmp-commands.hx 2012-09-07 07:41:45.000000000 +0200 |
250 | +++ new/qmp-commands.hx 2012-09-13 09:30:33.000000000 +0200 | |
71788d62 | 251 | @@ -2514,3 +2514,27 @@ |
7378b84a DM |
252 | .args_type = "", |
253 | .mhandler.cmd_new = qmp_marshal_input_query_target, | |
254 | }, | |
255 | + | |
256 | + { | |
257 | + .name = "snapshot-start", | |
5db17e8c | 258 | + .args_type = "statefile:s?", |
7378b84a DM |
259 | + .mhandler.cmd_new = qmp_marshal_input_snapshot_start, |
260 | + }, | |
261 | + | |
262 | + { | |
263 | + .name = "snapshot-drive", | |
264 | + .args_type = "device:s,name:s", | |
265 | + .mhandler.cmd_new = qmp_marshal_input_snapshot_drive, | |
266 | + }, | |
267 | + | |
268 | + { | |
71788d62 DM |
269 | + .name = "delete-drive-snapshot", |
270 | + .args_type = "device:s,name:s", | |
271 | + .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot, | |
272 | + }, | |
273 | + | |
274 | + { | |
7378b84a DM |
275 | + .name = "snapshot-end", |
276 | + .args_type = "", | |
277 | + .mhandler.cmd_new = qmp_marshal_input_snapshot_end, | |
278 | + }, | |
279 | Index: new/hmp.c | |
280 | =================================================================== | |
5db17e8c DM |
281 | --- new.orig/hmp.c 2012-09-07 07:41:45.000000000 +0200 |
282 | +++ new/hmp.c 2012-09-13 09:22:06.000000000 +0200 | |
71788d62 | 283 | @@ -1102,3 +1102,40 @@ |
7378b84a DM |
284 | qmp_closefd(fdname, &errp); |
285 | hmp_handle_error(mon, &errp); | |
286 | } | |
287 | + | |
288 | +void hmp_snapshot_start(Monitor *mon, const QDict *qdict) | |
289 | +{ | |
290 | + Error *errp = NULL; | |
71788d62 | 291 | + const char *statefile = qdict_get_try_str(qdict, "statefile"); |
7378b84a | 292 | + |
71788d62 | 293 | + qmp_snapshot_start(statefile != NULL, statefile, &errp); |
7378b84a DM |
294 | + hmp_handle_error(mon, &errp); |
295 | +} | |
296 | + | |
297 | +void hmp_snapshot_drive(Monitor *mon, const QDict *qdict) | |
298 | +{ | |
299 | + Error *errp = NULL; | |
300 | + const char *name = qdict_get_str(qdict, "name"); | |
301 | + const char *device = qdict_get_str(qdict, "device"); | |
302 | + | |
303 | + qmp_snapshot_drive(device, name, &errp); | |
304 | + hmp_handle_error(mon, &errp); | |
305 | +} | |
306 | + | |
71788d62 DM |
307 | +void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict) |
308 | +{ | |
309 | + Error *errp = NULL; | |
310 | + const char *name = qdict_get_str(qdict, "name"); | |
311 | + const char *device = qdict_get_str(qdict, "device"); | |
312 | + | |
313 | + qmp_delete_drive_snapshot(device, name, &errp); | |
314 | + hmp_handle_error(mon, &errp); | |
315 | +} | |
316 | + | |
7378b84a DM |
317 | +void hmp_snapshot_end(Monitor *mon, const QDict *qdict) |
318 | +{ | |
319 | + Error *errp = NULL; | |
320 | + | |
321 | + qmp_snapshot_end(&errp); | |
322 | + hmp_handle_error(mon, &errp); | |
323 | +} | |
324 | Index: new/hmp.h | |
325 | =================================================================== | |
5db17e8c DM |
326 | --- new.orig/hmp.h 2012-09-07 07:41:45.000000000 +0200 |
327 | +++ new/hmp.h 2012-09-13 09:22:06.000000000 +0200 | |
71788d62 | 328 | @@ -71,5 +71,9 @@ |
7378b84a DM |
329 | void hmp_netdev_del(Monitor *mon, const QDict *qdict); |
330 | void hmp_getfd(Monitor *mon, const QDict *qdict); | |
331 | void hmp_closefd(Monitor *mon, const QDict *qdict); | |
332 | +void hmp_snapshot_start(Monitor *mon, const QDict *qdict); | |
333 | +void hmp_snapshot_drive(Monitor *mon, const QDict *qdict); | |
71788d62 | 334 | +void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict); |
7378b84a DM |
335 | +void hmp_snapshot_end(Monitor *mon, const QDict *qdict); |
336 | ||
337 | #endif | |
338 | Index: new/hmp-commands.hx | |
339 | =================================================================== | |
5db17e8c DM |
340 | --- new.orig/hmp-commands.hx 2012-09-07 07:41:45.000000000 +0200 |
341 | +++ new/hmp-commands.hx 2012-09-13 09:22:06.000000000 +0200 | |
71788d62 | 342 | @@ -1494,3 +1494,35 @@ |
7378b84a DM |
343 | STEXI |
344 | @end table | |
345 | ETEXI | |
346 | + | |
347 | + { | |
348 | + .name = "snapshot-start", | |
71788d62 DM |
349 | + .args_type = "statefile:s?", |
350 | + .params = "[statefile]", | |
7378b84a DM |
351 | + .help = "Prepare for snapshot and halt VM. Save VM state to statefile.", |
352 | + .mhandler.cmd = hmp_snapshot_start, | |
353 | + }, | |
354 | + | |
7378b84a DM |
355 | + { |
356 | + .name = "snapshot-drive", | |
357 | + .args_type = "device:s,name:s", | |
358 | + .params = "device name", | |
359 | + .help = "Create internal snapshot.", | |
360 | + .mhandler.cmd = hmp_snapshot_drive, | |
361 | + }, | |
362 | + | |
363 | + { | |
71788d62 DM |
364 | + .name = "delete-drive-snapshot", |
365 | + .args_type = "device:s,name:s", | |
366 | + .params = "device name", | |
367 | + .help = "Delete internal snapshot.", | |
368 | + .mhandler.cmd = hmp_delete_drive_snapshot, | |
369 | + }, | |
370 | + | |
371 | + { | |
7378b84a DM |
372 | + .name = "snapshot-end", |
373 | + .args_type = "", | |
374 | + .params = "", | |
375 | + .help = "Resume VM after snaphot.", | |
376 | + .mhandler.cmd = hmp_snapshot_end, | |
377 | + }, | |
e3a98889 DM |
378 | Index: new/savevm.c |
379 | =================================================================== | |
5db17e8c | 380 | --- new.orig/savevm.c 2012-09-07 07:41:45.000000000 +0200 |
49adae7d DM |
381 | +++ new/savevm.c 2012-09-14 11:32:17.000000000 +0200 |
382 | @@ -404,7 +404,7 @@ | |
383 | return bdrv_flush(opaque); | |
384 | } | |
385 | ||
386 | -static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) | |
387 | +QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) | |
388 | { | |
389 | if (is_writable) | |
390 | return qemu_fopen_ops(bs, block_put_buffer, NULL, bdrv_fclose, | |
e3a98889 DM |
391 | @@ -1724,7 +1724,7 @@ |
392 | } | |
393 | } | |
394 | ||
395 | -static int qemu_savevm_state(QEMUFile *f) | |
396 | +int qemu_savevm_state(QEMUFile *f) | |
397 | { | |
398 | int ret; | |
399 | MigrationParams params = { | |
400 | Index: new/sysemu.h | |
401 | =================================================================== | |
5db17e8c DM |
402 | --- new.orig/sysemu.h 2012-09-07 07:41:46.000000000 +0200 |
403 | +++ new/sysemu.h 2012-09-13 09:22:06.000000000 +0200 | |
e3a98889 DM |
404 | @@ -83,6 +83,7 @@ |
405 | int qemu_savevm_state_iterate(QEMUFile *f); | |
406 | int qemu_savevm_state_complete(QEMUFile *f); | |
407 | void qemu_savevm_state_cancel(QEMUFile *f); | |
408 | +int qemu_savevm_state(QEMUFile *f); | |
409 | int qemu_loadvm_state(QEMUFile *f); | |
410 | ||
411 | /* SLIRP */ | |
49adae7d DM |
412 | Index: new/qemu-file.h |
413 | =================================================================== | |
414 | --- new.orig/qemu-file.h 2012-09-14 11:51:31.000000000 +0200 | |
415 | +++ new/qemu-file.h 2012-09-14 11:51:47.000000000 +0200 | |
416 | @@ -68,6 +68,7 @@ | |
417 | QEMUFile *qemu_fopen(const char *filename, const char *mode); | |
418 | QEMUFile *qemu_fdopen(int fd, const char *mode); | |
419 | QEMUFile *qemu_fopen_socket(int fd); | |
420 | +QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable); | |
421 | QEMUFile *qemu_popen(FILE *popen_file, const char *mode); | |
422 | QEMUFile *qemu_popen_cmd(const char *command, const char *mode); | |
423 | int qemu_stdio_fd(QEMUFile *f); |