]> git.proxmox.com Git - pve-qemu-kvm.git/blame - debian/patches/backup-add-pve-monitor-commands.patch
backup: call backup_cancel inside coroutine
[pve-qemu-kvm.git] / debian / patches / backup-add-pve-monitor-commands.patch
CommitLineData
cbf9030a
DM
1Index: new/blockdev.c
2===================================================================
0c5f6a91
DM
3--- new.orig/blockdev.c 2013-12-02 11:40:36.000000000 +0100
4+++ new/blockdev.c 2013-12-02 12:07:54.000000000 +0100
cbf9030a
DM
5@@ -45,6 +45,7 @@
6 #include "qmp-commands.h"
7 #include "trace.h"
8 #include "sysemu/arch_init.h"
9+#include "vma.h"
10
11 static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
12
0c5f6a91 13@@ -1438,6 +1439,417 @@
cbf9030a
DM
14 }
15 }
16
17+/* PVE backup related function */
18+
19+static struct PVEBackupState {
20+ Error *error;
21+ bool cancel;
22+ uuid_t uuid;
23+ char uuid_str[37];
24+ int64_t speed;
25+ time_t start_time;
26+ time_t end_time;
27+ char *backup_file;
28+ VmaWriter *vmaw;
29+ GList *di_list;
30+ size_t total;
31+ size_t transferred;
32+ size_t zero_bytes;
33+} backup_state;
34+
35+typedef struct PVEBackupDevInfo {
36+ BlockDriverState *bs;
37+ uint8_t dev_id;
38+ //bool started;
39+ bool completed;
40+} PVEBackupDevInfo;
41+
42+static void pvebackup_run_next_job(void);
43+
44+static int pvebackup_dump_cb(void *opaque, BlockDriverState *bs,
45+ int64_t cluster_num, unsigned char *buf)
46+{
47+ PVEBackupDevInfo *di = opaque;
48+
49+ assert(backup_state.vmaw);
50+
51+ size_t zero_bytes = 0;
52+ return vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num,
53+ buf, &zero_bytes);
54+}
55+
56+static void pvebackup_update_status(void)
57+{
58+ g_assert(backup_state.vmaw);
59+
60+ VmaStatus vmastat;
61+
62+ vma_writer_get_status(backup_state.vmaw, &vmastat);
63+
64+ uint64_t total = 0;
65+ uint64_t transferred = 0;
66+ uint64_t zero_bytes = 0;
67+
68+ int i;
69+ for (i = 0; i < 256; i++) {
70+ if (vmastat.stream_info[i].size) {
71+ total += vmastat.stream_info[i].size;
72+ transferred += vmastat.stream_info[i].transferred;
73+ zero_bytes += vmastat.stream_info[i].zero_bytes;
74+ }
75+ }
76+
77+ backup_state.total = total;
78+ backup_state.transferred = transferred;
79+ backup_state.zero_bytes = zero_bytes;
80+}
81+
82+static void pvebackup_cleanup(void)
83+{
84+ if (backup_state.vmaw) {
85+ backup_state.end_time = time(NULL);
86+ Error *local_err = NULL;
87+ pvebackup_update_status();
88+ vma_writer_close(backup_state.vmaw, &local_err);
89+ error_propagate(&backup_state.error, local_err);
90+ backup_state.vmaw = NULL;
91+ }
92+
93+ if (backup_state.di_list) {
94+ GList *l = backup_state.di_list;
95+ while (l) {
96+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
97+ l = g_list_next(l);
98+ g_free(di);
99+ }
100+ g_list_free(backup_state.di_list);
101+ backup_state.di_list = NULL;
102+ }
103+}
104+
105+static void pvebackup_complete_cb(void *opaque, int ret)
106+{
107+ PVEBackupDevInfo *di = opaque;
108+
109+ assert(backup_state.vmaw);
110+
111+ di->completed = true;
112+
113+ vma_writer_close_stream(backup_state.vmaw, di->dev_id);
114+
115+ if (!backup_state.cancel) {
116+ pvebackup_run_next_job();
117+ }
118+}
119+
0c5f6a91 120+static void pvebackup_cancel(void *opaque)
cbf9030a
DM
121+{
122+ backup_state.cancel = true;
123+
124+ if (!backup_state.error) {
125+ error_setg(&backup_state.error, "backup cancelled");
126+ }
127+
128+ /* drain all i/o (awake jobs waiting for aio) */
129+ bdrv_drain_all();
130+
131+ int job_count = 0;
132+ GList *l = backup_state.di_list;
133+ while (l) {
134+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
135+ l = g_list_next(l);
136+ BlockJob *job = di->bs->job;
137+ if (job) {
138+ job_count++;
139+ if (!di->completed) {
140+ block_job_cancel_sync(job);
141+ }
142+ }
143+ }
144+
145+ pvebackup_cleanup();
146+}
147+
148+void qmp_backup_cancel(Error **errp)
149+{
0c5f6a91
DM
150+ Coroutine *co = qemu_coroutine_create(pvebackup_cancel);
151+ qemu_coroutine_enter(co, NULL);
152+
153+ while (backup_state.vmaw) {
154+ qemu_aio_wait();
155+ }
cbf9030a
DM
156+}
157+
158+static void pvebackup_run_next_job(void)
159+{
160+ GList *l = backup_state.di_list;
161+ while (l) {
162+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
163+ l = g_list_next(l);
164+
165+ if (di->bs && di->bs->job && !di->completed) {
166+ if (block_job_is_paused(di->bs->job)) {
167+ bool cancel = backup_state.error || backup_state.cancel;
168+ if (cancel) {
169+ block_job_cancel(di->bs->job);
170+ } else {
171+ block_job_resume(di->bs->job);
172+ }
173+ }
174+ return;
175+ }
176+ }
177+
178+ pvebackup_cleanup();
179+}
180+
181+char *qmp_backup(const char *backup_file, bool has_format,
182+ BackupFormat format,
183+ bool has_config_file, const char *config_file,
184+ bool has_devlist, const char *devlist,
185+ bool has_speed, int64_t speed, Error **errp)
186+{
187+ BlockDriverState *bs;
188+ Error *local_err = NULL;
189+ uuid_t uuid;
190+ VmaWriter *vmaw = NULL;
191+ gchar **devs = NULL;
192+ GList *di_list = NULL;
193+ GList *l;
194+
195+ if (backup_state.di_list) {
196+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
197+ "previous backup not finished");
198+ return NULL;
199+ }
200+
201+ /* Todo: try to auto-detect format based on file name */
202+ format = has_format ? format : BACKUP_FORMAT_VMA;
203+
204+ if (format != BACKUP_FORMAT_VMA) {
205+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
206+ return NULL;
207+ }
208+
209+ if (has_devlist) {
210+ devs = g_strsplit_set(devlist, ",;:", -1);
211+
212+ gchar **d = devs;
213+ while (d && *d) {
214+ bs = bdrv_find(*d);
215+ if (bs) {
216+ if (bdrv_is_read_only(bs)) {
217+ error_set(errp, QERR_DEVICE_IS_READ_ONLY, *d);
218+ goto err;
219+ }
220+ if (!bdrv_is_inserted(bs)) {
221+ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d);
222+ goto err;
223+ }
224+ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1);
225+ di->bs = bs;
226+ di_list = g_list_append(di_list, di);
227+ } else {
228+ error_set(errp, QERR_DEVICE_NOT_FOUND, *d);
229+ goto err;
230+ }
231+ d++;
232+ }
233+
234+ } else {
235+
236+ bs = NULL;
237+ while ((bs = bdrv_next(bs))) {
238+
239+ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
240+ continue;
241+ }
242+
243+ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1);
244+ di->bs = bs;
245+ di_list = g_list_append(di_list, di);
246+ }
247+ }
248+
249+ if (!di_list) {
250+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list");
251+ goto err;
252+ }
253+
254+ l = di_list;
255+ while (l) {
256+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
257+ l = g_list_next(l);
258+ if (di->bs->job) {
259+ error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(di->bs));
260+ goto err;
261+ }
262+ }
263+
264+ uuid_generate(uuid);
265+
266+ vmaw = vma_writer_create(backup_file, uuid, &local_err);
267+ if (!vmaw) {
268+ if (error_is_set(&local_err)) {
269+ error_propagate(errp, local_err);
270+ }
271+ goto err;
272+ }
273+
274+ /* register all devices for vma writer */
275+ l = di_list;
276+ while (l) {
277+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
278+ l = g_list_next(l);
279+
280+ int64_t size = bdrv_getlength(di->bs);
281+ const char *devname = bdrv_get_device_name(di->bs);
282+ di->dev_id = vma_writer_register_stream(vmaw, devname, size);
283+ if (di->dev_id <= 0) {
284+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
285+ "register_stream failed");
286+ goto err;
287+ }
288+ }
289+
290+ /* add configuration file to archive */
291+ if (has_config_file) {
292+ char *cdata = NULL;
293+ gsize clen = 0;
294+ GError *err = NULL;
295+ if (!g_file_get_contents(config_file, &cdata, &clen, &err)) {
296+ error_setg(errp, "unable to read file '%s'", config_file);
297+ goto err;
298+ }
299+
300+ const char *basename = g_path_get_basename(config_file);
301+ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
302+ error_setg(errp, "unable to add config data to vma archive");
303+ g_free(cdata);
304+ goto err;
305+ }
306+ g_free(cdata);
307+ }
308+
309+ /* initialize global backup_state now */
310+
311+ backup_state.cancel = false;
312+
313+ if (backup_state.error) {
314+ error_free(backup_state.error);
315+ backup_state.error = NULL;
316+ }
317+
318+ backup_state.speed = (has_speed && speed > 0) ? speed : 0;
319+
320+ backup_state.start_time = time(NULL);
321+ backup_state.end_time = 0;
322+
323+ if (backup_state.backup_file) {
324+ g_free(backup_state.backup_file);
325+ }
326+ backup_state.backup_file = g_strdup(backup_file);
327+
328+ backup_state.vmaw = vmaw;
329+
330+ uuid_copy(backup_state.uuid, uuid);
331+ uuid_unparse_lower(uuid, backup_state.uuid_str);
332+
333+ backup_state.di_list = di_list;
334+
335+ pvebackup_update_status();
336+
337+ /* start all jobs (paused state) */
338+ l = di_list;
339+ while (l) {
340+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
341+ l = g_list_next(l);
342+
343+ backup_start(di->bs, NULL, speed, MIRROR_SYNC_MODE_FULL,
344+ BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
345+ pvebackup_dump_cb, pvebackup_complete_cb, di, &local_err);
346+ if (local_err != NULL) {
347+ error_setg(&backup_state.error, "backup_job_create failed");
0c5f6a91 348+ pvebackup_cancel(NULL);
cbf9030a
DM
349+ }
350+ }
351+
352+ if (!backup_state.error) {
353+ pvebackup_run_next_job(); // run one job
354+ }
355+
356+ return g_strdup(backup_state.uuid_str);
357+
358+err:
359+
360+ l = di_list;
361+ while (l) {
362+ g_free(l->data);
363+ l = g_list_next(l);
364+ }
365+ g_list_free(di_list);
366+
367+ if (devs) {
368+ g_strfreev(devs);
369+ }
370+
371+ if (vmaw) {
372+ Error *err = NULL;
373+ vma_writer_close(vmaw, &err);
374+ unlink(backup_file);
375+ }
376+
377+ return NULL;
378+}
379+
380+BackupStatus *qmp_query_backup(Error **errp)
381+{
382+ BackupStatus *info = g_malloc0(sizeof(*info));
383+
384+ if (!backup_state.start_time) {
385+ /* not started, return {} */
386+ return info;
387+ }
388+
389+ info->has_status = true;
390+ info->has_start_time = true;
391+ info->start_time = backup_state.start_time;
392+
393+ if (backup_state.backup_file) {
394+ info->has_backup_file = true;
395+ info->backup_file = g_strdup(backup_state.backup_file);
396+ }
397+
398+ info->has_uuid = true;
399+ info->uuid = g_strdup(backup_state.uuid_str);
400+
401+ if (backup_state.end_time) {
402+ if (backup_state.error) {
403+ info->status = g_strdup("error");
404+ info->has_errmsg = true;
405+ info->errmsg = g_strdup(error_get_pretty(backup_state.error));
406+ } else {
407+ info->status = g_strdup("done");
408+ }
409+ info->has_end_time = true;
410+ info->end_time = backup_state.end_time;
411+ } else {
412+ info->status = g_strdup("active");
413+ }
414+
415+ if (backup_state.vmaw) {
416+ pvebackup_update_status();
417+ }
418+
419+ info->has_total = true;
420+ info->total = backup_state.total;
421+ info->has_zero_bytes = true;
422+ info->zero_bytes = backup_state.zero_bytes;
423+ info->has_transferred = true;
424+ info->transferred = backup_state.transferred;
425+
426+ return info;
427+}
428
429 static void eject_device(BlockDriverState *bs, int force, Error **errp)
430 {
431Index: new/hmp-commands.hx
432===================================================================
0c5f6a91
DM
433--- new.orig/hmp-commands.hx 2013-11-27 10:51:33.000000000 +0100
434+++ new/hmp-commands.hx 2013-12-02 11:40:36.000000000 +0100
cbf9030a
DM
435@@ -83,6 +83,35 @@
436 Copy data from a backing file into a block device.
437 ETEXI
438
439+ {
440+ .name = "backup",
441+ .args_type = "backupfile:s,speed:o?,devlist:s?",
442+ .params = "backupfile [speed [devlist]]",
443+ .help = "create a VM Backup.",
444+ .mhandler.cmd = hmp_backup,
445+ },
446+
447+STEXI
448+@item backup
449+@findex backup
450+Create a VM backup.
451+ETEXI
452+
453+ {
454+ .name = "backup_cancel",
455+ .args_type = "",
456+ .params = "",
457+ .help = "cancel the current VM backup",
458+ .mhandler.cmd = hmp_backup_cancel,
459+ },
460+
461+STEXI
462+@item backup_cancel
463+@findex backup_cancel
464+Cancel the current VM backup.
465+
466+ETEXI
467+
468 {
469 .name = "block_job_set_speed",
470 .args_type = "device:B,speed:o",
471@@ -1692,6 +1721,8 @@
472 show CPU statistics
473 @item info usernet
474 show user network stack connection states
475+@item info backup
476+show backup status
477 @item info migrate
478 show migration status
479 @item info migrate_capabilities
480Index: new/hmp.c
481===================================================================
0c5f6a91
DM
482--- new.orig/hmp.c 2013-12-02 11:40:35.000000000 +0100
483+++ new/hmp.c 2013-12-02 11:40:36.000000000 +0100
cbf9030a
DM
484@@ -133,6 +133,38 @@
485 qapi_free_MouseInfoList(mice_list);
486 }
487
488+void hmp_info_backup(Monitor *mon, const QDict *qdict)
489+{
490+ BackupStatus *info;
491+
492+ info = qmp_query_backup(NULL);
493+ if (info->has_status) {
494+ if (info->has_errmsg) {
495+ monitor_printf(mon, "Backup status: %s - %s\n",
496+ info->status, info->errmsg);
497+ } else {
498+ monitor_printf(mon, "Backup status: %s\n", info->status);
499+ }
500+ }
501+ if (info->has_backup_file) {
502+ int per = (info->has_total && info->total &&
503+ info->has_transferred && info->transferred) ?
504+ (info->transferred * 100)/info->total : 0;
505+ int zero_per = (info->has_total && info->total &&
506+ info->has_zero_bytes && info->zero_bytes) ?
507+ (info->zero_bytes * 100)/info->total : 0;
508+ monitor_printf(mon, "Backup file: %s\n", info->backup_file);
509+ monitor_printf(mon, "Backup uuid: %s\n", info->uuid);
510+ monitor_printf(mon, "Total size: %zd\n", info->total);
511+ monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n",
512+ info->transferred, per);
513+ monitor_printf(mon, "Zero bytes: %zd (%d%%)\n",
514+ info->zero_bytes, zero_per);
515+ }
516+
517+ qapi_free_BackupStatus(info);
518+}
519+
520 void hmp_info_migrate(Monitor *mon, const QDict *qdict)
521 {
522 MigrationInfo *info;
523@@ -1194,6 +1226,37 @@
524 hmp_handle_error(mon, &error);
525 }
526
527+void hmp_backup_cancel(Monitor *mon, const QDict *qdict)
528+{
529+ Error *errp = NULL;
530+
531+ qmp_backup_cancel(&errp);
532+
533+ if (error_is_set(&errp)) {
534+ monitor_printf(mon, "%s\n", error_get_pretty(errp));
535+ error_free(errp);
536+ return;
537+ }
538+}
539+
540+void hmp_backup(Monitor *mon, const QDict *qdict)
541+{
542+ const char *backup_file = qdict_get_str(qdict, "backup-file");
543+ const char *devlist = qdict_get_try_str(qdict, "devlist");
544+ int64_t speed = qdict_get_try_int(qdict, "speed", 0);
545+
546+ Error *errp = NULL;
547+
548+ qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
549+ devlist, qdict_haskey(qdict, "speed"), speed, &errp);
550+
551+ if (error_is_set(&errp)) {
552+ monitor_printf(mon, "%s\n", error_get_pretty(errp));
553+ error_free(errp);
554+ return;
555+ }
556+}
557+
558 void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict)
559 {
560 Error *error = NULL;
561Index: new/hmp.h
562===================================================================
0c5f6a91
DM
563--- new.orig/hmp.h 2013-11-27 10:51:33.000000000 +0100
564+++ new/hmp.h 2013-12-02 11:40:36.000000000 +0100
cbf9030a
DM
565@@ -28,6 +28,7 @@
566 void hmp_info_migrate(Monitor *mon, const QDict *qdict);
567 void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
568 void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
569+void hmp_info_backup(Monitor *mon, const QDict *qdict);
570 void hmp_info_cpus(Monitor *mon, const QDict *qdict);
571 void hmp_info_block(Monitor *mon, const QDict *qdict);
572 void hmp_info_blockstats(Monitor *mon, const QDict *qdict);
573@@ -69,6 +70,8 @@
574 void hmp_change(Monitor *mon, const QDict *qdict);
575 void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict);
576 void hmp_block_stream(Monitor *mon, const QDict *qdict);
577+void hmp_backup(Monitor *mon, const QDict *qdict);
578+void hmp_backup_cancel(Monitor *mon, const QDict *qdict);
579 void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
580 void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
581 void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
582Index: new/monitor.c
583===================================================================
0c5f6a91
DM
584--- new.orig/monitor.c 2013-11-27 10:51:33.000000000 +0100
585+++ new/monitor.c 2013-12-02 11:40:36.000000000 +0100
cbf9030a
DM
586@@ -2880,6 +2880,13 @@
587 },
588 #endif
589 {
590+ .name = "backup",
591+ .args_type = "",
592+ .params = "",
593+ .help = "show backup status",
594+ .mhandler.cmd = hmp_info_backup,
595+ },
596+ {
597 .name = "migrate",
598 .args_type = "",
599 .params = "",
600Index: new/qapi-schema.json
601===================================================================
0c5f6a91
DM
602--- new.orig/qapi-schema.json 2013-12-02 11:40:35.000000000 +0100
603+++ new/qapi-schema.json 2013-12-02 11:40:36.000000000 +0100
cbf9030a
DM
604@@ -547,6 +547,95 @@
605 ##
606 { 'command': 'query-events', 'returns': ['EventInfo'] }
607
608+# @BackupStatus:
609+#
610+# Detailed backup status.
611+#
612+# @status: #optional string describing the current backup status.
613+# This can be 'active', 'done', 'error'. If this field is not
614+# returned, no backup process has been initiated
615+#
616+# @errmsg: #optional error message (only returned if status is 'error')
617+#
618+# @total: #optional total amount of bytes involved in the backup process
619+#
620+# @transferred: #optional amount of bytes already backed up.
621+#
622+# @zero-bytes: #optional amount of 'zero' bytes detected.
623+#
624+# @start-time: #optional time (epoch) when backup job started.
625+#
626+# @end-time: #optional time (epoch) when backup job finished.
627+#
628+# @backupfile: #optional backup file name
629+#
630+# @uuid: #optional uuid for this backup job
631+#
632+##
633+{ 'type': 'BackupStatus',
634+ 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int',
635+ '*transferred': 'int', '*zero-bytes': 'int',
636+ '*start-time': 'int', '*end-time': 'int',
637+ '*backup-file': 'str', '*uuid': 'str' } }
638+
639+##
640+# @BackupFormat
641+#
642+# An enumeration of supported backup formats.
643+#
644+# @vma: Proxmox vma backup format
645+##
646+{ 'enum': 'BackupFormat',
647+ 'data': [ 'vma' ] }
648+
649+##
650+# @backup:
651+#
652+# Starts a VM backup.
653+#
654+# @backup-file: the backup file name
655+#
656+# @format: format of the backup file
657+#
658+# @config-filename: #optional name of a configuration file to include into
659+# the backup archive.
660+#
661+# @speed: #optional the maximum speed, in bytes per second
662+#
663+# @devlist: #optional list of block device names (separated by ',', ';'
664+# or ':'). By default the backup includes all writable block devices.
665+#
666+# Returns: the uuid of the backup job
667+#
668+##
669+{ 'command': 'backup', 'data': { 'backup-file': 'str',
670+ '*format': 'BackupFormat',
671+ '*config-file': 'str',
672+ '*devlist': 'str', '*speed': 'int' },
673+ 'returns': 'str' }
674+
675+##
676+# @query-backup
677+#
678+# Returns information about current/last backup task.
679+#
680+# Returns: @BackupStatus
681+#
682+##
683+{ 'command': 'query-backup', 'returns': 'BackupStatus' }
684+
685+##
686+# @backup-cancel
687+#
688+# Cancel the current executing backup process.
689+#
690+# Returns: nothing on success
691+#
692+# Notes: This command succeeds even if there is no backup process running.
693+#
694+##
695+{ 'command': 'backup-cancel' }
696+
697 ##
698 # @MigrationStats
699 #
700Index: new/qmp-commands.hx
701===================================================================
0c5f6a91
DM
702--- new.orig/qmp-commands.hx 2013-12-02 11:40:35.000000000 +0100
703+++ new/qmp-commands.hx 2013-12-02 11:40:36.000000000 +0100
cbf9030a
DM
704@@ -966,6 +966,24 @@
705 EQMP
706
707 {
708+ .name = "backup",
709+ .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?",
710+ .mhandler.cmd_new = qmp_marshal_input_backup,
711+ },
712+
713+ {
714+ .name = "backup-cancel",
715+ .args_type = "",
716+ .mhandler.cmd_new = qmp_marshal_input_backup_cancel,
717+ },
718+
719+ {
720+ .name = "query-backup",
721+ .args_type = "",
722+ .mhandler.cmd_new = qmp_marshal_input_query_backup,
723+ },
724+
725+ {
726 .name = "block-job-set-speed",
727 .args_type = "device:B,speed:o",
728 .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed,