]> git.proxmox.com Git - pve-qemu-kvm.git/blob - debian/patches/old/0006-add-vm-state-to-backups.patch
d68b4270d1661cdb73d84de51c537b26a80d42f2
[pve-qemu-kvm.git] / debian / patches / old / 0006-add-vm-state-to-backups.patch
1 From c209554171466e2aa8b1eca92d952429faca5fc6 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 v5 6/6] add vm state to backups
5
6
7 Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
8 ---
9 blockdev.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
10 hmp.c | 3 +-
11 qapi-schema.json | 5 +-
12 3 files changed, 200 insertions(+), 4 deletions(-)
13
14 diff --git a/blockdev.c b/blockdev.c
15 index 683f7da..dd20631 100644
16 --- a/blockdev.c
17 +++ b/blockdev.c
18 @@ -22,6 +22,8 @@
19 #include "sysemu/arch_init.h"
20 #include "backup.h"
21 #include "vma.h"
22 +#include "migration/qemu-file.h"
23 +#include "migration/migration.h"
24
25 static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
26
27 @@ -1355,6 +1357,10 @@ static struct GenericBackupState {
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 {
38 @@ -1510,10 +1516,170 @@ static void backup_start_jobs(void)
39 backup_run_next_job();
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(
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);
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(
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(backup_state.writer,
166 + backup_state.vmstate_dev_id, 0) < 0) {
167 + err = g_strdup("backup_start_savevm: complete failed");
168 + goto abort;
169 + }
170 + backup_run_next_job();
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(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 +
201 char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format,
202 bool has_config_file, const char *config_file,
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;
210 @@ -1528,6 +1694,8 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format,
211 return NULL;
212 }
213
214 + bool save_state = has_state ? state : false;
215 +
216 /* Todo: try to auto-detect format based on file name */
217 format = has_format ? format : BACKUP_FORMAT_VMA;
218
219 @@ -1608,6 +1776,22 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format,
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(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;
242 @@ -1675,6 +1859,9 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat 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;
249
250 /* Grab a reference so hotplug does not delete the
251 * BlockDriverState from underneath us.
252 @@ -1686,7 +1873,12 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format,
253 drive_get_ref(drive_get_by_blockdev(bcb->bs));
254 }
255
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 {
261 + backup_start_jobs();
262 + }
263
264 return g_strdup(backup_state.uuid_str);
265
266 diff --git a/hmp.c b/hmp.c
267 index b2c1f23..370cdf8 100644
268 --- a/hmp.c
269 +++ b/hmp.c
270 @@ -1052,7 +1052,8 @@ void hmp_backup(Monitor *mon, const QDict *qdict)
271 Error *errp = NULL;
272
273 qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
274 - devlist, qdict_haskey(qdict, "speed"), speed, &errp);
275 + devlist, qdict_haskey(qdict, "speed"), speed, false, false,
276 + &errp);
277
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
281 index 09ca8ef..1fabb67 100644
282 --- a/qapi-schema.json
283 +++ b/qapi-schema.json
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.
287 #
288 +# @state: #optional flag to include vm state
289 +#
290 # Returns: the uuid of the backup job
291 #
292 # Since: 1.5.0
293 @@ -1892,7 +1894,8 @@
294 { 'command': 'backup', 'data': { 'backup-file': 'str',
295 '*format': 'BackupFormat',
296 '*config-file': 'str',
297 - '*devlist': 'str', '*speed': 'int' },
298 + '*devlist': 'str', '*speed': 'int',
299 + '*state': 'bool' },
300 'returns': 'str' }
301
302 ##
303 --
304 1.7.2.5
305