1 From d4d33a401830ce903c598c04fb9352d91fda5399 Mon Sep 17 00:00:00 2001
2 From: Dietmar Maurer <dietmar@proxmox.com>
3 Date: Thu, 29 Nov 2012 10:46:49 +0100
4 Subject: [PATCH v3 6/6] add vm state to backups
7 Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
9 blockdev.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
11 qapi-schema.json | 6 +-
12 3 files changed, 199 insertions(+), 5 deletions(-)
14 diff --git a/blockdev.c b/blockdev.c
15 index 139e350..087af8c 100644
19 #include "arch_init.h"
22 +#include "qemu-file.h"
23 +#include "migration.h"
25 static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
27 @@ -1338,6 +1340,10 @@ static struct GenericBackupState {
31 + unsigned char buf[BACKUP_CLUSTER_SIZE];
33 + size_t buf_cluster_num;
34 + guint8 vmstate_dev_id;
37 typedef struct BackupCB {
38 @@ -1469,10 +1475,170 @@ static void backup_start_jobs(void)
42 +static int backup_state_close(void *opaque)
44 + if (!backup_state.buf_index) {
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);
53 + int bytes = backup_state.driver->dump_cb(
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;
59 + return bytes < 0 ? -1 : 0;
62 +static int backup_state_put_buffer(void *opaque, const uint8_t *buf,
63 + int64_t pos, int size)
65 + assert(backup_state.driver);
66 + assert(backup_state.writer);
67 + assert(backup_state.driver->dump_cb);
69 + /* Note: our backup driver expects to get whole clusters (64KB) */
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;
80 + if (backup_state.buf_index == BACKUP_CLUSTER_SIZE) {
81 + size_t zero_bytes = 0;
82 + int bytes = backup_state.driver->dump_cb(
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;
96 +static const QEMUFileOps backup_file_ops = {
97 + .put_buffer = backup_state_put_buffer,
98 + .close = backup_state_close,
101 +static void coroutine_fn backup_start_savevm(void *opaque)
103 + assert(backup_state.driver);
104 + assert(backup_state.writer);
107 + uint64_t remaining;
109 + MigrationParams params = {
116 + QEMUFile *file = qemu_fopen_ops(NULL, &backup_file_ops);
118 + ret = qemu_savevm_state_begin(file, ¶ms);
121 + err = g_strdup("qemu_savevm_state_begin failed");
126 + ret = qemu_savevm_state_iterate(file);
127 + remaining = ram_bytes_remaining();
131 + err = g_strdup_printf("qemu_savevm_state_iterate error %d", ret);
135 + /* stop the VM if we use too much space,
136 + * or if remaining is just a few MB
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()) {
143 + vm_stop(RUN_STATE_SAVE_VM);
147 + if (ret == 1) { /* finished */
148 + if (runstate_is_running()) {
150 + vm_stop(RUN_STATE_SAVE_VM);
153 + ret = qemu_savevm_state_complete(file);
156 + err = g_strdup("qemu_savevm_state_complete error");
160 + if (qemu_fclose(file) < 0) {
161 + error_setg(&backup_state.error,
162 + "backup_start_savevm: qemu_fclose failed");
165 + if (backup_state.driver->complete_cb(backup_state.writer,
166 + backup_state.vmstate_dev_id, 0) < 0) {
167 + err = g_strdup("backup_start_savevm: complete_cb failed");
170 + backup_start_jobs();
183 + backup_state.end_time = time(NULL);
185 + Error *local_err = NULL;
186 + backup_state.driver->close_cb(backup_state.writer, &local_err);
187 + backup_state.writer = NULL;
189 + error_propagate(&backup_state.error, local_err);
192 + if (!backup_state.error) {
193 + error_setg(&backup_state.error, "%s", err);
201 char *qmp_backup(const char *backupfile, bool has_format, const char *format,
202 bool has_config_filename, const char *config_filename,
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)
208 BlockDriverState *bs;
209 Error *local_err = NULL;
210 @@ -1481,6 +1647,8 @@ char *qmp_backup(const char *backupfile, bool has_format, const char *format,
212 GList *bcblist = NULL;
214 + bool save_state = has_state ? state : false;
216 /* Todo: try to auto-detect format based on file name */
217 format = has_format ? format : "vma";
219 @@ -1562,6 +1730,22 @@ char *qmp_backup(const char *backupfile, bool has_format, const char *format,
222 /* register all devices for vma writer */
224 + guint8 vmstate_dev_id = 0;
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).
231 + size_t ramsize = ram_bytes_total();
232 + vmstate_dev_id = driver->register_stream_cb(writer, "vmstate", ramsize);
233 + if (vmstate_dev_id <= 0) {
234 + error_setg(errp, "register vmstate stream failed");
241 BackupCB *bcb = l->data;
242 @@ -1625,8 +1809,16 @@ char *qmp_backup(const char *backupfile, bool has_format, const char *format,
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;
250 - backup_start_jobs();
252 + Coroutine *co = qemu_coroutine_create(backup_start_savevm);
253 + qemu_coroutine_enter(co, NULL);
255 + backup_start_jobs();
258 return g_strdup(backup_state.uuid_str);
260 diff --git a/hmp.c b/hmp.c
261 index 600792f..e92bba9 100644
264 @@ -1031,7 +1031,7 @@ void hmp_backup(Monitor *mon, const QDict *qdict)
267 qmp_backup(backupfile, false, NULL, false, NULL, !!devlist, devlist,
268 - qdict_haskey(qdict, "speed"), speed, &errp);
269 + qdict_haskey(qdict, "speed"), speed, false, false, &errp);
271 if (error_is_set(&errp)) {
272 monitor_printf(mon, "%s\n", error_get_pretty(errp));
273 diff --git a/qapi-schema.json b/qapi-schema.json
274 index 71e0ed9..2d7ecaa 100644
275 --- a/qapi-schema.json
276 +++ b/qapi-schema.json
277 @@ -1812,13 +1812,15 @@
279 # @speed: #optional the maximum speed, in bytes per second
281 +# @state: #optional flag to include vm state
283 # Returns: the uuid of the backup job
287 { 'command': 'backup', 'data': { 'backupfile': 'str', '*format': 'str',
288 - '*config-filename': 'str',
289 - '*devlist': 'str', '*speed': 'int' },
290 + '*config-filename': 'str', '*devlist': 'str',
291 + '*speed': 'int', '*state': 'bool' },