All of lore.kernel.org
 help / color / mirror / Atom feed
From: Max Reitz <mreitz@redhat.com>
To: qemu-block@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>,
	Peter Maydell <peter.maydell@linaro.org>,
	qemu-devel@nongnu.org, Max Reitz <mreitz@redhat.com>
Subject: [PULL 43/53] backup: move to block-copy
Date: Tue, 26 Jan 2021 15:20:06 +0100	[thread overview]
Message-ID: <20210126142016.806073-44-mreitz@redhat.com> (raw)
In-Reply-To: <20210126142016.806073-1-mreitz@redhat.com>

From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>

This brings async request handling and block-status driven chunk sizes
to backup out of the box, which improves backup performance.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Message-Id: <20210116214705.822267-18-vsementsov@virtuozzo.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/backup.c | 187 +++++++++++++++++++++++++++++++------------------
 1 file changed, 120 insertions(+), 67 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index 466608ee55..cc525d5544 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -22,7 +22,6 @@
 #include "block/block-copy.h"
 #include "qapi/error.h"
 #include "qapi/qmp/qerror.h"
-#include "qemu/ratelimit.h"
 #include "qemu/cutils.h"
 #include "sysemu/block-backend.h"
 #include "qemu/bitmap.h"
@@ -44,41 +43,17 @@ typedef struct BackupBlockJob {
     BlockdevOnError on_source_error;
     BlockdevOnError on_target_error;
     uint64_t len;
-    uint64_t bytes_read;
     int64_t cluster_size;
     BackupPerf perf;
 
     BlockCopyState *bcs;
+
+    bool wait;
+    BlockCopyCallState *bg_bcs_call;
 } BackupBlockJob;
 
 static const BlockJobDriver backup_job_driver;
 
-static void backup_progress_bytes_callback(int64_t bytes, void *opaque)
-{
-    BackupBlockJob *s = opaque;
-
-    s->bytes_read += bytes;
-}
-
-static int coroutine_fn backup_do_cow(BackupBlockJob *job,
-                                      int64_t offset, uint64_t bytes,
-                                      bool *error_is_read)
-{
-    int ret = 0;
-    int64_t start, end; /* bytes */
-
-    start = QEMU_ALIGN_DOWN(offset, job->cluster_size);
-    end = QEMU_ALIGN_UP(bytes + offset, job->cluster_size);
-
-    trace_backup_do_cow_enter(job, start, offset, bytes);
-
-    ret = block_copy(job->bcs, start, end - start, true, error_is_read);
-
-    trace_backup_do_cow_return(job, offset, bytes, ret);
-
-    return ret;
-}
-
 static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
 {
     BdrvDirtyBitmap *bm;
@@ -158,53 +133,96 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
     }
 }
 
-static bool coroutine_fn yield_and_check(BackupBlockJob *job)
+static void coroutine_fn backup_block_copy_callback(void *opaque)
 {
-    uint64_t delay_ns;
-
-    if (job_is_cancelled(&job->common.job)) {
-        return true;
-    }
-
-    /*
-     * We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
-     * return. Without a yield, the VM would not reboot.
-     */
-    delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
-    job->bytes_read = 0;
-    job_sleep_ns(&job->common.job, delay_ns);
+    BackupBlockJob *s = opaque;
 
-    if (job_is_cancelled(&job->common.job)) {
-        return true;
+    if (s->wait) {
+        s->wait = false;
+        aio_co_wake(s->common.job.co);
+    } else {
+        job_enter(&s->common.job);
     }
-
-    return false;
 }
 
 static int coroutine_fn backup_loop(BackupBlockJob *job)
 {
-    bool error_is_read;
-    int64_t offset;
-    BdrvDirtyBitmapIter *bdbi;
+    BlockCopyCallState *s = NULL;
     int ret = 0;
+    bool error_is_read;
+    BlockErrorAction act;
+
+    while (true) { /* retry loop */
+        job->bg_bcs_call = s = block_copy_async(job->bcs, 0,
+                QEMU_ALIGN_UP(job->len, job->cluster_size),
+                job->perf.max_workers, job->perf.max_chunk,
+                backup_block_copy_callback, job);
+
+        while (!block_copy_call_finished(s) &&
+               !job_is_cancelled(&job->common.job))
+        {
+            job_yield(&job->common.job);
+        }
 
-    bdbi = bdrv_dirty_iter_new(block_copy_dirty_bitmap(job->bcs));
-    while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) {
-        do {
-            if (yield_and_check(job)) {
-                goto out;
-            }
-            ret = backup_do_cow(job, offset, job->cluster_size, &error_is_read);
-            if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
-                           BLOCK_ERROR_ACTION_REPORT)
-            {
-                goto out;
-            }
-        } while (ret < 0);
+        if (!block_copy_call_finished(s)) {
+            assert(job_is_cancelled(&job->common.job));
+            /*
+             * Note that we can't use job_yield() here, as it doesn't work for
+             * cancelled job.
+             */
+            block_copy_call_cancel(s);
+            job->wait = true;
+            qemu_coroutine_yield();
+            assert(block_copy_call_finished(s));
+            ret = 0;
+            goto out;
+        }
+
+        if (job_is_cancelled(&job->common.job) ||
+            block_copy_call_succeeded(s))
+        {
+            ret = 0;
+            goto out;
+        }
+
+        if (block_copy_call_cancelled(s)) {
+            /*
+             * Job is not cancelled but only block-copy call. This is possible
+             * after job pause. Now the pause is finished, start new block-copy
+             * iteration.
+             */
+            block_copy_call_free(s);
+            continue;
+        }
+
+        /* The only remaining case is failed block-copy call. */
+        assert(block_copy_call_failed(s));
+
+        ret = block_copy_call_status(s, &error_is_read);
+        act = backup_error_action(job, error_is_read, -ret);
+        switch (act) {
+        case BLOCK_ERROR_ACTION_REPORT:
+            goto out;
+        case BLOCK_ERROR_ACTION_STOP:
+            /*
+             * Go to pause prior to starting new block-copy call on the next
+             * iteration.
+             */
+            job_pause_point(&job->common.job);
+            break;
+        case BLOCK_ERROR_ACTION_IGNORE:
+            /* Proceed to new block-copy call to retry. */
+            break;
+        default:
+            abort();
+        }
+
+        block_copy_call_free(s);
     }
 
- out:
-    bdrv_dirty_iter_free(bdbi);
+out:
+    block_copy_call_free(s);
+    job->bg_bcs_call = NULL;
     return ret;
 }
 
@@ -245,7 +263,13 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
         int64_t count;
 
         for (offset = 0; offset < s->len; ) {
-            if (yield_and_check(s)) {
+            if (job_is_cancelled(job)) {
+                return -ECANCELED;
+            }
+
+            job_pause_point(job);
+
+            if (job_is_cancelled(job)) {
                 return -ECANCELED;
             }
 
@@ -278,6 +302,33 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
     return 0;
 }
 
+static void coroutine_fn backup_pause(Job *job)
+{
+    BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
+
+    if (s->bg_bcs_call && !block_copy_call_finished(s->bg_bcs_call)) {
+        block_copy_call_cancel(s->bg_bcs_call);
+        s->wait = true;
+        qemu_coroutine_yield();
+    }
+}
+
+static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed)
+{
+    BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+
+    /*
+     * block_job_set_speed() is called first from block_job_create(), when we
+     * don't yet have s->bcs.
+     */
+    if (s->bcs) {
+        block_copy_set_speed(s->bcs, speed);
+        if (s->bg_bcs_call) {
+            block_copy_kick(s->bg_bcs_call);
+        }
+    }
+}
+
 static const BlockJobDriver backup_job_driver = {
     .job_driver = {
         .instance_size          = sizeof(BackupBlockJob),
@@ -288,7 +339,9 @@ static const BlockJobDriver backup_job_driver = {
         .commit                 = backup_commit,
         .abort                  = backup_abort,
         .clean                  = backup_clean,
-    }
+        .pause                  = backup_pause,
+    },
+    .set_speed = backup_set_speed,
 };
 
 static int64_t backup_calculate_cluster_size(BlockDriverState *target,
@@ -485,8 +538,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
     job->len = len;
     job->perf = *perf;
 
-    block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job);
     block_copy_set_progress_meter(bcs, &job->common.job.progress);
+    block_copy_set_speed(bcs, speed);
 
     /* Required permissions are already taken by backup-top target */
     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
-- 
2.29.2



  parent reply	other threads:[~2021-01-26 14:55 UTC|newest]

Thread overview: 59+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-26 14:19 [PULL 00/53] Block patches Max Reitz
2021-01-26 14:19 ` [PULL 01/53] iotests: fix _check_o_direct Max Reitz
2021-01-26 14:19 ` [PULL 02/53] copy-on-read: support preadv/pwritev_part functions Max Reitz
2021-01-26 14:19 ` [PULL 03/53] block: add API function to insert a node Max Reitz
2021-01-26 14:19 ` [PULL 04/53] copy-on-read: add filter drop function Max Reitz
2021-01-26 14:19 ` [PULL 05/53] qapi: add filter-node-name to block-stream Max Reitz
2021-01-26 14:19 ` [PULL 06/53] qapi: copy-on-read filter: add 'bottom' option Max Reitz
2021-01-26 14:19 ` [PULL 07/53] iotests: add #310 to test bottom node in COR driver Max Reitz
2021-01-26 14:19 ` [PULL 08/53] block: include supported_read_flags into BDS structure Max Reitz
2021-01-26 14:19 ` [PULL 09/53] copy-on-read: skip non-guest reads if no copy needed Max Reitz
2021-01-26 14:19 ` [PULL 10/53] stream: rework backing-file changing Max Reitz
2021-01-26 14:19 ` [PULL 11/53] qapi: block-stream: add "bottom" argument Max Reitz
2021-01-26 14:19 ` [PULL 12/53] iotests: 30: prepare to COR filter insertion by stream job Max Reitz
2021-01-26 14:19 ` [PULL 13/53] block/stream: add s->target_bs Max Reitz
2021-01-26 14:19 ` [PULL 14/53] block: apply COR-filter to block-stream jobs Max Reitz
2021-01-28 18:38   ` Philippe Mathieu-Daudé
2021-01-29  5:26     ` Vladimir Sementsov-Ogievskiy
2021-01-29  8:23       ` Max Reitz
2021-01-29  9:23         ` Philippe Mathieu-Daudé
2021-01-26 14:19 ` [PULL 15/53] iotests.py: Assume a couple of variables as given Max Reitz
2021-01-26 14:19 ` [PULL 16/53] iotests/297: Rewrite in Python and extend reach Max Reitz
2021-01-26 14:19 ` [PULL 17/53] iotests: Move try_remove to iotests.py Max Reitz
2021-01-26 14:19 ` [PULL 18/53] iotests/129: Remove test images in tearDown() Max Reitz
2021-01-26 14:19 ` [PULL 19/53] iotests/129: Do not check @busy Max Reitz
2021-01-26 14:19 ` [PULL 20/53] iotests/129: Use throttle node Max Reitz
2021-01-26 14:19 ` [PULL 21/53] iotests/129: Actually test a commit job Max Reitz
2021-01-26 14:19 ` [PULL 22/53] iotests/129: Limit mirror job's buffer size Max Reitz
2021-01-26 14:19 ` [PULL 23/53] iotests/129: Clean up pylint and mypy complaints Max Reitz
2021-01-26 14:19 ` [PULL 24/53] iotests/300: " Max Reitz
2021-01-26 14:19 ` [PULL 25/53] coroutine-sigaltstack: Add SIGUSR2 mutex Max Reitz
2021-01-26 14:19 ` [PULL 26/53] qapi: backup: add perf.use-copy-range parameter Max Reitz
2021-01-26 14:19 ` [PULL 27/53] block/block-copy: More explicit call_state Max Reitz
2021-01-26 14:19 ` [PULL 28/53] block/block-copy: implement block_copy_async Max Reitz
2021-01-26 14:19 ` [PULL 29/53] block/block-copy: add max_chunk and max_workers parameters Max Reitz
2021-01-26 14:19 ` [PULL 30/53] block/block-copy: add list of all call-states Max Reitz
2021-01-26 14:19 ` [PULL 31/53] block/block-copy: add ratelimit to block-copy Max Reitz
2021-01-26 14:19 ` [PULL 32/53] block/block-copy: add block_copy_cancel Max Reitz
2021-01-26 14:19 ` [PULL 33/53] blockjob: add set_speed to BlockJobDriver Max Reitz
2021-01-26 14:19 ` [PULL 34/53] job: call job_enter from job_pause Max Reitz
2021-01-26 14:19 ` [PULL 35/53] qapi: backup: add max-chunk and max-workers to x-perf struct Max Reitz
2021-01-26 14:19 ` [PULL 36/53] iotests: 56: prepare for backup over block-copy Max Reitz
2021-01-26 14:20 ` [PULL 37/53] iotests/129: Limit backup's max-chunk/max-workers Max Reitz
2021-01-26 14:20 ` [PULL 38/53] iotests: 185: prepare for backup over block-copy Max Reitz
2021-01-26 14:20 ` [PULL 39/53] iotests: 219: " Max Reitz
2021-01-26 14:20 ` [PULL 40/53] iotests: 257: " Max Reitz
2021-01-26 14:20 ` [PULL 41/53] block/block-copy: make progress_bytes_callback optional Max Reitz
2021-01-26 14:20 ` [PULL 42/53] block/backup: drop extra gotos from backup_run() Max Reitz
2021-01-26 14:20 ` Max Reitz [this message]
2021-01-26 14:20 ` [PULL 44/53] qapi: backup: disable copy_range by default Max Reitz
2021-01-26 14:20 ` [PULL 45/53] block/block-copy: drop unused block_copy_set_progress_callback() Max Reitz
2021-01-26 14:20 ` [PULL 46/53] block/block-copy: drop unused argument of block_copy() Max Reitz
2021-01-26 14:20 ` [PULL 47/53] simplebench/bench_block_job: use correct shebang line with python3 Max Reitz
2021-01-26 14:20 ` [PULL 48/53] simplebench: bench_block_job: add cmd_options argument Max Reitz
2021-01-26 14:20 ` [PULL 49/53] simplebench: add bench-backup.py Max Reitz
2021-01-26 14:20 ` [PULL 50/53] block: report errno when flock fcntl fails Max Reitz
2021-01-26 14:20 ` [PULL 51/53] iotests: Add test for the regression fixed in c8bf9a9169 Max Reitz
2021-01-26 14:20 ` [PULL 52/53] iotests/118: Drop 'change' test Max Reitz
2021-01-26 14:20 ` [PULL 53/53] iotests/178: Pass value to invalid option Max Reitz
2021-01-27 17:40 ` [PULL 00/53] Block patches Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210126142016.806073-44-mreitz@redhat.com \
    --to=mreitz@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.