]> git.proxmox.com Git - pve-qemu-kvm.git/blobdiff - debian/patches/0003-add-backup-related-monitor-commands.patch
Two more fixes
[pve-qemu-kvm.git] / debian / patches / 0003-add-backup-related-monitor-commands.patch
index 3d4724f3dd58346ca285c2f69a0184a617ea1f3f..0a23a45bc941322f364966c1f388a2f60cbebed3 100644 (file)
@@ -1,9 +1,9 @@
-From 6e030cd398065d17ab01fe7b2a3e655c5e5f19eb Mon Sep 17 00:00:00 2001
+From cf9cc8878d3246069da3fdf7ec865b7b983487fe Mon Sep 17 00:00:00 2001
 From: Dietmar Maurer <dietmar@proxmox.com>
 Date: Tue, 13 Nov 2012 11:27:56 +0100
-Subject: [PATCH v3 3/6] add backup related monitor commands
+Subject: [PATCH v5 3/6] add backup related monitor commands
 
-We use a generic BackupDriver struct to encaplulated all archive format
+We use a generic BackupDriver struct to encapsulate all archive format
 related function.
 
 Another option would be to simply dump <devid,cluster_num,cluster_data> to
@@ -12,63 +12,76 @@ could move the whole archive format related code out of qemu.
 
 Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
 ---
- backup.h         |   13 ++
- blockdev.c       |  376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- hmp-commands.hx  |   31 +++++
- hmp.c            |   63 +++++++++
+ backup.h         |   15 ++
+ blockdev.c       |  423 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ hmp-commands.hx  |   31 ++++
+ hmp.c            |   63 ++++++++
  hmp.h            |    3 +
  monitor.c        |    7 +
- qapi-schema.json |   81 ++++++++++++
+ qapi-schema.json |   95 ++++++++++++
  qmp-commands.hx  |   27 ++++
- 8 files changed, 601 insertions(+), 0 deletions(-)
+ 8 files changed, 664 insertions(+), 0 deletions(-)
 
 diff --git a/backup.h b/backup.h
-index e1f0290..ae4aa8c 100644
+index 9b1ea1c..22598a6 100644
 --- a/backup.h
 +++ b/backup.h
-@@ -27,4 +27,17 @@ int backup_job_start(BlockDriverState *bs, BackupDumpFunc *backup_dump_cb,
-                      BlockDriverCompletionFunc *backup_complete_cb,
-                      void *opaque);
+@@ -14,6 +14,9 @@
+ #ifndef QEMU_BACKUP_H
+ #define QEMU_BACKUP_H
++#include <uuid/uuid.h>
++#include "block/block.h"
++
+ #define BACKUP_CLUSTER_BITS 16
+ #define BACKUP_CLUSTER_SIZE (1<<BACKUP_CLUSTER_BITS)
+ #define BACKUP_BLOCKS_PER_CLUSTER (BACKUP_CLUSTER_SIZE/BDRV_SECTOR_SIZE)
+@@ -27,4 +30,16 @@ int backup_job_create(BlockDriverState *bs, BackupDumpFunc *backup_dump_cb,
+                       BlockDriverCompletionFunc *backup_complete_cb,
+                       void *opaque, int64_t speed);
  
 +typedef struct BackupDriver {
 +    const char *format;
-+    void *(*open_cb)(const char *filename, uuid_t uuid, int64_t speed,
-+                     Error **errp);
-+    int (*close_cb)(void *opaque, Error **errp);
-+    int (*register_config_cb)(void *opaque, const char *name, gpointer data,
++    void *(*open)(const char *filename, uuid_t uuid, Error **errp);
++    int (*close)(void *opaque, Error **errp);
++    int (*register_config)(void *opaque, const char *name, gpointer data,
 +                              size_t data_len);
-+    int (*register_stream_cb)(void *opaque, const char *devname, size_t size);
-+    int (*dump_cb)(void *opaque, uint8_t dev_id, int64_t cluster_num,
++    int (*register_stream)(void *opaque, const char *devname, size_t size);
++    int (*dump)(void *opaque, uint8_t dev_id, int64_t cluster_num,
 +                   unsigned char *buf, size_t *zero_bytes);
-+    int (*complete_cb)(void *opaque, uint8_t dev_id, int ret);
++    int (*complete)(void *opaque, uint8_t dev_id, int ret);
 +} BackupDriver;
 +
  #endif /* QEMU_BACKUP_H */
 diff --git a/blockdev.c b/blockdev.c
-index e73fd6e..c635d21 100644
+index 63e6f1e..84f598d 100644
 --- a/blockdev.c
 +++ b/blockdev.c
 @@ -20,6 +20,7 @@
  #include "qmp-commands.h"
  #include "trace.h"
- #include "arch_init.h"
+ #include "sysemu/arch_init.h"
 +#include "backup.h"
  
  static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
  
-@@ -1321,6 +1322,381 @@ void qmp_drive_mirror(const char *device, const char *target,
+@@ -1334,6 +1335,428 @@ void qmp_drive_mirror(const char *device, const char *target,
      drive_get_ref(drive_get_by_blockdev(bs));
  }
  
 +/* Backup related function */
 +
++static void backup_run_next_job(void);
++
 +static struct GenericBackupState {
 +    Error *error;
++    bool cancel;
 +    uuid_t uuid;
 +    char uuid_str[37];
++    int64_t speed;
 +    time_t start_time;
 +    time_t end_time;
-+    char *backupfile;
++    char *backup_file;
 +    const BackupDriver *driver;
 +    void *writer;
 +    GList *bcb_list;
@@ -79,8 +92,9 @@ index e73fd6e..c635d21 100644
 +
 +typedef struct BackupCB {
 +    BlockDriverState *bs;
-+    bool job_started;
 +    uint8_t dev_id;
++    bool started;
++    bool completed;
 +    size_t size;
 +    size_t transferred;
 +    size_t zero_bytes;
@@ -93,12 +107,12 @@ index e73fd6e..c635d21 100644
 +
 +    assert(backup_state.driver);
 +    assert(backup_state.writer);
-+    assert(backup_state.driver->dump_cb);
++    assert(backup_state.driver->dump);
 +
 +    size_t zero_bytes = 0;
-+    int bytes = backup_state.driver->dump_cb(backup_state.writer,
-+                                             bcb->dev_id, cluster_num,
-+                                             buf, &zero_bytes);
++    int bytes = backup_state.driver->dump(backup_state.writer,
++                                          bcb->dev_id, cluster_num,
++                                          buf, &zero_bytes);
 +
 +    if (bytes > 0) {
 +        bcb->transferred += bytes;
@@ -117,14 +131,18 @@ index e73fd6e..c635d21 100644
 +    if (backup_state.writer && backup_state.driver) {
 +        backup_state.end_time = time(NULL);
 +        Error *local_err = NULL;
-+        backup_state.driver->close_cb(backup_state.writer, &local_err);
++        backup_state.driver->close(backup_state.writer, &local_err);
 +        error_propagate(&backup_state.error, local_err);
 +        backup_state.writer = NULL;
++    }
 +
++    if (backup_state.bcb_list) {
 +        GList *l = backup_state.bcb_list;
 +        while (l) {
-+            g_free(l->data);
++            BackupCB *bcb = l->data;
 +            l = g_list_next(l);
++            drive_put_ref_bh_schedule(drive_get_by_blockdev(bcb->bs));
++            g_free(bcb);
 +        }
 +        g_list_free(backup_state.bcb_list);
 +        backup_state.bcb_list = NULL;
@@ -137,77 +155,96 @@ index e73fd6e..c635d21 100644
 +
 +    assert(backup_state.driver);
 +    assert(backup_state.writer);
-+    assert(backup_state.driver->complete_cb);
-+    assert(backup_state.driver->close_cb);
-+
-+    drive_put_ref_bh_schedule(drive_get_by_blockdev(bcb->bs));
++    assert(backup_state.driver->complete);
++    assert(backup_state.driver->close);
 +
-+    backup_state.bcb_list = g_list_remove(backup_state.bcb_list, bcb);
++    bcb->completed = true;
 +
-+    backup_state.driver->complete_cb(backup_state.writer, bcb->dev_id, ret);
++    backup_state.driver->complete(backup_state.writer, bcb->dev_id, ret);
 +
-+    if (g_list_length(backup_state.bcb_list) == 0) {
-+        backup_cleanup();
++    if (!backup_state.cancel) {
++        backup_run_next_job();
 +    }
-+
-+    g_free(bcb);
 +}
 +
 +static void backup_cancel(void)
 +{
-+    GList *l = backup_state.bcb_list;
-+    int job_count = 0;
++    backup_state.cancel = true;
 +
++    if (!backup_state.error) {
++        error_setg(&backup_state.error, "backup cancelled");
++    }
++
++    /* drain all i/o (awake jobs waiting for aio) */
++    bdrv_drain_all();
++
++    int job_count = 0;
++    GList *l = backup_state.bcb_list;
 +    while (l) {
 +        BackupCB *bcb = l->data;
 +        l = g_list_next(l);
-+        if (bcb->bs->job && bcb->job_started) {
-+            block_job_cancel(bcb->bs->job);
++        BlockJob *job = bcb->bs->job;
++        if (job) {
 +            job_count++;
++            if (!bcb->started) {
++                bcb->started = true;
++                backup_job_start(bcb->bs, true);
++            }
++            if (!bcb->completed) {
++                block_job_cancel_sync(job);
++            }
 +        }
 +    }
 +
-+    if (!job_count) {
-+        backup_cleanup();
-+    }
++    backup_cleanup();
 +}
 +
 +void qmp_backup_cancel(Error **errp)
 +{
-+    if (!backup_state.error) {
-+        error_setg(&backup_state.error, "backup cancelled");
++    backup_cancel();
++}
++
++static void backup_run_next_job(void)
++{
++    GList *l = backup_state.bcb_list;
++    while (l) {
++        BackupCB *bcb = l->data;
++        l = g_list_next(l);
++
++        if (bcb->bs && bcb->bs->job && !bcb->completed) {
++            if (!bcb->started) {
++                bcb->started = true;
++                bool cancel = backup_state.error || backup_state.cancel;
++                backup_job_start(bcb->bs, cancel);
++            }
++            return;
++        }
 +    }
 +
-+    backup_cancel();
++    backup_cleanup();
 +}
 +
 +static void backup_start_jobs(void)
 +{
-+    /* start all jobs (one for each device) */
++    /* create all jobs (one for each device), start first one */
 +    GList *l = backup_state.bcb_list;
 +    while (l) {
 +        BackupCB *bcb = l->data;
 +        l = g_list_next(l);
 +
-+        if (backup_job_start(bcb->bs, backup_dump_cb, backup_complete_cb,
-+                             bcb) == 0) {
-+            bcb->job_started = true;
-+            /* Grab a reference so hotplug does not delete the
-+             * BlockDriverState from underneath us.
-+             */
-+            drive_get_ref(drive_get_by_blockdev(bcb->bs));
-+        } else {
-+            if (!backup_state.error) {
-+                error_setg(&backup_state.error, "backup_job_start failed");
-+            }
++        if (backup_job_create(bcb->bs, backup_dump_cb, backup_complete_cb,
++                              bcb, backup_state.speed) != 0) {
++            error_setg(&backup_state.error, "backup_job_create failed");
 +            backup_cancel();
 +            return;
-+         }
++        }
 +    }
++
++    backup_run_next_job();
 +}
 +
-+char *qmp_backup(const char *backupfile, bool has_format, const char *format,
-+                 bool has_config_filename, const char *config_filename,
++char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format,
++                 bool has_config_file, const char *config_file,
 +                 bool has_devlist, const char *devlist,
 +                 bool has_speed, int64_t speed, Error **errp)
 +{
@@ -218,20 +255,25 @@ index e73fd6e..c635d21 100644
 +    gchar **devs = NULL;
 +    GList *bcblist = NULL;
 +
++    if (backup_state.bcb_list) {
++        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++                  "previous backup not finished");
++        return NULL;
++    }
++
 +    /* Todo: try to auto-detect format based on file name */
-+    format = has_format ? format : "vma";
++    format = has_format ? format : BACKUP_FORMAT_VMA;
 +
 +    /* fixme: find driver for specifued format */
 +    const BackupDriver *driver = NULL;
 +
 +    if (!driver) {
-+        error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+                  "no backup driver for format '%s'", format);
++        error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
 +        return NULL;
 +    }
 +
 +    if (has_devlist) {
-+        devs = g_strsplit(devlist, ",;:", -1);
++        devs = g_strsplit_set(devlist, ",;:", -1);
 +
 +        gchar **d = devs;
 +        while (d && *d) {
@@ -287,7 +329,7 @@ index e73fd6e..c635d21 100644
 +
 +    uuid_generate(uuid);
 +
-+    writer = driver->open_cb(backupfile, uuid, speed, &local_err);
++    writer = driver->open(backup_file, uuid, &local_err);
 +    if (!writer) {
 +        if (error_is_set(&local_err)) {
 +            error_propagate(errp, local_err);
@@ -305,7 +347,7 @@ index e73fd6e..c635d21 100644
 +
 +        int64_t size = bdrv_getlength(bcb->bs);
 +        const char *devname = bdrv_get_device_name(bcb->bs);
-+        bcb->dev_id = driver->register_stream_cb(writer, devname, size);
++        bcb->dev_id = driver->register_stream(writer, devname, size);
 +        if (bcb->dev_id <= 0) {
 +            error_set(errp, ERROR_CLASS_GENERIC_ERROR,
 +                      "register_stream failed");
@@ -316,17 +358,17 @@ index e73fd6e..c635d21 100644
 +    }
 +
 +    /* add configuration file to archive */
-+    if (has_config_filename) {
++    if (has_config_file) {
 +        char *cdata = NULL;
 +        gsize clen = 0;
 +        GError *err = NULL;
-+        if (!g_file_get_contents(config_filename, &cdata, &clen, &err)) {
-+            error_setg(errp, "unable to read file '%s'", config_filename);
++        if (!g_file_get_contents(config_file, &cdata, &clen, &err)) {
++            error_setg(errp, "unable to read file '%s'", config_file);
 +            goto err;
 +        }
 +
-+        const char *basename = g_path_get_basename(config_filename);
-+        if (driver->register_config_cb(writer, basename, cdata, clen) <= 0) {
++        const char *basename = g_path_get_basename(config_file);
++        if (driver->register_config(writer, basename, cdata, clen) < 0) {
 +            error_setg(errp, "register_config failed");
 +            g_free(cdata);
 +            goto err;
@@ -336,6 +378,8 @@ index e73fd6e..c635d21 100644
 +
 +    /* initialize global backup_state now */
 +
++    backup_state.cancel = false;
++
 +    if (backup_state.error) {
 +        error_free(backup_state.error);
 +        backup_state.error = NULL;
@@ -343,13 +387,15 @@ index e73fd6e..c635d21 100644
 +
 +    backup_state.driver = driver;
 +
++    backup_state.speed = (has_speed && speed > 0) ? speed : 0;
++
 +    backup_state.start_time = time(NULL);
 +    backup_state.end_time = 0;
 +
-+    if (backup_state.backupfile) {
-+        g_free(backup_state.backupfile);
++    if (backup_state.backup_file) {
++        g_free(backup_state.backup_file);
 +    }
-+    backup_state.backupfile = g_strdup(backupfile);
++    backup_state.backup_file = g_strdup(backup_file);
 +
 +    backup_state.writer = writer;
 +
@@ -362,6 +408,16 @@ index e73fd6e..c635d21 100644
 +    backup_state.transferred = 0;
 +    backup_state.zero_bytes = 0;
 +
++    /* Grab a reference so hotplug does not delete the
++     * BlockDriverState from underneath us.
++     */
++    l = bcblist;
++    while (l) {
++        BackupCB *bcb = l->data;
++        l = g_list_next(l);
++        drive_get_ref(drive_get_by_blockdev(bcb->bs));
++    }
++
 +    backup_start_jobs();
 +
 +    return g_strdup(backup_state.uuid_str);
@@ -380,10 +436,10 @@ index e73fd6e..c635d21 100644
 +    }
 +
 +    if (writer) {
-+        unlink(backupfile);
++        unlink(backup_file);
 +        if (driver) {
 +            Error *err = NULL;
-+            driver->close_cb(writer, &err);
++            driver->close(writer, &err);
 +        }
 +    }
 +
@@ -403,9 +459,9 @@ index e73fd6e..c635d21 100644
 +    info->has_start_time = true;
 +    info->start_time = backup_state.start_time;
 +
-+    if (backup_state.backupfile) {
-+        info->has_backupfile = true;
-+        info->backupfile = g_strdup(backup_state.backupfile);
++    if (backup_state.backup_file) {
++        info->has_backup_file = true;
++        info->backup_file = g_strdup(backup_state.backup_file);
 +    }
 +
 +    info->has_uuid = true;
@@ -439,7 +495,7 @@ index e73fd6e..c635d21 100644
  {
      BlockDriverState *bs;
 diff --git a/hmp-commands.hx b/hmp-commands.hx
-index 010b8c9..57be357 100644
+index 64008a9..0f178d8 100644
 --- a/hmp-commands.hx
 +++ b/hmp-commands.hx
 @@ -83,6 +83,35 @@ STEXI
@@ -478,7 +534,7 @@ index 010b8c9..57be357 100644
      {
          .name       = "block_job_set_speed",
          .args_type  = "device:B,speed:o",
-@@ -1558,6 +1587,8 @@ show CPU statistics
+@@ -1630,6 +1659,8 @@ show CPU statistics
  show user network stack connection states
  @item info migrate
  show migration status
@@ -488,14 +544,14 @@ index 010b8c9..57be357 100644
  show current migration capabilities
  @item info migrate_cache_size
 diff --git a/hmp.c b/hmp.c
-index 180ba2b..600792f 100644
+index 2f47a8a..b2c1f23 100644
 --- a/hmp.c
 +++ b/hmp.c
-@@ -130,6 +130,38 @@ void hmp_info_mice(Monitor *mon)
+@@ -131,6 +131,38 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict)
      qapi_free_MouseInfoList(mice_list);
  }
  
-+void hmp_info_backup(Monitor *mon)
++void hmp_info_backup(Monitor *mon, const QDict *qdict)
 +{
 +    BackupStatus *info;
 +
@@ -508,14 +564,14 @@ index 180ba2b..600792f 100644
 +            monitor_printf(mon, "Backup status: %s\n", info->status);
 +        }
 +    }
-+    if (info->has_backupfile) {
++    if (info->has_backup_file) {
 +        int per = (info->has_total && info->total &&
 +            info->has_transferred && info->transferred) ?
 +            (info->transferred * 100)/info->total : 0;
 +        int zero_per = (info->has_total && info->total &&
 +                        info->has_zero_bytes && info->zero_bytes) ?
 +            (info->zero_bytes * 100)/info->total : 0;
-+        monitor_printf(mon, "Backup file: %s\n", info->backupfile);
++        monitor_printf(mon, "Backup file: %s\n", info->backup_file);
 +        monitor_printf(mon, "Backup uuid: %s\n", info->uuid);
 +        monitor_printf(mon, "Total size: %zd\n", info->total);
 +        monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n",
@@ -527,10 +583,10 @@ index 180ba2b..600792f 100644
 +    qapi_free_BackupStatus(info);
 +}
 +
- void hmp_info_migrate(Monitor *mon)
+ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
  {
      MigrationInfo *info;
-@@ -977,6 +1009,37 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
+@@ -998,6 +1030,37 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
      hmp_handle_error(mon, &error);
  }
  
@@ -549,14 +605,14 @@ index 180ba2b..600792f 100644
 +
 +void hmp_backup(Monitor *mon, const QDict *qdict)
 +{
-+    const char *backupfile = qdict_get_str(qdict, "backupfile");
++    const char *backup_file = qdict_get_str(qdict, "backup-file");
 +    const char *devlist = qdict_get_try_str(qdict, "devlist");
 +    int64_t speed = qdict_get_try_int(qdict, "speed", 0);
 +
 +    Error *errp = NULL;
 +
-+    qmp_backup(backupfile, false, NULL, false, NULL, !!devlist, devlist,
-+               qdict_haskey(qdict, "speed"), speed, &errp);
++    qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
++               devlist, qdict_haskey(qdict, "speed"), speed, &errp);
 +
 +    if (error_is_set(&errp)) {
 +        monitor_printf(mon, "%s\n", error_get_pretty(errp));
@@ -569,18 +625,18 @@ index 180ba2b..600792f 100644
  {
      Error *error = NULL;
 diff --git a/hmp.h b/hmp.h
-index 0ab03be..20c9a62 100644
+index 30b3c20..ad4cf80 100644
 --- a/hmp.h
 +++ b/hmp.h
-@@ -28,6 +28,7 @@ void hmp_info_mice(Monitor *mon);
- void hmp_info_migrate(Monitor *mon);
- void hmp_info_migrate_capabilities(Monitor *mon);
- void hmp_info_migrate_cache_size(Monitor *mon);
-+void hmp_info_backup(Monitor *mon);
- void hmp_info_cpus(Monitor *mon);
- void hmp_info_block(Monitor *mon);
- void hmp_info_blockstats(Monitor *mon);
-@@ -63,6 +64,8 @@ void hmp_eject(Monitor *mon, const QDict *qdict);
+@@ -28,6 +28,7 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict);
+ void hmp_info_migrate(Monitor *mon, const QDict *qdict);
+ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
+ void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
++void hmp_info_backup(Monitor *mon, const QDict *qdict);
+ void hmp_info_cpus(Monitor *mon, const QDict *qdict);
+ void hmp_info_block(Monitor *mon, const QDict *qdict);
+ void hmp_info_blockstats(Monitor *mon, const QDict *qdict);
+@@ -65,6 +66,8 @@ void hmp_eject(Monitor *mon, const QDict *qdict);
  void hmp_change(Monitor *mon, const QDict *qdict);
  void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict);
  void hmp_block_stream(Monitor *mon, const QDict *qdict);
@@ -590,10 +646,10 @@ index 0ab03be..20c9a62 100644
  void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
  void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
 diff --git a/monitor.c b/monitor.c
-index c0e32d6..85cf47e 100644
+index 6a0f257..e4a810c 100644
 --- a/monitor.c
 +++ b/monitor.c
-@@ -2680,6 +2680,13 @@ static mon_cmd_t info_cmds[] = {
+@@ -2666,6 +2666,13 @@ static mon_cmd_t info_cmds[] = {
      },
  #endif
      {
@@ -601,17 +657,17 @@ index c0e32d6..85cf47e 100644
 +        .args_type  = "",
 +        .params     = "",
 +        .help       = "show backup status",
-+        .mhandler.info = hmp_info_backup,
++        .mhandler.cmd = hmp_info_backup,
 +    },
 +    {
          .name       = "migrate",
          .args_type  = "",
          .params     = "",
 diff --git a/qapi-schema.json b/qapi-schema.json
-index 5dfa052..71e0ed9 100644
+index 7275b5d..09ca8ef 100644
 --- a/qapi-schema.json
 +++ b/qapi-schema.json
-@@ -358,6 +358,39 @@
+@@ -425,6 +425,39 @@
  { 'type': 'EventInfo', 'data': {'name': 'str'} }
  
  ##
@@ -620,7 +676,7 @@ index 5dfa052..71e0ed9 100644
 +# Detailed backup status.
 +#
 +# @status: #optional string describing the current backup status.
-+#          Tthis can be 'active', 'done', 'error'. If this field is not
++#          This can be 'active', 'done', 'error'. If this field is not
 +#          returned, no backup process has been initiated
 +#
 +# @errmsg: #optional error message (only returned if status is 'error')
@@ -639,29 +695,39 @@ index 5dfa052..71e0ed9 100644
 +#
 +# @uuid: #optional uuid for this backup job
 +#
-+# Since: 1.4.0
++# Since: 1.5.0
 +##
 +{ 'type': 'BackupStatus',
 +  'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int',
 +           '*transferred': 'int', '*zero-bytes': 'int',
 +           '*start-time': 'int', '*end-time': 'int',
-+           '*backupfile': 'str', '*uuid': 'str' } }
++           '*backup-file': 'str', '*uuid': 'str' } }
 +
 +##
  # @query-events:
  #
  # Return a list of supported QMP events by this server
-@@ -1764,6 +1797,54 @@
+@@ -1824,6 +1857,68 @@
    'data': { 'path': 'str' },
    'returns': [ 'ObjectPropertyInfo' ] }
  
++
++##
++# @BackupFormat
++#
++# An enumeration of supported backup formats.
++#
++# @vma: Proxmox vma backup format
++##
++{ 'enum': 'BackupFormat',
++  'data': [ 'vma' ] }
 +
 +##
 +# @backup:
 +#
 +# Starts a VM backup.
 +#
-+# @backupfile: the backup file name
++# @backup-file: the backup file name
 +#
 +# @format: format of the backup file
 +#
@@ -670,12 +736,16 @@ index 5dfa052..71e0ed9 100644
 +#
 +# @speed: #optional the maximum speed, in bytes per second
 +#
++# @devlist: #optional list of block device names (separated by ',', ';'
++# or ':'). By default the backup includes all writable block devices.
++#
 +# Returns: the uuid of the backup job
 +#
-+# Since: 1.4.0
++# Since: 1.5.0
 +##
-+{ 'command': 'backup', 'data': { 'backupfile': 'str', '*format': 'str',
-+                                 '*config-filename': 'str',
++{ 'command': 'backup', 'data': { 'backup-file': 'str',
++                                 '*format': 'BackupFormat',
++                                 '*config-file': 'str',
 +                                 '*devlist': 'str', '*speed': 'int' },
 +  'returns': 'str' }
 +
@@ -686,7 +756,7 @@ index 5dfa052..71e0ed9 100644
 +#
 +# Returns: @BackupStatus
 +#
-+# Since: 1.4.0
++# Since: 1.5.0
 +##
 +{ 'command': 'query-backup', 'returns': 'BackupStatus' }
 +
@@ -699,7 +769,7 @@ index 5dfa052..71e0ed9 100644
 +#
 +# Notes: This command succeeds even if there is no backup process running.
 +#
-+# Since: 1.4.0
++# Since: 1.5.0
 +##
 +{ 'command': 'backup-cancel' }
 +
@@ -707,20 +777,20 @@ index 5dfa052..71e0ed9 100644
  # @qom-get:
  #
 diff --git a/qmp-commands.hx b/qmp-commands.hx
-index 5c692d0..c46fdc4 100644
+index 799adea..17e381b 100644
 --- a/qmp-commands.hx
 +++ b/qmp-commands.hx
-@@ -822,6 +822,18 @@ EQMP
+@@ -889,6 +889,18 @@ EQMP
      },
  
      {
 +        .name       = "backup",
-+        .args_type  = "backupfile:s,format:s?,config-filename:F?,speed:o?,devlist:s?",
++        .args_type  = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?",
 +        .mhandler.cmd_new = qmp_marshal_input_backup,
 +    },
 +
 +    {
-+        .name       = "backup_cancel",
++        .name       = "backup-cancel",
 +        .args_type  = "",
 +        .mhandler.cmd_new = qmp_marshal_input_backup_cancel,
 +    },
@@ -729,7 +799,7 @@ index 5c692d0..c46fdc4 100644
          .name       = "block-job-set-speed",
          .args_type  = "device:B,speed:o",
          .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed,
-@@ -2491,6 +2503,21 @@ EQMP
+@@ -2566,6 +2578,21 @@ EQMP
      },
  
  SQMP