@@ -2216,6 +2216,54 @@
{ 'command': 'block-dirty-bitmap-merge',
'data': 'BlockDirtyBitmapMerge' }
+##
+# @BitmapPattern:
+#
+# An enumeration of possible patterns that can be written into a bitmap.
+#
+# @allocation-top: The allocation status of the top layer
+# of the attached storage node.
+#
+# Since: 5.2
+##
+{ 'enum': 'BitmapPattern',
+ 'data': ['allocation-top'] }
+
+##
+# @BlockDirtyBitmapPopulate:
+#
+# @job-id: identifier for the newly-created block job.
+#
+# @pattern: What pattern should be written into the bitmap?
+#
+# @on-error: the action to take if an error is encountered on a bitmap's
+# attached node, default 'report'.
+# 'stop' and 'enospc' can only be used if the block device supports
+# io-status (see BlockInfo).
+#
+# @auto-finalize: When false, this job will wait in a PENDING state after it
+# has finished its work, waiting for @block-job-finalize
+# before making any block graph changes.
+# When true, this job will automatically
+# perform its abort or commit actions.
+# Defaults to true.
+#
+# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it
+# has completely ceased all work, and awaits @block-job-dismiss.
+# When true, this job will automatically disappear from the
+# query list without user intervention.
+# Defaults to true.
+#
+# Since: 5.2
+##
+{ 'struct': 'BlockDirtyBitmapPopulate',
+ 'base': 'BlockDirtyBitmap',
+ 'data': { 'job-id': 'str',
+ 'pattern': 'BitmapPattern',
+ '*on-error': 'BlockdevOnError',
+ '*auto-finalize': 'bool',
+ '*auto-dismiss': 'bool' } }
+
##
# @BlockDirtyBitmapSha256:
#
@@ -22,10 +22,14 @@
#
# @amend: image options amend job type, see "x-blockdev-amend" (since 5.1)
#
+# @bitmap-populate: drive bitmap population job type, see
+# "block-dirty-bitmap-populate" (since 5.2)
+#
# Since: 1.7
##
{ 'enum': 'JobType',
- 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend'] }
+ 'data': ['commit', 'stream', 'mirror', 'backup', 'create', 'amend',
+ 'bitmap-populate'] }
##
# @JobStatus:
@@ -200,6 +200,7 @@ typedef struct BDRVReopenState {
typedef enum BlockOpType {
BLOCK_OP_TYPE_BACKUP_SOURCE,
BLOCK_OP_TYPE_BACKUP_TARGET,
+ BLOCK_OP_TYPE_BITMAP_POPULATE,
BLOCK_OP_TYPE_CHANGE,
BLOCK_OP_TYPE_COMMIT_SOURCE,
BLOCK_OP_TYPE_COMMIT_TARGET,
@@ -1257,6 +1257,27 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BlockCompletionFunc *cb, void *opaque,
JobTxn *txn, Error **errp);
+/*
+ * bitpop_job_create: Create a new bitmap population job.
+ *
+ * @job_id: The id of the newly-created job.
+ * @bs: Block device associated with the @target_bitmap.
+ * @target_bitmap: The bitmap to populate.
+ * @on_error: What to do if an error on @bs is encountered.
+ * @creation_flags: Flags that control the behavior of the Job lifetime.
+ * See @BlockJobCreateFlags
+ * @cb: Completion function for the job.
+ * @opaque: Opaque pointer value passed to @cb.
+ * @txn: Transaction that this job is part of (may be NULL).
+ */
+BlockJob *bitpop_job_create(const char *job_id, BlockDriverState *bs,
+ BdrvDirtyBitmap *target_bitmap,
+ BitmapPattern pattern,
+ BlockdevOnError on_error,
+ int creation_flags,
+ BlockCompletionFunc *cb, void *opaque,
+ JobTxn *txn, Error **errp);
+
BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
const char *child_name,
const BdrvChildClass *child_class,
new file mode 100644
@@ -0,0 +1,207 @@
+/*
+ * Async Dirty Bitmap Populator
+ *
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * Authors:
+ * John Snow <jsnow@redhat.com>
+ *
+ * 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 "trace.h"
+#include "block/block.h"
+#include "block/block_int.h"
+#include "block/blockjob_int.h"
+#include "block/block_backup.h"
+#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"
+#include "qemu/error-report.h"
+
+typedef struct BitpopBlockJob {
+ BlockJob common;
+ BlockDriverState *bs;
+ BdrvDirtyBitmap *target_bitmap;
+ BdrvDirtyBitmap *new_bitmap;
+ BlockdevOnError on_error;
+ uint64_t len;
+} BitpopBlockJob;
+
+static const BlockJobDriver bitpop_job_driver;
+
+static void bitpop_commit(Job *job)
+{
+ BitpopBlockJob *s = container_of(job, BitpopBlockJob, common.job);
+
+ bdrv_dirty_bitmap_merge_internal(s->target_bitmap, s->new_bitmap,
+ NULL, true);
+}
+
+/* no abort needed; just clean without committing. */
+
+static void bitpop_clean(Job *job)
+{
+ BitpopBlockJob *s = container_of(job, BitpopBlockJob, common.job);
+
+ bdrv_release_dirty_bitmap(s->new_bitmap);
+ bdrv_dirty_bitmap_set_busy(s->target_bitmap, false);
+}
+
+static BlockErrorAction bitpop_error_action(BitpopBlockJob *job, int error)
+{
+ return block_job_error_action(&job->common, job->on_error, true, error);
+}
+
+static bool coroutine_fn yield_and_check(Job *job)
+{
+ if (job_is_cancelled(job)) {
+ return true;
+ }
+
+ job_sleep_ns(job, 0);
+
+ if (job_is_cancelled(job)) {
+ return true;
+ }
+
+ return false;
+}
+
+static int coroutine_fn bitpop_run(Job *job, Error **errp)
+{
+ BitpopBlockJob *s = container_of(job, BitpopBlockJob, common.job);
+ int ret = 0;
+ int64_t offset;
+ int64_t count;
+ int64_t bytes;
+
+ for (offset = 0; offset < s->len; ) {
+ if (yield_and_check(job)) {
+ ret = -ECANCELED;
+ break;
+ }
+
+ bytes = s->len - offset;
+ ret = bdrv_is_allocated(s->bs, offset, bytes, &count);
+ if (ret < 0) {
+ if (bitpop_error_action(s, -ret) == BLOCK_ERROR_ACTION_REPORT) {
+ break;
+ }
+ continue;
+ }
+
+ if (!count) {
+ ret = 0;
+ break;
+ }
+
+ if (ret) {
+ bdrv_set_dirty_bitmap(s->new_bitmap, offset, count);
+ ret = 0;
+ }
+
+ job_progress_update(job, count);
+ offset += count;
+ }
+
+ return ret;
+}
+
+static const BlockJobDriver bitpop_job_driver = {
+ .job_driver = {
+ .instance_size = sizeof(BitpopBlockJob),
+ .job_type = JOB_TYPE_BITMAP_POPULATE,
+ .free = block_job_free,
+ .user_resume = block_job_user_resume,
+ .run = bitpop_run,
+ .commit = bitpop_commit,
+ .clean = bitpop_clean,
+ }
+};
+
+
+BlockJob *bitpop_job_create(
+ const char *job_id,
+ BlockDriverState *bs,
+ BdrvDirtyBitmap *target_bitmap,
+ BitmapPattern pattern,
+ BlockdevOnError on_error,
+ int creation_flags,
+ BlockCompletionFunc *cb,
+ void *opaque,
+ JobTxn *txn,
+ Error **errp)
+{
+ int64_t len;
+ BitpopBlockJob *job = NULL;
+ int64_t cluster_size;
+ BdrvDirtyBitmap *new_bitmap = NULL;
+
+ assert(bs);
+ assert(target_bitmap);
+
+ if (!bdrv_is_inserted(bs)) {
+ error_setg(errp, "Device is not inserted: %s",
+ bdrv_get_device_name(bs));
+ return NULL;
+ }
+
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BITMAP_POPULATE, errp)) {
+ return NULL;
+ }
+
+ if (bdrv_dirty_bitmap_check(target_bitmap, BDRV_BITMAP_DEFAULT, errp)) {
+ return NULL;
+ }
+
+ if (pattern != BITMAP_PATTERN_ALLOCATION_TOP) {
+ error_setg(errp, "Unrecognized bitmap pattern");
+ return NULL;
+ }
+
+ len = bdrv_getlength(bs);
+ if (len < 0) {
+ error_setg_errno(errp, -len, "unable to get length for '%s'",
+ bdrv_get_device_or_node_name(bs));
+ return NULL;
+ }
+
+ /* NB: new bitmap is anonymous and enabled */
+ cluster_size = bdrv_dirty_bitmap_granularity(target_bitmap);
+ new_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp);
+ if (!new_bitmap) {
+ return NULL;
+ }
+
+ /* Take ownership; we reserve the right to write into this on-commit. */
+ bdrv_dirty_bitmap_set_busy(target_bitmap, true);
+
+ job = block_job_create(job_id, &bitpop_job_driver, txn, bs,
+ BLK_PERM_CONSISTENT_READ,
+ BLK_PERM_ALL & ~BLK_PERM_RESIZE,
+ 0, creation_flags,
+ cb, opaque, errp);
+ if (!job) {
+ bdrv_dirty_bitmap_set_busy(target_bitmap, false);
+ bdrv_release_dirty_bitmap(new_bitmap);
+ return NULL;
+ }
+
+ job->bs = bs;
+ job->on_error = on_error;
+ job->target_bitmap = target_bitmap;
+ job->new_bitmap = new_bitmap;
+ job->len = len;
+ job_progress_set_remaining(&job->common.job, job->len);
+
+ return &job->common;
+}
@@ -56,7 +56,8 @@ static bool is_block_job(Job *job)
return job_type(job) == JOB_TYPE_BACKUP ||
job_type(job) == JOB_TYPE_COMMIT ||
job_type(job) == JOB_TYPE_MIRROR ||
- job_type(job) == JOB_TYPE_STREAM;
+ job_type(job) == JOB_TYPE_STREAM ||
+ job_type(job) == JOB_TYPE_BITMAP_POPULATE;
}
BlockJob *block_job_next(BlockJob *bjob)
@@ -2156,6 +2156,7 @@ S: Supported
F: include/qemu/hbitmap.h
F: include/block/dirty-bitmap.h
F: block/monitor/bitmap-qmp-cmds.c
+F: block/bitmap-populate.c
F: block/dirty-bitmap.c
F: block/qcow2-bitmap.c
F: migration/block-dirty-bitmap.c
@@ -5,6 +5,7 @@ block_ss.add(files(
'amend.c',
'backup.c',
'backup-top.c',
+ 'bitmap-populate.c',
'blkdebug.c',
'blklogwrites.c',
'blkreplay.c',