]> git.proxmox.com Git - pve-qemu-kvm.git/blame - debian/patches/0006-add-vm-state-to-backups.patch
Two more fixes
[pve-qemu-kvm.git] / debian / patches / 0006-add-vm-state-to-backups.patch
CommitLineData
4dec8413 1From c209554171466e2aa8b1eca92d952429faca5fc6 Mon Sep 17 00:00:00 2001
5ad5891c
DM
2From: Dietmar Maurer <dietmar@proxmox.com>
3Date: Thu, 29 Nov 2012 10:46:49 +0100
884c5e9f 4Subject: [PATCH v5 6/6] 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 +-
89af8a77
DM
11 qapi-schema.json | 5 +-
12 3 files changed, 200 insertions(+), 4 deletions(-)
5ad5891c
DM
13
14diff --git a/blockdev.c b/blockdev.c
884c5e9f 15index 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, &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+ }
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
266diff --git a/hmp.c b/hmp.c
89af8a77 267index 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));
280diff --git a/qapi-schema.json b/qapi-schema.json
89af8a77 281index 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--
3041.7.2.5
305