]> git.proxmox.com Git - pve-qemu.git/blame - debian/patches/pve/0030-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch
update submodule and patches to QEMU 8.1.2
[pve-qemu.git] / debian / patches / pve / 0030-PVE-Backup-Proxmox-backup-patches-for-QEMU.patch
CommitLineData
6402d961
TL
1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: Dietmar Maurer <dietmar@proxmox.com>
83faa3fe 3Date: Mon, 6 Apr 2020 12:16:59 +0200
db5d2a4b
FE
4Subject: [PATCH] PVE-Backup: Proxmox backup patches for QEMU
5MIME-Version: 1.0
6Content-Type: text/plain; charset=UTF-8
7Content-Transfer-Encoding: 8bit
8
9For PBS, using dirty bitmaps is supported via QEMU's
10MIRROR_SYNC_MODE_BITMAP. When the feature is used, the data-write
11callback is only executed for any changed chunks, the PBS rust code
12will reuse chunks from the previous index for everything it doesn't
13receive if reuse_index is true. On error or cancellation, all dirty
14bitmaps are removed to ensure consistency.
15
16By using a JobTxn, we can sync dirty bitmaps only when *all* jobs were
17successful - meaning we don't need to remove them when the backup
18fails, since QEMU's BITMAP_SYNC_MODE_ON_SUCCESS will now handle that
19for us. A sequential transaction is used, so drives will be backed up
20one after the other.
21
22The backup and backup-cancel QMP calls are coroutines. This has the
23benefit that calls are asynchronous to the main loop, i.e. long
24running operations like connecting to a PBS server will no longer hang
25the VM.
26
27backup_job_create() and job_cancel_sync() cannot be run from a
28coroutine and requires an acuqired AioContext, so the job creation and
29canceling are extracted as bottom halves and called from the
30respective QMP coroutines.
31
32To communicate the finishing state, a dedicated property is used for
33query-backup: 'finishing'. A dedicated state is explicitly not used,
34since that would break compatibility with older qemu-server versions.
35
36The first call to job_cancel_sync() will cancel and free all jobs in
37the transaction, but it is necessary to pick a job that is:
381. still referenced. For this, there is a job_ref directly after job
39creation paired with a job_unref in cleanup paths.
402. not yet finalized. In job_cancel_bh(), the first job that's not
41completed yet is used. This is not necessarily the first job in the
42list, because pvebackup_co_complete_stream() might not yet have
43removed a completed job when job_cancel_bh() runs. Why even bother
44with the bottom half at all and not use job_cancel() in
45qmp_backup_cancel() directly? The reason is that qmp_backup_cancel()
46is a coroutine, so it will hang when reaching AIO_WAIT_WHILE() and
47job_cancel() might end up calling that.
48
49Regarding BackupPerf performance settings. For now, only the
50max-workers setting is exposed, because:
511. use-copy-range would need to be implemented in backup-dump and the
52feature was actually turned off by default in QEMU itself, because it
53didn't provide the expected benefit, see commit 6a30f663d4 ("qapi:
54backup: disable copy_range by default").
552. max-chunk: enforced to be at least the backup cluster size (4 MiB
56for PBS) and otherwise maximum of source and target cluster size.
57And block-copy has a maximum buffer size of 1 MiB, so setting a larger
58max-chunk doesn't even have an effect. To make the setting sensibly
59usable the check would need to be removed and optionally the
60block-copy max buffer size would need to be bumped. I tried doing just
61that, and tested different source/target combinations with different
62max-chunk settings, but there were no noticable improvements over the
63default "unlimited" (resulting in 1 MiB for block-copy).
6402d961 64
0c893fd8 65Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
db5d2a4b
FE
66[SR: Add dirty-bitmap tracking for incremental backups
67 Add query_proxmox_support and query-pbs-bitmap-info QMP calls
68 Use a transaction to synchronize job states
69 Co-routine and async-related improvements
70 Improve finishing backups/cleanups
71 Various other improvements]
0c893fd8 72Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
db5d2a4b
FE
73[FG: add master key support]
74Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
ddbf7a87 75Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
db5d2a4b
FE
76[WB: add PBS namespace support]
77Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
d03e1b3c 78[FE: add new force parameter to job_cancel_sync calls
bf251437 79 adapt for new job lock mechanism replacing AioContext locks
db5d2a4b
FE
80 adapt to QAPI changes
81 improve canceling
5f9cb29c 82 allow passing max-workers setting
0cffb504
FE
83 use malloc_trim after backup
84 create jobs in a drained section]
d03e1b3c 85Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
6402d961 86---
10e10933
FE
87 block/backup-dump.c | 10 +-
88 block/meson.build | 5 +
89 block/monitor/block-hmp-cmds.c | 39 ++
90 blockdev.c | 1 +
91 hmp-commands-info.hx | 14 +
92 hmp-commands.hx | 29 +
93 include/block/block_int-common.h | 2 +-
94 include/monitor/hmp.h | 3 +
95 meson.build | 1 +
96 monitor/hmp-cmds.c | 72 ++
97 proxmox-backup-client.c | 146 ++++
98 proxmox-backup-client.h | 60 ++
99 pve-backup.c | 1067 ++++++++++++++++++++++++++++++
100 qapi/block-core.json | 229 +++++++
101 qapi/common.json | 14 +
102 qapi/machine.json | 16 +-
103 16 files changed, 1690 insertions(+), 18 deletions(-)
6402d961
TL
104 create mode 100644 proxmox-backup-client.c
105 create mode 100644 proxmox-backup-client.h
106 create mode 100644 pve-backup.c
107
10e10933
FE
108diff --git a/block/backup-dump.c b/block/backup-dump.c
109index 232a094426..e46abf1070 100644
110--- a/block/backup-dump.c
111+++ b/block/backup-dump.c
112@@ -9,6 +9,8 @@
113 */
114
115 #include "qemu/osdep.h"
116+
117+#include "qapi/qmp/qdict.h"
118 #include "qom/object_interfaces.h"
119 #include "block/block_int.h"
120
121@@ -141,7 +143,7 @@ static void bdrv_backup_dump_init(void)
122 block_init(bdrv_backup_dump_init);
123
124
125-BlockDriverState *bdrv_backup_dump_create(
126+BlockDriverState *coroutine_fn bdrv_co_backup_dump_create(
127 int dump_cb_block_size,
128 uint64_t byte_size,
129 BackupDumpFunc *dump_cb,
130@@ -149,9 +151,11 @@ BlockDriverState *bdrv_backup_dump_create(
131 Error **errp)
132 {
133 BDRVBackupDumpState *state;
134- BlockDriverState *bs = bdrv_new_open_driver(
135- &bdrv_backup_dump_drive, NULL, BDRV_O_RDWR, errp);
136
137+ QDict *options = qdict_new();
138+ qdict_put_str(options, "driver", "backup-dump-drive");
139+
140+ BlockDriverState *bs = bdrv_co_open(NULL, NULL, options, BDRV_O_RDWR, errp);
141 if (!bs) {
142 return NULL;
143 }
817b7667 144diff --git a/block/meson.build b/block/meson.build
10e10933 145index 6fde9f7dcd..6d468f89e5 100644
817b7667
SR
146--- a/block/meson.build
147+++ b/block/meson.build
10e10933 148@@ -45,6 +45,11 @@ block_ss.add(files(
8dca018b 149 ), zstd, zlib, gnutls)
83faa3fe 150
817b7667
SR
151 block_ss.add(files('../vma-writer.c'), libuuid)
152+block_ss.add(files(
153+ '../proxmox-backup-client.c',
154+ '../pve-backup.c',
155+), libproxmox_backup_qemu)
156+
83faa3fe 157
10e10933
FE
158 system_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c'))
159 system_ss.add(files('block-ram-registrar.c'))
83faa3fe 160diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
9e0186f2 161index ca2599de44..6efe28cef5 100644
83faa3fe
TL
162--- a/block/monitor/block-hmp-cmds.c
163+++ b/block/monitor/block-hmp-cmds.c
9e0186f2 164@@ -1029,3 +1029,42 @@ void hmp_change_medium(Monitor *mon, const char *device, const char *target,
bf251437
FE
165 qmp_blockdev_change_medium(device, NULL, target, arg, true, force,
166 !!read_only, read_only_mode, errp);
83faa3fe
TL
167 }
168+
db5d2a4b 169+void coroutine_fn hmp_backup_cancel(Monitor *mon, const QDict *qdict)
83faa3fe
TL
170+{
171+ Error *error = NULL;
172+
173+ qmp_backup_cancel(&error);
174+
175+ hmp_handle_error(mon, error);
176+}
177+
db5d2a4b 178+void coroutine_fn hmp_backup(Monitor *mon, const QDict *qdict)
83faa3fe
TL
179+{
180+ Error *error = NULL;
181+
83faa3fe
TL
182+ const char *backup_file = qdict_get_str(qdict, "backupfile");
183+ const char *devlist = qdict_get_try_str(qdict, "devlist");
184+ int64_t speed = qdict_get_try_int(qdict, "speed", 0);
185+
186+ qmp_backup(
187+ backup_file,
bf251437
FE
188+ NULL, // PBS password
189+ NULL, // PBS keyfile
190+ NULL, // PBS key_password
db5d2a4b 191+ NULL, // PBS master_keyfile
bf251437 192+ NULL, // PBS fingerprint
db5d2a4b 193+ NULL, // PBS backup-ns
bf251437 194+ NULL, // PBS backup-id
c96a4a38 195+ false, 0, // PBS backup-time
db5d2a4b
FE
196+ false, false, // PBS use-dirty-bitmap
197+ false, false, // PBS compress
198+ false, false, // PBS encrypt
9e0186f2 199+ true, BACKUP_FORMAT_VMA,
bf251437 200+ NULL, NULL,
db5d2a4b
FE
201+ devlist, qdict_haskey(qdict, "speed"), speed,
202+ false, 0, // BackupPerf max-workers
203+ &error);
83faa3fe
TL
204+
205+ hmp_handle_error(mon, error);
206+}
6402d961 207diff --git a/blockdev.c b/blockdev.c
10e10933 208index 060d86a65f..79c3575612 100644
6402d961
TL
209--- a/blockdev.c
210+++ b/blockdev.c
bf251437 211@@ -37,6 +37,7 @@
6402d961 212 #include "block/blockjob.h"
bf251437 213 #include "block/dirty-bitmap.h"
6402d961
TL
214 #include "block/qdict.h"
215+#include "block/blockjob_int.h"
216 #include "block/throttle-groups.h"
217 #include "monitor/monitor.h"
218 #include "qemu/error-report.h"
219diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
10e10933 220index 10fdd822e0..15937793c1 100644
6402d961
TL
221--- a/hmp-commands-info.hx
222+++ b/hmp-commands-info.hx
10e10933 223@@ -471,6 +471,20 @@ SRST
f376b2b9 224 Show the current VM UUID.
83faa3fe
TL
225 ERST
226
f376b2b9 227+
6402d961
TL
228+ {
229+ .name = "backup",
230+ .args_type = "",
231+ .params = "",
232+ .help = "show backup status",
83faa3fe 233+ .cmd = hmp_info_backup,
6402d961
TL
234+ },
235+
83faa3fe
TL
236+SRST
237+ ``info backup``
238+ Show backup status.
239+ERST
240+
6402d961 241 #if defined(CONFIG_SLIRP)
83faa3fe
TL
242 {
243 .name = "usernet",
6402d961 244diff --git a/hmp-commands.hx b/hmp-commands.hx
10e10933 245index e352f86872..0c8b6725fb 100644
6402d961
TL
246--- a/hmp-commands.hx
247+++ b/hmp-commands.hx
9e0186f2 248@@ -101,6 +101,35 @@ ERST
83faa3fe
TL
249 SRST
250 ``block_stream``
251 Copy data from a backing file into a block device.
252+ERST
6402d961
TL
253+
254+ {
255+ .name = "backup",
9e0186f2
FE
256+ .args_type = "backupfile:s,speed:o?,devlist:s?",
257+ .params = "backupfile [speed [devlist]]",
258+ .help = "create a VM backup (VMA format).",
6402d961 259+ .cmd = hmp_backup,
db5d2a4b 260+ .coroutine = true,
6402d961
TL
261+ },
262+
83faa3fe
TL
263+SRST
264+``backup``
265+ Create a VM backup.
266+ERST
6402d961
TL
267+
268+ {
269+ .name = "backup_cancel",
270+ .args_type = "",
271+ .params = "",
272+ .help = "cancel the current VM backup",
83faa3fe 273+ .cmd = hmp_backup_cancel,
db5d2a4b 274+ .coroutine = true,
6402d961
TL
275+ },
276+
83faa3fe
TL
277+SRST
278+``backup_cancel``
279+ Cancel the current VM backup.
6402d961 280+
83faa3fe 281 ERST
6402d961
TL
282
283 {
10e10933
FE
284diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
285index 0f2e1817ad..0a0339eee4 100644
286--- a/include/block/block_int-common.h
287+++ b/include/block/block_int-common.h
288@@ -63,7 +63,7 @@
289
290 typedef int BackupDumpFunc(void *opaque, uint64_t offset, uint64_t bytes, const void *buf);
291
292-BlockDriverState *bdrv_backup_dump_create(
293+BlockDriverState *coroutine_fn bdrv_co_backup_dump_create(
294 int dump_cb_block_size,
295 uint64_t byte_size,
296 BackupDumpFunc *dump_cb,
6402d961 297diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
10e10933 298index 7a7def7530..cba7afe70c 100644
6402d961
TL
299--- a/include/monitor/hmp.h
300+++ b/include/monitor/hmp.h
bf251437 301@@ -32,6 +32,7 @@ void hmp_info_savevm(Monitor *mon, const QDict *qdict);
8dca018b 302 void hmp_info_migrate(Monitor *mon, const QDict *qdict);
6402d961
TL
303 void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
304 void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict);
6402d961
TL
305+void hmp_info_backup(Monitor *mon, const QDict *qdict);
306 void hmp_info_cpus(Monitor *mon, const QDict *qdict);
83faa3fe
TL
307 void hmp_info_vnc(Monitor *mon, const QDict *qdict);
308 void hmp_info_spice(Monitor *mon, const QDict *qdict);
bf251437
FE
309@@ -84,6 +85,8 @@ void hmp_change_vnc(Monitor *mon, const char *device, const char *target,
310 void hmp_change_medium(Monitor *mon, const char *device, const char *target,
311 const char *arg, const char *read_only, bool force,
312 Error **errp);
6402d961
TL
313+void hmp_backup(Monitor *mon, const QDict *qdict);
314+void hmp_backup_cancel(Monitor *mon, const QDict *qdict);
83faa3fe
TL
315 void hmp_migrate(Monitor *mon, const QDict *qdict);
316 void hmp_device_add(Monitor *mon, const QDict *qdict);
317 void hmp_device_del(Monitor *mon, const QDict *qdict);
817b7667 318diff --git a/meson.build b/meson.build
10e10933 319index cd95530d3b..d53976d621 100644
817b7667
SR
320--- a/meson.build
321+++ b/meson.build
10e10933 322@@ -1779,6 +1779,7 @@ endif
817b7667
SR
323 has_gettid = cc.has_function('gettid')
324
325 libuuid = cc.find_library('uuid', required: true)
326+libproxmox_backup_qemu = cc.find_library('proxmox_backup_qemu', required: true)
327
4567474e
FE
328 # libselinux
329 selinux = dependency('libselinux',
6402d961 330diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
99f9ce2c 331index 91be698308..5b9c231a4c 100644
6402d961
TL
332--- a/monitor/hmp-cmds.c
333+++ b/monitor/hmp-cmds.c
bf251437
FE
334@@ -21,6 +21,7 @@
335 #include "qemu/help_option.h"
336 #include "monitor/monitor-internal.h"
337 #include "qapi/error.h"
338+#include "qapi/qapi-commands-block-core.h"
339 #include "qapi/qapi-commands-control.h"
340 #include "qapi/qapi-commands-migration.h"
341 #include "qapi/qapi-commands-misc.h"
db5d2a4b 342@@ -144,6 +145,77 @@ void hmp_sync_profile(Monitor *mon, const QDict *qdict)
bf251437 343 }
6402d961
TL
344 }
345
346+void hmp_info_backup(Monitor *mon, const QDict *qdict)
347+{
348+ BackupStatus *info;
db5d2a4b 349+ PBSBitmapInfoList *bitmap_info;
6402d961
TL
350+
351+ info = qmp_query_backup(NULL);
352+
353+ if (!info) {
354+ monitor_printf(mon, "Backup status: not initialized\n");
355+ return;
356+ }
357+
bf251437
FE
358+ if (info->status) {
359+ if (info->errmsg) {
6402d961
TL
360+ monitor_printf(mon, "Backup status: %s - %s\n",
361+ info->status, info->errmsg);
362+ } else {
363+ monitor_printf(mon, "Backup status: %s\n", info->status);
364+ }
365+ }
366+
bf251437 367+ if (info->backup_file) {
6402d961
TL
368+ monitor_printf(mon, "Start time: %s", ctime(&info->start_time));
369+ if (info->end_time) {
370+ monitor_printf(mon, "End time: %s", ctime(&info->end_time));
371+ }
372+
6402d961
TL
373+ monitor_printf(mon, "Backup file: %s\n", info->backup_file);
374+ monitor_printf(mon, "Backup uuid: %s\n", info->uuid);
db5d2a4b
FE
375+
376+ if (!(info->has_total && info->total)) {
377+ // this should not happen normally
378+ monitor_printf(mon, "Total size: %d\n", 0);
379+ } else {
380+ size_t total_or_dirty = info->total;
381+ bitmap_info = qmp_query_pbs_bitmap_info(NULL);
382+
383+ while (bitmap_info) {
384+ monitor_printf(mon, "Drive %s:\n",
385+ bitmap_info->value->drive);
386+ monitor_printf(mon, " bitmap action: %s\n",
387+ PBSBitmapAction_str(bitmap_info->value->action));
388+ monitor_printf(mon, " size: %zd\n",
389+ bitmap_info->value->size);
390+ monitor_printf(mon, " dirty: %zd\n",
391+ bitmap_info->value->dirty);
392+ bitmap_info = bitmap_info->next;
393+ }
394+
395+ qapi_free_PBSBitmapInfoList(bitmap_info);
396+
397+ int zero_per = (info->has_zero_bytes && info->zero_bytes) ?
398+ (info->zero_bytes * 100)/info->total : 0;
399+ monitor_printf(mon, "Total size: %zd\n", info->total);
400+ int trans_per = (info->transferred * 100)/total_or_dirty;
401+ monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n",
402+ info->transferred, trans_per);
403+ monitor_printf(mon, "Zero bytes: %zd (%d%%)\n",
404+ info->zero_bytes, zero_per);
405+
406+ if (info->has_reused) {
407+ int reused_per = (info->reused * 100)/total_or_dirty;
408+ monitor_printf(mon, "Reused bytes: %zd (%d%%)\n",
409+ info->reused, reused_per);
410+ }
411+ }
6402d961
TL
412+ }
413+
414+ qapi_free_BackupStatus(info);
415+}
416+
bf251437 417 void hmp_exit_preconfig(Monitor *mon, const QDict *qdict)
6402d961 418 {
bf251437 419 Error *err = NULL;
6402d961
TL
420diff --git a/proxmox-backup-client.c b/proxmox-backup-client.c
421new file mode 100644
db5d2a4b 422index 0000000000..0923037dec
6402d961
TL
423--- /dev/null
424+++ b/proxmox-backup-client.c
db5d2a4b 425@@ -0,0 +1,146 @@
6402d961
TL
426+#include "proxmox-backup-client.h"
427+#include "qemu/main-loop.h"
428+#include "block/aio-wait.h"
429+#include "qapi/error.h"
430+
431+/* Proxmox Backup Server client bindings using coroutines */
432+
6402d961
TL
433+// This is called from another thread, so we use aio_co_schedule()
434+static void proxmox_backup_schedule_wake(void *data) {
72ae34ec 435+ CoCtxData *waker = (CoCtxData *)data;
6402d961
TL
436+ aio_co_schedule(waker->ctx, waker->co);
437+}
438+
439+int coroutine_fn
440+proxmox_backup_co_connect(ProxmoxBackupHandle *pbs, Error **errp)
441+{
442+ Coroutine *co = qemu_coroutine_self();
443+ AioContext *ctx = qemu_get_current_aio_context();
72ae34ec 444+ CoCtxData waker = { .co = co, .ctx = ctx };
6402d961
TL
445+ char *pbs_err = NULL;
446+ int pbs_res = -1;
447+
448+ proxmox_backup_connect_async(pbs, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err);
449+ qemu_coroutine_yield();
450+ if (pbs_res < 0) {
451+ if (errp) error_setg(errp, "backup connect failed: %s", pbs_err ? pbs_err : "unknown error");
452+ if (pbs_err) proxmox_backup_free_error(pbs_err);
453+ }
454+ return pbs_res;
455+}
456+
457+int coroutine_fn
458+proxmox_backup_co_add_config(
459+ ProxmoxBackupHandle *pbs,
460+ const char *name,
461+ const uint8_t *data,
462+ uint64_t size,
463+ Error **errp)
464+{
465+ Coroutine *co = qemu_coroutine_self();
466+ AioContext *ctx = qemu_get_current_aio_context();
72ae34ec 467+ CoCtxData waker = { .co = co, .ctx = ctx };
6402d961
TL
468+ char *pbs_err = NULL;
469+ int pbs_res = -1;
470+
471+ proxmox_backup_add_config_async(
472+ pbs, name, data, size ,proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err);
473+ qemu_coroutine_yield();
474+ if (pbs_res < 0) {
475+ if (errp) error_setg(errp, "backup add_config %s failed: %s", name, pbs_err ? pbs_err : "unknown error");
476+ if (pbs_err) proxmox_backup_free_error(pbs_err);
477+ }
478+ return pbs_res;
479+}
480+
481+int coroutine_fn
482+proxmox_backup_co_register_image(
483+ ProxmoxBackupHandle *pbs,
484+ const char *device_name,
485+ uint64_t size,
db5d2a4b 486+ bool incremental,
6402d961
TL
487+ Error **errp)
488+{
489+ Coroutine *co = qemu_coroutine_self();
490+ AioContext *ctx = qemu_get_current_aio_context();
72ae34ec 491+ CoCtxData waker = { .co = co, .ctx = ctx };
6402d961
TL
492+ char *pbs_err = NULL;
493+ int pbs_res = -1;
494+
495+ proxmox_backup_register_image_async(
db5d2a4b 496+ pbs, device_name, size, incremental, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err);
6402d961
TL
497+ qemu_coroutine_yield();
498+ if (pbs_res < 0) {
499+ if (errp) error_setg(errp, "backup register image failed: %s", pbs_err ? pbs_err : "unknown error");
500+ if (pbs_err) proxmox_backup_free_error(pbs_err);
501+ }
502+ return pbs_res;
503+}
504+
505+int coroutine_fn
506+proxmox_backup_co_finish(
507+ ProxmoxBackupHandle *pbs,
508+ Error **errp)
509+{
510+ Coroutine *co = qemu_coroutine_self();
511+ AioContext *ctx = qemu_get_current_aio_context();
72ae34ec 512+ CoCtxData waker = { .co = co, .ctx = ctx };
6402d961
TL
513+ char *pbs_err = NULL;
514+ int pbs_res = -1;
515+
516+ proxmox_backup_finish_async(
517+ pbs, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err);
518+ qemu_coroutine_yield();
519+ if (pbs_res < 0) {
520+ if (errp) error_setg(errp, "backup finish failed: %s", pbs_err ? pbs_err : "unknown error");
521+ if (pbs_err) proxmox_backup_free_error(pbs_err);
522+ }
523+ return pbs_res;
524+}
525+
526+int coroutine_fn
527+proxmox_backup_co_close_image(
528+ ProxmoxBackupHandle *pbs,
529+ uint8_t dev_id,
530+ Error **errp)
531+{
532+ Coroutine *co = qemu_coroutine_self();
533+ AioContext *ctx = qemu_get_current_aio_context();
72ae34ec 534+ CoCtxData waker = { .co = co, .ctx = ctx };
6402d961
TL
535+ char *pbs_err = NULL;
536+ int pbs_res = -1;
537+
538+ proxmox_backup_close_image_async(
539+ pbs, dev_id, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err);
540+ qemu_coroutine_yield();
541+ if (pbs_res < 0) {
542+ if (errp) error_setg(errp, "backup close image failed: %s", pbs_err ? pbs_err : "unknown error");
543+ if (pbs_err) proxmox_backup_free_error(pbs_err);
544+ }
545+ return pbs_res;
546+}
547+
548+int coroutine_fn
549+proxmox_backup_co_write_data(
550+ ProxmoxBackupHandle *pbs,
551+ uint8_t dev_id,
552+ const uint8_t *data,
553+ uint64_t offset,
554+ uint64_t size,
555+ Error **errp)
556+{
557+ Coroutine *co = qemu_coroutine_self();
558+ AioContext *ctx = qemu_get_current_aio_context();
72ae34ec 559+ CoCtxData waker = { .co = co, .ctx = ctx };
6402d961
TL
560+ char *pbs_err = NULL;
561+ int pbs_res = -1;
562+
563+ proxmox_backup_write_data_async(
564+ pbs, dev_id, data, offset, size, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err);
565+ qemu_coroutine_yield();
566+ if (pbs_res < 0) {
567+ if (errp) error_setg(errp, "backup write data failed: %s", pbs_err ? pbs_err : "unknown error");
568+ if (pbs_err) proxmox_backup_free_error(pbs_err);
569+ }
570+ return pbs_res;
571+}
572diff --git a/proxmox-backup-client.h b/proxmox-backup-client.h
573new file mode 100644
db5d2a4b 574index 0000000000..8cbf645b2c
6402d961
TL
575--- /dev/null
576+++ b/proxmox-backup-client.h
db5d2a4b 577@@ -0,0 +1,60 @@
6402d961
TL
578+#ifndef PROXMOX_BACKUP_CLIENT_H
579+#define PROXMOX_BACKUP_CLIENT_H
580+
581+#include "qemu/osdep.h"
582+#include "qemu/coroutine.h"
583+#include "proxmox-backup-qemu.h"
584+
72ae34ec
SR
585+typedef struct CoCtxData {
586+ Coroutine *co;
587+ AioContext *ctx;
588+ void *data;
589+} CoCtxData;
590+
591+// FIXME: Remove once coroutines are supported for QMP
6402d961
TL
592+void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg);
593+
594+int coroutine_fn
595+proxmox_backup_co_connect(
596+ ProxmoxBackupHandle *pbs,
597+ Error **errp);
598+
599+int coroutine_fn
600+proxmox_backup_co_add_config(
601+ ProxmoxBackupHandle *pbs,
602+ const char *name,
603+ const uint8_t *data,
604+ uint64_t size,
605+ Error **errp);
606+
607+int coroutine_fn
608+proxmox_backup_co_register_image(
609+ ProxmoxBackupHandle *pbs,
610+ const char *device_name,
611+ uint64_t size,
db5d2a4b 612+ bool incremental,
6402d961
TL
613+ Error **errp);
614+
615+
616+int coroutine_fn
617+proxmox_backup_co_finish(
618+ ProxmoxBackupHandle *pbs,
619+ Error **errp);
620+
621+int coroutine_fn
622+proxmox_backup_co_close_image(
623+ ProxmoxBackupHandle *pbs,
624+ uint8_t dev_id,
625+ Error **errp);
626+
627+int coroutine_fn
628+proxmox_backup_co_write_data(
629+ ProxmoxBackupHandle *pbs,
630+ uint8_t dev_id,
631+ const uint8_t *data,
632+ uint64_t offset,
633+ uint64_t size,
634+ Error **errp);
635+
636+
637+#endif /* PROXMOX_BACKUP_CLIENT_H */
638diff --git a/pve-backup.c b/pve-backup.c
639new file mode 100644
10e10933 640index 0000000000..d84d807654
6402d961
TL
641--- /dev/null
642+++ b/pve-backup.c
9e0186f2 643@@ -0,0 +1,1067 @@
6402d961
TL
644+#include "proxmox-backup-client.h"
645+#include "vma.h"
646+
647+#include "qemu/osdep.h"
648+#include "qemu/module.h"
649+#include "sysemu/block-backend.h"
650+#include "sysemu/blockdev.h"
bf251437 651+#include "block/block_int-global-state.h"
6402d961 652+#include "block/blockjob.h"
db5d2a4b 653+#include "block/dirty-bitmap.h"
6402d961
TL
654+#include "qapi/qapi-commands-block.h"
655+#include "qapi/qmp/qerror.h"
db5d2a4b
FE
656+#include "qemu/cutils.h"
657+
5f9cb29c
FE
658+#if defined(CONFIG_MALLOC_TRIM)
659+#include <malloc.h>
660+#endif
661+
db5d2a4b 662+#include <proxmox-backup-qemu.h>
6402d961
TL
663+
664+/* PVE backup state and related function */
665+
0c893fd8
SR
666+/*
667+ * Note: A resume from a qemu_coroutine_yield can happen in a different thread,
668+ * so you may not use normal mutexes within coroutines:
669+ *
670+ * ---bad-example---
671+ * qemu_rec_mutex_lock(lock)
672+ * ...
673+ * qemu_coroutine_yield() // wait for something
674+ * // we are now inside a different thread
675+ * qemu_rec_mutex_unlock(lock) // Crash - wrong thread!!
676+ * ---end-bad-example--
677+ *
678+ * ==> Always use CoMutext inside coroutines.
679+ * ==> Never acquire/release AioContext withing coroutines (because that use QemuRecMutex)
680+ *
681+ */
6402d961 682+
db5d2a4b
FE
683+const char *PBS_BITMAP_NAME = "pbs-incremental-dirty-bitmap";
684+
6402d961
TL
685+static struct PVEBackupState {
686+ struct {
db5d2a4b
FE
687+ // Everything accessed from qmp_backup_query command is protected using
688+ // this lock. Do NOT hold this lock for long times, as it is sometimes
689+ // acquired from coroutines, and thus any wait time may block the guest.
0c893fd8 690+ QemuMutex lock;
6402d961
TL
691+ Error *error;
692+ time_t start_time;
693+ time_t end_time;
694+ char *backup_file;
695+ uuid_t uuid;
696+ char uuid_str[37];
697+ size_t total;
db5d2a4b 698+ size_t dirty;
6402d961 699+ size_t transferred;
db5d2a4b 700+ size_t reused;
6402d961 701+ size_t zero_bytes;
db5d2a4b
FE
702+ GList *bitmap_list;
703+ bool finishing;
704+ bool starting;
6402d961
TL
705+ } stat;
706+ int64_t speed;
db5d2a4b 707+ BackupPerf perf;
6402d961
TL
708+ VmaWriter *vmaw;
709+ ProxmoxBackupHandle *pbs;
710+ GList *di_list;
db5d2a4b
FE
711+ JobTxn *txn;
712+ CoMutex backup_mutex;
0c893fd8 713+ CoMutex dump_callback_mutex;
6402d961
TL
714+} backup_state;
715+
716+static void pvebackup_init(void)
717+{
0c893fd8 718+ qemu_mutex_init(&backup_state.stat.lock);
db5d2a4b 719+ qemu_co_mutex_init(&backup_state.backup_mutex);
0c893fd8 720+ qemu_co_mutex_init(&backup_state.dump_callback_mutex);
6402d961
TL
721+}
722+
723+// initialize PVEBackupState at startup
724+opts_init(pvebackup_init);
725+
726+typedef struct PVEBackupDevInfo {
727+ BlockDriverState *bs;
728+ size_t size;
db5d2a4b 729+ uint64_t block_size;
6402d961 730+ uint8_t dev_id;
db5d2a4b 731+ int completed_ret; // INT_MAX if not completed
6402d961 732+ char targetfile[PATH_MAX];
db5d2a4b 733+ BdrvDirtyBitmap *bitmap;
6402d961 734+ BlockDriverState *target;
db5d2a4b 735+ BlockJob *job;
6402d961
TL
736+} PVEBackupDevInfo;
737+
0c893fd8
SR
738+static void pvebackup_propagate_error(Error *err)
739+{
740+ qemu_mutex_lock(&backup_state.stat.lock);
741+ error_propagate(&backup_state.stat.error, err);
742+ qemu_mutex_unlock(&backup_state.stat.lock);
743+}
744+
745+static bool pvebackup_error_or_canceled(void)
746+{
747+ qemu_mutex_lock(&backup_state.stat.lock);
748+ bool error_or_canceled = !!backup_state.stat.error;
749+ qemu_mutex_unlock(&backup_state.stat.lock);
750+
751+ return error_or_canceled;
752+}
753+
0ff45eb2 754+static void pvebackup_add_transferred_bytes(size_t transferred, size_t zero_bytes, size_t reused)
0c893fd8
SR
755+{
756+ qemu_mutex_lock(&backup_state.stat.lock);
757+ backup_state.stat.zero_bytes += zero_bytes;
758+ backup_state.stat.transferred += transferred;
db5d2a4b 759+ backup_state.stat.reused += reused;
0c893fd8
SR
760+ qemu_mutex_unlock(&backup_state.stat.lock);
761+}
762+
763+// This may get called from multiple coroutines in multiple io-threads
764+// Note1: this may get called after job_cancel()
6402d961 765+static int coroutine_fn
0c893fd8 766+pvebackup_co_dump_pbs_cb(
6402d961
TL
767+ void *opaque,
768+ uint64_t start,
769+ uint64_t bytes,
770+ const void *pbuf)
771+{
772+ assert(qemu_in_coroutine());
773+
774+ const uint64_t size = bytes;
775+ const unsigned char *buf = pbuf;
776+ PVEBackupDevInfo *di = opaque;
777+
0c893fd8 778+ assert(backup_state.pbs);
db5d2a4b 779+ assert(buf);
0c893fd8
SR
780+
781+ Error *local_err = NULL;
782+ int pbs_res = -1;
783+
db5d2a4b
FE
784+ bool is_zero_block = size == di->block_size && buffer_is_zero(buf, size);
785+
0c893fd8 786+ qemu_co_mutex_lock(&backup_state.dump_callback_mutex);
6402d961 787+
0c893fd8
SR
788+ // avoid deadlock if job is cancelled
789+ if (pvebackup_error_or_canceled()) {
790+ qemu_co_mutex_unlock(&backup_state.dump_callback_mutex);
791+ return -1;
6402d961
TL
792+ }
793+
db5d2a4b
FE
794+ uint64_t transferred = 0;
795+ uint64_t reused = 0;
796+ while (transferred < size) {
797+ uint64_t left = size - transferred;
798+ uint64_t to_transfer = left < di->block_size ? left : di->block_size;
0c893fd8 799+
db5d2a4b
FE
800+ pbs_res = proxmox_backup_co_write_data(backup_state.pbs, di->dev_id,
801+ is_zero_block ? NULL : buf + transferred, start + transferred,
802+ to_transfer, &local_err);
803+ transferred += to_transfer;
804+
805+ if (pbs_res < 0) {
806+ pvebackup_propagate_error(local_err);
807+ qemu_co_mutex_unlock(&backup_state.dump_callback_mutex);
808+ return pbs_res;
809+ }
810+
811+ reused += pbs_res == 0 ? to_transfer : 0;
0c893fd8
SR
812+ }
813+
db5d2a4b 814+ qemu_co_mutex_unlock(&backup_state.dump_callback_mutex);
0ff45eb2 815+ pvebackup_add_transferred_bytes(size, is_zero_block ? size : 0, reused);
db5d2a4b 816+
0c893fd8
SR
817+ return size;
818+}
819+
820+// This may get called from multiple coroutines in multiple io-threads
821+static int coroutine_fn
822+pvebackup_co_dump_vma_cb(
823+ void *opaque,
824+ uint64_t start,
825+ uint64_t bytes,
826+ const void *pbuf)
827+{
828+ assert(qemu_in_coroutine());
829+
830+ const uint64_t size = bytes;
831+ const unsigned char *buf = pbuf;
832+ PVEBackupDevInfo *di = opaque;
6402d961
TL
833+
834+ int ret = -1;
835+
0c893fd8 836+ assert(backup_state.vmaw);
db5d2a4b 837+ assert(buf);
6402d961 838+
0c893fd8 839+ uint64_t remaining = size;
6402d961 840+
0c893fd8
SR
841+ uint64_t cluster_num = start / VMA_CLUSTER_SIZE;
842+ if ((cluster_num * VMA_CLUSTER_SIZE) != start) {
6402d961 843+ Error *local_err = NULL;
0c893fd8
SR
844+ error_setg(&local_err,
845+ "got unaligned write inside backup dump "
846+ "callback (sector %ld)", start);
847+ pvebackup_propagate_error(local_err);
848+ return -1; // not aligned to cluster size
849+ }
6402d961 850+
0c893fd8
SR
851+ while (remaining > 0) {
852+ qemu_co_mutex_lock(&backup_state.dump_callback_mutex);
853+ // avoid deadlock if job is cancelled
854+ if (pvebackup_error_or_canceled()) {
855+ qemu_co_mutex_unlock(&backup_state.dump_callback_mutex);
856+ return -1;
857+ }
6402d961 858+
0c893fd8
SR
859+ size_t zero_bytes = 0;
860+ ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num, buf, &zero_bytes);
861+ qemu_co_mutex_unlock(&backup_state.dump_callback_mutex);
6402d961 862+
0c893fd8 863+ ++cluster_num;
db5d2a4b 864+ buf += VMA_CLUSTER_SIZE;
0c893fd8
SR
865+ if (ret < 0) {
866+ Error *local_err = NULL;
867+ vma_writer_error_propagate(backup_state.vmaw, &local_err);
868+ pvebackup_propagate_error(local_err);
869+ return ret;
6402d961 870+ } else {
0c893fd8
SR
871+ if (remaining >= VMA_CLUSTER_SIZE) {
872+ assert(ret == VMA_CLUSTER_SIZE);
0ff45eb2 873+ pvebackup_add_transferred_bytes(VMA_CLUSTER_SIZE, zero_bytes, 0);
0c893fd8
SR
874+ remaining -= VMA_CLUSTER_SIZE;
875+ } else {
876+ assert(ret == remaining);
0ff45eb2 877+ pvebackup_add_transferred_bytes(remaining, zero_bytes, 0);
0c893fd8 878+ remaining = 0;
6402d961 879+ }
6402d961 880+ }
6402d961
TL
881+ }
882+
6402d961
TL
883+ return size;
884+}
885+
0c893fd8 886+// assumes the caller holds backup_mutex
db5d2a4b 887+static void coroutine_fn pvebackup_co_cleanup(void)
6402d961
TL
888+{
889+ assert(qemu_in_coroutine());
890+
0c893fd8 891+ qemu_mutex_lock(&backup_state.stat.lock);
db5d2a4b 892+ backup_state.stat.finishing = true;
0c893fd8 893+ qemu_mutex_unlock(&backup_state.stat.lock);
6402d961
TL
894+
895+ if (backup_state.vmaw) {
896+ Error *local_err = NULL;
897+ vma_writer_close(backup_state.vmaw, &local_err);
898+
899+ if (local_err != NULL) {
0c893fd8
SR
900+ pvebackup_propagate_error(local_err);
901+ }
6402d961
TL
902+
903+ backup_state.vmaw = NULL;
904+ }
905+
906+ if (backup_state.pbs) {
0c893fd8 907+ if (!pvebackup_error_or_canceled()) {
6402d961
TL
908+ Error *local_err = NULL;
909+ proxmox_backup_co_finish(backup_state.pbs, &local_err);
910+ if (local_err != NULL) {
0c893fd8
SR
911+ pvebackup_propagate_error(local_err);
912+ }
6402d961 913+ }
6402d961
TL
914+
915+ proxmox_backup_disconnect(backup_state.pbs);
916+ backup_state.pbs = NULL;
917+ }
918+
919+ g_list_free(backup_state.di_list);
920+ backup_state.di_list = NULL;
db5d2a4b
FE
921+
922+ qemu_mutex_lock(&backup_state.stat.lock);
923+ backup_state.stat.end_time = time(NULL);
924+ backup_state.stat.finishing = false;
925+ qemu_mutex_unlock(&backup_state.stat.lock);
5f9cb29c
FE
926+
927+#if defined(CONFIG_MALLOC_TRIM)
928+ /*
929+ * Try to reclaim memory for buffers (and, in case of PBS, Rust futures), etc.
930+ * Won't happen by default if there is fragmentation.
931+ */
932+ malloc_trim(4 * 1024 * 1024);
933+#endif
6402d961
TL
934+}
935+
db5d2a4b 936+static void coroutine_fn pvebackup_co_complete_stream(void *opaque)
6402d961 937+{
0c893fd8 938+ PVEBackupDevInfo *di = opaque;
db5d2a4b
FE
939+ int ret = di->completed_ret;
940+
941+ qemu_mutex_lock(&backup_state.stat.lock);
942+ bool starting = backup_state.stat.starting;
943+ qemu_mutex_unlock(&backup_state.stat.lock);
944+ if (starting) {
945+ /* in 'starting' state, no tasks have been run yet, meaning we can (and
946+ * must) skip all cleanup, as we don't know what has and hasn't been
947+ * initialized yet. */
948+ return;
949+ }
950+
951+ qemu_co_mutex_lock(&backup_state.backup_mutex);
952+
953+ if (ret < 0) {
954+ Error *local_err = NULL;
955+ error_setg(&local_err, "job failed with err %d - %s", ret, strerror(-ret));
956+ pvebackup_propagate_error(local_err);
957+ }
958+
959+ di->bs = NULL;
960+
961+ assert(di->target == NULL);
6402d961 962+
0c893fd8 963+ bool error_or_canceled = pvebackup_error_or_canceled();
6402d961
TL
964+
965+ if (backup_state.vmaw) {
966+ vma_writer_close_stream(backup_state.vmaw, di->dev_id);
967+ }
968+
969+ if (backup_state.pbs && !error_or_canceled) {
970+ Error *local_err = NULL;
971+ proxmox_backup_co_close_image(backup_state.pbs, di->dev_id, &local_err);
972+ if (local_err != NULL) {
0c893fd8 973+ pvebackup_propagate_error(local_err);
6402d961
TL
974+ }
975+ }
6402d961 976+
db5d2a4b
FE
977+ if (di->job) {
978+ WITH_JOB_LOCK_GUARD() {
979+ job_unref_locked(&di->job->job);
980+ di->job = NULL;
981+ }
6402d961 982+ }
6402d961 983+
db5d2a4b 984+ // remove self from job list
0c893fd8 985+ backup_state.di_list = g_list_remove(backup_state.di_list, di);
6402d961 986+
0c893fd8 987+ g_free(di);
6402d961 988+
db5d2a4b
FE
989+ /* call cleanup if we're the last job */
990+ if (!g_list_first(backup_state.di_list)) {
991+ pvebackup_co_cleanup();
992+ }
6402d961 993+
db5d2a4b 994+ qemu_co_mutex_unlock(&backup_state.backup_mutex);
0c893fd8
SR
995+}
996+
db5d2a4b 997+static void pvebackup_complete_cb(void *opaque, int ret)
0c893fd8 998+{
db5d2a4b
FE
999+ PVEBackupDevInfo *di = opaque;
1000+ di->completed_ret = ret;
1001+
1002+ /*
1003+ * Schedule stream cleanup in async coroutine. close_image and finish might
1004+ * take a while, so we can't block on them here. This way it also doesn't
1005+ * matter if we're already running in a coroutine or not.
1006+ * Note: di is a pointer to an entry in the global backup_state struct, so
1007+ * it stays valid.
1008+ */
1009+ Coroutine *co = qemu_coroutine_create(pvebackup_co_complete_stream, di);
1010+ aio_co_enter(qemu_get_aio_context(), co);
1011+}
0c893fd8 1012+
db5d2a4b
FE
1013+/*
1014+ * job_cancel(_sync) does not like to be called from coroutines, so defer to
1015+ * main loop processing via a bottom half. Assumes that caller holds
1016+ * backup_mutex.
1017+ */
1018+static void job_cancel_bh(void *opaque) {
1019+ CoCtxData *data = (CoCtxData*)opaque;
1020+
1021+ /*
1022+ * Be careful to pick a valid job to cancel:
1023+ * 1. job_cancel_sync() does not expect the job to be finalized already.
1024+ * 2. job_exit() might run between scheduling and running job_cancel_bh()
1025+ * and pvebackup_co_complete_stream() might not have removed the job from
1026+ * the list yet (in fact, cannot, because it waits for the backup_mutex).
1027+ * Requiring !job_is_completed() ensures that no finalized job is picked.
1028+ */
1029+ GList *bdi = g_list_first(backup_state.di_list);
1030+ while (bdi) {
1031+ if (bdi->data) {
1032+ BlockJob *bj = ((PVEBackupDevInfo *)bdi->data)->job;
1033+ if (bj) {
1034+ Job *job = &bj->job;
1035+ WITH_JOB_LOCK_GUARD() {
1036+ if (!job_is_completed_locked(job)) {
1037+ job_cancel_sync_locked(job, true);
1038+ /*
1039+ * It's enough to cancel one job in the transaction, the
1040+ * rest will follow automatically.
1041+ */
1042+ break;
1043+ }
1044+ }
1045+ }
1046+ }
1047+ bdi = g_list_next(bdi);
1048+ }
1049+
1050+ aio_co_enter(data->ctx, data->co);
1051+}
1052+
1053+void coroutine_fn qmp_backup_cancel(Error **errp)
1054+{
0c893fd8
SR
1055+ Error *cancel_err = NULL;
1056+ error_setg(&cancel_err, "backup canceled");
1057+ pvebackup_propagate_error(cancel_err);
1058+
db5d2a4b 1059+ qemu_co_mutex_lock(&backup_state.backup_mutex);
6402d961
TL
1060+
1061+ if (backup_state.vmaw) {
1062+ /* make sure vma writer does not block anymore */
0c893fd8 1063+ vma_writer_set_error(backup_state.vmaw, "backup canceled");
6402d961
TL
1064+ }
1065+
1066+ if (backup_state.pbs) {
0c893fd8 1067+ proxmox_backup_abort(backup_state.pbs, "backup canceled");
6402d961
TL
1068+ }
1069+
db5d2a4b
FE
1070+ CoCtxData data = {
1071+ .ctx = qemu_get_current_aio_context(),
1072+ .co = qemu_coroutine_self(),
1073+ };
1074+ aio_bh_schedule_oneshot(data.ctx, job_cancel_bh, &data);
1075+ qemu_coroutine_yield();
6402d961 1076+
db5d2a4b 1077+ qemu_co_mutex_unlock(&backup_state.backup_mutex);
6402d961
TL
1078+}
1079+
0c893fd8 1080+// assumes the caller holds backup_mutex
6402d961
TL
1081+static int coroutine_fn pvebackup_co_add_config(
1082+ const char *file,
1083+ const char *name,
1084+ BackupFormat format,
6402d961
TL
1085+ VmaWriter *vmaw,
1086+ ProxmoxBackupHandle *pbs,
1087+ Error **errp)
1088+{
1089+ int res = 0;
1090+
1091+ char *cdata = NULL;
1092+ gsize clen = 0;
1093+ GError *err = NULL;
1094+ if (!g_file_get_contents(file, &cdata, &clen, &err)) {
1095+ error_setg(errp, "unable to read file '%s'", file);
1096+ return 1;
1097+ }
1098+
1099+ char *basename = g_path_get_basename(file);
1100+ if (name == NULL) name = basename;
1101+
1102+ if (format == BACKUP_FORMAT_VMA) {
1103+ if (vma_writer_add_config(vmaw, name, cdata, clen) != 0) {
1104+ error_setg(errp, "unable to add %s config data to vma archive", file);
1105+ goto err;
1106+ }
1107+ } else if (format == BACKUP_FORMAT_PBS) {
1108+ if (proxmox_backup_co_add_config(pbs, name, (unsigned char *)cdata, clen, errp) < 0)
1109+ goto err;
6402d961
TL
1110+ }
1111+
1112+ out:
1113+ g_free(basename);
1114+ g_free(cdata);
1115+ return res;
1116+
1117+ err:
1118+ res = -1;
1119+ goto out;
1120+}
1121+
db5d2a4b
FE
1122+/*
1123+ * backup_job_create can *not* be run from a coroutine (and requires an
1124+ * acquired AioContext), so this can't either.
1125+ * The caller is responsible that backup_mutex is held nonetheless.
1126+ */
1127+static void create_backup_jobs_bh(void *opaque) {
6402d961 1128+
0c893fd8 1129+ assert(!qemu_in_coroutine());
6402d961 1130+
db5d2a4b
FE
1131+ CoCtxData *data = (CoCtxData*)opaque;
1132+ Error **errp = (Error**)data->data;
0c893fd8
SR
1133+
1134+ Error *local_err = NULL;
1135+
db5d2a4b
FE
1136+ /* create job transaction to synchronize bitmap commit and cancel all
1137+ * jobs in case one errors */
1138+ if (backup_state.txn) {
1139+ job_txn_unref(backup_state.txn);
1140+ }
1141+ backup_state.txn = job_txn_new_seq();
8dca018b 1142+
0c893fd8
SR
1143+ /* create and start all jobs (paused state) */
1144+ GList *l = backup_state.di_list;
1145+ while (l) {
1146+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
1147+ l = g_list_next(l);
1148+
1149+ assert(di->target != NULL);
1150+
db5d2a4b
FE
1151+ MirrorSyncMode sync_mode = MIRROR_SYNC_MODE_FULL;
1152+ BitmapSyncMode bitmap_mode = BITMAP_SYNC_MODE_NEVER;
1153+ if (di->bitmap) {
1154+ sync_mode = MIRROR_SYNC_MODE_BITMAP;
1155+ bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS;
1156+ }
0c893fd8
SR
1157+ AioContext *aio_context = bdrv_get_aio_context(di->bs);
1158+ aio_context_acquire(aio_context);
1159+
0cffb504
FE
1160+ bdrv_drained_begin(di->bs);
1161+
0c893fd8 1162+ BlockJob *job = backup_job_create(
db5d2a4b
FE
1163+ NULL, di->bs, di->target, backup_state.speed, sync_mode, di->bitmap,
1164+ bitmap_mode, false, NULL, &backup_state.perf, BLOCKDEV_ON_ERROR_REPORT,
1165+ BLOCKDEV_ON_ERROR_REPORT, JOB_DEFAULT, pvebackup_complete_cb, di, backup_state.txn,
1166+ &local_err);
0c893fd8 1167+
0cffb504
FE
1168+ bdrv_drained_end(di->bs);
1169+
0c893fd8
SR
1170+ aio_context_release(aio_context);
1171+
db5d2a4b
FE
1172+ di->job = job;
1173+ if (job) {
1174+ WITH_JOB_LOCK_GUARD() {
1175+ job_ref_locked(&job->job);
1176+ }
1177+ }
0c893fd8 1178+
db5d2a4b
FE
1179+ if (!job || local_err) {
1180+ error_setg(errp, "backup_job_create failed: %s",
1181+ local_err ? error_get_pretty(local_err) : "null");
0c893fd8
SR
1182+ break;
1183+ }
0c893fd8
SR
1184+
1185+ bdrv_unref(di->target);
1186+ di->target = NULL;
1187+ }
1188+
db5d2a4b
FE
1189+ if (*errp) {
1190+ /*
1191+ * It's enough to cancel one job in the transaction, the rest will
1192+ * follow automatically.
1193+ */
1194+ bool canceled = false;
0c893fd8
SR
1195+ l = backup_state.di_list;
1196+ while (l) {
1197+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
1198+ l = g_list_next(l);
1199+
1200+ if (di->target) {
1201+ bdrv_unref(di->target);
1202+ di->target = NULL;
1203+ }
db5d2a4b
FE
1204+
1205+ if (di->job) {
1206+ WITH_JOB_LOCK_GUARD() {
1207+ if (!canceled) {
1208+ job_cancel_sync_locked(&di->job->job, true);
1209+ canceled = true;
1210+ }
1211+ job_unref_locked(&di->job->job);
1212+ di->job = NULL;
1213+ }
1214+ }
0c893fd8
SR
1215+ }
1216+ }
1217+
db5d2a4b
FE
1218+ /* return */
1219+ aio_co_enter(data->ctx, data->co);
6402d961
TL
1220+}
1221+
db5d2a4b
FE
1222+UuidInfo coroutine_fn *qmp_backup(
1223+ const char *backup_file,
1224+ const char *password,
1225+ const char *keyfile,
1226+ const char *key_password,
1227+ const char *master_keyfile,
1228+ const char *fingerprint,
1229+ const char *backup_ns,
1230+ const char *backup_id,
1231+ bool has_backup_time, int64_t backup_time,
1232+ bool has_use_dirty_bitmap, bool use_dirty_bitmap,
1233+ bool has_compress, bool compress,
1234+ bool has_encrypt, bool encrypt,
1235+ bool has_format, BackupFormat format,
1236+ const char *config_file,
1237+ const char *firewall_file,
1238+ const char *devlist,
1239+ bool has_speed, int64_t speed,
1240+ bool has_max_workers, int64_t max_workers,
1241+ Error **errp)
6402d961
TL
1242+{
1243+ assert(qemu_in_coroutine());
1244+
db5d2a4b 1245+ qemu_co_mutex_lock(&backup_state.backup_mutex);
6402d961
TL
1246+
1247+ BlockBackend *blk;
1248+ BlockDriverState *bs = NULL;
6402d961
TL
1249+ Error *local_err = NULL;
1250+ uuid_t uuid;
1251+ VmaWriter *vmaw = NULL;
1252+ ProxmoxBackupHandle *pbs = NULL;
1253+ gchar **devs = NULL;
1254+ GList *di_list = NULL;
1255+ GList *l;
1256+ UuidInfo *uuid_info;
6402d961
TL
1257+
1258+ const char *config_name = "qemu-server.conf";
1259+ const char *firewall_name = "qemu-server.fw";
1260+
6402d961 1261+ if (backup_state.di_list) {
db5d2a4b 1262+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
6402d961 1263+ "previous backup not finished");
db5d2a4b
FE
1264+ qemu_co_mutex_unlock(&backup_state.backup_mutex);
1265+ return NULL;
6402d961
TL
1266+ }
1267+
1268+ /* Todo: try to auto-detect format based on file name */
db5d2a4b 1269+ format = has_format ? format : BACKUP_FORMAT_VMA;
6402d961 1270+
db5d2a4b
FE
1271+ if (devlist) {
1272+ devs = g_strsplit_set(devlist, ",;:", -1);
6402d961
TL
1273+
1274+ gchar **d = devs;
1275+ while (d && *d) {
1276+ blk = blk_by_name(*d);
1277+ if (blk) {
1278+ bs = blk_bs(blk);
db5d2a4b
FE
1279+ if (!bdrv_co_is_inserted(bs)) {
1280+ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d);
6402d961
TL
1281+ goto err;
1282+ }
1283+ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1);
1284+ di->bs = bs;
1285+ di_list = g_list_append(di_list, di);
1286+ } else {
db5d2a4b 1287+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
6402d961
TL
1288+ "Device '%s' not found", *d);
1289+ goto err;
1290+ }
1291+ d++;
1292+ }
1293+
1294+ } else {
1295+ BdrvNextIterator it;
1296+
1297+ bs = NULL;
1298+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
db5d2a4b 1299+ if (!bdrv_co_is_inserted(bs) || bdrv_is_read_only(bs)) {
6402d961
TL
1300+ continue;
1301+ }
1302+
1303+ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1);
1304+ di->bs = bs;
1305+ di_list = g_list_append(di_list, di);
1306+ }
1307+ }
1308+
1309+ if (!di_list) {
db5d2a4b 1310+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list");
6402d961
TL
1311+ goto err;
1312+ }
1313+
1314+ size_t total = 0;
1315+
1316+ l = di_list;
1317+ while (l) {
1318+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
1319+ l = g_list_next(l);
db5d2a4b 1320+ if (bdrv_op_is_blocked(di->bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
6402d961
TL
1321+ goto err;
1322+ }
1323+
1324+ ssize_t size = bdrv_getlength(di->bs);
1325+ if (size < 0) {
db5d2a4b 1326+ error_setg_errno(errp, -size, "bdrv_getlength failed");
6402d961
TL
1327+ goto err;
1328+ }
1329+ di->size = size;
1330+ total += size;
db5d2a4b
FE
1331+
1332+ di->completed_ret = INT_MAX;
6402d961
TL
1333+ }
1334+
1335+ uuid_generate(uuid);
1336+
db5d2a4b
FE
1337+ qemu_mutex_lock(&backup_state.stat.lock);
1338+ backup_state.stat.reused = 0;
1339+
1340+ /* clear previous backup's bitmap_list */
1341+ if (backup_state.stat.bitmap_list) {
1342+ GList *bl = backup_state.stat.bitmap_list;
1343+ while (bl) {
1344+ g_free(((PBSBitmapInfo *)bl->data)->drive);
1345+ g_free(bl->data);
1346+ bl = g_list_next(bl);
1347+ }
1348+ g_list_free(backup_state.stat.bitmap_list);
1349+ backup_state.stat.bitmap_list = NULL;
1350+ }
1351+
6402d961 1352+ if (format == BACKUP_FORMAT_PBS) {
db5d2a4b
FE
1353+ if (!password) {
1354+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'password'");
1355+ goto err_mutex;
6402d961 1356+ }
db5d2a4b
FE
1357+ if (!backup_id) {
1358+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'backup-id'");
1359+ goto err_mutex;
6402d961 1360+ }
db5d2a4b
FE
1361+ if (!has_backup_time) {
1362+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'backup-time'");
1363+ goto err_mutex;
6402d961
TL
1364+ }
1365+
1366+ int dump_cb_block_size = PROXMOX_BACKUP_DEFAULT_CHUNK_SIZE; // Hardcoded (4M)
1367+ firewall_name = "fw.conf";
1368+
1369+ char *pbs_err = NULL;
db5d2a4b
FE
1370+ pbs = proxmox_backup_new_ns(
1371+ backup_file,
1372+ backup_ns,
1373+ backup_id,
1374+ backup_time,
6402d961 1375+ dump_cb_block_size,
db5d2a4b
FE
1376+ password,
1377+ keyfile,
1378+ key_password,
1379+ master_keyfile,
1380+ has_compress ? compress : true,
1381+ has_encrypt ? encrypt : !!keyfile,
1382+ fingerprint,
6402d961
TL
1383+ &pbs_err);
1384+
1385+ if (!pbs) {
db5d2a4b 1386+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
6402d961
TL
1387+ "proxmox_backup_new failed: %s", pbs_err);
1388+ proxmox_backup_free_error(pbs_err);
db5d2a4b 1389+ goto err_mutex;
6402d961
TL
1390+ }
1391+
db5d2a4b
FE
1392+ int connect_result = proxmox_backup_co_connect(pbs, errp);
1393+ if (connect_result < 0)
1394+ goto err_mutex;
6402d961
TL
1395+
1396+ /* register all devices */
1397+ l = di_list;
1398+ while (l) {
1399+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
1400+ l = g_list_next(l);
1401+
db5d2a4b
FE
1402+ di->block_size = dump_cb_block_size;
1403+
6402d961 1404+ const char *devname = bdrv_get_device_name(di->bs);
db5d2a4b
FE
1405+ PBSBitmapAction action = PBS_BITMAP_ACTION_NOT_USED;
1406+ size_t dirty = di->size;
6402d961 1407+
db5d2a4b
FE
1408+ BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(di->bs, PBS_BITMAP_NAME);
1409+ bool expect_only_dirty = false;
6402d961 1410+
db5d2a4b
FE
1411+ if (has_use_dirty_bitmap && use_dirty_bitmap) {
1412+ if (bitmap == NULL) {
1413+ bitmap = bdrv_create_dirty_bitmap(di->bs, dump_cb_block_size, PBS_BITMAP_NAME, errp);
1414+ if (!bitmap) {
1415+ goto err_mutex;
1416+ }
1417+ action = PBS_BITMAP_ACTION_NEW;
1418+ } else {
1419+ expect_only_dirty = proxmox_backup_check_incremental(pbs, devname, di->size) != 0;
1420+ }
1421+
1422+ if (expect_only_dirty) {
1423+ /* track clean chunks as reused */
1424+ dirty = MIN(bdrv_get_dirty_count(bitmap), di->size);
1425+ backup_state.stat.reused += di->size - dirty;
1426+ action = PBS_BITMAP_ACTION_USED;
1427+ } else {
1428+ /* mark entire bitmap as dirty to make full backup */
1429+ bdrv_set_dirty_bitmap(bitmap, 0, di->size);
1430+ if (action != PBS_BITMAP_ACTION_NEW) {
1431+ action = PBS_BITMAP_ACTION_INVALID;
1432+ }
1433+ }
1434+ di->bitmap = bitmap;
1435+ } else {
1436+ /* after a full backup the old dirty bitmap is invalid anyway */
1437+ if (bitmap != NULL) {
1438+ bdrv_release_dirty_bitmap(bitmap);
1439+ action = PBS_BITMAP_ACTION_NOT_USED_REMOVED;
1440+ }
1441+ }
1442+
1443+ int dev_id = proxmox_backup_co_register_image(pbs, devname, di->size, expect_only_dirty, errp);
1444+ if (dev_id < 0) {
1445+ goto err_mutex;
1446+ }
1447+
10e10933 1448+ if (!(di->target = bdrv_co_backup_dump_create(dump_cb_block_size, di->size, pvebackup_co_dump_pbs_cb, di, errp))) {
db5d2a4b 1449+ goto err_mutex;
6402d961
TL
1450+ }
1451+
1452+ di->dev_id = dev_id;
db5d2a4b
FE
1453+
1454+ PBSBitmapInfo *info = g_malloc(sizeof(*info));
1455+ info->drive = g_strdup(devname);
1456+ info->action = action;
1457+ info->size = di->size;
1458+ info->dirty = dirty;
1459+ backup_state.stat.bitmap_list = g_list_append(backup_state.stat.bitmap_list, info);
6402d961
TL
1460+ }
1461+ } else if (format == BACKUP_FORMAT_VMA) {
db5d2a4b 1462+ vmaw = vma_writer_create(backup_file, uuid, &local_err);
6402d961
TL
1463+ if (!vmaw) {
1464+ if (local_err) {
db5d2a4b 1465+ error_propagate(errp, local_err);
6402d961 1466+ }
db5d2a4b 1467+ goto err_mutex;
6402d961
TL
1468+ }
1469+
1470+ /* register all devices for vma writer */
1471+ l = di_list;
1472+ while (l) {
1473+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
1474+ l = g_list_next(l);
1475+
10e10933 1476+ if (!(di->target = bdrv_co_backup_dump_create(VMA_CLUSTER_SIZE, di->size, pvebackup_co_dump_vma_cb, di, errp))) {
db5d2a4b 1477+ goto err_mutex;
6402d961
TL
1478+ }
1479+
1480+ const char *devname = bdrv_get_device_name(di->bs);
1481+ di->dev_id = vma_writer_register_stream(vmaw, devname, di->size);
1482+ if (di->dev_id <= 0) {
db5d2a4b 1483+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
6402d961 1484+ "register_stream failed");
db5d2a4b 1485+ goto err_mutex;
6402d961
TL
1486+ }
1487+ }
6402d961 1488+ } else {
db5d2a4b
FE
1489+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
1490+ goto err_mutex;
6402d961
TL
1491+ }
1492+
6402d961 1493+ /* add configuration file to archive */
db5d2a4b 1494+ if (config_file) {
9e0186f2 1495+ if (pvebackup_co_add_config(config_file, config_name, format, vmaw, pbs, errp) != 0) {
db5d2a4b 1496+ goto err_mutex;
6402d961
TL
1497+ }
1498+ }
1499+
1500+ /* add firewall file to archive */
db5d2a4b 1501+ if (firewall_file) {
9e0186f2 1502+ if (pvebackup_co_add_config(firewall_file, firewall_name, format, vmaw, pbs, errp) != 0) {
db5d2a4b 1503+ goto err_mutex;
6402d961
TL
1504+ }
1505+ }
1506+ /* initialize global backup_state now */
db5d2a4b 1507+ /* note: 'reused' and 'bitmap_list' are initialized earlier */
6402d961
TL
1508+
1509+ if (backup_state.stat.error) {
1510+ error_free(backup_state.stat.error);
1511+ backup_state.stat.error = NULL;
1512+ }
1513+
1514+ backup_state.stat.start_time = time(NULL);
1515+ backup_state.stat.end_time = 0;
1516+
1517+ if (backup_state.stat.backup_file) {
1518+ g_free(backup_state.stat.backup_file);
1519+ }
db5d2a4b 1520+ backup_state.stat.backup_file = g_strdup(backup_file);
6402d961
TL
1521+
1522+ uuid_copy(backup_state.stat.uuid, uuid);
1523+ uuid_unparse_lower(uuid, backup_state.stat.uuid_str);
1524+ char *uuid_str = g_strdup(backup_state.stat.uuid_str);
1525+
1526+ backup_state.stat.total = total;
db5d2a4b 1527+ backup_state.stat.dirty = total - backup_state.stat.reused;
6402d961
TL
1528+ backup_state.stat.transferred = 0;
1529+ backup_state.stat.zero_bytes = 0;
db5d2a4b
FE
1530+ backup_state.stat.finishing = false;
1531+ backup_state.stat.starting = true;
6402d961 1532+
0c893fd8 1533+ qemu_mutex_unlock(&backup_state.stat.lock);
6402d961 1534+
db5d2a4b
FE
1535+ backup_state.speed = (has_speed && speed > 0) ? speed : 0;
1536+
1537+ backup_state.perf = (BackupPerf){ .max_workers = 16 };
1538+ if (has_max_workers) {
1539+ backup_state.perf.max_workers = max_workers;
1540+ }
6402d961
TL
1541+
1542+ backup_state.vmaw = vmaw;
1543+ backup_state.pbs = pbs;
1544+
1545+ backup_state.di_list = di_list;
1546+
6402d961
TL
1547+ uuid_info = g_malloc0(sizeof(*uuid_info));
1548+ uuid_info->UUID = uuid_str;
1549+
db5d2a4b
FE
1550+ /* Run create_backup_jobs_bh outside of coroutine (in BH) but keep
1551+ * backup_mutex locked. This is fine, a CoMutex can be held across yield
1552+ * points, and we'll release it as soon as the BH reschedules us.
1553+ */
1554+ CoCtxData waker = {
1555+ .co = qemu_coroutine_self(),
1556+ .ctx = qemu_get_current_aio_context(),
1557+ .data = &local_err,
1558+ };
1559+ aio_bh_schedule_oneshot(waker.ctx, create_backup_jobs_bh, &waker);
1560+ qemu_coroutine_yield();
1561+
1562+ if (local_err) {
1563+ error_propagate(errp, local_err);
1564+ goto err;
1565+ }
1566+
1567+ qemu_co_mutex_unlock(&backup_state.backup_mutex);
1568+
1569+ qemu_mutex_lock(&backup_state.stat.lock);
1570+ backup_state.stat.starting = false;
1571+ qemu_mutex_unlock(&backup_state.stat.lock);
1572+
1573+ /* start the first job in the transaction */
1574+ job_txn_start_seq(backup_state.txn);
1575+
1576+ return uuid_info;
1577+
1578+err_mutex:
1579+ qemu_mutex_unlock(&backup_state.stat.lock);
6402d961
TL
1580+
1581+err:
1582+
1583+ l = di_list;
1584+ while (l) {
1585+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
1586+ l = g_list_next(l);
1587+
1588+ if (di->target) {
53b56ca7 1589+ bdrv_co_unref(di->target);
6402d961
TL
1590+ }
1591+
1592+ if (di->targetfile[0]) {
1593+ unlink(di->targetfile);
1594+ }
1595+ g_free(di);
1596+ }
1597+ g_list_free(di_list);
db5d2a4b 1598+ backup_state.di_list = NULL;
6402d961
TL
1599+
1600+ if (devs) {
1601+ g_strfreev(devs);
1602+ }
1603+
1604+ if (vmaw) {
1605+ Error *err = NULL;
1606+ vma_writer_close(vmaw, &err);
db5d2a4b 1607+ unlink(backup_file);
6402d961
TL
1608+ }
1609+
1610+ if (pbs) {
1611+ proxmox_backup_disconnect(pbs);
db5d2a4b 1612+ backup_state.pbs = NULL;
6402d961
TL
1613+ }
1614+
db5d2a4b
FE
1615+ qemu_co_mutex_unlock(&backup_state.backup_mutex);
1616+ return NULL;
0c893fd8 1617+}
6402d961 1618+
0c893fd8 1619+BackupStatus *qmp_query_backup(Error **errp)
6402d961 1620+{
6402d961
TL
1621+ BackupStatus *info = g_malloc0(sizeof(*info));
1622+
0c893fd8 1623+ qemu_mutex_lock(&backup_state.stat.lock);
6402d961
TL
1624+
1625+ if (!backup_state.stat.start_time) {
1626+ /* not started, return {} */
0c893fd8
SR
1627+ qemu_mutex_unlock(&backup_state.stat.lock);
1628+ return info;
6402d961
TL
1629+ }
1630+
6402d961
TL
1631+ info->has_start_time = true;
1632+ info->start_time = backup_state.stat.start_time;
1633+
1634+ if (backup_state.stat.backup_file) {
6402d961
TL
1635+ info->backup_file = g_strdup(backup_state.stat.backup_file);
1636+ }
1637+
6402d961
TL
1638+ info->uuid = g_strdup(backup_state.stat.uuid_str);
1639+
1640+ if (backup_state.stat.end_time) {
1641+ if (backup_state.stat.error) {
1642+ info->status = g_strdup("error");
6402d961
TL
1643+ info->errmsg = g_strdup(error_get_pretty(backup_state.stat.error));
1644+ } else {
1645+ info->status = g_strdup("done");
1646+ }
1647+ info->has_end_time = true;
1648+ info->end_time = backup_state.stat.end_time;
1649+ } else {
1650+ info->status = g_strdup("active");
1651+ }
1652+
1653+ info->has_total = true;
1654+ info->total = backup_state.stat.total;
db5d2a4b
FE
1655+ info->has_dirty = true;
1656+ info->dirty = backup_state.stat.dirty;
6402d961
TL
1657+ info->has_zero_bytes = true;
1658+ info->zero_bytes = backup_state.stat.zero_bytes;
1659+ info->has_transferred = true;
1660+ info->transferred = backup_state.stat.transferred;
db5d2a4b
FE
1661+ info->has_reused = true;
1662+ info->reused = backup_state.stat.reused;
1663+ info->finishing = backup_state.stat.finishing;
6402d961 1664+
0c893fd8 1665+ qemu_mutex_unlock(&backup_state.stat.lock);
6402d961 1666+
0c893fd8 1667+ return info;
6402d961 1668+}
db5d2a4b
FE
1669+
1670+PBSBitmapInfoList *qmp_query_pbs_bitmap_info(Error **errp)
1671+{
1672+ PBSBitmapInfoList *head = NULL, **p_next = &head;
1673+
1674+ qemu_mutex_lock(&backup_state.stat.lock);
1675+
1676+ GList *l = backup_state.stat.bitmap_list;
1677+ while (l) {
1678+ PBSBitmapInfo *info = (PBSBitmapInfo *)l->data;
1679+ l = g_list_next(l);
1680+
1681+ /* clone bitmap info to avoid auto free after QMP marshalling */
1682+ PBSBitmapInfo *info_ret = g_malloc0(sizeof(*info_ret));
1683+ info_ret->drive = g_strdup(info->drive);
1684+ info_ret->action = info->action;
1685+ info_ret->size = info->size;
1686+ info_ret->dirty = info->dirty;
1687+
1688+ PBSBitmapInfoList *info_list = g_malloc0(sizeof(*info_list));
1689+ info_list->value = info_ret;
1690+
1691+ *p_next = info_list;
1692+ p_next = &info_list->next;
1693+ }
1694+
1695+ qemu_mutex_unlock(&backup_state.stat.lock);
1696+
1697+ return head;
1698+}
1699+
1700+ProxmoxSupportStatus *qmp_query_proxmox_support(Error **errp)
1701+{
1702+ ProxmoxSupportStatus *ret = g_malloc0(sizeof(*ret));
1703+ ret->pbs_library_version = g_strdup(proxmox_backup_qemu_version());
1704+ ret->pbs_dirty_bitmap = true;
1705+ ret->pbs_dirty_bitmap_savevm = true;
1706+ ret->query_bitmap_info = true;
1707+ ret->pbs_masterkey = true;
1708+ ret->backup_max_workers = true;
1709+ return ret;
1710+}
6402d961 1711diff --git a/qapi/block-core.json b/qapi/block-core.json
10e10933 1712index 125aa89858..331c8336d1 100644
6402d961
TL
1713--- a/qapi/block-core.json
1714+++ b/qapi/block-core.json
10e10933 1715@@ -839,6 +839,235 @@
5b15e2ec
FE
1716 { 'command': 'query-block', 'returns': ['BlockInfo'],
1717 'allow-preconfig': true }
6402d961
TL
1718
1719+##
1720+# @BackupStatus:
1721+#
1722+# Detailed backup status.
1723+#
1724+# @status: string describing the current backup status.
1725+# This can be 'active', 'done', 'error'. If this field is not
1726+# returned, no backup process has been initiated
1727+#
1728+# @errmsg: error message (only returned if status is 'error')
1729+#
1730+# @total: total amount of bytes involved in the backup process
1731+#
db5d2a4b
FE
1732+# @dirty: with incremental mode (PBS) this is the amount of bytes involved
1733+# in the backup process which are marked dirty.
1734+#
6402d961
TL
1735+# @transferred: amount of bytes already backed up.
1736+#
db5d2a4b
FE
1737+# @reused: amount of bytes reused due to deduplication.
1738+#
6402d961
TL
1739+# @zero-bytes: amount of 'zero' bytes detected.
1740+#
1741+# @start-time: time (epoch) when backup job started.
1742+#
1743+# @end-time: time (epoch) when backup job finished.
1744+#
1745+# @backup-file: backup file name
1746+#
1747+# @uuid: uuid for this backup job
1748+#
db5d2a4b
FE
1749+# @finishing: if status='active' and finishing=true, then the backup process is
1750+# waiting for the target to finish.
1751+#
6402d961
TL
1752+##
1753+{ 'struct': 'BackupStatus',
db5d2a4b
FE
1754+ 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int', '*dirty': 'int',
1755+ '*transferred': 'int', '*zero-bytes': 'int', '*reused': 'int',
6402d961 1756+ '*start-time': 'int', '*end-time': 'int',
db5d2a4b 1757+ '*backup-file': 'str', '*uuid': 'str', 'finishing': 'bool' } }
6402d961
TL
1758+
1759+##
1760+# @BackupFormat:
1761+#
1762+# An enumeration of supported backup formats.
1763+#
1764+# @vma: Proxmox vma backup format
9e0186f2
FE
1765+#
1766+# @pbs: Proxmox backup server format
1767+#
6402d961
TL
1768+##
1769+{ 'enum': 'BackupFormat',
9e0186f2 1770+ 'data': [ 'vma', 'pbs' ] }
6402d961
TL
1771+
1772+##
1773+# @backup:
1774+#
1775+# Starts a VM backup.
1776+#
1777+# @backup-file: the backup file name
1778+#
1779+# @format: format of the backup file
1780+#
1781+# @config-file: a configuration file to include into
817b7667 1782+# the backup archive.
6402d961
TL
1783+#
1784+# @speed: the maximum speed, in bytes per second
1785+#
1786+# @devlist: list of block device names (separated by ',', ';'
817b7667 1787+# or ':'). By default the backup includes all writable block devices.
6402d961
TL
1788+#
1789+# @password: backup server passsword (required for format 'pbs')
1790+#
1791+# @keyfile: keyfile used for encryption (optional for format 'pbs')
1792+#
1793+# @key-password: password for keyfile (optional for format 'pbs')
1794+#
db5d2a4b
FE
1795+# @master-keyfile: PEM-formatted master public keyfile (optional for format 'pbs')
1796+#
6402d961
TL
1797+# @fingerprint: server cert fingerprint (optional for format 'pbs')
1798+#
db5d2a4b
FE
1799+# @backup-ns: backup namespace (required for format 'pbs')
1800+#
6402d961
TL
1801+# @backup-id: backup ID (required for format 'pbs')
1802+#
1803+# @backup-time: backup timestamp (Unix epoch, required for format 'pbs')
1804+#
db5d2a4b
FE
1805+# @use-dirty-bitmap: use dirty bitmap to detect incremental changes since last job (optional for format 'pbs')
1806+#
1807+# @compress: use compression (optional for format 'pbs', defaults to true)
1808+#
1809+# @encrypt: use encryption ((optional for format 'pbs', defaults to true if there is a keyfile)
1810+#
1811+# @max-workers: see @BackupPerf for details. Default 16.
1812+#
6402d961
TL
1813+# Returns: the uuid of the backup job
1814+#
1815+##
1816+{ 'command': 'backup', 'data': { 'backup-file': 'str',
1817+ '*password': 'str',
1818+ '*keyfile': 'str',
1819+ '*key-password': 'str',
db5d2a4b 1820+ '*master-keyfile': 'str',
6402d961 1821+ '*fingerprint': 'str',
db5d2a4b 1822+ '*backup-ns': 'str',
6402d961
TL
1823+ '*backup-id': 'str',
1824+ '*backup-time': 'int',
db5d2a4b
FE
1825+ '*use-dirty-bitmap': 'bool',
1826+ '*compress': 'bool',
1827+ '*encrypt': 'bool',
6402d961
TL
1828+ '*format': 'BackupFormat',
1829+ '*config-file': 'str',
1830+ '*firewall-file': 'str',
db5d2a4b
FE
1831+ '*devlist': 'str',
1832+ '*speed': 'int',
1833+ '*max-workers': 'int' },
1834+ 'returns': 'UuidInfo', 'coroutine': true }
6402d961
TL
1835+
1836+##
1837+# @query-backup:
1838+#
1839+# Returns information about current/last backup task.
1840+#
1841+# Returns: @BackupStatus
1842+#
1843+##
1844+{ 'command': 'query-backup', 'returns': 'BackupStatus' }
1845+
1846+##
1847+# @backup-cancel:
1848+#
1849+# Cancel the current executing backup process.
1850+#
1851+# Returns: nothing on success
1852+#
1853+# Notes: This command succeeds even if there is no backup process running.
1854+#
1855+##
db5d2a4b
FE
1856+{ 'command': 'backup-cancel', 'coroutine': true }
1857+
1858+##
1859+# @ProxmoxSupportStatus:
1860+#
1861+# Contains info about supported features added by Proxmox.
1862+#
1863+# @pbs-dirty-bitmap: True if dirty-bitmap-incremental backups to PBS are
1864+# supported.
1865+#
1866+# @query-bitmap-info: True if the 'query-pbs-bitmap-info' QMP call is supported.
1867+#
1868+# @pbs-dirty-bitmap-savevm: True if 'dirty-bitmaps' migration capability can
1869+# safely be set for savevm-async.
1870+#
1871+# @pbs-masterkey: True if the QMP backup call supports the 'master_keyfile'
1872+# parameter.
1873+#
1874+# @pbs-library-version: Running version of libproxmox-backup-qemu0 library.
1875+#
1876+##
1877+{ 'struct': 'ProxmoxSupportStatus',
1878+ 'data': { 'pbs-dirty-bitmap': 'bool',
1879+ 'query-bitmap-info': 'bool',
1880+ 'pbs-dirty-bitmap-savevm': 'bool',
1881+ 'pbs-masterkey': 'bool',
1882+ 'pbs-library-version': 'str',
1883+ 'backup-max-workers': 'bool' } }
1884+
1885+##
1886+# @query-proxmox-support:
1887+#
1888+# Returns information about supported features added by Proxmox.
1889+#
1890+# Returns: @ProxmoxSupportStatus
1891+#
1892+##
1893+{ 'command': 'query-proxmox-support', 'returns': 'ProxmoxSupportStatus' }
1894+
1895+##
1896+# @PBSBitmapAction:
1897+#
1898+# An action taken on a dirty-bitmap when a backup job was started.
1899+#
1900+# @not-used: Bitmap mode was not enabled.
1901+#
1902+# @not-used-removed: Bitmap mode was not enabled, but a bitmap from a
1903+# previous backup still existed and was removed.
1904+#
1905+# @new: A new bitmap was attached to the drive for this backup.
1906+#
1907+# @used: An existing bitmap will be used to only backup changed data.
1908+#
1909+# @invalid: A bitmap existed, but had to be cleared since it's associated
1910+# base snapshot did not match the base given for the current job or
1911+# the crypt mode has changed.
1912+#
1913+##
1914+{ 'enum': 'PBSBitmapAction',
1915+ 'data': ['not-used', 'not-used-removed', 'new', 'used', 'invalid'] }
1916+
1917+##
1918+# @PBSBitmapInfo:
1919+#
1920+# Contains information about dirty bitmaps used for each drive in a PBS backup.
1921+#
1922+# @drive: The underlying drive.
1923+#
1924+# @action: The action that was taken when the backup started.
1925+#
1926+# @size: The total size of the drive.
1927+#
1928+# @dirty: How much of the drive is considered dirty and will be backed up,
1929+# or 'size' if everything will be.
1930+#
1931+##
1932+{ 'struct': 'PBSBitmapInfo',
1933+ 'data': { 'drive': 'str', 'action': 'PBSBitmapAction', 'size': 'int',
1934+ 'dirty': 'int' } }
1935+
1936+##
1937+# @query-pbs-bitmap-info:
1938+#
1939+# Returns information about dirty bitmaps used on the most recently started
1940+# backup. Returns nothing when the last backup was not using PBS or if no
1941+# backup occured in this session.
1942+#
1943+# Returns: @PBSBitmapInfo
1944+#
1945+##
1946+{ 'command': 'query-pbs-bitmap-info', 'returns': ['PBSBitmapInfo'] }
6402d961
TL
1947+
1948 ##
1949 # @BlockDeviceTimedStats:
1950 #
1951diff --git a/qapi/common.json b/qapi/common.json
10e10933 1952index 6fed9cde1a..630a2a8f9a 100644
6402d961
TL
1953--- a/qapi/common.json
1954+++ b/qapi/common.json
10e10933 1955@@ -207,3 +207,17 @@
4567474e
FE
1956 ##
1957 { 'struct': 'HumanReadableText',
1958 'data': { 'human-readable-text': 'str' } }
6402d961
TL
1959+
1960+##
1961+# @UuidInfo:
1962+#
1963+# Guest UUID information (Universally Unique Identifier).
1964+#
1965+# @UUID: the UUID of the guest
1966+#
1967+# Since: 0.14.0
1968+#
10e10933
FE
1969+# Notes: If no UUID was specified for the guest, a null UUID is
1970+# returned.
6402d961
TL
1971+##
1972+{ 'struct': 'UuidInfo', 'data': {'UUID': 'str'} }
817b7667 1973diff --git a/qapi/machine.json b/qapi/machine.json
10e10933 1974index 7da3c519ba..888457f810 100644
817b7667
SR
1975--- a/qapi/machine.json
1976+++ b/qapi/machine.json
1977@@ -4,6 +4,8 @@
1978 # This work is licensed under the terms of the GNU GPL, version 2 or later.
1979 # See the COPYING file in the top-level directory.
1980
1981+{ 'include': 'common.json' }
1982+
1983 ##
1984 # = Machines
1985 ##
10e10933 1986@@ -230,20 +232,6 @@
6402d961 1987 ##
817b7667 1988 { 'command': 'query-target', 'returns': 'TargetInfo' }
6402d961
TL
1989
1990-##
1991-# @UuidInfo:
1992-#
1993-# Guest UUID information (Universally Unique Identifier).
1994-#
1995-# @UUID: the UUID of the guest
1996-#
8dca018b 1997-# Since: 0.14
6402d961 1998-#
10e10933
FE
1999-# Notes: If no UUID was specified for the guest, a null UUID is
2000-# returned.
6402d961
TL
2001-##
2002-{ 'struct': 'UuidInfo', 'data': {'UUID': 'str'} }
2003-
2004 ##
2005 # @query-uuid:
2006 #