--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Dietmar Maurer <dietmar@proxmox.com>
+Date: Mon, 6 Apr 2020 12:16:58 +0200
+Subject: [PATCH] PVE-Backup: add backup-dump block driver
+
+- add backup-dump block driver block/backup-dump.c
+- move BackupBlockJob declaration from block/backup.c to include/block/block_int.h
+- block/backup.c - backup-job-create: also consider source cluster size
+- job.c: make job_should_pause non-static
+
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+---
+ block/backup-dump.c | 167 +++++++++++++++++++++++++++++++
+ block/backup.c | 30 ++----
+ block/meson.build | 1 +
+ include/block/block_int-common.h | 35 +++++++
+ job.c | 3 +-
+ 5 files changed, 213 insertions(+), 23 deletions(-)
+ create mode 100644 block/backup-dump.c
+
+diff --git a/block/backup-dump.c b/block/backup-dump.c
+new file mode 100644
+index 0000000000..04718a94e2
+--- /dev/null
++++ b/block/backup-dump.c
+@@ -0,0 +1,167 @@
++/*
++ * BlockDriver to send backup data stream to a callback function
++ *
++ * Copyright (C) 2020 Proxmox Server Solutions GmbH
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ *
++ */
++
++#include "qemu/osdep.h"
++#include "qom/object_interfaces.h"
++#include "block/block_int.h"
++
++typedef struct {
++ int dump_cb_block_size;
++ uint64_t byte_size;
++ BackupDumpFunc *dump_cb;
++ void *dump_cb_data;
++} BDRVBackupDumpState;
++
++static int qemu_backup_dump_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
++{
++ BDRVBackupDumpState *s = bs->opaque;
++
++ bdi->cluster_size = s->dump_cb_block_size;
++ return 0;
++}
++
++static int qemu_backup_dump_check_perm(
++ BlockDriverState *bs,
++ uint64_t perm,
++ uint64_t shared,
++ Error **errp)
++{
++ /* Nothing to do. */
++ return 0;
++}
++
++static void qemu_backup_dump_set_perm(
++ BlockDriverState *bs,
++ uint64_t perm,
++ uint64_t shared)
++{
++ /* Nothing to do. */
++}
++
++static void qemu_backup_dump_abort_perm_update(BlockDriverState *bs)
++{
++ /* Nothing to do. */
++}
++
++static void qemu_backup_dump_refresh_limits(BlockDriverState *bs, Error **errp)
++{
++ bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */
++}
++
++static void qemu_backup_dump_close(BlockDriverState *bs)
++{
++ /* Nothing to do. */
++}
++
++static int64_t qemu_backup_dump_getlength(BlockDriverState *bs)
++{
++ BDRVBackupDumpState *s = bs->opaque;
++
++ return s->byte_size;
++}
++
++static coroutine_fn int qemu_backup_dump_co_writev(
++ BlockDriverState *bs,
++ int64_t sector_num,
++ int nb_sectors,
++ QEMUIOVector *qiov,
++ int flags)
++{
++ /* flags can be only values we set in supported_write_flags */
++ assert(flags == 0);
++
++ BDRVBackupDumpState *s = bs->opaque;
++ off_t offset = sector_num * BDRV_SECTOR_SIZE;
++
++ uint64_t written = 0;
++
++ for (int i = 0; i < qiov->niov; ++i) {
++ const struct iovec *v = &qiov->iov[i];
++
++ int rc = s->dump_cb(s->dump_cb_data, offset, v->iov_len, v->iov_base);
++ if (rc < 0) {
++ return rc;
++ }
++
++ if (rc != v->iov_len) {
++ return -EIO;
++ }
++
++ written += v->iov_len;
++ offset += v->iov_len;
++ }
++
++ return written;
++}
++
++static void qemu_backup_dump_child_perm(
++ BlockDriverState *bs,
++ BdrvChild *c,
++ BdrvChildRole role,
++ BlockReopenQueue *reopen_queue,
++ uint64_t perm, uint64_t shared,
++ uint64_t *nperm, uint64_t *nshared)
++{
++ *nperm = BLK_PERM_ALL;
++ *nshared = BLK_PERM_ALL;
++}
++
++static BlockDriver bdrv_backup_dump_drive = {
++ .format_name = "backup-dump-drive",
++ .protocol_name = "backup-dump",
++ .instance_size = sizeof(BDRVBackupDumpState),
++
++ .bdrv_close = qemu_backup_dump_close,
++ .bdrv_has_zero_init = bdrv_has_zero_init_1,
++ .bdrv_getlength = qemu_backup_dump_getlength,
++ .bdrv_get_info = qemu_backup_dump_get_info,
++
++ .bdrv_co_writev = qemu_backup_dump_co_writev,
++
++ .bdrv_refresh_limits = qemu_backup_dump_refresh_limits,
++ .bdrv_check_perm = qemu_backup_dump_check_perm,
++ .bdrv_set_perm = qemu_backup_dump_set_perm,
++ .bdrv_abort_perm_update = qemu_backup_dump_abort_perm_update,
++ .bdrv_child_perm = qemu_backup_dump_child_perm,
++};
++
++static void bdrv_backup_dump_init(void)
++{
++ bdrv_register(&bdrv_backup_dump_drive);
++}
++
++block_init(bdrv_backup_dump_init);
++
++
++BlockDriverState *bdrv_backup_dump_create(
++ int dump_cb_block_size,
++ uint64_t byte_size,
++ BackupDumpFunc *dump_cb,
++ void *dump_cb_data,
++ Error **errp)
++{
++ BDRVBackupDumpState *state;
++ BlockDriverState *bs = bdrv_new_open_driver(
++ &bdrv_backup_dump_drive, NULL, BDRV_O_RDWR, errp);
++
++ if (!bs) {
++ return NULL;
++ }
++
++ bs->total_sectors = byte_size / BDRV_SECTOR_SIZE;
++ bs->opaque = state = g_new0(BDRVBackupDumpState, 1);
++
++ state->dump_cb_block_size = dump_cb_block_size;
++ state->byte_size = byte_size;
++ state->dump_cb = dump_cb;
++ state->dump_cb_data = dump_cb_data;
++
++ return bs;
++}
+diff --git a/block/backup.c b/block/backup.c
+index b6fa9e8a69..789f8b7799 100644
+--- a/block/backup.c
++++ b/block/backup.c
+@@ -29,28 +29,6 @@
+
+ #include "block/copy-before-write.h"
+
+-typedef struct BackupBlockJob {
+- BlockJob common;
+- BlockDriverState *cbw;
+- BlockDriverState *source_bs;
+- BlockDriverState *target_bs;
+-
+- BdrvDirtyBitmap *sync_bitmap;
+-
+- MirrorSyncMode sync_mode;
+- BitmapSyncMode bitmap_mode;
+- BlockdevOnError on_source_error;
+- BlockdevOnError on_target_error;
+- uint64_t len;
+- int64_t cluster_size;
+- BackupPerf perf;
+-
+- BlockCopyState *bcs;
+-
+- bool wait;
+- BlockCopyCallState *bg_bcs_call;
+-} BackupBlockJob;
+-
+ static const BlockJobDriver backup_job_driver;
+
+ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
+@@ -454,6 +432,14 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+ }
+
+ cluster_size = block_copy_cluster_size(bcs);
++ if (cluster_size < 0) {
++ goto error;
++ }
++
++ BlockDriverInfo bdi;
++ if (bdrv_get_info(bs, &bdi) == 0) {
++ cluster_size = MAX(cluster_size, bdi.cluster_size);
++ }
+
+ if (perf->max_chunk && perf->max_chunk < cluster_size) {
+ error_setg(errp, "Required max-chunk (%" PRIi64 ") is less than backup "
+diff --git a/block/meson.build b/block/meson.build
+index 3a0b84bc11..7f22e7f177 100644
+--- a/block/meson.build
++++ b/block/meson.build
+@@ -4,6 +4,7 @@ block_ss.add(files(
+ 'aio_task.c',
+ 'amend.c',
+ 'backup.c',
++ 'backup-dump.c',
+ 'copy-before-write.c',
+ 'blkdebug.c',
+ 'blklogwrites.c',
+diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
+index 8947abab76..f272d0d8dc 100644
+--- a/include/block/block_int-common.h
++++ b/include/block/block_int-common.h
+@@ -26,6 +26,7 @@
+
+ #include "block/accounting.h"
+ #include "block/block.h"
++#include "block/block-copy.h"
+ #include "block/aio-wait.h"
+ #include "qemu/queue.h"
+ #include "qemu/coroutine.h"
+@@ -64,6 +65,40 @@
+
+ #define BLOCK_PROBE_BUF_SIZE 512
+
++typedef int BackupDumpFunc(void *opaque, uint64_t offset, uint64_t bytes, const void *buf);
++
++BlockDriverState *bdrv_backup_dump_create(
++ int dump_cb_block_size,
++ uint64_t byte_size,
++ BackupDumpFunc *dump_cb,
++ void *dump_cb_data,
++ Error **errp);
++
++// Needs to be defined here, since it's used in blockdev.c to detect PVE backup
++// jobs with source_bs
++typedef struct BlockCopyState BlockCopyState;
++typedef struct BackupBlockJob {
++ BlockJob common;
++ BlockDriverState *cbw;
++ BlockDriverState *source_bs;
++ BlockDriverState *target_bs;
++
++ BdrvDirtyBitmap *sync_bitmap;
++
++ MirrorSyncMode sync_mode;
++ BitmapSyncMode bitmap_mode;
++ BlockdevOnError on_source_error;
++ BlockdevOnError on_target_error;
++ uint64_t len;
++ int64_t cluster_size;
++ BackupPerf perf;
++
++ BlockCopyState *bcs;
++
++ bool wait;
++ BlockCopyCallState *bg_bcs_call;
++} BackupBlockJob;
++
+ enum BdrvTrackedRequestType {
+ BDRV_TRACKED_READ,
+ BDRV_TRACKED_WRITE,
+diff --git a/job.c b/job.c
+index 075c6f3a20..e5699ad200 100644
+--- a/job.c
++++ b/job.c
+@@ -276,7 +276,8 @@ static bool job_started(Job *job)
+ return job->co;
+ }
+
+-static bool job_should_pause(Job *job)
++bool job_should_pause(Job *job);
++bool job_should_pause(Job *job)
+ {
+ return job->pause_count > 0;
+ }