From c46139b295f9edffd43a12e7f029fce4f9b2ea46 Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 15:04:57 +0100
Subject: [PATCH 14/48] backup: modify job api

Introduces a BackupDump function callback and a pause_count
for backup_start. For a dump-backup the target parameter
can now be NULL so access to target needs to be guarded now.
---
 block/backup.c            | 118 +++++++++++++++++++++++++++++-----------------
 block/replication.c       |   3 +-
 blockdev.c                |   4 +-
 include/block/block_int.h |   5 ++
 4 files changed, 83 insertions(+), 47 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index a4fb288..fe4ce7f 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -36,6 +36,7 @@ typedef struct BackupBlockJob {
     BdrvDirtyBitmap *sync_bitmap;
     MirrorSyncMode sync_mode;
     RateLimit limit;
+    BackupDumpFunc *dump_cb;
     BlockdevOnError on_source_error;
     BlockdevOnError on_target_error;
     CoRwlock flush_rwlock;
@@ -145,13 +146,24 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
             goto out;
         }
 
+        int64_t start_sec = start * sectors_per_cluster;
         if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
-            ret = blk_co_pwrite_zeroes(job->target, start * job->cluster_size,
-                                       bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
+            if (job->dump_cb) {
+                ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, NULL);
+            }
+            if (job->target) {
+                ret = blk_co_pwrite_zeroes(job->target, start * job->cluster_size,
+                                           bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
+            }
         } else {
-            ret = blk_co_pwritev(job->target, start * job->cluster_size,
-                                 bounce_qiov.size, &bounce_qiov,
-                                 job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
+            if (job->dump_cb) {
+                ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, bounce_buffer);
+            }
+            if (job->target) {
+                ret = blk_co_pwritev(job->target, start * job->cluster_size,
+                                     bounce_qiov.size, &bounce_qiov,
+                                     job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
+            }
         }
         if (ret < 0) {
             trace_backup_do_cow_write_fail(job, start, ret);
@@ -246,6 +258,8 @@ static void backup_abort(BlockJob *job)
 static void backup_clean(BlockJob *job)
 {
     BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+    if (!s->target)
+        return;
     assert(s->target);
     blk_unref(s->target);
     s->target = NULL;
@@ -330,9 +344,11 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
     if (read) {
         return block_job_error_action(&job->common, job->on_source_error,
                                       true, error);
-    } else {
+    } else if (job->target) {
         return block_job_error_action(&job->common, job->on_target_error,
                                       false, error);
+    } else {
+        return BLOCK_ERROR_ACTION_REPORT;
     }
 }
 
@@ -453,6 +469,7 @@ static void coroutine_fn backup_run(void *opaque)
 
     job->done_bitmap = bitmap_new(end);
 
+
     job->before_write.notify = backup_before_write_notify;
     bdrv_add_before_write_notifier(bs, &job->before_write);
 
@@ -557,7 +574,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
                   BlockdevOnError on_source_error,
                   BlockdevOnError on_target_error,
                   int creation_flags,
+                  BackupDumpFunc *dump_cb,
                   BlockCompletionFunc *cb, void *opaque,
+                  int pause_count,
                   BlockJobTxn *txn, Error **errp)
 {
     int64_t len;
@@ -566,7 +585,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
     int ret;
 
     assert(bs);
-    assert(target);
+    assert(target || dump_cb);
 
     if (bs == target) {
         error_setg(errp, "Source and target cannot be the same");
@@ -579,13 +598,13 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         return NULL;
     }
 
-    if (!bdrv_is_inserted(target)) {
+    if (target && !bdrv_is_inserted(target)) {
         error_setg(errp, "Device is not inserted: %s",
                    bdrv_get_device_name(target));
         return NULL;
     }
 
-    if (compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
+    if (target && compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
         error_setg(errp, "Compression is not supported for this drive %s",
                    bdrv_get_device_name(target));
         return NULL;
@@ -595,7 +614,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         return NULL;
     }
 
-    if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) {
+    if (target && bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) {
         return NULL;
     }
 
@@ -635,15 +654,18 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
         goto error;
     }
 
-    /* The target must match the source in size, so no resize here either */
-    job->target = blk_new(BLK_PERM_WRITE,
-                          BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
-                          BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
-    ret = blk_insert_bs(job->target, target, errp);
-    if (ret < 0) {
-        goto error;
+    if (target) {
+        /* The target must match the source in size, so no resize here either */
+        job->target = blk_new(BLK_PERM_WRITE,
+                              BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
+                              BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
+        ret = blk_insert_bs(job->target, target, errp);
+        if (ret < 0) {
+            goto error;
+        }
     }
 
+    job->dump_cb = dump_cb;
     job->on_source_error = on_source_error;
     job->on_target_error = on_target_error;
     job->sync_mode = sync_mode;
@@ -651,36 +673,44 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
                        sync_bitmap : NULL;
     job->compress = compress;
 
-    /* If there is no backing file on the target, we cannot rely on COW if our
-     * backup cluster size is smaller than the target cluster size. Even for
-     * targets with a backing file, try to avoid COW if possible. */
-    ret = bdrv_get_info(target, &bdi);
-    if (ret == -ENOTSUP && !target->backing) {
-        /* Cluster size is not defined */
-        error_report("WARNING: The target block device doesn't provide "
-                     "information about the block size and it doesn't have a "
-                     "backing file. The default block size of %u bytes is "
-                     "used. If the actual block size of the target exceeds "
-                     "this default, the backup may be unusable",
-                     BACKUP_CLUSTER_SIZE_DEFAULT);
-        job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
-    } else if (ret < 0 && !target->backing) {
-        error_setg_errno(errp, -ret,
-            "Couldn't determine the cluster size of the target image, "
-            "which has no backing file");
-        error_append_hint(errp,
-            "Aborting, since this may create an unusable destination image\n");
-        goto error;
-    } else if (ret < 0 && target->backing) {
-        /* Not fatal; just trudge on ahead. */
-        job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
+    if (target) {
+        /* If there is no backing file on the target, we cannot rely on COW if our
+         * backup cluster size is smaller than the target cluster size. Even for
+         * targets with a backing file, try to avoid COW if possible. */
+        ret = bdrv_get_info(target, &bdi);
+        if (ret == -ENOTSUP && !target->backing) {
+            /* Cluster size is not defined */
+            error_report("WARNING: The target block device doesn't provide "
+                         "information about the block size and it doesn't have a "
+                         "backing file. The default block size of %u bytes is "
+                         "used. If the actual block size of the target exceeds "
+                         "this default, the backup may be unusable",
+                         BACKUP_CLUSTER_SIZE_DEFAULT);
+            job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
+        } else if (ret < 0 && !target->backing) {
+            error_setg_errno(errp, -ret,
+                "Couldn't determine the cluster size of the target image, "
+                "which has no backing file");
+            error_append_hint(errp,
+                "Aborting, since this may create an unusable destination image\n");
+            goto error;
+        } else if (ret < 0 && target->backing) {
+            /* Not fatal; just trudge on ahead. */
+            job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
+        } else {
+            job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
+        }
     } else {
-        job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
+        job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
     }
 
-    /* Required permissions are already taken with target's blk_new() */
-    block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
-                       &error_abort);
+    if (target) {
+        /* Required permissions are already taken with target's blk_new() */
+        block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
+                           &error_abort);
+    } else {
+        job->common.pause_count = pause_count;
+    }
     job->common.len = len;
     block_job_txn_add_job(txn, &job->common);
 
diff --git a/block/replication.c b/block/replication.c
index bf3c395..60c6524 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -531,7 +531,8 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
                                 0, MIRROR_SYNC_MODE_NONE, NULL, false,
                                 BLOCKDEV_ON_ERROR_REPORT,
                                 BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
-                                backup_job_completed, bs, NULL, &local_err);
+                                NULL,
+                                backup_job_completed, bs, 0, NULL, &local_err);
         if (local_err) {
             error_propagate(errp, local_err);
             backup_job_cleanup(bs);
diff --git a/blockdev.c b/blockdev.c
index 040c152..bb3fc5b 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3273,7 +3273,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
                             backup->sync, bmap, backup->compress,
                             backup->on_source_error, backup->on_target_error,
-                            BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
+                            BLOCK_JOB_DEFAULT, NULL, NULL, NULL, 0, txn, &local_err);
     bdrv_unref(target_bs);
     if (local_err != NULL) {
         error_propagate(errp, local_err);
@@ -3352,7 +3352,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
                             backup->sync, NULL, backup->compress,
                             backup->on_source_error, backup->on_target_error,
-                            BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
+                            BLOCK_JOB_DEFAULT, NULL, NULL, NULL, 0, txn, &local_err);
     if (local_err != NULL) {
         error_propagate(errp, local_err);
     }
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 59400bd..ec65581 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -59,6 +59,9 @@
 
 #define BLOCK_PROBE_BUF_SIZE        512
 
+typedef int BackupDumpFunc(void *opaque, BlockDriverState *bs,
+                           int64_t sector_num, int n_sectors, unsigned char *buf);
+
 enum BdrvTrackedRequestType {
     BDRV_TRACKED_READ,
     BDRV_TRACKED_WRITE,
@@ -877,7 +880,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
                             BlockdevOnError on_source_error,
                             BlockdevOnError on_target_error,
                             int creation_flags,
+                            BackupDumpFunc *dump_cb,
                             BlockCompletionFunc *cb, void *opaque,
+                            int pause_count,
                             BlockJobTxn *txn, Error **errp);
 
 void hmp_drive_add_node(Monitor *mon, const char *optstr);
-- 
2.1.4

