]> git.proxmox.com Git - pve-qemu.git/commitdiff
add QAPI naming exceptions in patches introducing them
authorFiona Ebner <f.ebner@proxmox.com>
Tue, 10 Jan 2023 08:40:57 +0000 (09:40 +0100)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Tue, 10 Jan 2023 14:42:16 +0000 (15:42 +0100)
Avoids a patch and is required to compile when not all patches are
applied. No functional change is intended.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
33 files changed:
debian/patches/pve/0007-PVE-Up-qmp-add-get_link_status.patch
debian/patches/pve/0013-PVE-virtio-balloon-improve-query-balloon.patch
debian/patches/pve/0033-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
debian/patches/pve/0049-PVE-savevm-async-register-yank-before-migration_inco.patch [new file with mode: 0644]
debian/patches/pve/0049-PVE-whitelist-invalid-QAPI-names-for-backwards-compa.patch [deleted file]
debian/patches/pve/0050-PVE-savevm-async-register-yank-before-migration_inco.patch [deleted file]
debian/patches/pve/0050-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch [new file with mode: 0644]
debian/patches/pve/0051-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch [deleted file]
debian/patches/pve/0051-vma-allow-partial-restore.patch [new file with mode: 0644]
debian/patches/pve/0052-pbs-namespace-support.patch [new file with mode: 0644]
debian/patches/pve/0052-vma-allow-partial-restore.patch [deleted file]
debian/patches/pve/0053-Revert-block-rbd-workaround-for-ceph-issue-53784.patch [new file with mode: 0644]
debian/patches/pve/0053-pbs-namespace-support.patch [deleted file]
debian/patches/pve/0054-Revert-block-rbd-fix-handling-of-holes-in-.bdrv_co_b.patch [new file with mode: 0644]
debian/patches/pve/0054-Revert-block-rbd-workaround-for-ceph-issue-53784.patch [deleted file]
debian/patches/pve/0055-Revert-block-rbd-fix-handling-of-holes-in-.bdrv_co_b.patch [deleted file]
debian/patches/pve/0055-Revert-block-rbd-implement-bdrv_co_block_status.patch [new file with mode: 0644]
debian/patches/pve/0056-PVE-Backup-create-jobs-correctly-cancel-in-error-sce.patch [new file with mode: 0644]
debian/patches/pve/0056-Revert-block-rbd-implement-bdrv_co_block_status.patch [deleted file]
debian/patches/pve/0057-PVE-Backup-create-jobs-correctly-cancel-in-error-sce.patch [deleted file]
debian/patches/pve/0057-PVE-Backup-ensure-jobs-in-di_list-are-referenced.patch [new file with mode: 0644]
debian/patches/pve/0058-PVE-Backup-avoid-segfault-issues-upon-backup-cancel.patch [new file with mode: 0644]
debian/patches/pve/0058-PVE-Backup-ensure-jobs-in-di_list-are-referenced.patch [deleted file]
debian/patches/pve/0059-PVE-Backup-avoid-segfault-issues-upon-backup-cancel.patch [deleted file]
debian/patches/pve/0059-vma-create-support-64KiB-unaligned-input-images.patch [new file with mode: 0644]
debian/patches/pve/0060-vma-create-avoid-triggering-assertion-in-error-case.patch [new file with mode: 0644]
debian/patches/pve/0060-vma-create-support-64KiB-unaligned-input-images.patch [deleted file]
debian/patches/pve/0061-block-alloc-track-avoid-premature-break.patch [new file with mode: 0644]
debian/patches/pve/0061-vma-create-avoid-triggering-assertion-in-error-case.patch [deleted file]
debian/patches/pve/0062-PVE-Backup-allow-passing-max-workers-performance-set.patch [new file with mode: 0644]
debian/patches/pve/0062-block-alloc-track-avoid-premature-break.patch [deleted file]
debian/patches/pve/0063-PVE-Backup-allow-passing-max-workers-performance-set.patch [deleted file]
debian/patches/series

index 1e2ac12971c0bc2612051647893d3e257cbc4219..22ffc6340518a563790d5425054c6b63f0094d3c 100644 (file)
@@ -4,11 +4,13 @@ Date: Mon, 6 Apr 2020 12:16:37 +0200
 Subject: [PATCH] PVE: [Up] qmp: add get_link_status
 
 Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+[FE: add get_link_status to command name exceptions]
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
 ---
  net/net.c        | 27 +++++++++++++++++++++++++++
  qapi/net.json    | 15 +++++++++++++++
- qapi/pragma.json |  +
- 3 files changed, 43 insertions(+)
+ qapi/pragma.json |  2 ++
+ 3 files changed, 44 insertions(+)
 
 diff --git a/net/net.c b/net/net.c
 index 840ad9dca5..28e97c5d85 100644
@@ -75,10 +77,18 @@ index 522ac582ed..327d7c5a37 100644
  # @netdev_add:
  #
 diff --git a/qapi/pragma.json b/qapi/pragma.json
-index 7f810b0e97..a2358e303a 100644
+index 7f810b0e97..29233db825 100644
 --- a/qapi/pragma.json
 +++ b/qapi/pragma.json
-@@ -26,6 +26,7 @@
+@@ -15,6 +15,7 @@
+         'device_add',
+         'device_del',
+         'expire_password',
++        'get_link_status',
+         'migrate_cancel',
+         'netdev_add',
+         'netdev_del',
+@@ -26,6 +27,7 @@
          'system_wakeup' ],
      # Commands allowed to return a non-dictionary
      'command-returns-exceptions': [
index 35819c4517303b5051c106ce71320963b82563a1..ad3eb7642f65f4d4e256adf6356f4130ea0def39 100644 (file)
@@ -7,11 +7,14 @@ Actually provide memory information via the query-balloon
 command.
 
 Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+[FE: add BalloonInfo to member name exceptions list]
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
 ---
  hw/virtio/virtio-balloon.c | 33 +++++++++++++++++++++++++++++++--
  monitor/hmp-cmds.c         | 30 +++++++++++++++++++++++++++++-
  qapi/machine.json          | 22 +++++++++++++++++++++-
- 3 files changed, 81 insertions(+), 4 deletions(-)
+ qapi/pragma.json           |  1 +
+ 4 files changed, 82 insertions(+), 4 deletions(-)
 
 diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
 index 73ac5eb675..bbfe7eca62 100644
@@ -133,3 +136,15 @@ index b9228a5e46..10e77a9af3 100644
  
  ##
  # @query-balloon:
+diff --git a/qapi/pragma.json b/qapi/pragma.json
+index 29233db825..f2097b9020 100644
+--- a/qapi/pragma.json
++++ b/qapi/pragma.json
+@@ -37,6 +37,7 @@
+     'member-name-exceptions': [     # visible in:
+         'ACPISlotType',             # query-acpi-ospm-status
+         'AcpiTableOptions',         # -acpitable
++        'BalloonInfo',              # query-balloon
+         'BlkdebugEvent',            # blockdev-add, -blockdev
+         'BlkdebugSetStateOptions',  # blockdev-add, -blockdev
+         'BlockDeviceInfo',          # query-block
index 1f695894d9110a913504f970038e9ab4865b3dd0..ab281123960a05bf62ca57a7f3c80e561310e5ec 100644 (file)
@@ -16,7 +16,8 @@ Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
  configure            |   9 ++
  meson.build          |   2 +-
  qapi/block-core.json |  13 ++
- 5 files changed, 302 insertions(+), 1 deletion(-)
+ qapi/pragma.json     |   1 +
+ 6 files changed, 303 insertions(+), 1 deletion(-)
  create mode 100644 block/pbs.c
 
 diff --git a/block/meson.build b/block/meson.build
@@ -407,3 +408,15 @@ index 5ac6276dc1..45b63dfe26 100644
        'nvme':       'BlockdevOptionsNVMe',
        'nvme-io_uring': { 'type': 'BlockdevOptionsNvmeIoUring',
                           'if': 'CONFIG_BLKIO' },
+diff --git a/qapi/pragma.json b/qapi/pragma.json
+index f2097b9020..5ab1890519 100644
+--- a/qapi/pragma.json
++++ b/qapi/pragma.json
+@@ -47,6 +47,7 @@
+         'BlockInfo',                # query-block
+         'BlockdevAioOptions',       # blockdev-add, -blockdev
+         'BlockdevDriver',           # blockdev-add, query-blockstats, ...
++        'BlockdevOptionsPbs',       # for PBS backwards compat
+         'BlockdevVmdkAdapterType',  # blockdev-create (to match VMDK spec)
+         'BlockdevVmdkSubformat',    # blockdev-create (to match VMDK spec)
+         'ColoCompareProperties',    # object_add, -object
diff --git a/debian/patches/pve/0049-PVE-savevm-async-register-yank-before-migration_inco.patch b/debian/patches/pve/0049-PVE-savevm-async-register-yank-before-migration_inco.patch
new file mode 100644 (file)
index 0000000..b25c9fc
--- /dev/null
@@ -0,0 +1,35 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Stefan Reiter <s.reiter@proxmox.com>
+Date: Wed, 26 May 2021 17:36:55 +0200
+Subject: [PATCH] PVE: savevm-async: register yank before
+ migration_incoming_state_destroy
+
+Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+---
+ migration/savevm-async.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/migration/savevm-async.c b/migration/savevm-async.c
+index bafe6ae5eb..da3634048f 100644
+--- a/migration/savevm-async.c
++++ b/migration/savevm-async.c
+@@ -20,6 +20,7 @@
+ #include "qemu/timer.h"
+ #include "qemu/main-loop.h"
+ #include "qemu/rcu.h"
++#include "qemu/yank.h"
+ /* #define DEBUG_SAVEVM_STATE */
+@@ -514,6 +515,10 @@ int load_snapshot_from_blockdev(const char *filename, Error **errp)
+     dirty_bitmap_mig_before_vm_start();
+     qemu_fclose(f);
++
++    /* state_destroy assumes a real migration which would have added a yank */
++    yank_register_instance(MIGRATION_YANK_INSTANCE, &error_abort);
++
+     migration_incoming_state_destroy();
+     if (ret < 0) {
+         error_setg_errno(errp, -ret, "Error while loading VM state");
diff --git a/debian/patches/pve/0049-PVE-whitelist-invalid-QAPI-names-for-backwards-compa.patch b/debian/patches/pve/0049-PVE-whitelist-invalid-QAPI-names-for-backwards-compa.patch
deleted file mode 100644 (file)
index 95f191f..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Stefan Reiter <s.reiter@proxmox.com>
-Date: Wed, 26 May 2021 15:26:30 +0200
-Subject: [PATCH] PVE: whitelist 'invalid' QAPI names for backwards compat
-
-Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
-Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
----
- qapi/pragma.json | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/qapi/pragma.json b/qapi/pragma.json
-index a2358e303a..9ff5c84ffd 100644
---- a/qapi/pragma.json
-+++ b/qapi/pragma.json
-@@ -15,6 +15,7 @@
-         'device_add',
-         'device_del',
-         'expire_password',
-+        'get_link_status',
-         'migrate_cancel',
-         'netdev_add',
-         'netdev_del',
-@@ -64,6 +65,8 @@
-         'SysEmuTarget',             # query-cpu-fast, query-target
-         'UuidInfo',                 # query-uuid
-         'VncClientInfo',            # query-vnc, query-vnc-servers, ...
--        'X86CPURegister32'          # qom-get of x86 CPU properties
-+        'X86CPURegister32',         # qom-get of x86 CPU properties
-                                     # feature-words, filtered-features
-+        'BlockdevOptionsPbs',       # for PBS backwards compat
-+        'BalloonInfo'
-     ] } }
diff --git a/debian/patches/pve/0050-PVE-savevm-async-register-yank-before-migration_inco.patch b/debian/patches/pve/0050-PVE-savevm-async-register-yank-before-migration_inco.patch
deleted file mode 100644 (file)
index b25c9fc..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Stefan Reiter <s.reiter@proxmox.com>
-Date: Wed, 26 May 2021 17:36:55 +0200
-Subject: [PATCH] PVE: savevm-async: register yank before
- migration_incoming_state_destroy
-
-Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
-Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
----
- migration/savevm-async.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/migration/savevm-async.c b/migration/savevm-async.c
-index bafe6ae5eb..da3634048f 100644
---- a/migration/savevm-async.c
-+++ b/migration/savevm-async.c
-@@ -20,6 +20,7 @@
- #include "qemu/timer.h"
- #include "qemu/main-loop.h"
- #include "qemu/rcu.h"
-+#include "qemu/yank.h"
- /* #define DEBUG_SAVEVM_STATE */
-@@ -514,6 +515,10 @@ int load_snapshot_from_blockdev(const char *filename, Error **errp)
-     dirty_bitmap_mig_before_vm_start();
-     qemu_fclose(f);
-+
-+    /* state_destroy assumes a real migration which would have added a yank */
-+    yank_register_instance(MIGRATION_YANK_INSTANCE, &error_abort);
-+
-     migration_incoming_state_destroy();
-     if (ret < 0) {
-         error_setg_errno(errp, -ret, "Error while loading VM state");
diff --git a/debian/patches/pve/0050-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch b/debian/patches/pve/0050-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch
new file mode 100644 (file)
index 0000000..ed75a35
--- /dev/null
@@ -0,0 +1,130 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Mon, 7 Feb 2022 14:21:01 +0100
+Subject: [PATCH] qemu-img: dd: add -l option for loading a snapshot
+
+Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+---
+ docs/tools/qemu-img.rst |  6 +++---
+ qemu-img-cmds.hx        |  4 ++--
+ qemu-img.c              | 33 +++++++++++++++++++++++++++++++--
+ 3 files changed, 36 insertions(+), 7 deletions(-)
+
+diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst
+index 5e713e231d..9390d5e5cf 100644
+--- a/docs/tools/qemu-img.rst
++++ b/docs/tools/qemu-img.rst
+@@ -492,10 +492,10 @@ Command description:
+   it doesn't need to be specified separately in this case.
+-.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [-n] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] if=INPUT of=OUTPUT
++.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [-n] [-l SNAPSHOT_PARAM] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] if=INPUT of=OUTPUT
+-  dd copies from *INPUT* file to *OUTPUT* file converting it from
+-  *FMT* format to *OUTPUT_FMT* format.
++  dd copies from *INPUT* file or snapshot *SNAPSHOT_PARAM* to *OUTPUT* file
++  converting it from *FMT* format to *OUTPUT_FMT* format.
+   The data is by default read and written using blocks of 512 bytes but can be
+   modified by specifying *BLOCK_SIZE*. If count=\ *BLOCKS* is specified
+diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
+index b5b0bb4467..36f97e1f19 100644
+--- a/qemu-img-cmds.hx
++++ b/qemu-img-cmds.hx
+@@ -58,9 +58,9 @@ SRST
+ ERST
+ DEF("dd", img_dd,
+-    "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [-n] [bs=block_size] [count=blocks] [skip=blocks] [osize=output_size] if=input of=output")
++    "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [-n] [-l snapshot_param] [bs=block_size] [count=blocks] [skip=blocks] [osize=output_size] if=input of=output")
+ SRST
+-.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [-n] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] [osize=OUTPUT_SIZE] if=INPUT of=OUTPUT
++.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [-n] [-l SNAPSHOT_PARAM] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] [osize=OUTPUT_SIZE] if=INPUT of=OUTPUT
+ ERST
+ DEF("info", img_info,
+diff --git a/qemu-img.c b/qemu-img.c
+index 59c403373b..065a54cc42 100644
+--- a/qemu-img.c
++++ b/qemu-img.c
+@@ -4946,6 +4946,7 @@ static int img_dd(int argc, char **argv)
+     BlockDriver *drv = NULL, *proto_drv = NULL;
+     BlockBackend *blk1 = NULL, *blk2 = NULL;
+     QemuOpts *opts = NULL;
++    QemuOpts *sn_opts = NULL;
+     QemuOptsList *create_opts = NULL;
+     Error *local_err = NULL;
+     bool image_opts = false;
+@@ -4955,6 +4956,7 @@ static int img_dd(int argc, char **argv)
+     int64_t size = 0, readsize = 0;
+     int64_t out_pos, in_pos;
+     bool force_share = false, skip_create = false;
++    const char *snapshot_name = NULL;
+     struct DdInfo dd = {
+         .flags = 0,
+         .count = 0,
+@@ -4992,7 +4994,7 @@ static int img_dd(int argc, char **argv)
+         { 0, 0, 0, 0 }
+     };
+-    while ((c = getopt_long(argc, argv, ":hf:O:Un", long_options, NULL))) {
++    while ((c = getopt_long(argc, argv, ":hf:O:l:Un", long_options, NULL))) {
+         if (c == EOF) {
+             break;
+         }
+@@ -5015,6 +5017,19 @@ static int img_dd(int argc, char **argv)
+         case 'n':
+             skip_create = true;
+             break;
++        case 'l':
++            if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) {
++                sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts,
++                                                  optarg, false);
++                if (!sn_opts) {
++                    error_report("Failed in parsing snapshot param '%s'",
++                                 optarg);
++                    goto out;
++                }
++            } else {
++                snapshot_name = optarg;
++            }
++            break;
+         case 'U':
+             force_share = true;
+             break;
+@@ -5074,11 +5089,24 @@ static int img_dd(int argc, char **argv)
+     if (dd.flags & C_IF) {
+         blk1 = img_open(image_opts, in.filename, fmt, 0, false, false,
+                         force_share);
+-
+         if (!blk1) {
+             ret = -1;
+             goto out;
+         }
++        if (sn_opts) {
++            bdrv_snapshot_load_tmp(blk_bs(blk1),
++                                   qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID),
++                                   qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME),
++                                   &local_err);
++        } else if (snapshot_name != NULL) {
++            bdrv_snapshot_load_tmp_by_id_or_name(blk_bs(blk1), snapshot_name,
++                                                 &local_err);
++        }
++        if (local_err) {
++            error_reportf_err(local_err, "Failed to load snapshot: ");
++            ret = -1;
++            goto out;
++        }
+     }
+     if (dd.flags & C_OSIZE) {
+@@ -5233,6 +5261,7 @@ static int img_dd(int argc, char **argv)
+ out:
+     g_free(arg);
+     qemu_opts_del(opts);
++    qemu_opts_del(sn_opts);
+     qemu_opts_free(create_opts);
+     blk_unref(blk1);
+     blk_unref(blk2);
diff --git a/debian/patches/pve/0051-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch b/debian/patches/pve/0051-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch
deleted file mode 100644 (file)
index ed75a35..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Mon, 7 Feb 2022 14:21:01 +0100
-Subject: [PATCH] qemu-img: dd: add -l option for loading a snapshot
-
-Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
-Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
----
- docs/tools/qemu-img.rst |  6 +++---
- qemu-img-cmds.hx        |  4 ++--
- qemu-img.c              | 33 +++++++++++++++++++++++++++++++--
- 3 files changed, 36 insertions(+), 7 deletions(-)
-
-diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst
-index 5e713e231d..9390d5e5cf 100644
---- a/docs/tools/qemu-img.rst
-+++ b/docs/tools/qemu-img.rst
-@@ -492,10 +492,10 @@ Command description:
-   it doesn't need to be specified separately in this case.
--.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [-n] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] if=INPUT of=OUTPUT
-+.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [-n] [-l SNAPSHOT_PARAM] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] if=INPUT of=OUTPUT
--  dd copies from *INPUT* file to *OUTPUT* file converting it from
--  *FMT* format to *OUTPUT_FMT* format.
-+  dd copies from *INPUT* file or snapshot *SNAPSHOT_PARAM* to *OUTPUT* file
-+  converting it from *FMT* format to *OUTPUT_FMT* format.
-   The data is by default read and written using blocks of 512 bytes but can be
-   modified by specifying *BLOCK_SIZE*. If count=\ *BLOCKS* is specified
-diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
-index b5b0bb4467..36f97e1f19 100644
---- a/qemu-img-cmds.hx
-+++ b/qemu-img-cmds.hx
-@@ -58,9 +58,9 @@ SRST
- ERST
- DEF("dd", img_dd,
--    "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [-n] [bs=block_size] [count=blocks] [skip=blocks] [osize=output_size] if=input of=output")
-+    "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [-n] [-l snapshot_param] [bs=block_size] [count=blocks] [skip=blocks] [osize=output_size] if=input of=output")
- SRST
--.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [-n] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] [osize=OUTPUT_SIZE] if=INPUT of=OUTPUT
-+.. option:: dd [--image-opts] [-U] [-f FMT] [-O OUTPUT_FMT] [-n] [-l SNAPSHOT_PARAM] [bs=BLOCK_SIZE] [count=BLOCKS] [skip=BLOCKS] [osize=OUTPUT_SIZE] if=INPUT of=OUTPUT
- ERST
- DEF("info", img_info,
-diff --git a/qemu-img.c b/qemu-img.c
-index 59c403373b..065a54cc42 100644
---- a/qemu-img.c
-+++ b/qemu-img.c
-@@ -4946,6 +4946,7 @@ static int img_dd(int argc, char **argv)
-     BlockDriver *drv = NULL, *proto_drv = NULL;
-     BlockBackend *blk1 = NULL, *blk2 = NULL;
-     QemuOpts *opts = NULL;
-+    QemuOpts *sn_opts = NULL;
-     QemuOptsList *create_opts = NULL;
-     Error *local_err = NULL;
-     bool image_opts = false;
-@@ -4955,6 +4956,7 @@ static int img_dd(int argc, char **argv)
-     int64_t size = 0, readsize = 0;
-     int64_t out_pos, in_pos;
-     bool force_share = false, skip_create = false;
-+    const char *snapshot_name = NULL;
-     struct DdInfo dd = {
-         .flags = 0,
-         .count = 0,
-@@ -4992,7 +4994,7 @@ static int img_dd(int argc, char **argv)
-         { 0, 0, 0, 0 }
-     };
--    while ((c = getopt_long(argc, argv, ":hf:O:Un", long_options, NULL))) {
-+    while ((c = getopt_long(argc, argv, ":hf:O:l:Un", long_options, NULL))) {
-         if (c == EOF) {
-             break;
-         }
-@@ -5015,6 +5017,19 @@ static int img_dd(int argc, char **argv)
-         case 'n':
-             skip_create = true;
-             break;
-+        case 'l':
-+            if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) {
-+                sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts,
-+                                                  optarg, false);
-+                if (!sn_opts) {
-+                    error_report("Failed in parsing snapshot param '%s'",
-+                                 optarg);
-+                    goto out;
-+                }
-+            } else {
-+                snapshot_name = optarg;
-+            }
-+            break;
-         case 'U':
-             force_share = true;
-             break;
-@@ -5074,11 +5089,24 @@ static int img_dd(int argc, char **argv)
-     if (dd.flags & C_IF) {
-         blk1 = img_open(image_opts, in.filename, fmt, 0, false, false,
-                         force_share);
--
-         if (!blk1) {
-             ret = -1;
-             goto out;
-         }
-+        if (sn_opts) {
-+            bdrv_snapshot_load_tmp(blk_bs(blk1),
-+                                   qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID),
-+                                   qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME),
-+                                   &local_err);
-+        } else if (snapshot_name != NULL) {
-+            bdrv_snapshot_load_tmp_by_id_or_name(blk_bs(blk1), snapshot_name,
-+                                                 &local_err);
-+        }
-+        if (local_err) {
-+            error_reportf_err(local_err, "Failed to load snapshot: ");
-+            ret = -1;
-+            goto out;
-+        }
-     }
-     if (dd.flags & C_OSIZE) {
-@@ -5233,6 +5261,7 @@ static int img_dd(int argc, char **argv)
- out:
-     g_free(arg);
-     qemu_opts_del(opts);
-+    qemu_opts_del(sn_opts);
-     qemu_opts_free(create_opts);
-     blk_unref(blk1);
-     blk_unref(blk2);
diff --git a/debian/patches/pve/0051-vma-allow-partial-restore.patch b/debian/patches/pve/0051-vma-allow-partial-restore.patch
new file mode 100644 (file)
index 0000000..f1fd7cb
--- /dev/null
@@ -0,0 +1,407 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Thu, 21 Apr 2022 13:26:48 +0200
+Subject: [PATCH] vma: allow partial restore
+
+Introduce a new map line for skipping a certain drive, of the form
+skip=drive-scsi0
+
+Since in PVE, most archives are compressed and piped to vma for
+restore, it's not easily possible to skip reads.
+
+For the reader, a new skip flag for VmaRestoreState is added and the
+target is allowed to be NULL if skip is specified when registering. If
+the skip flag is set, no writes will be made as well as no check for
+duplicate clusters. Therefore, the flag is not set for verify.
+
+Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
+Acked-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+---
+ vma-reader.c |  64 ++++++++++++---------
+ vma.c        | 157 +++++++++++++++++++++++++++++----------------------
+ vma.h        |   2 +-
+ 3 files changed, 126 insertions(+), 97 deletions(-)
+
+diff --git a/vma-reader.c b/vma-reader.c
+index e65f1e8415..81a891c6b1 100644
+--- a/vma-reader.c
++++ b/vma-reader.c
+@@ -28,6 +28,7 @@ typedef struct VmaRestoreState {
+     bool write_zeroes;
+     unsigned long *bitmap;
+     int bitmap_size;
++    bool skip;
+ }  VmaRestoreState;
+ struct VmaReader {
+@@ -425,13 +426,14 @@ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id)
+ }
+ static void allocate_rstate(VmaReader *vmar,  guint8 dev_id,
+-                            BlockBackend *target, bool write_zeroes)
++                            BlockBackend *target, bool write_zeroes, bool skip)
+ {
+     assert(vmar);
+     assert(dev_id);
+     vmar->rstate[dev_id].target = target;
+     vmar->rstate[dev_id].write_zeroes = write_zeroes;
++    vmar->rstate[dev_id].skip = skip;
+     int64_t size = vmar->devinfo[dev_id].size;
+@@ -446,28 +448,30 @@ static void allocate_rstate(VmaReader *vmar,  guint8 dev_id,
+ }
+ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockBackend *target,
+-                           bool write_zeroes, Error **errp)
++                           bool write_zeroes, bool skip, Error **errp)
+ {
+     assert(vmar);
+-    assert(target != NULL);
++    assert(target != NULL || skip);
+     assert(dev_id);
+-    assert(vmar->rstate[dev_id].target == NULL);
+-
+-    int64_t size = blk_getlength(target);
+-    int64_t size_diff = size - vmar->devinfo[dev_id].size;
+-
+-    /* storage types can have different size restrictions, so it
+-     * is not always possible to create an image with exact size.
+-     * So we tolerate a size difference up to 4MB.
+-     */
+-    if ((size_diff < 0) || (size_diff > 4*1024*1024)) {
+-        error_setg(errp, "vma_reader_register_bs for stream %s failed - "
+-                   "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname,
+-                   size, vmar->devinfo[dev_id].size);
+-        return -1;
++    assert(vmar->rstate[dev_id].target == NULL && !vmar->rstate[dev_id].skip);
++
++    if (target != NULL) {
++        int64_t size = blk_getlength(target);
++        int64_t size_diff = size - vmar->devinfo[dev_id].size;
++
++        /* storage types can have different size restrictions, so it
++         * is not always possible to create an image with exact size.
++         * So we tolerate a size difference up to 4MB.
++         */
++        if ((size_diff < 0) || (size_diff > 4*1024*1024)) {
++            error_setg(errp, "vma_reader_register_bs for stream %s failed - "
++                       "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname,
++                       size, vmar->devinfo[dev_id].size);
++            return -1;
++        }
+     }
+-    allocate_rstate(vmar, dev_id, target, write_zeroes);
++    allocate_rstate(vmar, dev_id, target, write_zeroes, skip);
+     return 0;
+ }
+@@ -560,19 +564,23 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+         VmaRestoreState *rstate = &vmar->rstate[dev_id];
+         BlockBackend *target = NULL;
++        bool skip = rstate->skip;
++
+         if (dev_id != vmar->vmstate_stream) {
+             target = rstate->target;
+-            if (!verify && !target) {
++            if (!verify && !target && !skip) {
+                 error_setg(errp, "got wrong dev id %d", dev_id);
+                 return -1;
+             }
+-            if (vma_reader_get_bitmap(rstate, cluster_num)) {
+-                error_setg(errp, "found duplicated cluster %zd for stream %s",
+-                          cluster_num, vmar->devinfo[dev_id].devname);
+-                return -1;
++            if (!skip) {
++                if (vma_reader_get_bitmap(rstate, cluster_num)) {
++                    error_setg(errp, "found duplicated cluster %zd for stream %s",
++                              cluster_num, vmar->devinfo[dev_id].devname);
++                    return -1;
++                }
++                vma_reader_set_bitmap(rstate, cluster_num, 1);
+             }
+-            vma_reader_set_bitmap(rstate, cluster_num, 1);
+             max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE;
+         } else {
+@@ -618,7 +626,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+                 return -1;
+             }
+-            if (!verify) {
++            if (!verify && !skip) {
+                 int nb_sectors = end_sector - sector_num;
+                 if (restore_write_data(vmar, dev_id, target, vmstate_fd,
+                                        buf + start, sector_num, nb_sectors,
+@@ -654,7 +662,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+                         return -1;
+                     }
+-                    if (!verify) {
++                    if (!verify && !skip) {
+                         int nb_sectors = end_sector - sector_num;
+                         if (restore_write_data(vmar, dev_id, target, vmstate_fd,
+                                                buf + start, sector_num,
+@@ -679,7 +687,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+                             vmar->partial_zero_cluster_data += zero_size;
+                         }
+-                        if (rstate->write_zeroes && !verify) {
++                        if (rstate->write_zeroes && !verify && !skip) {
+                             if (restore_write_data(vmar, dev_id, target, vmstate_fd,
+                                                    zero_vma_block, sector_num,
+                                                    nb_sectors, errp) < 0) {
+@@ -850,7 +858,7 @@ int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp)
+     for (dev_id = 1; dev_id < 255; dev_id++) {
+         if (vma_reader_get_device_info(vmar, dev_id)) {
+-            allocate_rstate(vmar, dev_id, NULL, false);
++            allocate_rstate(vmar, dev_id, NULL, false, false);
+         }
+     }
+diff --git a/vma.c b/vma.c
+index e8dffb43e0..e6e9ffc7fe 100644
+--- a/vma.c
++++ b/vma.c
+@@ -138,6 +138,7 @@ typedef struct RestoreMap {
+     char *throttling_group;
+     char *cache;
+     bool write_zero;
++    bool skip;
+ } RestoreMap;
+ static bool try_parse_option(char **line, const char *optname, char **out, const char *inbuf) {
+@@ -245,47 +246,61 @@ static int extract_content(int argc, char **argv)
+             char *bps = NULL;
+             char *group = NULL;
+             char *cache = NULL;
++            char *devname = NULL;
++            bool skip = false;
++            uint64_t bps_value = 0;
++            const char *path = NULL;
++            bool write_zero = true;
++
+             if (!line || line[0] == '\0' || !strcmp(line, "done\n")) {
+                 break;
+             }
+             int len = strlen(line);
+             if (line[len - 1] == '\n') {
+                 line[len - 1] = '\0';
+-                if (len == 1) {
++                len = len - 1;
++                if (len == 0) {
+                     break;
+                 }
+             }
+-            while (1) {
+-                if (!try_parse_option(&line, "format", &format, inbuf) &&
+-                    !try_parse_option(&line, "throttling.bps", &bps, inbuf) &&
+-                    !try_parse_option(&line, "throttling.group", &group, inbuf) &&
+-                    !try_parse_option(&line, "cache", &cache, inbuf))
+-                {
+-                    break;
++            if (strncmp(line, "skip", 4) == 0) {
++                if (len < 6 || line[4] != '=') {
++                    g_error("read map failed - option 'skip' has no value ('%s')",
++                            inbuf);
++                } else {
++                    devname = line + 5;
++                    skip = true;
++                }
++            } else {
++                while (1) {
++                    if (!try_parse_option(&line, "format", &format, inbuf) &&
++                        !try_parse_option(&line, "throttling.bps", &bps, inbuf) &&
++                        !try_parse_option(&line, "throttling.group", &group, inbuf) &&
++                        !try_parse_option(&line, "cache", &cache, inbuf))
++                    {
++                        break;
++                    }
+                 }
+-            }
+-            uint64_t bps_value = 0;
+-            if (bps) {
+-                bps_value = verify_u64(bps);
+-                g_free(bps);
+-            }
++                if (bps) {
++                    bps_value = verify_u64(bps);
++                    g_free(bps);
++                }
+-            const char *path;
+-            bool write_zero;
+-            if (line[0] == '0' && line[1] == ':') {
+-                path = line + 2;
+-                write_zero = false;
+-            } else if (line[0] == '1' && line[1] == ':') {
+-                path = line + 2;
+-                write_zero = true;
+-            } else {
+-                g_error("read map failed - parse error ('%s')", inbuf);
++                if (line[0] == '0' && line[1] == ':') {
++                    path = line + 2;
++                    write_zero = false;
++                } else if (line[0] == '1' && line[1] == ':') {
++                    path = line + 2;
++                    write_zero = true;
++                } else {
++                    g_error("read map failed - parse error ('%s')", inbuf);
++                }
++
++                path = extract_devname(path, &devname, -1);
+             }
+-            char *devname = NULL;
+-            path = extract_devname(path, &devname, -1);
+             if (!devname) {
+                 g_error("read map failed - no dev name specified ('%s')",
+                         inbuf);
+@@ -299,6 +314,7 @@ static int extract_content(int argc, char **argv)
+             map->throttling_group = group;
+             map->cache = cache;
+             map->write_zero = write_zero;
++            map->skip = skip;
+             g_hash_table_insert(devmap, map->devname, map);
+@@ -328,6 +344,7 @@ static int extract_content(int argc, char **argv)
+             const char *cache = NULL;
+             int flags = BDRV_O_RDWR;
+             bool write_zero = true;
++            bool skip = false;
+             BlockBackend *blk = NULL;
+@@ -343,6 +360,7 @@ static int extract_content(int argc, char **argv)
+                 throttling_group = map->throttling_group;
+                 cache = map->cache;
+                 write_zero = map->write_zero;
++                skip = map->skip;
+             } else {
+                 devfn = g_strdup_printf("%s/tmp-disk-%s.raw",
+                                         dirname, di->devname);
+@@ -361,57 +379,60 @@ static int extract_content(int argc, char **argv)
+                 write_zero = false;
+             }
+-          size_t devlen = strlen(devfn);
+-          QDict *options = NULL;
+-            bool writethrough;
+-            if (format) {
+-                /* explicit format from commandline */
+-                options = qdict_new();
+-                qdict_put_str(options, "driver", format);
+-            } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) ||
+-                     strncmp(devfn, "/dev/", 5) == 0)
+-          {
+-                /* This part is now deprecated for PVE as well (just as qemu
+-                 * deprecated not specifying an explicit raw format, too.
+-                 */
+-              /* explicit raw format */
+-              options = qdict_new();
+-              qdict_put_str(options, "driver", "raw");
+-          }
+-            if (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) {
+-                g_error("invalid cache option: %s\n", cache);
+-            }
++            if (!skip) {
++                size_t devlen = strlen(devfn);
++                QDict *options = NULL;
++                bool writethrough;
++                if (format) {
++                    /* explicit format from commandline */
++                    options = qdict_new();
++                    qdict_put_str(options, "driver", format);
++                } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) ||
++                    strncmp(devfn, "/dev/", 5) == 0)
++                {
++                    /* This part is now deprecated for PVE as well (just as qemu
++                     * deprecated not specifying an explicit raw format, too.
++                     */
++                    /* explicit raw format */
++                    options = qdict_new();
++                    qdict_put_str(options, "driver", "raw");
++                }
+-          if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
+-                g_error("can't open file %s - %s", devfn,
+-                        error_get_pretty(errp));
+-            }
++                if (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) {
++                    g_error("invalid cache option: %s\n", cache);
++                }
+-            if (cache) {
+-                blk_set_enable_write_cache(blk, !writethrough);
+-            }
++                if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
++                    g_error("can't open file %s - %s", devfn,
++                            error_get_pretty(errp));
++                }
+-            if (throttling_group) {
+-                blk_io_limits_enable(blk, throttling_group);
+-            }
++                if (cache) {
++                    blk_set_enable_write_cache(blk, !writethrough);
++                }
+-            if (throttling_bps) {
+-                if (!throttling_group) {
+-                    blk_io_limits_enable(blk, devfn);
++                if (throttling_group) {
++                    blk_io_limits_enable(blk, throttling_group);
+                 }
+-                ThrottleConfig cfg;
+-                throttle_config_init(&cfg);
+-                cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps;
+-                Error *err = NULL;
+-                if (!throttle_is_valid(&cfg, &err)) {
+-                    error_report_err(err);
+-                    g_error("failed to apply throttling");
++                if (throttling_bps) {
++                    if (!throttling_group) {
++                        blk_io_limits_enable(blk, devfn);
++                    }
++
++                    ThrottleConfig cfg;
++                    throttle_config_init(&cfg);
++                    cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps;
++                    Error *err = NULL;
++                    if (!throttle_is_valid(&cfg, &err)) {
++                        error_report_err(err);
++                        g_error("failed to apply throttling");
++                    }
++                    blk_set_io_limits(blk, &cfg);
+                 }
+-                blk_set_io_limits(blk, &cfg);
+             }
+-            if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) {
++            if (vma_reader_register_bs(vmar, i, blk, write_zero, skip, &errp) < 0) {
+                 g_error("%s", error_get_pretty(errp));
+             }
+diff --git a/vma.h b/vma.h
+index c895c97f6d..1b62859165 100644
+--- a/vma.h
++++ b/vma.h
+@@ -142,7 +142,7 @@ GList *vma_reader_get_config_data(VmaReader *vmar);
+ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id);
+ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id,
+                            BlockBackend *target, bool write_zeroes,
+-                           Error **errp);
++                           bool skip, Error **errp);
+ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
+                        Error **errp);
+ int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp);
diff --git a/debian/patches/pve/0052-pbs-namespace-support.patch b/debian/patches/pve/0052-pbs-namespace-support.patch
new file mode 100644 (file)
index 0000000..2640b95
--- /dev/null
@@ -0,0 +1,233 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Tue, 26 Apr 2022 16:06:28 +0200
+Subject: [PATCH] pbs: namespace support
+
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+---
+ block/monitor/block-hmp-cmds.c |  1 +
+ block/pbs.c                    | 25 +++++++++++++++++++++----
+ pbs-restore.c                  | 19 ++++++++++++++++---
+ pve-backup.c                   |  6 +++++-
+ qapi/block-core.json           |  5 ++++-
+ 5 files changed, 47 insertions(+), 9 deletions(-)
+
+diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
+index c7468e5d3b..57b2457f1e 100644
+--- a/block/monitor/block-hmp-cmds.c
++++ b/block/monitor/block-hmp-cmds.c
+@@ -1041,6 +1041,7 @@ void coroutine_fn hmp_backup(Monitor *mon, const QDict *qdict)
+         false, NULL, // PBS key_password
+         false, NULL, // PBS master_keyfile
+         false, NULL, // PBS fingerprint
++        false, NULL, // PBS backup-ns
+         false, NULL, // PBS backup-id
+         false, 0, // PBS backup-time
+         false, false, // PBS use-dirty-bitmap
+diff --git a/block/pbs.c b/block/pbs.c
+index ce9a870885..9192f3e41b 100644
+--- a/block/pbs.c
++++ b/block/pbs.c
+@@ -14,6 +14,7 @@
+ #include <proxmox-backup-qemu.h>
+ #define PBS_OPT_REPOSITORY "repository"
++#define PBS_OPT_NAMESPACE "namespace"
+ #define PBS_OPT_SNAPSHOT "snapshot"
+ #define PBS_OPT_ARCHIVE "archive"
+ #define PBS_OPT_KEYFILE "keyfile"
+@@ -27,6 +28,7 @@ typedef struct {
+     int64_t length;
+     char *repository;
++    char *namespace;
+     char *snapshot;
+     char *archive;
+ } BDRVPBSState;
+@@ -40,6 +42,11 @@ static QemuOptsList runtime_opts = {
+             .type = QEMU_OPT_STRING,
+             .help = "The server address and repository to connect to.",
+         },
++        {
++            .name = PBS_OPT_NAMESPACE,
++            .type = QEMU_OPT_STRING,
++            .help = "Optional: The snapshot's namespace.",
++        },
+         {
+             .name = PBS_OPT_SNAPSHOT,
+             .type = QEMU_OPT_STRING,
+@@ -76,7 +83,7 @@ static QemuOptsList runtime_opts = {
+ // filename format:
+-// pbs:repository=<repo>,snapshot=<snap>,password=<pw>,key_password=<kpw>,fingerprint=<fp>,archive=<archive>
++// pbs:repository=<repo>,namespace=<ns>,snapshot=<snap>,password=<pw>,key_password=<kpw>,fingerprint=<fp>,archive=<archive>
+ static void pbs_parse_filename(const char *filename, QDict *options,
+                                      Error **errp)
+ {
+@@ -112,6 +119,7 @@ static int pbs_open(BlockDriverState *bs, QDict *options, int flags,
+     s->archive = g_strdup(qemu_opt_get(opts, PBS_OPT_ARCHIVE));
+     const char *keyfile = qemu_opt_get(opts, PBS_OPT_KEYFILE);
+     const char *password = qemu_opt_get(opts, PBS_OPT_PASSWORD);
++    const char *namespace = qemu_opt_get(opts, PBS_OPT_NAMESPACE);
+     const char *fingerprint = qemu_opt_get(opts, PBS_OPT_FINGERPRINT);
+     const char *key_password = qemu_opt_get(opts, PBS_OPT_ENCRYPTION_PASSWORD);
+@@ -124,9 +132,12 @@ static int pbs_open(BlockDriverState *bs, QDict *options, int flags,
+     if (!key_password) {
+         key_password = getenv("PBS_ENCRYPTION_PASSWORD");
+     }
++    if (namespace) {
++        s->namespace = g_strdup(namespace);
++    }
+     /* connect to PBS server in read mode */
+-    s->conn = proxmox_restore_new(s->repository, s->snapshot, password,
++    s->conn = proxmox_restore_new_ns(s->repository, s->snapshot, s->namespace, password,
+         keyfile, key_password, fingerprint, &pbs_error);
+     /* invalidates qemu_opt_get char pointers from above */
+@@ -171,6 +182,7 @@ static int pbs_file_open(BlockDriverState *bs, QDict *options, int flags,
+ static void pbs_close(BlockDriverState *bs) {
+     BDRVPBSState *s = bs->opaque;
+     g_free(s->repository);
++    g_free(s->namespace);
+     g_free(s->snapshot);
+     g_free(s->archive);
+     proxmox_restore_disconnect(s->conn);
+@@ -252,8 +264,13 @@ static coroutine_fn int pbs_co_pwritev(BlockDriverState *bs,
+ static void pbs_refresh_filename(BlockDriverState *bs)
+ {
+     BDRVPBSState *s = bs->opaque;
+-    snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s(%s)",
+-             s->repository, s->snapshot, s->archive);
++    if (s->namespace) {
++        snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s:%s(%s)",
++                 s->repository, s->namespace, s->snapshot, s->archive);
++    } else {
++        snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s(%s)",
++                 s->repository, s->snapshot, s->archive);
++    }
+ }
+ static const char *const pbs_strong_runtime_opts[] = {
+diff --git a/pbs-restore.c b/pbs-restore.c
+index 2f834cf42e..f03d9bab8d 100644
+--- a/pbs-restore.c
++++ b/pbs-restore.c
+@@ -29,7 +29,7 @@
+ static void help(void)
+ {
+     const char *help_msg =
+-        "usage: pbs-restore [--repository <repo>] snapshot archive-name target [command options]\n"
++        "usage: pbs-restore [--repository <repo>] [--ns namespace] snapshot archive-name target [command options]\n"
+         ;
+     printf("%s", help_msg);
+@@ -77,6 +77,7 @@ int main(int argc, char **argv)
+     Error *main_loop_err = NULL;
+     const char *format = "raw";
+     const char *repository = NULL;
++    const char *backup_ns = NULL;
+     const char *keyfile = NULL;
+     int verbose = false;
+     bool skip_zero = false;
+@@ -90,6 +91,7 @@ int main(int argc, char **argv)
+             {"verbose", no_argument, 0, 'v'},
+             {"format", required_argument, 0, 'f'},
+             {"repository", required_argument, 0, 'r'},
++            {"ns", required_argument, 0, 'n'},
+             {"keyfile", required_argument, 0, 'k'},
+             {0, 0, 0, 0}
+         };
+@@ -110,6 +112,9 @@ int main(int argc, char **argv)
+             case 'r':
+                 repository = g_strdup(argv[optind - 1]);
+                 break;
++            case 'n':
++                backup_ns = g_strdup(argv[optind - 1]);
++                break;
+             case 'k':
+                 keyfile = g_strdup(argv[optind - 1]);
+                 break;
+@@ -160,8 +165,16 @@ int main(int argc, char **argv)
+         fprintf(stderr, "connecting to repository '%s'\n", repository);
+     }
+     char *pbs_error = NULL;
+-    ProxmoxRestoreHandle *conn = proxmox_restore_new(
+-        repository, snapshot, password, keyfile, key_password, fingerprint, &pbs_error);
++    ProxmoxRestoreHandle *conn = proxmox_restore_new_ns(
++        repository,
++        snapshot,
++        backup_ns,
++        password,
++        keyfile,
++        key_password,
++        fingerprint,
++        &pbs_error
++    );
+     if (conn == NULL) {
+         fprintf(stderr, "restore failed: %s\n", pbs_error);
+         return -1;
+diff --git a/pve-backup.c b/pve-backup.c
+index 4b5134ed27..262e7d3894 100644
+--- a/pve-backup.c
++++ b/pve-backup.c
+@@ -10,6 +10,8 @@
+ #include "qapi/qmp/qerror.h"
+ #include "qemu/cutils.h"
++#include <proxmox-backup-qemu.h>
++
+ /* PVE backup state and related function */
+ /*
+@@ -531,6 +533,7 @@ UuidInfo coroutine_fn *qmp_backup(
+     bool has_key_password, const char *key_password,
+     bool has_master_keyfile, const char *master_keyfile,
+     bool has_fingerprint, const char *fingerprint,
++    bool has_backup_ns, const char *backup_ns,
+     bool has_backup_id, const char *backup_id,
+     bool has_backup_time, int64_t backup_time,
+     bool has_use_dirty_bitmap, bool use_dirty_bitmap,
+@@ -670,8 +673,9 @@ UuidInfo coroutine_fn *qmp_backup(
+         firewall_name = "fw.conf";
+         char *pbs_err = NULL;
+-        pbs = proxmox_backup_new(
++        pbs = proxmox_backup_new_ns(
+             backup_file,
++            has_backup_ns ? backup_ns : NULL,
+             backup_id,
+             backup_time,
+             dump_cb_block_size,
+diff --git a/qapi/block-core.json b/qapi/block-core.json
+index d8c7331090..889726fc26 100644
+--- a/qapi/block-core.json
++++ b/qapi/block-core.json
+@@ -817,6 +817,8 @@
+ #
+ # @fingerprint: server cert fingerprint (optional for format 'pbs')
+ #
++# @backup-ns: backup namespace (required for format 'pbs')
++#
+ # @backup-id: backup ID (required for format 'pbs')
+ #
+ # @backup-time: backup timestamp (Unix epoch, required for format 'pbs')
+@@ -836,6 +838,7 @@
+                                     '*key-password': 'str',
+                                     '*master-keyfile': 'str',
+                                     '*fingerprint': 'str',
++                                    '*backup-ns': 'str',
+                                     '*backup-id': 'str',
+                                     '*backup-time': 'int',
+                                     '*use-dirty-bitmap': 'bool',
+@@ -3290,7 +3293,7 @@
+ { 'struct': 'BlockdevOptionsPbs',
+   'data': { 'repository': 'str', 'snapshot': 'str', 'archive': 'str',
+             '*keyfile': 'str', '*password': 'str', '*fingerprint': 'str',
+-            '*key_password': 'str' } }
++            '*key_password': 'str', '*namespace': 'str' } }
+ ##
+ # @BlockdevOptionsNVMe:
diff --git a/debian/patches/pve/0052-vma-allow-partial-restore.patch b/debian/patches/pve/0052-vma-allow-partial-restore.patch
deleted file mode 100644 (file)
index f1fd7cb..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Thu, 21 Apr 2022 13:26:48 +0200
-Subject: [PATCH] vma: allow partial restore
-
-Introduce a new map line for skipping a certain drive, of the form
-skip=drive-scsi0
-
-Since in PVE, most archives are compressed and piped to vma for
-restore, it's not easily possible to skip reads.
-
-For the reader, a new skip flag for VmaRestoreState is added and the
-target is allowed to be NULL if skip is specified when registering. If
-the skip flag is set, no writes will be made as well as no check for
-duplicate clusters. Therefore, the flag is not set for verify.
-
-Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
-Acked-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
----
- vma-reader.c |  64 ++++++++++++---------
- vma.c        | 157 +++++++++++++++++++++++++++++----------------------
- vma.h        |   2 +-
- 3 files changed, 126 insertions(+), 97 deletions(-)
-
-diff --git a/vma-reader.c b/vma-reader.c
-index e65f1e8415..81a891c6b1 100644
---- a/vma-reader.c
-+++ b/vma-reader.c
-@@ -28,6 +28,7 @@ typedef struct VmaRestoreState {
-     bool write_zeroes;
-     unsigned long *bitmap;
-     int bitmap_size;
-+    bool skip;
- }  VmaRestoreState;
- struct VmaReader {
-@@ -425,13 +426,14 @@ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id)
- }
- static void allocate_rstate(VmaReader *vmar,  guint8 dev_id,
--                            BlockBackend *target, bool write_zeroes)
-+                            BlockBackend *target, bool write_zeroes, bool skip)
- {
-     assert(vmar);
-     assert(dev_id);
-     vmar->rstate[dev_id].target = target;
-     vmar->rstate[dev_id].write_zeroes = write_zeroes;
-+    vmar->rstate[dev_id].skip = skip;
-     int64_t size = vmar->devinfo[dev_id].size;
-@@ -446,28 +448,30 @@ static void allocate_rstate(VmaReader *vmar,  guint8 dev_id,
- }
- int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockBackend *target,
--                           bool write_zeroes, Error **errp)
-+                           bool write_zeroes, bool skip, Error **errp)
- {
-     assert(vmar);
--    assert(target != NULL);
-+    assert(target != NULL || skip);
-     assert(dev_id);
--    assert(vmar->rstate[dev_id].target == NULL);
--
--    int64_t size = blk_getlength(target);
--    int64_t size_diff = size - vmar->devinfo[dev_id].size;
--
--    /* storage types can have different size restrictions, so it
--     * is not always possible to create an image with exact size.
--     * So we tolerate a size difference up to 4MB.
--     */
--    if ((size_diff < 0) || (size_diff > 4*1024*1024)) {
--        error_setg(errp, "vma_reader_register_bs for stream %s failed - "
--                   "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname,
--                   size, vmar->devinfo[dev_id].size);
--        return -1;
-+    assert(vmar->rstate[dev_id].target == NULL && !vmar->rstate[dev_id].skip);
-+
-+    if (target != NULL) {
-+        int64_t size = blk_getlength(target);
-+        int64_t size_diff = size - vmar->devinfo[dev_id].size;
-+
-+        /* storage types can have different size restrictions, so it
-+         * is not always possible to create an image with exact size.
-+         * So we tolerate a size difference up to 4MB.
-+         */
-+        if ((size_diff < 0) || (size_diff > 4*1024*1024)) {
-+            error_setg(errp, "vma_reader_register_bs for stream %s failed - "
-+                       "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname,
-+                       size, vmar->devinfo[dev_id].size);
-+            return -1;
-+        }
-     }
--    allocate_rstate(vmar, dev_id, target, write_zeroes);
-+    allocate_rstate(vmar, dev_id, target, write_zeroes, skip);
-     return 0;
- }
-@@ -560,19 +564,23 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
-         VmaRestoreState *rstate = &vmar->rstate[dev_id];
-         BlockBackend *target = NULL;
-+        bool skip = rstate->skip;
-+
-         if (dev_id != vmar->vmstate_stream) {
-             target = rstate->target;
--            if (!verify && !target) {
-+            if (!verify && !target && !skip) {
-                 error_setg(errp, "got wrong dev id %d", dev_id);
-                 return -1;
-             }
--            if (vma_reader_get_bitmap(rstate, cluster_num)) {
--                error_setg(errp, "found duplicated cluster %zd for stream %s",
--                          cluster_num, vmar->devinfo[dev_id].devname);
--                return -1;
-+            if (!skip) {
-+                if (vma_reader_get_bitmap(rstate, cluster_num)) {
-+                    error_setg(errp, "found duplicated cluster %zd for stream %s",
-+                              cluster_num, vmar->devinfo[dev_id].devname);
-+                    return -1;
-+                }
-+                vma_reader_set_bitmap(rstate, cluster_num, 1);
-             }
--            vma_reader_set_bitmap(rstate, cluster_num, 1);
-             max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE;
-         } else {
-@@ -618,7 +626,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
-                 return -1;
-             }
--            if (!verify) {
-+            if (!verify && !skip) {
-                 int nb_sectors = end_sector - sector_num;
-                 if (restore_write_data(vmar, dev_id, target, vmstate_fd,
-                                        buf + start, sector_num, nb_sectors,
-@@ -654,7 +662,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
-                         return -1;
-                     }
--                    if (!verify) {
-+                    if (!verify && !skip) {
-                         int nb_sectors = end_sector - sector_num;
-                         if (restore_write_data(vmar, dev_id, target, vmstate_fd,
-                                                buf + start, sector_num,
-@@ -679,7 +687,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
-                             vmar->partial_zero_cluster_data += zero_size;
-                         }
--                        if (rstate->write_zeroes && !verify) {
-+                        if (rstate->write_zeroes && !verify && !skip) {
-                             if (restore_write_data(vmar, dev_id, target, vmstate_fd,
-                                                    zero_vma_block, sector_num,
-                                                    nb_sectors, errp) < 0) {
-@@ -850,7 +858,7 @@ int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp)
-     for (dev_id = 1; dev_id < 255; dev_id++) {
-         if (vma_reader_get_device_info(vmar, dev_id)) {
--            allocate_rstate(vmar, dev_id, NULL, false);
-+            allocate_rstate(vmar, dev_id, NULL, false, false);
-         }
-     }
-diff --git a/vma.c b/vma.c
-index e8dffb43e0..e6e9ffc7fe 100644
---- a/vma.c
-+++ b/vma.c
-@@ -138,6 +138,7 @@ typedef struct RestoreMap {
-     char *throttling_group;
-     char *cache;
-     bool write_zero;
-+    bool skip;
- } RestoreMap;
- static bool try_parse_option(char **line, const char *optname, char **out, const char *inbuf) {
-@@ -245,47 +246,61 @@ static int extract_content(int argc, char **argv)
-             char *bps = NULL;
-             char *group = NULL;
-             char *cache = NULL;
-+            char *devname = NULL;
-+            bool skip = false;
-+            uint64_t bps_value = 0;
-+            const char *path = NULL;
-+            bool write_zero = true;
-+
-             if (!line || line[0] == '\0' || !strcmp(line, "done\n")) {
-                 break;
-             }
-             int len = strlen(line);
-             if (line[len - 1] == '\n') {
-                 line[len - 1] = '\0';
--                if (len == 1) {
-+                len = len - 1;
-+                if (len == 0) {
-                     break;
-                 }
-             }
--            while (1) {
--                if (!try_parse_option(&line, "format", &format, inbuf) &&
--                    !try_parse_option(&line, "throttling.bps", &bps, inbuf) &&
--                    !try_parse_option(&line, "throttling.group", &group, inbuf) &&
--                    !try_parse_option(&line, "cache", &cache, inbuf))
--                {
--                    break;
-+            if (strncmp(line, "skip", 4) == 0) {
-+                if (len < 6 || line[4] != '=') {
-+                    g_error("read map failed - option 'skip' has no value ('%s')",
-+                            inbuf);
-+                } else {
-+                    devname = line + 5;
-+                    skip = true;
-+                }
-+            } else {
-+                while (1) {
-+                    if (!try_parse_option(&line, "format", &format, inbuf) &&
-+                        !try_parse_option(&line, "throttling.bps", &bps, inbuf) &&
-+                        !try_parse_option(&line, "throttling.group", &group, inbuf) &&
-+                        !try_parse_option(&line, "cache", &cache, inbuf))
-+                    {
-+                        break;
-+                    }
-                 }
--            }
--            uint64_t bps_value = 0;
--            if (bps) {
--                bps_value = verify_u64(bps);
--                g_free(bps);
--            }
-+                if (bps) {
-+                    bps_value = verify_u64(bps);
-+                    g_free(bps);
-+                }
--            const char *path;
--            bool write_zero;
--            if (line[0] == '0' && line[1] == ':') {
--                path = line + 2;
--                write_zero = false;
--            } else if (line[0] == '1' && line[1] == ':') {
--                path = line + 2;
--                write_zero = true;
--            } else {
--                g_error("read map failed - parse error ('%s')", inbuf);
-+                if (line[0] == '0' && line[1] == ':') {
-+                    path = line + 2;
-+                    write_zero = false;
-+                } else if (line[0] == '1' && line[1] == ':') {
-+                    path = line + 2;
-+                    write_zero = true;
-+                } else {
-+                    g_error("read map failed - parse error ('%s')", inbuf);
-+                }
-+
-+                path = extract_devname(path, &devname, -1);
-             }
--            char *devname = NULL;
--            path = extract_devname(path, &devname, -1);
-             if (!devname) {
-                 g_error("read map failed - no dev name specified ('%s')",
-                         inbuf);
-@@ -299,6 +314,7 @@ static int extract_content(int argc, char **argv)
-             map->throttling_group = group;
-             map->cache = cache;
-             map->write_zero = write_zero;
-+            map->skip = skip;
-             g_hash_table_insert(devmap, map->devname, map);
-@@ -328,6 +344,7 @@ static int extract_content(int argc, char **argv)
-             const char *cache = NULL;
-             int flags = BDRV_O_RDWR;
-             bool write_zero = true;
-+            bool skip = false;
-             BlockBackend *blk = NULL;
-@@ -343,6 +360,7 @@ static int extract_content(int argc, char **argv)
-                 throttling_group = map->throttling_group;
-                 cache = map->cache;
-                 write_zero = map->write_zero;
-+                skip = map->skip;
-             } else {
-                 devfn = g_strdup_printf("%s/tmp-disk-%s.raw",
-                                         dirname, di->devname);
-@@ -361,57 +379,60 @@ static int extract_content(int argc, char **argv)
-                 write_zero = false;
-             }
--          size_t devlen = strlen(devfn);
--          QDict *options = NULL;
--            bool writethrough;
--            if (format) {
--                /* explicit format from commandline */
--                options = qdict_new();
--                qdict_put_str(options, "driver", format);
--            } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) ||
--                     strncmp(devfn, "/dev/", 5) == 0)
--          {
--                /* This part is now deprecated for PVE as well (just as qemu
--                 * deprecated not specifying an explicit raw format, too.
--                 */
--              /* explicit raw format */
--              options = qdict_new();
--              qdict_put_str(options, "driver", "raw");
--          }
--            if (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) {
--                g_error("invalid cache option: %s\n", cache);
--            }
-+            if (!skip) {
-+                size_t devlen = strlen(devfn);
-+                QDict *options = NULL;
-+                bool writethrough;
-+                if (format) {
-+                    /* explicit format from commandline */
-+                    options = qdict_new();
-+                    qdict_put_str(options, "driver", format);
-+                } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) ||
-+                    strncmp(devfn, "/dev/", 5) == 0)
-+                {
-+                    /* This part is now deprecated for PVE as well (just as qemu
-+                     * deprecated not specifying an explicit raw format, too.
-+                     */
-+                    /* explicit raw format */
-+                    options = qdict_new();
-+                    qdict_put_str(options, "driver", "raw");
-+                }
--          if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
--                g_error("can't open file %s - %s", devfn,
--                        error_get_pretty(errp));
--            }
-+                if (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) {
-+                    g_error("invalid cache option: %s\n", cache);
-+                }
--            if (cache) {
--                blk_set_enable_write_cache(blk, !writethrough);
--            }
-+                if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
-+                    g_error("can't open file %s - %s", devfn,
-+                            error_get_pretty(errp));
-+                }
--            if (throttling_group) {
--                blk_io_limits_enable(blk, throttling_group);
--            }
-+                if (cache) {
-+                    blk_set_enable_write_cache(blk, !writethrough);
-+                }
--            if (throttling_bps) {
--                if (!throttling_group) {
--                    blk_io_limits_enable(blk, devfn);
-+                if (throttling_group) {
-+                    blk_io_limits_enable(blk, throttling_group);
-                 }
--                ThrottleConfig cfg;
--                throttle_config_init(&cfg);
--                cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps;
--                Error *err = NULL;
--                if (!throttle_is_valid(&cfg, &err)) {
--                    error_report_err(err);
--                    g_error("failed to apply throttling");
-+                if (throttling_bps) {
-+                    if (!throttling_group) {
-+                        blk_io_limits_enable(blk, devfn);
-+                    }
-+
-+                    ThrottleConfig cfg;
-+                    throttle_config_init(&cfg);
-+                    cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps;
-+                    Error *err = NULL;
-+                    if (!throttle_is_valid(&cfg, &err)) {
-+                        error_report_err(err);
-+                        g_error("failed to apply throttling");
-+                    }
-+                    blk_set_io_limits(blk, &cfg);
-                 }
--                blk_set_io_limits(blk, &cfg);
-             }
--            if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) {
-+            if (vma_reader_register_bs(vmar, i, blk, write_zero, skip, &errp) < 0) {
-                 g_error("%s", error_get_pretty(errp));
-             }
-diff --git a/vma.h b/vma.h
-index c895c97f6d..1b62859165 100644
---- a/vma.h
-+++ b/vma.h
-@@ -142,7 +142,7 @@ GList *vma_reader_get_config_data(VmaReader *vmar);
- VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id);
- int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id,
-                            BlockBackend *target, bool write_zeroes,
--                           Error **errp);
-+                           bool skip, Error **errp);
- int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
-                        Error **errp);
- int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp);
diff --git a/debian/patches/pve/0053-Revert-block-rbd-workaround-for-ceph-issue-53784.patch b/debian/patches/pve/0053-Revert-block-rbd-workaround-for-ceph-issue-53784.patch
new file mode 100644 (file)
index 0000000..9827983
--- /dev/null
@@ -0,0 +1,80 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Thu, 23 Jun 2022 14:00:05 +0200
+Subject: [PATCH] Revert "block/rbd: workaround for ceph issue #53784"
+
+This reverts commit fc176116cdea816ceb8dd969080b2b95f58edbc0 in
+preparation to revert 0347a8fd4c3faaedf119be04c197804be40a384b.
+
+Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
+---
+ block/rbd.c | 42 ++----------------------------------------
+ 1 file changed, 2 insertions(+), 40 deletions(-)
+
+diff --git a/block/rbd.c b/block/rbd.c
+index 64a8d7d48b..9fc6dcb957 100644
+--- a/block/rbd.c
++++ b/block/rbd.c
+@@ -1348,7 +1348,6 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
+     int status, r;
+     RBDDiffIterateReq req = { .offs = offset };
+     uint64_t features, flags;
+-    uint64_t head = 0;
+     assert(offset + bytes <= s->image_size);
+@@ -1376,43 +1375,7 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
+         return status;
+     }
+-#if LIBRBD_VERSION_CODE < LIBRBD_VERSION(1, 17, 0)
+-    /*
+-     * librbd had a bug until early 2022 that affected all versions of ceph that
+-     * supported fast-diff. This bug results in reporting of incorrect offsets
+-     * if the offset parameter to rbd_diff_iterate2 is not object aligned.
+-     * Work around this bug by rounding down the offset to object boundaries.
+-     * This is OK because we call rbd_diff_iterate2 with whole_object = true.
+-     * However, this workaround only works for non cloned images with default
+-     * striping.
+-     *
+-     * See: https://tracker.ceph.com/issues/53784
+-     */
+-
+-    /* check if RBD image has non-default striping enabled */
+-    if (features & RBD_FEATURE_STRIPINGV2) {
+-        return status;
+-    }
+-
+-#pragma GCC diagnostic push
+-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+-    /*
+-     * check if RBD image is a clone (= has a parent).
+-     *
+-     * rbd_get_parent_info is deprecated from Nautilus onwards, but the
+-     * replacement rbd_get_parent is not present in Luminous and Mimic.
+-     */
+-    if (rbd_get_parent_info(s->image, NULL, 0, NULL, 0, NULL, 0) != -ENOENT) {
+-        return status;
+-    }
+-#pragma GCC diagnostic pop
+-
+-    head = req.offs & (s->object_size - 1);
+-    req.offs -= head;
+-    bytes += head;
+-#endif
+-
+-    r = rbd_diff_iterate2(s->image, NULL, req.offs, bytes, true, true,
++    r = rbd_diff_iterate2(s->image, NULL, offset, bytes, true, true,
+                           qemu_rbd_diff_iterate_cb, &req);
+     if (r < 0 && r != QEMU_RBD_EXIT_DIFF_ITERATE2) {
+         return status;
+@@ -1431,8 +1394,7 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
+         status = BDRV_BLOCK_ZERO | BDRV_BLOCK_OFFSET_VALID;
+     }
+-    assert(req.bytes > head);
+-    *pnum = req.bytes - head;
++    *pnum = req.bytes;
+     return status;
+ }
diff --git a/debian/patches/pve/0053-pbs-namespace-support.patch b/debian/patches/pve/0053-pbs-namespace-support.patch
deleted file mode 100644 (file)
index 2640b95..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Tue, 26 Apr 2022 16:06:28 +0200
-Subject: [PATCH] pbs: namespace support
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
----
- block/monitor/block-hmp-cmds.c |  1 +
- block/pbs.c                    | 25 +++++++++++++++++++++----
- pbs-restore.c                  | 19 ++++++++++++++++---
- pve-backup.c                   |  6 +++++-
- qapi/block-core.json           |  5 ++++-
- 5 files changed, 47 insertions(+), 9 deletions(-)
-
-diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
-index c7468e5d3b..57b2457f1e 100644
---- a/block/monitor/block-hmp-cmds.c
-+++ b/block/monitor/block-hmp-cmds.c
-@@ -1041,6 +1041,7 @@ void coroutine_fn hmp_backup(Monitor *mon, const QDict *qdict)
-         false, NULL, // PBS key_password
-         false, NULL, // PBS master_keyfile
-         false, NULL, // PBS fingerprint
-+        false, NULL, // PBS backup-ns
-         false, NULL, // PBS backup-id
-         false, 0, // PBS backup-time
-         false, false, // PBS use-dirty-bitmap
-diff --git a/block/pbs.c b/block/pbs.c
-index ce9a870885..9192f3e41b 100644
---- a/block/pbs.c
-+++ b/block/pbs.c
-@@ -14,6 +14,7 @@
- #include <proxmox-backup-qemu.h>
- #define PBS_OPT_REPOSITORY "repository"
-+#define PBS_OPT_NAMESPACE "namespace"
- #define PBS_OPT_SNAPSHOT "snapshot"
- #define PBS_OPT_ARCHIVE "archive"
- #define PBS_OPT_KEYFILE "keyfile"
-@@ -27,6 +28,7 @@ typedef struct {
-     int64_t length;
-     char *repository;
-+    char *namespace;
-     char *snapshot;
-     char *archive;
- } BDRVPBSState;
-@@ -40,6 +42,11 @@ static QemuOptsList runtime_opts = {
-             .type = QEMU_OPT_STRING,
-             .help = "The server address and repository to connect to.",
-         },
-+        {
-+            .name = PBS_OPT_NAMESPACE,
-+            .type = QEMU_OPT_STRING,
-+            .help = "Optional: The snapshot's namespace.",
-+        },
-         {
-             .name = PBS_OPT_SNAPSHOT,
-             .type = QEMU_OPT_STRING,
-@@ -76,7 +83,7 @@ static QemuOptsList runtime_opts = {
- // filename format:
--// pbs:repository=<repo>,snapshot=<snap>,password=<pw>,key_password=<kpw>,fingerprint=<fp>,archive=<archive>
-+// pbs:repository=<repo>,namespace=<ns>,snapshot=<snap>,password=<pw>,key_password=<kpw>,fingerprint=<fp>,archive=<archive>
- static void pbs_parse_filename(const char *filename, QDict *options,
-                                      Error **errp)
- {
-@@ -112,6 +119,7 @@ static int pbs_open(BlockDriverState *bs, QDict *options, int flags,
-     s->archive = g_strdup(qemu_opt_get(opts, PBS_OPT_ARCHIVE));
-     const char *keyfile = qemu_opt_get(opts, PBS_OPT_KEYFILE);
-     const char *password = qemu_opt_get(opts, PBS_OPT_PASSWORD);
-+    const char *namespace = qemu_opt_get(opts, PBS_OPT_NAMESPACE);
-     const char *fingerprint = qemu_opt_get(opts, PBS_OPT_FINGERPRINT);
-     const char *key_password = qemu_opt_get(opts, PBS_OPT_ENCRYPTION_PASSWORD);
-@@ -124,9 +132,12 @@ static int pbs_open(BlockDriverState *bs, QDict *options, int flags,
-     if (!key_password) {
-         key_password = getenv("PBS_ENCRYPTION_PASSWORD");
-     }
-+    if (namespace) {
-+        s->namespace = g_strdup(namespace);
-+    }
-     /* connect to PBS server in read mode */
--    s->conn = proxmox_restore_new(s->repository, s->snapshot, password,
-+    s->conn = proxmox_restore_new_ns(s->repository, s->snapshot, s->namespace, password,
-         keyfile, key_password, fingerprint, &pbs_error);
-     /* invalidates qemu_opt_get char pointers from above */
-@@ -171,6 +182,7 @@ static int pbs_file_open(BlockDriverState *bs, QDict *options, int flags,
- static void pbs_close(BlockDriverState *bs) {
-     BDRVPBSState *s = bs->opaque;
-     g_free(s->repository);
-+    g_free(s->namespace);
-     g_free(s->snapshot);
-     g_free(s->archive);
-     proxmox_restore_disconnect(s->conn);
-@@ -252,8 +264,13 @@ static coroutine_fn int pbs_co_pwritev(BlockDriverState *bs,
- static void pbs_refresh_filename(BlockDriverState *bs)
- {
-     BDRVPBSState *s = bs->opaque;
--    snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s(%s)",
--             s->repository, s->snapshot, s->archive);
-+    if (s->namespace) {
-+        snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s:%s(%s)",
-+                 s->repository, s->namespace, s->snapshot, s->archive);
-+    } else {
-+        snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s(%s)",
-+                 s->repository, s->snapshot, s->archive);
-+    }
- }
- static const char *const pbs_strong_runtime_opts[] = {
-diff --git a/pbs-restore.c b/pbs-restore.c
-index 2f834cf42e..f03d9bab8d 100644
---- a/pbs-restore.c
-+++ b/pbs-restore.c
-@@ -29,7 +29,7 @@
- static void help(void)
- {
-     const char *help_msg =
--        "usage: pbs-restore [--repository <repo>] snapshot archive-name target [command options]\n"
-+        "usage: pbs-restore [--repository <repo>] [--ns namespace] snapshot archive-name target [command options]\n"
-         ;
-     printf("%s", help_msg);
-@@ -77,6 +77,7 @@ int main(int argc, char **argv)
-     Error *main_loop_err = NULL;
-     const char *format = "raw";
-     const char *repository = NULL;
-+    const char *backup_ns = NULL;
-     const char *keyfile = NULL;
-     int verbose = false;
-     bool skip_zero = false;
-@@ -90,6 +91,7 @@ int main(int argc, char **argv)
-             {"verbose", no_argument, 0, 'v'},
-             {"format", required_argument, 0, 'f'},
-             {"repository", required_argument, 0, 'r'},
-+            {"ns", required_argument, 0, 'n'},
-             {"keyfile", required_argument, 0, 'k'},
-             {0, 0, 0, 0}
-         };
-@@ -110,6 +112,9 @@ int main(int argc, char **argv)
-             case 'r':
-                 repository = g_strdup(argv[optind - 1]);
-                 break;
-+            case 'n':
-+                backup_ns = g_strdup(argv[optind - 1]);
-+                break;
-             case 'k':
-                 keyfile = g_strdup(argv[optind - 1]);
-                 break;
-@@ -160,8 +165,16 @@ int main(int argc, char **argv)
-         fprintf(stderr, "connecting to repository '%s'\n", repository);
-     }
-     char *pbs_error = NULL;
--    ProxmoxRestoreHandle *conn = proxmox_restore_new(
--        repository, snapshot, password, keyfile, key_password, fingerprint, &pbs_error);
-+    ProxmoxRestoreHandle *conn = proxmox_restore_new_ns(
-+        repository,
-+        snapshot,
-+        backup_ns,
-+        password,
-+        keyfile,
-+        key_password,
-+        fingerprint,
-+        &pbs_error
-+    );
-     if (conn == NULL) {
-         fprintf(stderr, "restore failed: %s\n", pbs_error);
-         return -1;
-diff --git a/pve-backup.c b/pve-backup.c
-index 4b5134ed27..262e7d3894 100644
---- a/pve-backup.c
-+++ b/pve-backup.c
-@@ -10,6 +10,8 @@
- #include "qapi/qmp/qerror.h"
- #include "qemu/cutils.h"
-+#include <proxmox-backup-qemu.h>
-+
- /* PVE backup state and related function */
- /*
-@@ -531,6 +533,7 @@ UuidInfo coroutine_fn *qmp_backup(
-     bool has_key_password, const char *key_password,
-     bool has_master_keyfile, const char *master_keyfile,
-     bool has_fingerprint, const char *fingerprint,
-+    bool has_backup_ns, const char *backup_ns,
-     bool has_backup_id, const char *backup_id,
-     bool has_backup_time, int64_t backup_time,
-     bool has_use_dirty_bitmap, bool use_dirty_bitmap,
-@@ -670,8 +673,9 @@ UuidInfo coroutine_fn *qmp_backup(
-         firewall_name = "fw.conf";
-         char *pbs_err = NULL;
--        pbs = proxmox_backup_new(
-+        pbs = proxmox_backup_new_ns(
-             backup_file,
-+            has_backup_ns ? backup_ns : NULL,
-             backup_id,
-             backup_time,
-             dump_cb_block_size,
-diff --git a/qapi/block-core.json b/qapi/block-core.json
-index d8c7331090..889726fc26 100644
---- a/qapi/block-core.json
-+++ b/qapi/block-core.json
-@@ -817,6 +817,8 @@
- #
- # @fingerprint: server cert fingerprint (optional for format 'pbs')
- #
-+# @backup-ns: backup namespace (required for format 'pbs')
-+#
- # @backup-id: backup ID (required for format 'pbs')
- #
- # @backup-time: backup timestamp (Unix epoch, required for format 'pbs')
-@@ -836,6 +838,7 @@
-                                     '*key-password': 'str',
-                                     '*master-keyfile': 'str',
-                                     '*fingerprint': 'str',
-+                                    '*backup-ns': 'str',
-                                     '*backup-id': 'str',
-                                     '*backup-time': 'int',
-                                     '*use-dirty-bitmap': 'bool',
-@@ -3290,7 +3293,7 @@
- { 'struct': 'BlockdevOptionsPbs',
-   'data': { 'repository': 'str', 'snapshot': 'str', 'archive': 'str',
-             '*keyfile': 'str', '*password': 'str', '*fingerprint': 'str',
--            '*key_password': 'str' } }
-+            '*key_password': 'str', '*namespace': 'str' } }
- ##
- # @BlockdevOptionsNVMe:
diff --git a/debian/patches/pve/0054-Revert-block-rbd-fix-handling-of-holes-in-.bdrv_co_b.patch b/debian/patches/pve/0054-Revert-block-rbd-fix-handling-of-holes-in-.bdrv_co_b.patch
new file mode 100644 (file)
index 0000000..6972373
--- /dev/null
@@ -0,0 +1,35 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Thu, 23 Jun 2022 14:00:07 +0200
+Subject: [PATCH] Revert "block/rbd: fix handling of holes in
+ .bdrv_co_block_status"
+
+This reverts commit 9e302f64bb407a9bb097b626da97228c2654cfee in
+preparation to revert 0347a8fd4c3faaedf119be04c197804be40a384b.
+
+Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
+---
+ block/rbd.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/block/rbd.c b/block/rbd.c
+index 9fc6dcb957..98f4ba2620 100644
+--- a/block/rbd.c
++++ b/block/rbd.c
+@@ -1307,11 +1307,11 @@ static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len,
+     RBDDiffIterateReq *req = opaque;
+     assert(req->offs + req->bytes <= offs);
+-
+-    /* treat a hole like an unallocated area and bail out */
+-    if (!exists) {
+-        return 0;
+-    }
++    /*
++     * we do not diff against a snapshot so we should never receive a callback
++     * for a hole.
++     */
++    assert(exists);
+     if (!req->exists && offs > req->offs) {
+         /*
diff --git a/debian/patches/pve/0054-Revert-block-rbd-workaround-for-ceph-issue-53784.patch b/debian/patches/pve/0054-Revert-block-rbd-workaround-for-ceph-issue-53784.patch
deleted file mode 100644 (file)
index 9827983..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Thu, 23 Jun 2022 14:00:05 +0200
-Subject: [PATCH] Revert "block/rbd: workaround for ceph issue #53784"
-
-This reverts commit fc176116cdea816ceb8dd969080b2b95f58edbc0 in
-preparation to revert 0347a8fd4c3faaedf119be04c197804be40a384b.
-
-Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
----
- block/rbd.c | 42 ++----------------------------------------
- 1 file changed, 2 insertions(+), 40 deletions(-)
-
-diff --git a/block/rbd.c b/block/rbd.c
-index 64a8d7d48b..9fc6dcb957 100644
---- a/block/rbd.c
-+++ b/block/rbd.c
-@@ -1348,7 +1348,6 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
-     int status, r;
-     RBDDiffIterateReq req = { .offs = offset };
-     uint64_t features, flags;
--    uint64_t head = 0;
-     assert(offset + bytes <= s->image_size);
-@@ -1376,43 +1375,7 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
-         return status;
-     }
--#if LIBRBD_VERSION_CODE < LIBRBD_VERSION(1, 17, 0)
--    /*
--     * librbd had a bug until early 2022 that affected all versions of ceph that
--     * supported fast-diff. This bug results in reporting of incorrect offsets
--     * if the offset parameter to rbd_diff_iterate2 is not object aligned.
--     * Work around this bug by rounding down the offset to object boundaries.
--     * This is OK because we call rbd_diff_iterate2 with whole_object = true.
--     * However, this workaround only works for non cloned images with default
--     * striping.
--     *
--     * See: https://tracker.ceph.com/issues/53784
--     */
--
--    /* check if RBD image has non-default striping enabled */
--    if (features & RBD_FEATURE_STRIPINGV2) {
--        return status;
--    }
--
--#pragma GCC diagnostic push
--#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
--    /*
--     * check if RBD image is a clone (= has a parent).
--     *
--     * rbd_get_parent_info is deprecated from Nautilus onwards, but the
--     * replacement rbd_get_parent is not present in Luminous and Mimic.
--     */
--    if (rbd_get_parent_info(s->image, NULL, 0, NULL, 0, NULL, 0) != -ENOENT) {
--        return status;
--    }
--#pragma GCC diagnostic pop
--
--    head = req.offs & (s->object_size - 1);
--    req.offs -= head;
--    bytes += head;
--#endif
--
--    r = rbd_diff_iterate2(s->image, NULL, req.offs, bytes, true, true,
-+    r = rbd_diff_iterate2(s->image, NULL, offset, bytes, true, true,
-                           qemu_rbd_diff_iterate_cb, &req);
-     if (r < 0 && r != QEMU_RBD_EXIT_DIFF_ITERATE2) {
-         return status;
-@@ -1431,8 +1394,7 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
-         status = BDRV_BLOCK_ZERO | BDRV_BLOCK_OFFSET_VALID;
-     }
--    assert(req.bytes > head);
--    *pnum = req.bytes - head;
-+    *pnum = req.bytes;
-     return status;
- }
diff --git a/debian/patches/pve/0055-Revert-block-rbd-fix-handling-of-holes-in-.bdrv_co_b.patch b/debian/patches/pve/0055-Revert-block-rbd-fix-handling-of-holes-in-.bdrv_co_b.patch
deleted file mode 100644 (file)
index 6972373..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Thu, 23 Jun 2022 14:00:07 +0200
-Subject: [PATCH] Revert "block/rbd: fix handling of holes in
- .bdrv_co_block_status"
-
-This reverts commit 9e302f64bb407a9bb097b626da97228c2654cfee in
-preparation to revert 0347a8fd4c3faaedf119be04c197804be40a384b.
-
-Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
----
- block/rbd.c | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/block/rbd.c b/block/rbd.c
-index 9fc6dcb957..98f4ba2620 100644
---- a/block/rbd.c
-+++ b/block/rbd.c
-@@ -1307,11 +1307,11 @@ static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len,
-     RBDDiffIterateReq *req = opaque;
-     assert(req->offs + req->bytes <= offs);
--
--    /* treat a hole like an unallocated area and bail out */
--    if (!exists) {
--        return 0;
--    }
-+    /*
-+     * we do not diff against a snapshot so we should never receive a callback
-+     * for a hole.
-+     */
-+    assert(exists);
-     if (!req->exists && offs > req->offs) {
-         /*
diff --git a/debian/patches/pve/0055-Revert-block-rbd-implement-bdrv_co_block_status.patch b/debian/patches/pve/0055-Revert-block-rbd-implement-bdrv_co_block_status.patch
new file mode 100644 (file)
index 0000000..f7025a3
--- /dev/null
@@ -0,0 +1,161 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Tue, 17 May 2022 09:46:02 +0200
+Subject: [PATCH] Revert "block/rbd: implement bdrv_co_block_status"
+
+During backup, bdrv_co_block_status is called for each block copy
+chunk. When RBD is used, the current implementation with
+rbd_diff_iterate2() using whole_object=true takes about linearly more
+time, depending on the image size. Since there are linearly more
+chunks, the slowdown is quadratic, becoming unacceptable for large
+images (starting somewhere between 500-1000 GiB in my testing).
+
+This reverts commit 0347a8fd4c3faaedf119be04c197804be40a384b as a
+stop-gap measure, until it's clear how to make the implemenation
+more efficient.
+
+Upstream bug report:
+https://gitlab.com/qemu-project/qemu/-/issues/1026
+
+Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
+---
+ block/rbd.c | 112 ----------------------------------------------------
+ 1 file changed, 112 deletions(-)
+
+diff --git a/block/rbd.c b/block/rbd.c
+index 98f4ba2620..efcbbe5949 100644
+--- a/block/rbd.c
++++ b/block/rbd.c
+@@ -97,12 +97,6 @@ typedef struct RBDTask {
+     int64_t ret;
+ } RBDTask;
+-typedef struct RBDDiffIterateReq {
+-    uint64_t offs;
+-    uint64_t bytes;
+-    bool exists;
+-} RBDDiffIterateReq;
+-
+ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
+                             BlockdevOptionsRbd *opts, bool cache,
+                             const char *keypairs, const char *secretid,
+@@ -1293,111 +1287,6 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs,
+     return spec_info;
+ }
+-/*
+- * rbd_diff_iterate2 allows to interrupt the exection by returning a negative
+- * value in the callback routine. Choose a value that does not conflict with
+- * an existing exitcode and return it if we want to prematurely stop the
+- * execution because we detected a change in the allocation status.
+- */
+-#define QEMU_RBD_EXIT_DIFF_ITERATE2 -9000
+-
+-static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len,
+-                                    int exists, void *opaque)
+-{
+-    RBDDiffIterateReq *req = opaque;
+-
+-    assert(req->offs + req->bytes <= offs);
+-    /*
+-     * we do not diff against a snapshot so we should never receive a callback
+-     * for a hole.
+-     */
+-    assert(exists);
+-
+-    if (!req->exists && offs > req->offs) {
+-        /*
+-         * we started in an unallocated area and hit the first allocated
+-         * block. req->bytes must be set to the length of the unallocated area
+-         * before the allocated area. stop further processing.
+-         */
+-        req->bytes = offs - req->offs;
+-        return QEMU_RBD_EXIT_DIFF_ITERATE2;
+-    }
+-
+-    if (req->exists && offs > req->offs + req->bytes) {
+-        /*
+-         * we started in an allocated area and jumped over an unallocated area,
+-         * req->bytes contains the length of the allocated area before the
+-         * unallocated area. stop further processing.
+-         */
+-        return QEMU_RBD_EXIT_DIFF_ITERATE2;
+-    }
+-
+-    req->bytes += len;
+-    req->exists = true;
+-
+-    return 0;
+-}
+-
+-static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
+-                                                 bool want_zero, int64_t offset,
+-                                                 int64_t bytes, int64_t *pnum,
+-                                                 int64_t *map,
+-                                                 BlockDriverState **file)
+-{
+-    BDRVRBDState *s = bs->opaque;
+-    int status, r;
+-    RBDDiffIterateReq req = { .offs = offset };
+-    uint64_t features, flags;
+-
+-    assert(offset + bytes <= s->image_size);
+-
+-    /* default to all sectors allocated */
+-    status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
+-    *map = offset;
+-    *file = bs;
+-    *pnum = bytes;
+-
+-    /* check if RBD image supports fast-diff */
+-    r = rbd_get_features(s->image, &features);
+-    if (r < 0) {
+-        return status;
+-    }
+-    if (!(features & RBD_FEATURE_FAST_DIFF)) {
+-        return status;
+-    }
+-
+-    /* check if RBD fast-diff result is valid */
+-    r = rbd_get_flags(s->image, &flags);
+-    if (r < 0) {
+-        return status;
+-    }
+-    if (flags & RBD_FLAG_FAST_DIFF_INVALID) {
+-        return status;
+-    }
+-
+-    r = rbd_diff_iterate2(s->image, NULL, offset, bytes, true, true,
+-                          qemu_rbd_diff_iterate_cb, &req);
+-    if (r < 0 && r != QEMU_RBD_EXIT_DIFF_ITERATE2) {
+-        return status;
+-    }
+-    assert(req.bytes <= bytes);
+-    if (!req.exists) {
+-        if (r == 0) {
+-            /*
+-             * rbd_diff_iterate2 does not invoke callbacks for unallocated
+-             * areas. This here catches the case where no callback was
+-             * invoked at all (req.bytes == 0).
+-             */
+-            assert(req.bytes == 0);
+-            req.bytes = bytes;
+-        }
+-        status = BDRV_BLOCK_ZERO | BDRV_BLOCK_OFFSET_VALID;
+-    }
+-
+-    *pnum = req.bytes;
+-    return status;
+-}
+-
+ static int64_t qemu_rbd_getlength(BlockDriverState *bs)
+ {
+     BDRVRBDState *s = bs->opaque;
+@@ -1633,7 +1522,6 @@ static BlockDriver bdrv_rbd = {
+ #ifdef LIBRBD_SUPPORTS_WRITE_ZEROES
+     .bdrv_co_pwrite_zeroes  = qemu_rbd_co_pwrite_zeroes,
+ #endif
+-    .bdrv_co_block_status   = qemu_rbd_co_block_status,
+     .bdrv_snapshot_create   = qemu_rbd_snap_create,
+     .bdrv_snapshot_delete   = qemu_rbd_snap_remove,
diff --git a/debian/patches/pve/0056-PVE-Backup-create-jobs-correctly-cancel-in-error-sce.patch b/debian/patches/pve/0056-PVE-Backup-create-jobs-correctly-cancel-in-error-sce.patch
new file mode 100644 (file)
index 0000000..3598205
--- /dev/null
@@ -0,0 +1,60 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Wed, 25 May 2022 13:59:37 +0200
+Subject: [PATCH] PVE-Backup: create jobs: correctly cancel in error scenario
+
+The first call to job_cancel_sync() will cancel and free all jobs in
+the transaction, so ensure that it's called only once and get rid of
+the job_unref() that would operate on freed memory.
+
+It's also necessary to NULL backup_state.pbs in the error scenario,
+because a subsequent backup_cancel QMP call (as happens in PVE when
+the backup QMP command fails) would try to call proxmox_backup_abort()
+and run into a segfault.
+
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+[FE: adapt for new job lock mechanism replacing AioContext locks]
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ pve-backup.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/pve-backup.c b/pve-backup.c
+index 262e7d3894..fde3554133 100644
+--- a/pve-backup.c
++++ b/pve-backup.c
+@@ -503,6 +503,11 @@ static void create_backup_jobs_bh(void *opaque) {
+     }
+     if (*errp) {
++        /*
++         * It's enough to cancel one job in the transaction, the rest will
++         * follow automatically.
++         */
++        bool canceled = false;
+         l = backup_state.di_list;
+         while (l) {
+             PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
+@@ -513,11 +518,11 @@ static void create_backup_jobs_bh(void *opaque) {
+                 di->target = NULL;
+             }
+-            if (di->job) {
++            if (!canceled && di->job) {
+                 WITH_JOB_LOCK_GUARD() {
+                     job_cancel_sync_locked(&di->job->job, true);
+-                    job_unref_locked(&di->job->job);
+                 }
++                canceled = true;
+             }
+         }
+     }
+@@ -943,6 +948,7 @@ err:
+     if (pbs) {
+         proxmox_backup_disconnect(pbs);
++        backup_state.pbs = NULL;
+     }
+     if (backup_dir) {
diff --git a/debian/patches/pve/0056-Revert-block-rbd-implement-bdrv_co_block_status.patch b/debian/patches/pve/0056-Revert-block-rbd-implement-bdrv_co_block_status.patch
deleted file mode 100644 (file)
index f7025a3..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Tue, 17 May 2022 09:46:02 +0200
-Subject: [PATCH] Revert "block/rbd: implement bdrv_co_block_status"
-
-During backup, bdrv_co_block_status is called for each block copy
-chunk. When RBD is used, the current implementation with
-rbd_diff_iterate2() using whole_object=true takes about linearly more
-time, depending on the image size. Since there are linearly more
-chunks, the slowdown is quadratic, becoming unacceptable for large
-images (starting somewhere between 500-1000 GiB in my testing).
-
-This reverts commit 0347a8fd4c3faaedf119be04c197804be40a384b as a
-stop-gap measure, until it's clear how to make the implemenation
-more efficient.
-
-Upstream bug report:
-https://gitlab.com/qemu-project/qemu/-/issues/1026
-
-Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
----
- block/rbd.c | 112 ----------------------------------------------------
- 1 file changed, 112 deletions(-)
-
-diff --git a/block/rbd.c b/block/rbd.c
-index 98f4ba2620..efcbbe5949 100644
---- a/block/rbd.c
-+++ b/block/rbd.c
-@@ -97,12 +97,6 @@ typedef struct RBDTask {
-     int64_t ret;
- } RBDTask;
--typedef struct RBDDiffIterateReq {
--    uint64_t offs;
--    uint64_t bytes;
--    bool exists;
--} RBDDiffIterateReq;
--
- static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
-                             BlockdevOptionsRbd *opts, bool cache,
-                             const char *keypairs, const char *secretid,
-@@ -1293,111 +1287,6 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs,
-     return spec_info;
- }
--/*
-- * rbd_diff_iterate2 allows to interrupt the exection by returning a negative
-- * value in the callback routine. Choose a value that does not conflict with
-- * an existing exitcode and return it if we want to prematurely stop the
-- * execution because we detected a change in the allocation status.
-- */
--#define QEMU_RBD_EXIT_DIFF_ITERATE2 -9000
--
--static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len,
--                                    int exists, void *opaque)
--{
--    RBDDiffIterateReq *req = opaque;
--
--    assert(req->offs + req->bytes <= offs);
--    /*
--     * we do not diff against a snapshot so we should never receive a callback
--     * for a hole.
--     */
--    assert(exists);
--
--    if (!req->exists && offs > req->offs) {
--        /*
--         * we started in an unallocated area and hit the first allocated
--         * block. req->bytes must be set to the length of the unallocated area
--         * before the allocated area. stop further processing.
--         */
--        req->bytes = offs - req->offs;
--        return QEMU_RBD_EXIT_DIFF_ITERATE2;
--    }
--
--    if (req->exists && offs > req->offs + req->bytes) {
--        /*
--         * we started in an allocated area and jumped over an unallocated area,
--         * req->bytes contains the length of the allocated area before the
--         * unallocated area. stop further processing.
--         */
--        return QEMU_RBD_EXIT_DIFF_ITERATE2;
--    }
--
--    req->bytes += len;
--    req->exists = true;
--
--    return 0;
--}
--
--static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
--                                                 bool want_zero, int64_t offset,
--                                                 int64_t bytes, int64_t *pnum,
--                                                 int64_t *map,
--                                                 BlockDriverState **file)
--{
--    BDRVRBDState *s = bs->opaque;
--    int status, r;
--    RBDDiffIterateReq req = { .offs = offset };
--    uint64_t features, flags;
--
--    assert(offset + bytes <= s->image_size);
--
--    /* default to all sectors allocated */
--    status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
--    *map = offset;
--    *file = bs;
--    *pnum = bytes;
--
--    /* check if RBD image supports fast-diff */
--    r = rbd_get_features(s->image, &features);
--    if (r < 0) {
--        return status;
--    }
--    if (!(features & RBD_FEATURE_FAST_DIFF)) {
--        return status;
--    }
--
--    /* check if RBD fast-diff result is valid */
--    r = rbd_get_flags(s->image, &flags);
--    if (r < 0) {
--        return status;
--    }
--    if (flags & RBD_FLAG_FAST_DIFF_INVALID) {
--        return status;
--    }
--
--    r = rbd_diff_iterate2(s->image, NULL, offset, bytes, true, true,
--                          qemu_rbd_diff_iterate_cb, &req);
--    if (r < 0 && r != QEMU_RBD_EXIT_DIFF_ITERATE2) {
--        return status;
--    }
--    assert(req.bytes <= bytes);
--    if (!req.exists) {
--        if (r == 0) {
--            /*
--             * rbd_diff_iterate2 does not invoke callbacks for unallocated
--             * areas. This here catches the case where no callback was
--             * invoked at all (req.bytes == 0).
--             */
--            assert(req.bytes == 0);
--            req.bytes = bytes;
--        }
--        status = BDRV_BLOCK_ZERO | BDRV_BLOCK_OFFSET_VALID;
--    }
--
--    *pnum = req.bytes;
--    return status;
--}
--
- static int64_t qemu_rbd_getlength(BlockDriverState *bs)
- {
-     BDRVRBDState *s = bs->opaque;
-@@ -1633,7 +1522,6 @@ static BlockDriver bdrv_rbd = {
- #ifdef LIBRBD_SUPPORTS_WRITE_ZEROES
-     .bdrv_co_pwrite_zeroes  = qemu_rbd_co_pwrite_zeroes,
- #endif
--    .bdrv_co_block_status   = qemu_rbd_co_block_status,
-     .bdrv_snapshot_create   = qemu_rbd_snap_create,
-     .bdrv_snapshot_delete   = qemu_rbd_snap_remove,
diff --git a/debian/patches/pve/0057-PVE-Backup-create-jobs-correctly-cancel-in-error-sce.patch b/debian/patches/pve/0057-PVE-Backup-create-jobs-correctly-cancel-in-error-sce.patch
deleted file mode 100644 (file)
index 3598205..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Wed, 25 May 2022 13:59:37 +0200
-Subject: [PATCH] PVE-Backup: create jobs: correctly cancel in error scenario
-
-The first call to job_cancel_sync() will cancel and free all jobs in
-the transaction, so ensure that it's called only once and get rid of
-the job_unref() that would operate on freed memory.
-
-It's also necessary to NULL backup_state.pbs in the error scenario,
-because a subsequent backup_cancel QMP call (as happens in PVE when
-the backup QMP command fails) would try to call proxmox_backup_abort()
-and run into a segfault.
-
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-[FE: adapt for new job lock mechanism replacing AioContext locks]
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- pve-backup.c | 10 ++++++++--
- 1 file changed, 8 insertions(+), 2 deletions(-)
-
-diff --git a/pve-backup.c b/pve-backup.c
-index 262e7d3894..fde3554133 100644
---- a/pve-backup.c
-+++ b/pve-backup.c
-@@ -503,6 +503,11 @@ static void create_backup_jobs_bh(void *opaque) {
-     }
-     if (*errp) {
-+        /*
-+         * It's enough to cancel one job in the transaction, the rest will
-+         * follow automatically.
-+         */
-+        bool canceled = false;
-         l = backup_state.di_list;
-         while (l) {
-             PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-@@ -513,11 +518,11 @@ static void create_backup_jobs_bh(void *opaque) {
-                 di->target = NULL;
-             }
--            if (di->job) {
-+            if (!canceled && di->job) {
-                 WITH_JOB_LOCK_GUARD() {
-                     job_cancel_sync_locked(&di->job->job, true);
--                    job_unref_locked(&di->job->job);
-                 }
-+                canceled = true;
-             }
-         }
-     }
-@@ -943,6 +948,7 @@ err:
-     if (pbs) {
-         proxmox_backup_disconnect(pbs);
-+        backup_state.pbs = NULL;
-     }
-     if (backup_dir) {
diff --git a/debian/patches/pve/0057-PVE-Backup-ensure-jobs-in-di_list-are-referenced.patch b/debian/patches/pve/0057-PVE-Backup-ensure-jobs-in-di_list-are-referenced.patch
new file mode 100644 (file)
index 0000000..1446569
--- /dev/null
@@ -0,0 +1,73 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Wed, 25 May 2022 13:59:38 +0200
+Subject: [PATCH] PVE-Backup: ensure jobs in di_list are referenced
+
+Ensures that qmp_backup_cancel doesn't pick a job that's already been
+freed. With unlucky timings it seems possible that:
+1. job_exit -> job_completed -> job_finalize_single starts
+2. pvebackup_co_complete_stream gets spawned in completion callback
+3. job finalize_single finishes -> job's refcount hits zero -> job is
+   freed
+4. qmp_backup_cancel comes in and locks backup_state.backup_mutex
+   before pvebackup_co_complete_stream can remove the job from the
+   di_list
+5. qmp_backup_cancel will pick a job that's already been freed
+
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+[FE: adapt for new job lock mechanism replacing AioContext locks]
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ pve-backup.c | 22 +++++++++++++++++++---
+ 1 file changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/pve-backup.c b/pve-backup.c
+index fde3554133..0cf30e1ced 100644
+--- a/pve-backup.c
++++ b/pve-backup.c
+@@ -316,6 +316,13 @@ static void coroutine_fn pvebackup_co_complete_stream(void *opaque)
+         }
+     }
++    if (di->job) {
++        WITH_JOB_LOCK_GUARD() {
++            job_unref_locked(&di->job->job);
++            di->job = NULL;
++        }
++    }
++
+     // remove self from job list
+     backup_state.di_list = g_list_remove(backup_state.di_list, di);
+@@ -491,6 +498,11 @@ static void create_backup_jobs_bh(void *opaque) {
+         aio_context_release(aio_context);
+         di->job = job;
++        if (job) {
++            WITH_JOB_LOCK_GUARD() {
++                job_ref_locked(&job->job);
++            }
++        }
+         if (!job || local_err) {
+             error_setg(errp, "backup_job_create failed: %s",
+@@ -518,11 +530,15 @@ static void create_backup_jobs_bh(void *opaque) {
+                 di->target = NULL;
+             }
+-            if (!canceled && di->job) {
++            if (di->job) {
+                 WITH_JOB_LOCK_GUARD() {
+-                    job_cancel_sync_locked(&di->job->job, true);
++                    if (!canceled) {
++                        job_cancel_sync_locked(&di->job->job, true);
++                        canceled = true;
++                    }
++                    job_unref_locked(&di->job->job);
++                    di->job = NULL;
+                 }
+-                canceled = true;
+             }
+         }
+     }
diff --git a/debian/patches/pve/0058-PVE-Backup-avoid-segfault-issues-upon-backup-cancel.patch b/debian/patches/pve/0058-PVE-Backup-avoid-segfault-issues-upon-backup-cancel.patch
new file mode 100644 (file)
index 0000000..1fbf04a
--- /dev/null
@@ -0,0 +1,118 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Wed, 25 May 2022 13:59:39 +0200
+Subject: [PATCH] PVE-Backup: avoid segfault issues upon backup-cancel
+
+When canceling a backup in PVE via a signal it's easy to run into a
+situation where the job is already failing when the backup_cancel QMP
+command comes in. With a bit of unlucky timing on top, it can happen
+that job_exit() runs between schedulung of job_cancel_bh() and
+execution of job_cancel_bh(). But job_cancel_sync() does not expect
+that the job is already finalized (in fact, the job might've been
+freed already, but even if it isn't, job_cancel_sync() would try to
+deref job->txn which would be NULL at that point).
+
+It is not possible to simply use the job_cancel() (which is advertised
+as being async but isn't in all cases) in qmp_backup_cancel() for the
+same reason job_cancel_sync() cannot be used. Namely, because it can
+invoke job_finish_sync() (which uses AIO_WAIT_WHILE and thus hangs if
+called from a coroutine). This happens when there's multiple jobs in
+the transaction and job->deferred_to_main_loop is true (is set before
+scheduling job_exit()) or if the job was not started yet.
+
+Fix the issue by selecting the job to cancel in job_cancel_bh() itself
+using the first job that's not completed yet. This is not necessarily
+the first job in the list, because pvebackup_co_complete_stream()
+might not yet have removed a completed job when job_cancel_bh() runs.
+
+An alternative would be to continue using only the first job and
+checking against JOB_STATUS_CONCLUDED or JOB_STATUS_NULL to decide if
+it's still necessary and possible to cancel, but the approach with
+using the first non-completed job seemed more robust.
+
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+[FE: adapt for new job lock mechanism replacing AioContext locks]
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ pve-backup.c | 57 ++++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 38 insertions(+), 19 deletions(-)
+
+diff --git a/pve-backup.c b/pve-backup.c
+index 0cf30e1ced..4067018dbe 100644
+--- a/pve-backup.c
++++ b/pve-backup.c
+@@ -354,12 +354,41 @@ static void pvebackup_complete_cb(void *opaque, int ret)
+ /*
+  * job_cancel(_sync) does not like to be called from coroutines, so defer to
+- * main loop processing via a bottom half.
++ * main loop processing via a bottom half. Assumes that caller holds
++ * backup_mutex.
+  */
+ static void job_cancel_bh(void *opaque) {
+     CoCtxData *data = (CoCtxData*)opaque;
+-    Job *job = (Job*)data->data;
+-    job_cancel_sync(job, true);
++
++    /*
++     * Be careful to pick a valid job to cancel:
++     * 1. job_cancel_sync() does not expect the job to be finalized already.
++     * 2. job_exit() might run between scheduling and running job_cancel_bh()
++     *    and pvebackup_co_complete_stream() might not have removed the job from
++     *    the list yet (in fact, cannot, because it waits for the backup_mutex).
++     * Requiring !job_is_completed() ensures that no finalized job is picked.
++     */
++    GList *bdi = g_list_first(backup_state.di_list);
++    while (bdi) {
++        if (bdi->data) {
++            BlockJob *bj = ((PVEBackupDevInfo *)bdi->data)->job;
++            if (bj) {
++                Job *job = &bj->job;
++                WITH_JOB_LOCK_GUARD() {
++                    if (!job_is_completed_locked(job)) {
++                        job_cancel_sync_locked(job, true);
++                        /*
++                         * It's enough to cancel one job in the transaction, the
++                         * rest will follow automatically.
++                         */
++                        break;
++                    }
++                }
++            }
++        }
++        bdi = g_list_next(bdi);
++    }
++
+     aio_co_enter(data->ctx, data->co);
+ }
+@@ -380,22 +409,12 @@ void coroutine_fn qmp_backup_cancel(Error **errp)
+         proxmox_backup_abort(backup_state.pbs, "backup canceled");
+     }
+-    /* it's enough to cancel one job in the transaction, the rest will follow
+-     * automatically */
+-    GList *bdi = g_list_first(backup_state.di_list);
+-    BlockJob *cancel_job = bdi && bdi->data ?
+-        ((PVEBackupDevInfo *)bdi->data)->job :
+-        NULL;
+-
+-    if (cancel_job) {
+-        CoCtxData data = {
+-            .ctx = qemu_get_current_aio_context(),
+-            .co = qemu_coroutine_self(),
+-            .data = &cancel_job->job,
+-        };
+-        aio_bh_schedule_oneshot(data.ctx, job_cancel_bh, &data);
+-        qemu_coroutine_yield();
+-    }
++    CoCtxData data = {
++        .ctx = qemu_get_current_aio_context(),
++        .co = qemu_coroutine_self(),
++    };
++    aio_bh_schedule_oneshot(data.ctx, job_cancel_bh, &data);
++    qemu_coroutine_yield();
+     qemu_co_mutex_unlock(&backup_state.backup_mutex);
+ }
diff --git a/debian/patches/pve/0058-PVE-Backup-ensure-jobs-in-di_list-are-referenced.patch b/debian/patches/pve/0058-PVE-Backup-ensure-jobs-in-di_list-are-referenced.patch
deleted file mode 100644 (file)
index 1446569..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Wed, 25 May 2022 13:59:38 +0200
-Subject: [PATCH] PVE-Backup: ensure jobs in di_list are referenced
-
-Ensures that qmp_backup_cancel doesn't pick a job that's already been
-freed. With unlucky timings it seems possible that:
-1. job_exit -> job_completed -> job_finalize_single starts
-2. pvebackup_co_complete_stream gets spawned in completion callback
-3. job finalize_single finishes -> job's refcount hits zero -> job is
-   freed
-4. qmp_backup_cancel comes in and locks backup_state.backup_mutex
-   before pvebackup_co_complete_stream can remove the job from the
-   di_list
-5. qmp_backup_cancel will pick a job that's already been freed
-
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-[FE: adapt for new job lock mechanism replacing AioContext locks]
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- pve-backup.c | 22 +++++++++++++++++++---
- 1 file changed, 19 insertions(+), 3 deletions(-)
-
-diff --git a/pve-backup.c b/pve-backup.c
-index fde3554133..0cf30e1ced 100644
---- a/pve-backup.c
-+++ b/pve-backup.c
-@@ -316,6 +316,13 @@ static void coroutine_fn pvebackup_co_complete_stream(void *opaque)
-         }
-     }
-+    if (di->job) {
-+        WITH_JOB_LOCK_GUARD() {
-+            job_unref_locked(&di->job->job);
-+            di->job = NULL;
-+        }
-+    }
-+
-     // remove self from job list
-     backup_state.di_list = g_list_remove(backup_state.di_list, di);
-@@ -491,6 +498,11 @@ static void create_backup_jobs_bh(void *opaque) {
-         aio_context_release(aio_context);
-         di->job = job;
-+        if (job) {
-+            WITH_JOB_LOCK_GUARD() {
-+                job_ref_locked(&job->job);
-+            }
-+        }
-         if (!job || local_err) {
-             error_setg(errp, "backup_job_create failed: %s",
-@@ -518,11 +530,15 @@ static void create_backup_jobs_bh(void *opaque) {
-                 di->target = NULL;
-             }
--            if (!canceled && di->job) {
-+            if (di->job) {
-                 WITH_JOB_LOCK_GUARD() {
--                    job_cancel_sync_locked(&di->job->job, true);
-+                    if (!canceled) {
-+                        job_cancel_sync_locked(&di->job->job, true);
-+                        canceled = true;
-+                    }
-+                    job_unref_locked(&di->job->job);
-+                    di->job = NULL;
-                 }
--                canceled = true;
-             }
-         }
-     }
diff --git a/debian/patches/pve/0059-PVE-Backup-avoid-segfault-issues-upon-backup-cancel.patch b/debian/patches/pve/0059-PVE-Backup-avoid-segfault-issues-upon-backup-cancel.patch
deleted file mode 100644 (file)
index 1fbf04a..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Wed, 25 May 2022 13:59:39 +0200
-Subject: [PATCH] PVE-Backup: avoid segfault issues upon backup-cancel
-
-When canceling a backup in PVE via a signal it's easy to run into a
-situation where the job is already failing when the backup_cancel QMP
-command comes in. With a bit of unlucky timing on top, it can happen
-that job_exit() runs between schedulung of job_cancel_bh() and
-execution of job_cancel_bh(). But job_cancel_sync() does not expect
-that the job is already finalized (in fact, the job might've been
-freed already, but even if it isn't, job_cancel_sync() would try to
-deref job->txn which would be NULL at that point).
-
-It is not possible to simply use the job_cancel() (which is advertised
-as being async but isn't in all cases) in qmp_backup_cancel() for the
-same reason job_cancel_sync() cannot be used. Namely, because it can
-invoke job_finish_sync() (which uses AIO_WAIT_WHILE and thus hangs if
-called from a coroutine). This happens when there's multiple jobs in
-the transaction and job->deferred_to_main_loop is true (is set before
-scheduling job_exit()) or if the job was not started yet.
-
-Fix the issue by selecting the job to cancel in job_cancel_bh() itself
-using the first job that's not completed yet. This is not necessarily
-the first job in the list, because pvebackup_co_complete_stream()
-might not yet have removed a completed job when job_cancel_bh() runs.
-
-An alternative would be to continue using only the first job and
-checking against JOB_STATUS_CONCLUDED or JOB_STATUS_NULL to decide if
-it's still necessary and possible to cancel, but the approach with
-using the first non-completed job seemed more robust.
-
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-[FE: adapt for new job lock mechanism replacing AioContext locks]
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- pve-backup.c | 57 ++++++++++++++++++++++++++++++++++------------------
- 1 file changed, 38 insertions(+), 19 deletions(-)
-
-diff --git a/pve-backup.c b/pve-backup.c
-index 0cf30e1ced..4067018dbe 100644
---- a/pve-backup.c
-+++ b/pve-backup.c
-@@ -354,12 +354,41 @@ static void pvebackup_complete_cb(void *opaque, int ret)
- /*
-  * job_cancel(_sync) does not like to be called from coroutines, so defer to
-- * main loop processing via a bottom half.
-+ * main loop processing via a bottom half. Assumes that caller holds
-+ * backup_mutex.
-  */
- static void job_cancel_bh(void *opaque) {
-     CoCtxData *data = (CoCtxData*)opaque;
--    Job *job = (Job*)data->data;
--    job_cancel_sync(job, true);
-+
-+    /*
-+     * Be careful to pick a valid job to cancel:
-+     * 1. job_cancel_sync() does not expect the job to be finalized already.
-+     * 2. job_exit() might run between scheduling and running job_cancel_bh()
-+     *    and pvebackup_co_complete_stream() might not have removed the job from
-+     *    the list yet (in fact, cannot, because it waits for the backup_mutex).
-+     * Requiring !job_is_completed() ensures that no finalized job is picked.
-+     */
-+    GList *bdi = g_list_first(backup_state.di_list);
-+    while (bdi) {
-+        if (bdi->data) {
-+            BlockJob *bj = ((PVEBackupDevInfo *)bdi->data)->job;
-+            if (bj) {
-+                Job *job = &bj->job;
-+                WITH_JOB_LOCK_GUARD() {
-+                    if (!job_is_completed_locked(job)) {
-+                        job_cancel_sync_locked(job, true);
-+                        /*
-+                         * It's enough to cancel one job in the transaction, the
-+                         * rest will follow automatically.
-+                         */
-+                        break;
-+                    }
-+                }
-+            }
-+        }
-+        bdi = g_list_next(bdi);
-+    }
-+
-     aio_co_enter(data->ctx, data->co);
- }
-@@ -380,22 +409,12 @@ void coroutine_fn qmp_backup_cancel(Error **errp)
-         proxmox_backup_abort(backup_state.pbs, "backup canceled");
-     }
--    /* it's enough to cancel one job in the transaction, the rest will follow
--     * automatically */
--    GList *bdi = g_list_first(backup_state.di_list);
--    BlockJob *cancel_job = bdi && bdi->data ?
--        ((PVEBackupDevInfo *)bdi->data)->job :
--        NULL;
--
--    if (cancel_job) {
--        CoCtxData data = {
--            .ctx = qemu_get_current_aio_context(),
--            .co = qemu_coroutine_self(),
--            .data = &cancel_job->job,
--        };
--        aio_bh_schedule_oneshot(data.ctx, job_cancel_bh, &data);
--        qemu_coroutine_yield();
--    }
-+    CoCtxData data = {
-+        .ctx = qemu_get_current_aio_context(),
-+        .co = qemu_coroutine_self(),
-+    };
-+    aio_bh_schedule_oneshot(data.ctx, job_cancel_bh, &data);
-+    qemu_coroutine_yield();
-     qemu_co_mutex_unlock(&backup_state.backup_mutex);
- }
diff --git a/debian/patches/pve/0059-vma-create-support-64KiB-unaligned-input-images.patch b/debian/patches/pve/0059-vma-create-support-64KiB-unaligned-input-images.patch
new file mode 100644 (file)
index 0000000..ffa8d0e
--- /dev/null
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Wed, 22 Jun 2022 10:45:11 +0200
+Subject: [PATCH] vma: create: support 64KiB-unaligned input images
+
+which fixes backing up templates with such disks in PVE, for example
+efitype=4m EFI disks on a file-based storage (size = 540672).
+
+If there is not enough left to read, blk_co_preadv will return -EIO,
+so limit the size in the last iteration.
+
+For writing, an unaligned end is already handled correctly.
+
+The call to memset is not strictly necessary, because writing also
+checks that it doesn't write data beyond the end of the image. But
+there are two reasons to do it:
+1. It's cleaner that way.
+2. It allows detecting when the final piece is all zeroes, which might
+   not happen if the buffer still contains data from the previous
+   iteration.
+
+Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
+---
+ vma.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/vma.c b/vma.c
+index e6e9ffc7fe..304f02bc84 100644
+--- a/vma.c
++++ b/vma.c
+@@ -548,7 +548,7 @@ static void coroutine_fn backup_run(void *opaque)
+     struct iovec iov;
+     QEMUIOVector qiov;
+-    int64_t start, end;
++    int64_t start, end, readlen;
+     int ret = 0;
+     unsigned char *buf = blk_blockalign(job->target, VMA_CLUSTER_SIZE);
+@@ -562,8 +562,16 @@ static void coroutine_fn backup_run(void *opaque)
+         iov.iov_len = VMA_CLUSTER_SIZE;
+         qemu_iovec_init_external(&qiov, &iov, 1);
++        if (start + 1 == end) {
++            memset(buf, 0, VMA_CLUSTER_SIZE);
++            readlen = job->len - start * VMA_CLUSTER_SIZE;
++            assert(readlen > 0 && readlen <= VMA_CLUSTER_SIZE);
++        } else {
++            readlen = VMA_CLUSTER_SIZE;
++        }
++
+         ret = blk_co_preadv(job->target, start * VMA_CLUSTER_SIZE,
+-                            VMA_CLUSTER_SIZE, &qiov, 0);
++                            readlen, &qiov, 0);
+         if (ret < 0) {
+             vma_writer_set_error(job->vmaw, "read error", -1);
+             goto out;
diff --git a/debian/patches/pve/0060-vma-create-avoid-triggering-assertion-in-error-case.patch b/debian/patches/pve/0060-vma-create-avoid-triggering-assertion-in-error-case.patch
new file mode 100644 (file)
index 0000000..c7961ae
--- /dev/null
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Wed, 22 Jun 2022 10:45:12 +0200
+Subject: [PATCH] vma: create: avoid triggering assertion in error case
+
+error_setg expects its argument to not be initialized yet.
+
+Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
+---
+ vma-writer.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/vma-writer.c b/vma-writer.c
+index df4b20793d..ac7da237d0 100644
+--- a/vma-writer.c
++++ b/vma-writer.c
+@@ -311,6 +311,8 @@ VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp)
+         }
+         if (vmaw->fd < 0) {
++            error_free(*errp);
++            *errp = NULL;
+             error_setg(errp, "can't open file %s - %s\n", filename,
+                        g_strerror(errno));
+             goto err;
diff --git a/debian/patches/pve/0060-vma-create-support-64KiB-unaligned-input-images.patch b/debian/patches/pve/0060-vma-create-support-64KiB-unaligned-input-images.patch
deleted file mode 100644 (file)
index ffa8d0e..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Wed, 22 Jun 2022 10:45:11 +0200
-Subject: [PATCH] vma: create: support 64KiB-unaligned input images
-
-which fixes backing up templates with such disks in PVE, for example
-efitype=4m EFI disks on a file-based storage (size = 540672).
-
-If there is not enough left to read, blk_co_preadv will return -EIO,
-so limit the size in the last iteration.
-
-For writing, an unaligned end is already handled correctly.
-
-The call to memset is not strictly necessary, because writing also
-checks that it doesn't write data beyond the end of the image. But
-there are two reasons to do it:
-1. It's cleaner that way.
-2. It allows detecting when the final piece is all zeroes, which might
-   not happen if the buffer still contains data from the previous
-   iteration.
-
-Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
----
- vma.c | 12 ++++++++++--
- 1 file changed, 10 insertions(+), 2 deletions(-)
-
-diff --git a/vma.c b/vma.c
-index e6e9ffc7fe..304f02bc84 100644
---- a/vma.c
-+++ b/vma.c
-@@ -548,7 +548,7 @@ static void coroutine_fn backup_run(void *opaque)
-     struct iovec iov;
-     QEMUIOVector qiov;
--    int64_t start, end;
-+    int64_t start, end, readlen;
-     int ret = 0;
-     unsigned char *buf = blk_blockalign(job->target, VMA_CLUSTER_SIZE);
-@@ -562,8 +562,16 @@ static void coroutine_fn backup_run(void *opaque)
-         iov.iov_len = VMA_CLUSTER_SIZE;
-         qemu_iovec_init_external(&qiov, &iov, 1);
-+        if (start + 1 == end) {
-+            memset(buf, 0, VMA_CLUSTER_SIZE);
-+            readlen = job->len - start * VMA_CLUSTER_SIZE;
-+            assert(readlen > 0 && readlen <= VMA_CLUSTER_SIZE);
-+        } else {
-+            readlen = VMA_CLUSTER_SIZE;
-+        }
-+
-         ret = blk_co_preadv(job->target, start * VMA_CLUSTER_SIZE,
--                            VMA_CLUSTER_SIZE, &qiov, 0);
-+                            readlen, &qiov, 0);
-         if (ret < 0) {
-             vma_writer_set_error(job->vmaw, "read error", -1);
-             goto out;
diff --git a/debian/patches/pve/0061-block-alloc-track-avoid-premature-break.patch b/debian/patches/pve/0061-block-alloc-track-avoid-premature-break.patch
new file mode 100644 (file)
index 0000000..435326c
--- /dev/null
@@ -0,0 +1,36 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fabian Ebner <f.ebner@proxmox.com>
+Date: Wed, 22 Jun 2022 10:45:13 +0200
+Subject: [PATCH] block: alloc-track: avoid premature break
+
+While the bdrv_co_preadv() calls are expected to return 0 on success,
+qemu_iovec_memset() will return the number of bytes set (will be
+local_bytes, because the slice with that size was just initialized).
+
+Don't break out of the loop after the branch with qemu_iovec_memset(),
+because there might still be work to do. Additionally, ret is an int,
+which on 64-bit platforms is too small to hold the size_t returned by
+qemu_iovec_memset().
+
+The branch seems to be difficult to reach in practice, because the
+whole point of alloc-track is to be used with a backing device.
+
+Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
+---
+ block/alloc-track.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/block/alloc-track.c b/block/alloc-track.c
+index 43d40d11af..95c9c67cd8 100644
+--- a/block/alloc-track.c
++++ b/block/alloc-track.c
+@@ -174,7 +174,8 @@ static int coroutine_fn track_co_preadv(BlockDriverState *bs,
+             ret = bdrv_co_preadv(bs->backing, local_offset, local_bytes,
+                                  &local_qiov, flags);
+         } else {
+-            ret = qemu_iovec_memset(&local_qiov, cur_offset, 0, local_bytes);
++            qemu_iovec_memset(&local_qiov, cur_offset, 0, local_bytes);
++            ret = 0;
+         }
+         if (ret != 0) {
diff --git a/debian/patches/pve/0061-vma-create-avoid-triggering-assertion-in-error-case.patch b/debian/patches/pve/0061-vma-create-avoid-triggering-assertion-in-error-case.patch
deleted file mode 100644 (file)
index c7961ae..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Wed, 22 Jun 2022 10:45:12 +0200
-Subject: [PATCH] vma: create: avoid triggering assertion in error case
-
-error_setg expects its argument to not be initialized yet.
-
-Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
----
- vma-writer.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/vma-writer.c b/vma-writer.c
-index df4b20793d..ac7da237d0 100644
---- a/vma-writer.c
-+++ b/vma-writer.c
-@@ -311,6 +311,8 @@ VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp)
-         }
-         if (vmaw->fd < 0) {
-+            error_free(*errp);
-+            *errp = NULL;
-             error_setg(errp, "can't open file %s - %s\n", filename,
-                        g_strerror(errno));
-             goto err;
diff --git a/debian/patches/pve/0062-PVE-Backup-allow-passing-max-workers-performance-set.patch b/debian/patches/pve/0062-PVE-Backup-allow-passing-max-workers-performance-set.patch
new file mode 100644 (file)
index 0000000..c22b380
--- /dev/null
@@ -0,0 +1,144 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Fiona Ebner <f.ebner@proxmox.com>
+Date: Mon, 3 Oct 2022 15:52:04 +0200
+Subject: [PATCH] PVE Backup: allow passing max-workers performance setting
+
+For query-proxmox-support, add an indication that it's possible to use
+the setting.
+
+For now, the other two BackupPerf settings are not exposed:
+
+* use-copy-range: would need to be implemented by the backup-dump
+block driver first, and in fact, the default for backup was changed,
+because it wasn't as fast for backup in QEMU, see commit
+6a30f663d4c0b3c45a544d541e0c4e214b2473a1.
+
+* max-chunk: enforced to be at least the backup cluster size, which is
+4 MiB for PBS and otherwise maximum of source and target cluster size.
+And block-copy has a maximum buffer size of 1 MiB, so setting a larger
+max-chunk doesn't even have an effect. To make the setting sensibly
+usable the check would need to be removed and optionally the
+block-copy max buffer size would need to be bumped. I tried doing just
+that, and tested different source/target combinations with different
+max-chunk settings, but there were no noticable improvements over the
+default "unlimited" (resulting in 1 MiB for block-copy).
+
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
+---
+ block/monitor/block-hmp-cmds.c |  4 +++-
+ pve-backup.c                   | 18 +++++++++++++-----
+ qapi/block-core.json           |  9 +++++++--
+ 3 files changed, 23 insertions(+), 8 deletions(-)
+
+diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
+index 57b2457f1e..ab0c988ae9 100644
+--- a/block/monitor/block-hmp-cmds.c
++++ b/block/monitor/block-hmp-cmds.c
+@@ -1049,7 +1049,9 @@ void coroutine_fn hmp_backup(Monitor *mon, const QDict *qdict)
+         false, false, // PBS encrypt
+         true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA,
+         false, NULL, false, NULL, !!devlist,
+-        devlist, qdict_haskey(qdict, "speed"), speed, &error);
++        devlist, qdict_haskey(qdict, "speed"), speed,
++        false, 0, // BackupPerf max-workers
++        &error);
+     hmp_handle_error(mon, error);
+ }
+diff --git a/pve-backup.c b/pve-backup.c
+index 4067018dbe..3ca4f74cb8 100644
+--- a/pve-backup.c
++++ b/pve-backup.c
+@@ -55,6 +55,7 @@ static struct PVEBackupState {
+         bool starting;
+     } stat;
+     int64_t speed;
++    BackupPerf perf;
+     VmaWriter *vmaw;
+     ProxmoxBackupHandle *pbs;
+     GList *di_list;
+@@ -490,8 +491,6 @@ static void create_backup_jobs_bh(void *opaque) {
+     }
+     backup_state.txn = job_txn_new_seq();
+-    BackupPerf perf = { .max_workers = 16 };
+-
+     /* create and start all jobs (paused state) */
+     GList *l =  backup_state.di_list;
+     while (l) {
+@@ -511,8 +510,9 @@ static void create_backup_jobs_bh(void *opaque) {
+         BlockJob *job = backup_job_create(
+             NULL, di->bs, di->target, backup_state.speed, sync_mode, di->bitmap,
+-            bitmap_mode, false, NULL, &perf, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
+-            JOB_DEFAULT, pvebackup_complete_cb, di, backup_state.txn, &local_err);
++            bitmap_mode, false, NULL, &backup_state.perf, BLOCKDEV_ON_ERROR_REPORT,
++            BLOCKDEV_ON_ERROR_REPORT, JOB_DEFAULT, pvebackup_complete_cb, di, backup_state.txn,
++            &local_err);
+         aio_context_release(aio_context);
+@@ -583,7 +583,9 @@ UuidInfo coroutine_fn *qmp_backup(
+     bool has_config_file, const char *config_file,
+     bool has_firewall_file, const char *firewall_file,
+     bool has_devlist, const char *devlist,
+-    bool has_speed, int64_t speed, Error **errp)
++    bool has_speed, int64_t speed,
++    bool has_max_workers, int64_t max_workers,
++    Error **errp)
+ {
+     assert(qemu_in_coroutine());
+@@ -913,6 +915,11 @@ UuidInfo coroutine_fn *qmp_backup(
+     backup_state.speed = (has_speed && speed > 0) ? speed : 0;
++    backup_state.perf = (BackupPerf){ .max_workers = 16 };
++    if (has_max_workers) {
++        backup_state.perf.max_workers = max_workers;
++    }
++
+     backup_state.vmaw = vmaw;
+     backup_state.pbs = pbs;
+@@ -1088,5 +1095,6 @@ ProxmoxSupportStatus *qmp_query_proxmox_support(Error **errp)
+     ret->pbs_dirty_bitmap_migration = true;
+     ret->query_bitmap_info = true;
+     ret->pbs_masterkey = true;
++    ret->backup_max_workers = true;
+     return ret;
+ }
+diff --git a/qapi/block-core.json b/qapi/block-core.json
+index 889726fc26..65795b7204 100644
+--- a/qapi/block-core.json
++++ b/qapi/block-core.json
+@@ -829,6 +829,8 @@
+ #
+ # @encrypt: use encryption ((optional for format 'pbs', defaults to true if there is a keyfile)
+ #
++# @max-workers: see @BackupPerf for details. Default 16.
++#
+ # Returns: the uuid of the backup job
+ #
+ ##
+@@ -847,7 +849,9 @@
+                                     '*format': 'BackupFormat',
+                                     '*config-file': 'str',
+                                     '*firewall-file': 'str',
+-                                    '*devlist': 'str', '*speed': 'int' },
++                                    '*devlist': 'str',
++                                    '*speed': 'int',
++                                    '*max-workers': 'int' },
+   'returns': 'UuidInfo', 'coroutine': true }
+ ##
+@@ -902,7 +906,8 @@
+             'pbs-dirty-bitmap-savevm': 'bool',
+             'pbs-dirty-bitmap-migration': 'bool',
+             'pbs-masterkey': 'bool',
+-            'pbs-library-version': 'str' } }
++            'pbs-library-version': 'str',
++            'backup-max-workers': 'bool' } }
+ ##
+ # @query-proxmox-support:
diff --git a/debian/patches/pve/0062-block-alloc-track-avoid-premature-break.patch b/debian/patches/pve/0062-block-alloc-track-avoid-premature-break.patch
deleted file mode 100644 (file)
index 435326c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Wed, 22 Jun 2022 10:45:13 +0200
-Subject: [PATCH] block: alloc-track: avoid premature break
-
-While the bdrv_co_preadv() calls are expected to return 0 on success,
-qemu_iovec_memset() will return the number of bytes set (will be
-local_bytes, because the slice with that size was just initialized).
-
-Don't break out of the loop after the branch with qemu_iovec_memset(),
-because there might still be work to do. Additionally, ret is an int,
-which on 64-bit platforms is too small to hold the size_t returned by
-qemu_iovec_memset().
-
-The branch seems to be difficult to reach in practice, because the
-whole point of alloc-track is to be used with a backing device.
-
-Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
----
- block/alloc-track.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/block/alloc-track.c b/block/alloc-track.c
-index 43d40d11af..95c9c67cd8 100644
---- a/block/alloc-track.c
-+++ b/block/alloc-track.c
-@@ -174,7 +174,8 @@ static int coroutine_fn track_co_preadv(BlockDriverState *bs,
-             ret = bdrv_co_preadv(bs->backing, local_offset, local_bytes,
-                                  &local_qiov, flags);
-         } else {
--            ret = qemu_iovec_memset(&local_qiov, cur_offset, 0, local_bytes);
-+            qemu_iovec_memset(&local_qiov, cur_offset, 0, local_bytes);
-+            ret = 0;
-         }
-         if (ret != 0) {
diff --git a/debian/patches/pve/0063-PVE-Backup-allow-passing-max-workers-performance-set.patch b/debian/patches/pve/0063-PVE-Backup-allow-passing-max-workers-performance-set.patch
deleted file mode 100644 (file)
index c22b380..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fiona Ebner <f.ebner@proxmox.com>
-Date: Mon, 3 Oct 2022 15:52:04 +0200
-Subject: [PATCH] PVE Backup: allow passing max-workers performance setting
-
-For query-proxmox-support, add an indication that it's possible to use
-the setting.
-
-For now, the other two BackupPerf settings are not exposed:
-
-* use-copy-range: would need to be implemented by the backup-dump
-block driver first, and in fact, the default for backup was changed,
-because it wasn't as fast for backup in QEMU, see commit
-6a30f663d4c0b3c45a544d541e0c4e214b2473a1.
-
-* max-chunk: enforced to be at least the backup cluster size, which is
-4 MiB for PBS and otherwise maximum of source and target cluster size.
-And block-copy has a maximum buffer size of 1 MiB, so setting a larger
-max-chunk doesn't even have an effect. To make the setting sensibly
-usable the check would need to be removed and optionally the
-block-copy max buffer size would need to be bumped. I tried doing just
-that, and tested different source/target combinations with different
-max-chunk settings, but there were no noticable improvements over the
-default "unlimited" (resulting in 1 MiB for block-copy).
-
-Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
----
- block/monitor/block-hmp-cmds.c |  4 +++-
- pve-backup.c                   | 18 +++++++++++++-----
- qapi/block-core.json           |  9 +++++++--
- 3 files changed, 23 insertions(+), 8 deletions(-)
-
-diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
-index 57b2457f1e..ab0c988ae9 100644
---- a/block/monitor/block-hmp-cmds.c
-+++ b/block/monitor/block-hmp-cmds.c
-@@ -1049,7 +1049,9 @@ void coroutine_fn hmp_backup(Monitor *mon, const QDict *qdict)
-         false, false, // PBS encrypt
-         true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA,
-         false, NULL, false, NULL, !!devlist,
--        devlist, qdict_haskey(qdict, "speed"), speed, &error);
-+        devlist, qdict_haskey(qdict, "speed"), speed,
-+        false, 0, // BackupPerf max-workers
-+        &error);
-     hmp_handle_error(mon, error);
- }
-diff --git a/pve-backup.c b/pve-backup.c
-index 4067018dbe..3ca4f74cb8 100644
---- a/pve-backup.c
-+++ b/pve-backup.c
-@@ -55,6 +55,7 @@ static struct PVEBackupState {
-         bool starting;
-     } stat;
-     int64_t speed;
-+    BackupPerf perf;
-     VmaWriter *vmaw;
-     ProxmoxBackupHandle *pbs;
-     GList *di_list;
-@@ -490,8 +491,6 @@ static void create_backup_jobs_bh(void *opaque) {
-     }
-     backup_state.txn = job_txn_new_seq();
--    BackupPerf perf = { .max_workers = 16 };
--
-     /* create and start all jobs (paused state) */
-     GList *l =  backup_state.di_list;
-     while (l) {
-@@ -511,8 +510,9 @@ static void create_backup_jobs_bh(void *opaque) {
-         BlockJob *job = backup_job_create(
-             NULL, di->bs, di->target, backup_state.speed, sync_mode, di->bitmap,
--            bitmap_mode, false, NULL, &perf, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
--            JOB_DEFAULT, pvebackup_complete_cb, di, backup_state.txn, &local_err);
-+            bitmap_mode, false, NULL, &backup_state.perf, BLOCKDEV_ON_ERROR_REPORT,
-+            BLOCKDEV_ON_ERROR_REPORT, JOB_DEFAULT, pvebackup_complete_cb, di, backup_state.txn,
-+            &local_err);
-         aio_context_release(aio_context);
-@@ -583,7 +583,9 @@ UuidInfo coroutine_fn *qmp_backup(
-     bool has_config_file, const char *config_file,
-     bool has_firewall_file, const char *firewall_file,
-     bool has_devlist, const char *devlist,
--    bool has_speed, int64_t speed, Error **errp)
-+    bool has_speed, int64_t speed,
-+    bool has_max_workers, int64_t max_workers,
-+    Error **errp)
- {
-     assert(qemu_in_coroutine());
-@@ -913,6 +915,11 @@ UuidInfo coroutine_fn *qmp_backup(
-     backup_state.speed = (has_speed && speed > 0) ? speed : 0;
-+    backup_state.perf = (BackupPerf){ .max_workers = 16 };
-+    if (has_max_workers) {
-+        backup_state.perf.max_workers = max_workers;
-+    }
-+
-     backup_state.vmaw = vmaw;
-     backup_state.pbs = pbs;
-@@ -1088,5 +1095,6 @@ ProxmoxSupportStatus *qmp_query_proxmox_support(Error **errp)
-     ret->pbs_dirty_bitmap_migration = true;
-     ret->query_bitmap_info = true;
-     ret->pbs_masterkey = true;
-+    ret->backup_max_workers = true;
-     return ret;
- }
-diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 889726fc26..65795b7204 100644
---- a/qapi/block-core.json
-+++ b/qapi/block-core.json
-@@ -829,6 +829,8 @@
- #
- # @encrypt: use encryption ((optional for format 'pbs', defaults to true if there is a keyfile)
- #
-+# @max-workers: see @BackupPerf for details. Default 16.
-+#
- # Returns: the uuid of the backup job
- #
- ##
-@@ -847,7 +849,9 @@
-                                     '*format': 'BackupFormat',
-                                     '*config-file': 'str',
-                                     '*firewall-file': 'str',
--                                    '*devlist': 'str', '*speed': 'int' },
-+                                    '*devlist': 'str',
-+                                    '*speed': 'int',
-+                                    '*max-workers': 'int' },
-   'returns': 'UuidInfo', 'coroutine': true }
- ##
-@@ -902,7 +906,8 @@
-             'pbs-dirty-bitmap-savevm': 'bool',
-             'pbs-dirty-bitmap-migration': 'bool',
-             'pbs-masterkey': 'bool',
--            'pbs-library-version': 'str' } }
-+            'pbs-library-version': 'str',
-+            'backup-max-workers': 'bool' } }
- ##
- # @query-proxmox-support:
index 191ba29da574822ed65a5420c18f02bec35c433f..1b7795a9527a180a31ede5e3382f8d9709ab3337 100644 (file)
@@ -54,18 +54,17 @@ pve/0045-PVE-block-pbs-fast-path-reads-without-allocation-if-.patch
 pve/0046-PVE-block-stream-increase-chunk-size.patch
 pve/0047-block-io-accept-NULL-qiov-in-bdrv_pad_request.patch
 pve/0048-block-add-alloc-track-driver.patch
-pve/0049-PVE-whitelist-invalid-QAPI-names-for-backwards-compa.patch
-pve/0050-PVE-savevm-async-register-yank-before-migration_inco.patch
-pve/0051-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch
-pve/0052-vma-allow-partial-restore.patch
-pve/0053-pbs-namespace-support.patch
-pve/0054-Revert-block-rbd-workaround-for-ceph-issue-53784.patch
-pve/0055-Revert-block-rbd-fix-handling-of-holes-in-.bdrv_co_b.patch
-pve/0056-Revert-block-rbd-implement-bdrv_co_block_status.patch
-pve/0057-PVE-Backup-create-jobs-correctly-cancel-in-error-sce.patch
-pve/0058-PVE-Backup-ensure-jobs-in-di_list-are-referenced.patch
-pve/0059-PVE-Backup-avoid-segfault-issues-upon-backup-cancel.patch
-pve/0060-vma-create-support-64KiB-unaligned-input-images.patch
-pve/0061-vma-create-avoid-triggering-assertion-in-error-case.patch
-pve/0062-block-alloc-track-avoid-premature-break.patch
-pve/0063-PVE-Backup-allow-passing-max-workers-performance-set.patch
+pve/0049-PVE-savevm-async-register-yank-before-migration_inco.patch
+pve/0050-qemu-img-dd-add-l-option-for-loading-a-snapshot.patch
+pve/0051-vma-allow-partial-restore.patch
+pve/0052-pbs-namespace-support.patch
+pve/0053-Revert-block-rbd-workaround-for-ceph-issue-53784.patch
+pve/0054-Revert-block-rbd-fix-handling-of-holes-in-.bdrv_co_b.patch
+pve/0055-Revert-block-rbd-implement-bdrv_co_block_status.patch
+pve/0056-PVE-Backup-create-jobs-correctly-cancel-in-error-sce.patch
+pve/0057-PVE-Backup-ensure-jobs-in-di_list-are-referenced.patch
+pve/0058-PVE-Backup-avoid-segfault-issues-upon-backup-cancel.patch
+pve/0059-vma-create-support-64KiB-unaligned-input-images.patch
+pve/0060-vma-create-avoid-triggering-assertion-in-error-case.patch
+pve/0061-block-alloc-track-avoid-premature-break.patch
+pve/0062-PVE-Backup-allow-passing-max-workers-performance-set.patch