All of lore.kernel.org
 help / color / mirror / Atom feed
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
To: qemu-block@nongnu.org
Cc: qemu-devel@nongnu.org, vsementsov@virtuozzo.com,
	eblake@redhat.com, armbru@redhat.com, xiechanglong.d@gmail.com,
	wencongyang2@huawei.com, jsnow@redhat.com, mreitz@redhat.com,
	kwolf@redhat.com, den@openvz.org
Subject: [PATCH v3 19/25] backup: move to block-copy
Date: Mon, 26 Oct 2020 20:18:09 +0300	[thread overview]
Message-ID: <20201026171815.13233-20-vsementsov@virtuozzo.com> (raw)
In-Reply-To: <20201026171815.13233-1-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>
---
 block/backup.c | 187 ++++++++++++++++++++++++++++++++-----------------
 1 file changed, 121 insertions(+), 66 deletions(-)

diff --git a/block/backup.c b/block/backup.c
index 449b763ce4..0d1cd64eab 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -44,41 +44,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 +134,97 @@ 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) &&
+            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 +265,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 +304,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 +341,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 +540,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.21.3



  parent reply	other threads:[~2020-10-26 17:36 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-26 17:17 [PATCH v3 00/25] backup performance: block_status + async Vladimir Sementsov-Ogievskiy
2020-10-26 17:17 ` [PATCH v3 01/25] iotests: 129 don't check backup "busy" Vladimir Sementsov-Ogievskiy
2020-10-26 17:17 ` [PATCH v3 02/25] qapi: backup: add perf.use-copy-range parameter Vladimir Sementsov-Ogievskiy
2021-01-11 16:07   ` Max Reitz
2021-01-19 20:21   ` Eric Blake
2021-01-20  6:09     ` Vladimir Sementsov-Ogievskiy
2021-01-20  8:24       ` Max Reitz
2020-10-26 17:17 ` [PATCH v3 03/25] block/block-copy: More explicit call_state Vladimir Sementsov-Ogievskiy
2020-10-26 17:17 ` [PATCH v3 04/25] block/block-copy: implement block_copy_async Vladimir Sementsov-Ogievskiy
2021-01-11 16:27   ` Max Reitz
2020-10-26 17:17 ` [PATCH v3 05/25] block/block-copy: add max_chunk and max_workers parameters Vladimir Sementsov-Ogievskiy
2021-01-11 16:47   ` Max Reitz
2020-10-26 17:17 ` [PATCH v3 06/25] block/block-copy: add list of all call-states Vladimir Sementsov-Ogievskiy
2021-01-11 17:02   ` Max Reitz
2020-10-26 17:17 ` [PATCH v3 07/25] block/block-copy: add ratelimit to block-copy Vladimir Sementsov-Ogievskiy
2021-01-12 10:48   ` Max Reitz
2020-10-26 17:17 ` [PATCH v3 08/25] block/block-copy: add block_copy_cancel Vladimir Sementsov-Ogievskiy
2021-01-12 11:05   ` Max Reitz
2020-10-26 17:17 ` [PATCH v3 09/25] blockjob: add set_speed to BlockJobDriver Vladimir Sementsov-Ogievskiy
2020-10-26 17:18 ` [PATCH v3 10/25] job: call job_enter from job_user_pause Vladimir Sementsov-Ogievskiy
2020-10-26 17:18 ` [PATCH v3 11/25] qapi: backup: add max-chunk and max-workers to x-perf struct Vladimir Sementsov-Ogievskiy
2020-11-09 12:23   ` Vladimir Sementsov-Ogievskiy
2021-01-12 11:59   ` Max Reitz
2020-10-26 17:18 ` [PATCH v3 12/25] iotests: 56: prepare for backup over block-copy Vladimir Sementsov-Ogievskiy
2021-01-12 12:07   ` Max Reitz
2020-10-26 17:18 ` [PATCH v3 13/25] iotests: 129: " Vladimir Sementsov-Ogievskiy
2021-01-12 12:14   ` Max Reitz
2021-01-16 21:03     ` Vladimir Sementsov-Ogievskiy
2020-10-26 17:18 ` [PATCH v3 14/25] iotests: 185: " Vladimir Sementsov-Ogievskiy
2020-10-26 17:18 ` [PATCH v3 15/25] iotests: 219: " Vladimir Sementsov-Ogievskiy
2021-01-12 12:32   ` Max Reitz
2020-10-26 17:18 ` [PATCH v3 16/25] iotests: 257: " Vladimir Sementsov-Ogievskiy
2020-10-26 17:18 ` [PATCH v3 17/25] block/block-copy: make progress_bytes_callback optional Vladimir Sementsov-Ogievskiy
2021-01-12 12:39   ` Max Reitz
2020-10-26 17:18 ` [PATCH v3 18/25] block/backup: drop extra gotos from backup_run() Vladimir Sementsov-Ogievskiy
2021-01-12 12:40   ` Max Reitz
2020-10-26 17:18 ` Vladimir Sementsov-Ogievskiy [this message]
2021-01-12 13:23   ` [PATCH v3 19/25] backup: move to block-copy Max Reitz
2021-01-12 15:27     ` Vladimir Sementsov-Ogievskiy
2020-10-26 17:18 ` [PATCH v3 20/25] qapi: backup: disable copy_range by default Vladimir Sementsov-Ogievskiy
2021-01-12 14:05   ` Max Reitz
2021-01-12 15:44     ` Vladimir Sementsov-Ogievskiy
2021-01-12 15:55       ` Max Reitz
2020-10-26 17:18 ` [PATCH v3 21/25] block/block-copy: drop unused block_copy_set_progress_callback() Vladimir Sementsov-Ogievskiy
2021-01-12 14:07   ` Max Reitz
2020-10-26 17:18 ` [PATCH v3 22/25] block/block-copy: drop unused argument of block_copy() Vladimir Sementsov-Ogievskiy
2020-10-26 17:18 ` [PATCH v3 23/25] simplebench/bench_block_job: use correct shebang line with python3 Vladimir Sementsov-Ogievskiy
2021-01-12 14:10   ` Max Reitz
2020-10-26 17:18 ` [PATCH v3 24/25] simplebench: bench_block_job: add cmd_options argument Vladimir Sementsov-Ogievskiy
2020-10-26 17:18 ` [PATCH v3 25/25] simplebench: add bench-backup.py Vladimir Sementsov-Ogievskiy
2021-01-12 14:50   ` Max Reitz
2021-01-12 17:12     ` Vladimir Sementsov-Ogievskiy
2021-01-09 10:18 ` [PATCH v3 00/25] backup performance: block_status + async Vladimir Sementsov-Ogievskiy

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=20201026171815.13233-20-vsementsov@virtuozzo.com \
    --to=vsementsov@virtuozzo.com \
    --cc=armbru@redhat.com \
    --cc=den@openvz.org \
    --cc=eblake@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=wencongyang2@huawei.com \
    --cc=xiechanglong.d@gmail.com \
    /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.