]> git.proxmox.com Git - pve-qemu.git/blame - debian/patches/pve/0058-PVE-Use-coroutine-QMP-for-backup-cancel_backup.patch
Update to QEMU 5.2
[pve-qemu.git] / debian / patches / pve / 0058-PVE-Use-coroutine-QMP-for-backup-cancel_backup.patch
CommitLineData
817b7667
SR
1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: Stefan Reiter <s.reiter@proxmox.com>
3Date: Tue, 26 Jan 2021 15:45:30 +0100
4Subject: [PATCH] PVE: Use coroutine QMP for backup/cancel_backup
5
6Finally turn backup QMP calls into coroutines, now that it's possible.
7This has the benefit that calls are asynchronous to the main loop, i.e.
8long running operations like connecting to a PBS server will no longer
9hang the VM.
10
11Additionally, it allows us to get rid of block_on_coroutine_fn, which
12was always a hacky workaround.
13
14While we're already spring cleaning, also remove the QmpBackupTask
15struct, since we can now put the 'prepare' function directly into
16qmp_backup and thus no longer need those giant walls of text.
17
18(Note that for our patches to work with 5.2.0 this change is actually
19required, otherwise monitor_get_fd() fails as we're not in a QMP
20coroutine, but one we start ourselves - we could of course set the
21monitor for that coroutine ourselves, but let's just fix it the right
22way instead)
23
24Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
25---
26 block/monitor/block-hmp-cmds.c | 4 +-
27 hmp-commands.hx | 2 +
28 proxmox-backup-client.c | 31 -----
29 pve-backup.c | 232 ++++++++++-----------------------
30 qapi/block-core.json | 4 +-
31 5 files changed, 77 insertions(+), 196 deletions(-)
32
33diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
34index 46c63b1cf9..11c84d5508 100644
35--- a/block/monitor/block-hmp-cmds.c
36+++ b/block/monitor/block-hmp-cmds.c
37@@ -1013,7 +1013,7 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
38 g_free(global_snapshots);
39 }
40
41-void hmp_backup_cancel(Monitor *mon, const QDict *qdict)
42+void coroutine_fn hmp_backup_cancel(Monitor *mon, const QDict *qdict)
43 {
44 Error *error = NULL;
45
46@@ -1022,7 +1022,7 @@ void hmp_backup_cancel(Monitor *mon, const QDict *qdict)
47 hmp_handle_error(mon, error);
48 }
49
50-void hmp_backup(Monitor *mon, const QDict *qdict)
51+void coroutine_fn hmp_backup(Monitor *mon, const QDict *qdict)
52 {
53 Error *error = NULL;
54
55diff --git a/hmp-commands.hx b/hmp-commands.hx
56index 0c6b944850..54de3f80e6 100644
57--- a/hmp-commands.hx
58+++ b/hmp-commands.hx
59@@ -108,6 +108,7 @@ ERST
60 "\n\t\t\t Use -d to dump data into a directory instead"
61 "\n\t\t\t of using VMA format.",
62 .cmd = hmp_backup,
63+ .coroutine = true,
64 },
65
66 SRST
67@@ -121,6 +122,7 @@ ERST
68 .params = "",
69 .help = "cancel the current VM backup",
70 .cmd = hmp_backup_cancel,
71+ .coroutine = true,
72 },
73
74 SRST
75diff --git a/proxmox-backup-client.c b/proxmox-backup-client.c
76index 4ce7bc0b5e..0923037dec 100644
77--- a/proxmox-backup-client.c
78+++ b/proxmox-backup-client.c
79@@ -5,37 +5,6 @@
80
81 /* Proxmox Backup Server client bindings using coroutines */
82
83-typedef struct BlockOnCoroutineWrapper {
84- AioContext *ctx;
85- CoroutineEntry *entry;
86- void *entry_arg;
87- bool finished;
88-} BlockOnCoroutineWrapper;
89-
90-static void coroutine_fn block_on_coroutine_wrapper(void *opaque)
91-{
92- BlockOnCoroutineWrapper *wrapper = opaque;
93- wrapper->entry(wrapper->entry_arg);
94- wrapper->finished = true;
95- aio_wait_kick();
96-}
97-
98-void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg)
99-{
100- assert(!qemu_in_coroutine());
101-
102- AioContext *ctx = qemu_get_current_aio_context();
103- BlockOnCoroutineWrapper wrapper = {
104- .finished = false,
105- .entry = entry,
106- .entry_arg = entry_arg,
107- .ctx = ctx,
108- };
109- Coroutine *wrapper_co = qemu_coroutine_create(block_on_coroutine_wrapper, &wrapper);
110- aio_co_enter(ctx, wrapper_co);
111- AIO_WAIT_WHILE(ctx, !wrapper.finished);
112-}
113-
114 // This is called from another thread, so we use aio_co_schedule()
115 static void proxmox_backup_schedule_wake(void *data) {
116 CoCtxData *waker = (CoCtxData *)data;
117diff --git a/pve-backup.c b/pve-backup.c
118index 6b25292ba1..f7597ae55c 100644
119--- a/pve-backup.c
120+++ b/pve-backup.c
121@@ -357,7 +357,7 @@ static void job_cancel_bh(void *opaque) {
122 aio_co_enter(data->ctx, data->co);
123 }
124
125-static void coroutine_fn pvebackup_co_cancel(void *opaque)
126+void coroutine_fn qmp_backup_cancel(Error **errp)
127 {
128 Error *cancel_err = NULL;
129 error_setg(&cancel_err, "backup canceled");
130@@ -394,11 +394,6 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque)
131 qemu_co_mutex_unlock(&backup_state.backup_mutex);
132 }
133
134-void qmp_backup_cancel(Error **errp)
135-{
136- block_on_coroutine_fn(pvebackup_co_cancel, NULL);
137-}
138-
139 // assumes the caller holds backup_mutex
140 static int coroutine_fn pvebackup_co_add_config(
141 const char *file,
142@@ -531,50 +526,27 @@ static void create_backup_jobs_bh(void *opaque) {
143 aio_co_enter(data->ctx, data->co);
144 }
145
146-typedef struct QmpBackupTask {
147- const char *backup_file;
148- bool has_password;
149- const char *password;
150- bool has_keyfile;
151- const char *keyfile;
152- bool has_key_password;
153- const char *key_password;
154- bool has_backup_id;
155- const char *backup_id;
156- bool has_backup_time;
157- const char *fingerprint;
158- bool has_fingerprint;
159- int64_t backup_time;
160- bool has_use_dirty_bitmap;
161- bool use_dirty_bitmap;
162- bool has_format;
163- BackupFormat format;
164- bool has_config_file;
165- const char *config_file;
166- bool has_firewall_file;
167- const char *firewall_file;
168- bool has_devlist;
169- const char *devlist;
170- bool has_compress;
171- bool compress;
172- bool has_encrypt;
173- bool encrypt;
174- bool has_speed;
175- int64_t speed;
176- Error **errp;
177- UuidInfo *result;
178-} QmpBackupTask;
179-
180-static void coroutine_fn pvebackup_co_prepare(void *opaque)
181+UuidInfo coroutine_fn *qmp_backup(
182+ const char *backup_file,
183+ bool has_password, const char *password,
184+ bool has_keyfile, const char *keyfile,
185+ bool has_key_password, const char *key_password,
186+ bool has_fingerprint, const char *fingerprint,
187+ bool has_backup_id, const char *backup_id,
188+ bool has_backup_time, int64_t backup_time,
189+ bool has_use_dirty_bitmap, bool use_dirty_bitmap,
190+ bool has_compress, bool compress,
191+ bool has_encrypt, bool encrypt,
192+ bool has_format, BackupFormat format,
193+ bool has_config_file, const char *config_file,
194+ bool has_firewall_file, const char *firewall_file,
195+ bool has_devlist, const char *devlist,
196+ bool has_speed, int64_t speed, Error **errp)
197 {
198 assert(qemu_in_coroutine());
199
200 qemu_co_mutex_lock(&backup_state.backup_mutex);
201
202- QmpBackupTask *task = opaque;
203-
204- task->result = NULL; // just to be sure
205-
206 BlockBackend *blk;
207 BlockDriverState *bs = NULL;
208 const char *backup_dir = NULL;
209@@ -591,17 +563,17 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
210 const char *firewall_name = "qemu-server.fw";
211
212 if (backup_state.di_list) {
213- error_set(task->errp, ERROR_CLASS_GENERIC_ERROR,
214+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
215 "previous backup not finished");
216 qemu_co_mutex_unlock(&backup_state.backup_mutex);
217- return;
218+ return NULL;
219 }
220
221 /* Todo: try to auto-detect format based on file name */
222- BackupFormat format = task->has_format ? task->format : BACKUP_FORMAT_VMA;
223+ format = has_format ? format : BACKUP_FORMAT_VMA;
224
225- if (task->has_devlist) {
226- devs = g_strsplit_set(task->devlist, ",;:", -1);
227+ if (has_devlist) {
228+ devs = g_strsplit_set(devlist, ",;:", -1);
229
230 gchar **d = devs;
231 while (d && *d) {
232@@ -609,14 +581,14 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
233 if (blk) {
234 bs = blk_bs(blk);
235 if (!bdrv_is_inserted(bs)) {
236- error_setg(task->errp, QERR_DEVICE_HAS_NO_MEDIUM, *d);
237+ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d);
238 goto err;
239 }
240 PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1);
241 di->bs = bs;
242 di_list = g_list_append(di_list, di);
243 } else {
244- error_set(task->errp, ERROR_CLASS_DEVICE_NOT_FOUND,
245+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
246 "Device '%s' not found", *d);
247 goto err;
248 }
249@@ -639,7 +611,7 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
250 }
251
252 if (!di_list) {
253- error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "empty device list");
254+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list");
255 goto err;
256 }
257
258@@ -649,13 +621,13 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
259 while (l) {
260 PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
261 l = g_list_next(l);
262- if (bdrv_op_is_blocked(di->bs, BLOCK_OP_TYPE_BACKUP_SOURCE, task->errp)) {
263+ if (bdrv_op_is_blocked(di->bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
264 goto err;
265 }
266
267 ssize_t size = bdrv_getlength(di->bs);
268 if (size < 0) {
269- error_setg_errno(task->errp, -di->size, "bdrv_getlength failed");
270+ error_setg_errno(errp, -di->size, "bdrv_getlength failed");
271 goto err;
272 }
273 di->size = size;
274@@ -682,47 +654,44 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
275 }
276
277 if (format == BACKUP_FORMAT_PBS) {
278- if (!task->has_password) {
279- error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'password'");
280+ if (!has_password) {
281+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'password'");
282 goto err_mutex;
283 }
284- if (!task->has_backup_id) {
285- error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'backup-id'");
286+ if (!has_backup_id) {
287+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'backup-id'");
288 goto err_mutex;
289 }
290- if (!task->has_backup_time) {
291- error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'backup-time'");
292+ if (!has_backup_time) {
293+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'backup-time'");
294 goto err_mutex;
295 }
296
297 int dump_cb_block_size = PROXMOX_BACKUP_DEFAULT_CHUNK_SIZE; // Hardcoded (4M)
298 firewall_name = "fw.conf";
299
300- bool use_dirty_bitmap = task->has_use_dirty_bitmap && task->use_dirty_bitmap;
301-
302-
303 char *pbs_err = NULL;
304 pbs = proxmox_backup_new(
305- task->backup_file,
306- task->backup_id,
307- task->backup_time,
308+ backup_file,
309+ backup_id,
310+ backup_time,
311 dump_cb_block_size,
312- task->has_password ? task->password : NULL,
313- task->has_keyfile ? task->keyfile : NULL,
314- task->has_key_password ? task->key_password : NULL,
315- task->has_compress ? task->compress : true,
316- task->has_encrypt ? task->encrypt : task->has_keyfile,
317- task->has_fingerprint ? task->fingerprint : NULL,
318+ has_password ? password : NULL,
319+ has_keyfile ? keyfile : NULL,
320+ has_key_password ? key_password : NULL,
321+ has_compress ? compress : true,
322+ has_encrypt ? encrypt : has_keyfile,
323+ has_fingerprint ? fingerprint : NULL,
324 &pbs_err);
325
326 if (!pbs) {
327- error_set(task->errp, ERROR_CLASS_GENERIC_ERROR,
328+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
329 "proxmox_backup_new failed: %s", pbs_err);
330 proxmox_backup_free_error(pbs_err);
331 goto err_mutex;
332 }
333
334- int connect_result = proxmox_backup_co_connect(pbs, task->errp);
335+ int connect_result = proxmox_backup_co_connect(pbs, errp);
336 if (connect_result < 0)
337 goto err_mutex;
338
339@@ -741,9 +710,9 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
340 BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(di->bs, PBS_BITMAP_NAME);
341 bool expect_only_dirty = false;
342
343- if (use_dirty_bitmap) {
344+ if (has_use_dirty_bitmap && use_dirty_bitmap) {
345 if (bitmap == NULL) {
346- bitmap = bdrv_create_dirty_bitmap(di->bs, dump_cb_block_size, PBS_BITMAP_NAME, task->errp);
347+ bitmap = bdrv_create_dirty_bitmap(di->bs, dump_cb_block_size, PBS_BITMAP_NAME, errp);
348 if (!bitmap) {
349 goto err_mutex;
350 }
351@@ -773,12 +742,12 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
352 }
353 }
354
355- int dev_id = proxmox_backup_co_register_image(pbs, devname, di->size, expect_only_dirty, task->errp);
356+ int dev_id = proxmox_backup_co_register_image(pbs, devname, di->size, expect_only_dirty, errp);
357 if (dev_id < 0) {
358 goto err_mutex;
359 }
360
361- if (!(di->target = bdrv_backup_dump_create(dump_cb_block_size, di->size, pvebackup_co_dump_pbs_cb, di, task->errp))) {
362+ if (!(di->target = bdrv_backup_dump_create(dump_cb_block_size, di->size, pvebackup_co_dump_pbs_cb, di, errp))) {
363 goto err_mutex;
364 }
365
366@@ -792,10 +761,10 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
367 backup_state.stat.bitmap_list = g_list_append(backup_state.stat.bitmap_list, info);
368 }
369 } else if (format == BACKUP_FORMAT_VMA) {
370- vmaw = vma_writer_create(task->backup_file, uuid, &local_err);
371+ vmaw = vma_writer_create(backup_file, uuid, &local_err);
372 if (!vmaw) {
373 if (local_err) {
374- error_propagate(task->errp, local_err);
375+ error_propagate(errp, local_err);
376 }
377 goto err_mutex;
378 }
379@@ -806,25 +775,25 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
380 PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
381 l = g_list_next(l);
382
383- if (!(di->target = bdrv_backup_dump_create(VMA_CLUSTER_SIZE, di->size, pvebackup_co_dump_vma_cb, di, task->errp))) {
384+ if (!(di->target = bdrv_backup_dump_create(VMA_CLUSTER_SIZE, di->size, pvebackup_co_dump_vma_cb, di, errp))) {
385 goto err_mutex;
386 }
387
388 const char *devname = bdrv_get_device_name(di->bs);
389 di->dev_id = vma_writer_register_stream(vmaw, devname, di->size);
390 if (di->dev_id <= 0) {
391- error_set(task->errp, ERROR_CLASS_GENERIC_ERROR,
392+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
393 "register_stream failed");
394 goto err_mutex;
395 }
396 }
397 } else if (format == BACKUP_FORMAT_DIR) {
398- if (mkdir(task->backup_file, 0640) != 0) {
399- error_setg_errno(task->errp, errno, "can't create directory '%s'\n",
400- task->backup_file);
401+ if (mkdir(backup_file, 0640) != 0) {
402+ error_setg_errno(errp, errno, "can't create directory '%s'\n",
403+ backup_file);
404 goto err_mutex;
405 }
406- backup_dir = task->backup_file;
407+ backup_dir = backup_file;
408
409 l = di_list;
410 while (l) {
411@@ -838,34 +807,34 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
412 bdrv_img_create(di->targetfile, "raw", NULL, NULL, NULL,
413 di->size, flags, false, &local_err);
414 if (local_err) {
415- error_propagate(task->errp, local_err);
416+ error_propagate(errp, local_err);
417 goto err_mutex;
418 }
419
420 di->target = bdrv_open(di->targetfile, NULL, NULL, flags, &local_err);
421 if (!di->target) {
422- error_propagate(task->errp, local_err);
423+ error_propagate(errp, local_err);
424 goto err_mutex;
425 }
426 }
427 } else {
428- error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
429+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
430 goto err_mutex;
431 }
432
433
434 /* add configuration file to archive */
435- if (task->has_config_file) {
436- if (pvebackup_co_add_config(task->config_file, config_name, format, backup_dir,
437- vmaw, pbs, task->errp) != 0) {
438+ if (has_config_file) {
439+ if (pvebackup_co_add_config(config_file, config_name, format, backup_dir,
440+ vmaw, pbs, errp) != 0) {
441 goto err_mutex;
442 }
443 }
444
445 /* add firewall file to archive */
446- if (task->has_firewall_file) {
447- if (pvebackup_co_add_config(task->firewall_file, firewall_name, format, backup_dir,
448- vmaw, pbs, task->errp) != 0) {
449+ if (has_firewall_file) {
450+ if (pvebackup_co_add_config(firewall_file, firewall_name, format, backup_dir,
451+ vmaw, pbs, errp) != 0) {
452 goto err_mutex;
453 }
454 }
455@@ -883,7 +852,7 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
456 if (backup_state.stat.backup_file) {
457 g_free(backup_state.stat.backup_file);
458 }
459- backup_state.stat.backup_file = g_strdup(task->backup_file);
460+ backup_state.stat.backup_file = g_strdup(backup_file);
461
462 uuid_copy(backup_state.stat.uuid, uuid);
463 uuid_unparse_lower(uuid, backup_state.stat.uuid_str);
464@@ -898,7 +867,7 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
465
466 qemu_mutex_unlock(&backup_state.stat.lock);
467
468- backup_state.speed = (task->has_speed && task->speed > 0) ? task->speed : 0;
469+ backup_state.speed = (has_speed && speed > 0) ? speed : 0;
470
471 backup_state.vmaw = vmaw;
472 backup_state.pbs = pbs;
473@@ -908,8 +877,6 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
474 uuid_info = g_malloc0(sizeof(*uuid_info));
475 uuid_info->UUID = uuid_str;
476
477- task->result = uuid_info;
478-
479 /* Run create_backup_jobs_bh outside of coroutine (in BH) but keep
480 * backup_mutex locked. This is fine, a CoMutex can be held across yield
481 * points, and we'll release it as soon as the BH reschedules us.
482@@ -923,7 +890,7 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
483 qemu_coroutine_yield();
484
485 if (local_err) {
486- error_propagate(task->errp, local_err);
487+ error_propagate(errp, local_err);
488 goto err;
489 }
490
491@@ -936,7 +903,7 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
492 /* start the first job in the transaction */
493 job_txn_start_seq(backup_state.txn);
494
495- return;
496+ return uuid_info;
497
498 err_mutex:
499 qemu_mutex_unlock(&backup_state.stat.lock);
500@@ -967,7 +934,7 @@ err:
501 if (vmaw) {
502 Error *err = NULL;
503 vma_writer_close(vmaw, &err);
504- unlink(task->backup_file);
505+ unlink(backup_file);
506 }
507
508 if (pbs) {
509@@ -978,65 +945,8 @@ err:
510 rmdir(backup_dir);
511 }
512
513- task->result = NULL;
514-
515 qemu_co_mutex_unlock(&backup_state.backup_mutex);
516- return;
517-}
518-
519-UuidInfo *qmp_backup(
520- const char *backup_file,
521- bool has_password, const char *password,
522- bool has_keyfile, const char *keyfile,
523- bool has_key_password, const char *key_password,
524- bool has_fingerprint, const char *fingerprint,
525- bool has_backup_id, const char *backup_id,
526- bool has_backup_time, int64_t backup_time,
527- bool has_use_dirty_bitmap, bool use_dirty_bitmap,
528- bool has_compress, bool compress,
529- bool has_encrypt, bool encrypt,
530- bool has_format, BackupFormat format,
531- bool has_config_file, const char *config_file,
532- bool has_firewall_file, const char *firewall_file,
533- bool has_devlist, const char *devlist,
534- bool has_speed, int64_t speed, Error **errp)
535-{
536- QmpBackupTask task = {
537- .backup_file = backup_file,
538- .has_password = has_password,
539- .password = password,
540- .has_keyfile = has_keyfile,
541- .keyfile = keyfile,
542- .has_key_password = has_key_password,
543- .key_password = key_password,
544- .has_fingerprint = has_fingerprint,
545- .fingerprint = fingerprint,
546- .has_backup_id = has_backup_id,
547- .backup_id = backup_id,
548- .has_backup_time = has_backup_time,
549- .backup_time = backup_time,
550- .has_use_dirty_bitmap = has_use_dirty_bitmap,
551- .use_dirty_bitmap = use_dirty_bitmap,
552- .has_compress = has_compress,
553- .compress = compress,
554- .has_encrypt = has_encrypt,
555- .encrypt = encrypt,
556- .has_format = has_format,
557- .format = format,
558- .has_config_file = has_config_file,
559- .config_file = config_file,
560- .has_firewall_file = has_firewall_file,
561- .firewall_file = firewall_file,
562- .has_devlist = has_devlist,
563- .devlist = devlist,
564- .has_speed = has_speed,
565- .speed = speed,
566- .errp = errp,
567- };
568-
569- block_on_coroutine_fn(pvebackup_co_prepare, &task);
570-
571- return task.result;
572+ return NULL;
573 }
574
575 BackupStatus *qmp_query_backup(Error **errp)
576diff --git a/qapi/block-core.json b/qapi/block-core.json
577index dee3d87efe..82133e2bee 100644
578--- a/qapi/block-core.json
579+++ b/qapi/block-core.json
580@@ -847,7 +847,7 @@
581 '*config-file': 'str',
582 '*firewall-file': 'str',
583 '*devlist': 'str', '*speed': 'int' },
584- 'returns': 'UuidInfo' }
585+ 'returns': 'UuidInfo', 'coroutine': true }
586
587 ##
588 # @query-backup:
589@@ -869,7 +869,7 @@
590 # Notes: This command succeeds even if there is no backup process running.
591 #
592 ##
593-{ 'command': 'backup-cancel' }
594+{ 'command': 'backup-cancel', 'coroutine': true }
595
596 ##
597 # @ProxmoxSupportStatus: