#include "qemu/bitmap.h"
#include "qemu/error-report.h"
-#include "block/backup-top.h"
-
-#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
+#include "block/copy-before-write.h"
typedef struct BackupBlockJob {
BlockJob common;
- BlockDriverState *backup_top;
+ BlockDriverState *cbw;
BlockDriverState *source_bs;
BlockDriverState *target_bs;
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
block_job_remove_all_bdrv(&s->common);
- bdrv_backup_top_drop(s->backup_top);
+ bdrv_cbw_drop(s->cbw);
}
void backup_do_checkpoint(BlockJob *job, Error **errp)
static void backup_init_bcs_bitmap(BackupBlockJob *job)
{
- bool ret;
uint64_t estimate;
BdrvDirtyBitmap *bcs_bitmap = block_copy_dirty_bitmap(job->bcs);
if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
- ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap,
- NULL, true);
- assert(ret);
- } else {
- if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
- /*
- * We can't hog the coroutine to initialize this thoroughly.
- * Set a flag and resume work when we are able to yield safely.
- */
- block_copy_set_skip_unallocated(job->bcs, true);
- }
- bdrv_set_dirty_bitmap(bcs_bitmap, 0, job->len);
+ bdrv_clear_dirty_bitmap(bcs_bitmap, NULL);
+ bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap, NULL,
+ true);
+ } else if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
+ /*
+ * We can't hog the coroutine to initialize this thoroughly.
+ * Set a flag and resume work when we are able to yield safely.
+ */
+ block_copy_set_skip_unallocated(job->bcs, true);
}
estimate = bdrv_get_dirty_count(bcs_bitmap);
}
}
-static void backup_cancel(Job *job, bool force)
+static bool backup_cancel(Job *job, bool force)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
bdrv_cancel_in_flight(s->target_bs);
+ return true;
}
static const BlockJobDriver backup_job_driver = {
.set_speed = backup_set_speed,
};
-static int64_t backup_calculate_cluster_size(BlockDriverState *target,
- Error **errp)
-{
- int ret;
- BlockDriverInfo bdi;
- bool target_does_cow = bdrv_backing_chain_next(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_does_cow) {
- /* Cluster size is not defined */
- warn_report("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);
- return BACKUP_CLUSTER_SIZE_DEFAULT;
- } else if (ret < 0 && !target_does_cow) {
- 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");
- return ret;
- } else if (ret < 0 && target_does_cow) {
- /* Not fatal; just trudge on ahead. */
- return BACKUP_CLUSTER_SIZE_DEFAULT;
- }
-
- return MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
-}
-
BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
int64_t len, target_len;
BackupBlockJob *job = NULL;
int64_t cluster_size;
- BdrvRequestFlags write_flags;
- BlockDriverState *backup_top = NULL;
+ BlockDriverState *cbw = NULL;
BlockCopyState *bcs = NULL;
assert(bs);
assert(target);
+ GLOBAL_STATE_CODE();
/* QMP interface protects us from these cases */
assert(sync_mode != MIRROR_SYNC_MODE_INCREMENTAL);
return NULL;
}
- cluster_size = backup_calculate_cluster_size(target, errp);
- if (cluster_size < 0) {
- goto error;
- }
-
- if (perf->max_workers < 1) {
- error_setg(errp, "max-workers must be greater than zero");
+ if (perf->max_workers < 1 || perf->max_workers > INT_MAX) {
+ error_setg(errp, "max-workers must be between 1 and %d", INT_MAX);
return NULL;
}
return NULL;
}
- if (perf->max_chunk && perf->max_chunk < cluster_size) {
- error_setg(errp, "Required max-chunk (%" PRIi64 ") is less than backup "
- "cluster size (%" PRIi64 ")", perf->max_chunk, cluster_size);
- return NULL;
- }
-
-
if (sync_bitmap) {
/* If we need to write to this bitmap, check that we can: */
if (bitmap_mode != BITMAP_SYNC_MODE_NEVER &&
goto error;
}
- /*
- * If source is in backing chain of target assume that target is going to be
- * used for "image fleecing", i.e. it should represent a kind of snapshot of
- * source at backup-start point in time. And target is going to be read by
- * somebody (for example, used as NBD export) during backup job.
- *
- * In this case, we need to add BDRV_REQ_SERIALISING write flag to avoid
- * intersection of backup writes and third party reads from target,
- * otherwise reading from target we may occasionally read already updated by
- * guest data.
- *
- * For more information see commit f8d59dfb40bb and test
- * tests/qemu-iotests/222
- */
- write_flags = (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
- (compress ? BDRV_REQ_WRITE_COMPRESSED : 0),
+ cbw = bdrv_cbw_append(bs, target, filter_node_name, &bcs, errp);
+ if (!cbw) {
+ goto error;
+ }
- backup_top = bdrv_backup_top_append(bs, target, filter_node_name,
- cluster_size, perf,
- write_flags, &bcs, errp);
- if (!backup_top) {
+ cluster_size = block_copy_cluster_size(bcs);
+
+ if (perf->max_chunk && perf->max_chunk < cluster_size) {
+ error_setg(errp, "Required max-chunk (%" PRIi64 ") is less than backup "
+ "cluster size (%" PRIi64 ")", perf->max_chunk, cluster_size);
goto error;
}
/* job->len is fixed, so we can't allow resize */
- job = block_job_create(job_id, &backup_job_driver, txn, backup_top,
+ job = block_job_create(job_id, &backup_job_driver, txn, cbw,
0, BLK_PERM_ALL,
speed, creation_flags, cb, opaque, errp);
if (!job) {
goto error;
}
- job->backup_top = backup_top;
+ job->cbw = cbw;
job->source_bs = bs;
job->target_bs = target;
job->on_source_error = on_source_error;
job->len = len;
job->perf = *perf;
+ block_copy_set_copy_opts(bcs, perf->use_copy_range, compress);
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 */
+ /* Required permissions are taken by copy-before-write filter target */
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
if (sync_bitmap) {
bdrv_reclaim_dirty_bitmap(sync_bitmap, NULL);
}
- if (backup_top) {
- bdrv_backup_top_drop(backup_top);
+ if (cbw) {
+ bdrv_cbw_drop(cbw);
}
return NULL;