Commit d58d8453 authored by John Snow's avatar John Snow Committed by Kevin Wolf
Browse files

qmp: Add support of "dirty-bitmap" sync mode for drive-backup



For "dirty-bitmap" sync mode, the block job will iterate through the
given dirty bitmap to decide if a sector needs backup (backup all the
dirty clusters and skip clean ones), just as allocation conditions of
"top" sync mode.

Signed-off-by: default avatarFam Zheng <famz@redhat.com>
Signed-off-by: default avatarJohn Snow <jsnow@redhat.com>
Reviewed-by: default avatarMax Reitz <mreitz@redhat.com>
Reviewed-by: default avatarEric Blake <eblake@redhat.com>
Reviewed-by: default avatarStefan Hajnoczi <stefanha@redhat.com>
Message-id: 1429314609-29776-11-git-send-email-jsnow@redhat.com
Signed-off-by: default avatarStefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
parent 9bd2b08f
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -5783,6 +5783,15 @@ static void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
    }
}

/**
 * Advance an HBitmapIter to an arbitrary offset.
 */
void bdrv_set_dirty_iter(HBitmapIter *hbi, int64_t offset)
{
    assert(hbi->hb);
    hbitmap_iter_init(hbi, hbi->hb, offset);
}

int64_t bdrv_get_dirty_count(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
{
    return hbitmap_count(bitmap->bitmap);
+133 −22
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ typedef struct CowRequest {
typedef struct BackupBlockJob {
    BlockJob common;
    BlockDriverState *target;
    /* bitmap for sync=dirty-bitmap */
    BdrvDirtyBitmap *sync_bitmap;
    MirrorSyncMode sync_mode;
    RateLimit limit;
    BlockdevOnError on_source_error;
@@ -242,6 +244,91 @@ static void backup_complete(BlockJob *job, void *opaque)
    g_free(data);
}

static bool coroutine_fn yield_and_check(BackupBlockJob *job)
{
    if (block_job_is_cancelled(&job->common)) {
        return true;
    }

    /* we need to yield so that bdrv_drain_all() returns.
     * (without, VM does not reboot)
     */
    if (job->common.speed) {
        uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
                                                      job->sectors_read);
        job->sectors_read = 0;
        block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, delay_ns);
    } else {
        block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0);
    }

    if (block_job_is_cancelled(&job->common)) {
        return true;
    }

    return false;
}

static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
{
    bool error_is_read;
    int ret = 0;
    int clusters_per_iter;
    uint32_t granularity;
    int64_t sector;
    int64_t cluster;
    int64_t end;
    int64_t last_cluster = -1;
    BlockDriverState *bs = job->common.bs;
    HBitmapIter hbi;

    granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
    clusters_per_iter = MAX((granularity / BACKUP_CLUSTER_SIZE), 1);
    bdrv_dirty_iter_init(bs, job->sync_bitmap, &hbi);

    /* Find the next dirty sector(s) */
    while ((sector = hbitmap_iter_next(&hbi)) != -1) {
        cluster = sector / BACKUP_SECTORS_PER_CLUSTER;

        /* Fake progress updates for any clusters we skipped */
        if (cluster != last_cluster + 1) {
            job->common.offset += ((cluster - last_cluster - 1) *
                                   BACKUP_CLUSTER_SIZE);
        }

        for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
            do {
                if (yield_and_check(job)) {
                    return ret;
                }
                ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER,
                                    BACKUP_SECTORS_PER_CLUSTER, &error_is_read);
                if ((ret < 0) &&
                    backup_error_action(job, error_is_read, -ret) ==
                    BLOCK_ERROR_ACTION_REPORT) {
                    return ret;
                }
            } while (ret < 0);
        }

        /* If the bitmap granularity is smaller than the backup granularity,
         * we need to advance the iterator pointer to the next cluster. */
        if (granularity < BACKUP_CLUSTER_SIZE) {
            bdrv_set_dirty_iter(&hbi, cluster * BACKUP_SECTORS_PER_CLUSTER);
        }

        last_cluster = cluster - 1;
    }

    /* Play some final catchup with the progress meter */
    end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
    if (last_cluster + 1 < end) {
        job->common.offset += ((end - last_cluster - 1) * BACKUP_CLUSTER_SIZE);
    }

    return ret;
}

static void coroutine_fn backup_run(void *opaque)
{
    BackupBlockJob *job = opaque;
@@ -259,8 +346,7 @@ static void coroutine_fn backup_run(void *opaque)
    qemu_co_rwlock_init(&job->flush_rwlock);

    start = 0;
    end = DIV_ROUND_UP(job->common.len / BDRV_SECTOR_SIZE,
                       BACKUP_SECTORS_PER_CLUSTER);
    end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);

    job->bitmap = hbitmap_alloc(end, 0);

@@ -278,28 +364,13 @@ static void coroutine_fn backup_run(void *opaque)
            qemu_coroutine_yield();
            job->common.busy = true;
        }
    } else if (job->sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) {
        ret = backup_run_incremental(job);
    } else {
        /* Both FULL and TOP SYNC_MODE's require copying.. */
        for (; start < end; start++) {
            bool error_is_read;

            if (block_job_is_cancelled(&job->common)) {
                break;
            }

            /* we need to yield so that bdrv_drain_all() returns.
             * (without, VM does not reboot)
             */
            if (job->common.speed) {
                uint64_t delay_ns = ratelimit_calculate_delay(
                        &job->limit, job->sectors_read);
                job->sectors_read = 0;
                block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, delay_ns);
            } else {
                block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0);
            }

            if (block_job_is_cancelled(&job->common)) {
            if (yield_and_check(job)) {
                break;
            }

@@ -357,6 +428,18 @@ static void coroutine_fn backup_run(void *opaque)
    qemu_co_rwlock_wrlock(&job->flush_rwlock);
    qemu_co_rwlock_unlock(&job->flush_rwlock);

    if (job->sync_bitmap) {
        BdrvDirtyBitmap *bm;
        if (ret < 0) {
            /* Merge the successor back into the parent, delete nothing. */
            bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
            assert(bm);
        } else {
            /* Everything is fine, delete this bitmap and install the backup. */
            bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL);
            assert(bm);
        }
    }
    hbitmap_free(job->bitmap);

    bdrv_iostatus_disable(target);
@@ -369,6 +452,7 @@ static void coroutine_fn backup_run(void *opaque)

void backup_start(BlockDriverState *bs, BlockDriverState *target,
                  int64_t speed, MirrorSyncMode sync_mode,
                  BdrvDirtyBitmap *sync_bitmap,
                  BlockdevOnError on_source_error,
                  BlockdevOnError on_target_error,
                  BlockCompletionFunc *cb, void *opaque,
@@ -412,17 +496,36 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
        return;
    }

    if (sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) {
        if (!sync_bitmap) {
            error_setg(errp, "must provide a valid bitmap name for "
                             "\"dirty-bitmap\" sync mode");
            return;
        }

        /* Create a new bitmap, and freeze/disable this one. */
        if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, errp) < 0) {
            return;
        }
    } else if (sync_bitmap) {
        error_setg(errp,
                   "a sync_bitmap was provided to backup_run, "
                   "but received an incompatible sync_mode (%s)",
                   MirrorSyncMode_lookup[sync_mode]);
        return;
    }

    len = bdrv_getlength(bs);
    if (len < 0) {
        error_setg_errno(errp, -len, "unable to get length for '%s'",
                         bdrv_get_device_name(bs));
        return;
        goto error;
    }

    BackupBlockJob *job = block_job_create(&backup_job_driver, bs, speed,
                                           cb, opaque, errp);
    if (!job) {
        return;
        goto error;
    }

    bdrv_op_block_all(target, job->common.blocker);
@@ -431,7 +534,15 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
    job->on_target_error = on_target_error;
    job->target = target;
    job->sync_mode = sync_mode;
    job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_DIRTY_BITMAP ?
                       sync_bitmap : NULL;
    job->common.len = len;
    job->common.co = qemu_coroutine_create(backup_run);
    qemu_coroutine_enter(job->common.co, job);
    return;

 error:
    if (sync_bitmap) {
        bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -718,6 +718,10 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
    bool is_none_mode;
    BlockDriverState *base;

    if (mode == MIRROR_SYNC_MODE_DIRTY_BITMAP) {
        error_setg(errp, "Sync mode 'dirty-bitmap' not supported");
        return;
    }
    is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
    base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
    mirror_start_job(bs, target, replaces,
+15 −3
Original line number Diff line number Diff line
@@ -1585,6 +1585,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
                     backup->sync,
                     backup->has_mode, backup->mode,
                     backup->has_speed, backup->speed,
                     backup->has_bitmap, backup->bitmap,
                     backup->has_on_source_error, backup->on_source_error,
                     backup->has_on_target_error, backup->on_target_error,
                     &local_err);
@@ -2395,6 +2396,7 @@ void qmp_drive_backup(const char *device, const char *target,
                      enum MirrorSyncMode sync,
                      bool has_mode, enum NewImageMode mode,
                      bool has_speed, int64_t speed,
                      bool has_bitmap, const char *bitmap,
                      bool has_on_source_error, BlockdevOnError on_source_error,
                      bool has_on_target_error, BlockdevOnError on_target_error,
                      Error **errp)
@@ -2403,6 +2405,7 @@ void qmp_drive_backup(const char *device, const char *target,
    BlockDriverState *bs;
    BlockDriverState *target_bs;
    BlockDriverState *source = NULL;
    BdrvDirtyBitmap *bmap = NULL;
    AioContext *aio_context;
    BlockDriver *drv = NULL;
    Error *local_err = NULL;
@@ -2502,7 +2505,16 @@ void qmp_drive_backup(const char *device, const char *target,

    bdrv_set_aio_context(target_bs, aio_context);

    backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
    if (has_bitmap) {
        bmap = bdrv_find_dirty_bitmap(bs, bitmap);
        if (!bmap) {
            error_setg(errp, "Bitmap '%s' could not be found", bitmap);
            goto out;
        }
    }

    backup_start(bs, target_bs, speed, sync, bmap,
                 on_source_error, on_target_error,
                 block_job_cb, bs, &local_err);
    if (local_err != NULL) {
        bdrv_unref(target_bs);
@@ -2563,8 +2575,8 @@ void qmp_blockdev_backup(const char *device, const char *target,

    bdrv_ref(target_bs);
    bdrv_set_aio_context(target_bs, aio_context);
    backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
                 block_job_cb, bs, &local_err);
    backup_start(bs, target_bs, speed, sync, NULL, on_source_error,
                 on_target_error, block_job_cb, bs, &local_err);
    if (local_err != NULL) {
        bdrv_unref(target_bs);
        error_propagate(errp, local_err);
+2 −1
Original line number Diff line number Diff line
@@ -1061,7 +1061,8 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict)

    qmp_drive_backup(device, filename, !!format, format,
                     full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
                     true, mode, false, 0, false, 0, false, 0, &err);
                     true, mode, false, 0, false, NULL,
                     false, 0, false, 0, &err);
    hmp_handle_error(mon, &err);
}

Loading