]> git.proxmox.com Git - pve-qemu-kvm.git/blame - debian/patches/0006-add-vm-state-to-backups.patch
update backup patches
[pve-qemu-kvm.git] / debian / patches / 0006-add-vm-state-to-backups.patch
CommitLineData
309874bd 1From 9c8e193692a2f96116793ddbe3fa89650985f272 Mon Sep 17 00:00:00 2001
5ad5891c
DM
2From: Dietmar Maurer <dietmar@proxmox.com>
3Date: Thu, 29 Nov 2012 10:46:49 +0100
309874bd 4Subject: [PATCH v3 6/7] add vm state to backups
5ad5891c
DM
5
6
7Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
8---
9 blockdev.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
309874bd 10 hmp.c | 3 +-
5ad5891c 11 qapi-schema.json | 6 +-
309874bd 12 3 files changed, 200 insertions(+), 5 deletions(-)
5ad5891c
DM
13
14diff --git a/blockdev.c b/blockdev.c
309874bd 15index 80cb04d..96d7a3b 100644
5ad5891c
DM
16--- a/blockdev.c
17+++ b/blockdev.c
18@@ -22,6 +22,8 @@
19 #include "arch_init.h"
20 #include "backup.h"
21 #include "vma.h"
22+#include "qemu-file.h"
23+#include "migration.h"
24
25 static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
26
309874bd 27@@ -1340,6 +1342,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 {
309874bd 38@@ -1479,10 +1485,170 @@ static void backup_start_jobs(void)
5ad5891c
DM
39 }
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+ }
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;
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);
67+ assert(backup_state.driver->dump_cb);
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;
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;
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, &params);
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+ }
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");
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;
186+ backup_state.driver->close_cb(backup_state.writer, &local_err);
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+
3055eeb4 201 char *qmp_backup(const char *backupfile, bool has_format, BackupFormat format,
5ad5891c
DM
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)
207 {
208 BlockDriverState *bs;
209 Error *local_err = NULL;
309874bd 210@@ -1491,6 +1657,8 @@ char *qmp_backup(const char *backupfile, bool has_format, BackupFormat format,
5ad5891c
DM
211 gchar **devs = NULL;
212 GList *bcblist = NULL;
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
309874bd 219@@ -1572,6 +1740,22 @@ char *qmp_backup(const char *backupfile, 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();
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");
235+ goto err;
236+ }
237+ }
238+
239 l = bcblist;
240 while (l) {
241 BackupCB *bcb = l->data;
309874bd 242@@ -1635,6 +1819,9 @@ char *qmp_backup(const char *backupfile, 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.
252@@ -1646,7 +1833,12 @@ char *qmp_backup(const char *backupfile, bool has_format, BackupFormat format,
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 {
309874bd 261+ backup_start_jobs();
5ad5891c
DM
262+ }
263
264 return g_strdup(backup_state.uuid_str);
265
266diff --git a/hmp.c b/hmp.c
309874bd 267index 27fd421..56e1cdc 100644
5ad5891c
DM
268--- a/hmp.c
269+++ b/hmp.c
309874bd 270@@ -1031,7 +1031,8 @@ void hmp_backup(Monitor *mon, const QDict *qdict)
5ad5891c
DM
271 Error *errp = NULL;
272
309874bd
DM
273 qmp_backup(backupfile, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
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));
280diff --git a/qapi-schema.json b/qapi-schema.json
3055eeb4 281index 2d3699b..3f4889e 100644
5ad5891c
DM
282--- a/qapi-schema.json
283+++ b/qapi-schema.json
3055eeb4 284@@ -1822,13 +1822,15 @@
5ad5891c
DM
285 #
286 # @speed: #optional the maximum speed, in bytes per second
287 #
288+# @state: #optional flag to include vm state
289+#
290 # Returns: the uuid of the backup job
291 #
292 # Since: 1.4.0
293 ##
3055eeb4 294 { 'command': 'backup', 'data': { 'backupfile': 'str', '*format': 'BackupFormat',
5ad5891c
DM
295- '*config-filename': 'str',
296- '*devlist': 'str', '*speed': 'int' },
297+ '*config-filename': 'str', '*devlist': 'str',
298+ '*speed': 'int', '*state': 'bool' },
299 'returns': 'str' }
300
301 ##
302--
3031.7.2.5
304