Block jobs patches for 2024-04-29
v2: add "iotests/pylintrc: allow up to 10 similar lines" to fix check-python-minreqs - backup: discard-source parameter - blockcommit: Reopen base image as RO after abort -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEi5wmzbL9FHyIDoahVh8kwfGfefsFAmZV4UwACgkQVh8kwfGf eftBIA/9Em1xR7yEK5gE9kiGc+qSBsRPB8sJZ/JB+GukDPvzQ+/CktIJJgTryI/q QC08KyHnuE6WknUfJPkV5kfINj8vTDtkMjwgccrMu8enc9W5wnRfVBQomS8qWpZY maJhyW+Sva7k82v/U1mpdur5cTF1cu8VmwMSNurBYVd84E33KHkgQikEbXSLzFBu N8dG4WOgtwuLmP5BMgg5ftzwC3W7qv+sq1DhnZwDATUKVbjX1lLtKAYwu66bH8du ekZtWqtJNJqRTcOIiSyl52lPm3xo9+U8khXWQ/lmq1jjvdKcC90y76bT16yIQw98 74aBiKSRu2MO/EraEgPQKU2LpSzbzr4Eu1kRjmDXcVDAB183vaFW3Ogym8BuGJ9n ZiNFYLZqOqUL4RkyaXEwci6THEyjHqQvK2HYGmjoidZPvATf5G52FWrKZT3S9LVT Q4oUhb6dQW4EtU4WoVJpqSg7xozVI/swJ04+gLTjQskitXQm2jX8ifD6MI+85tVp nntS5BtMfTe/z5K4L7bv8KOe7J+gK0NUo3YCdw3zQKa+u7tX/QQKnPmNtUK8ohjO g6wIuwrxn/GsHxvXaeOKftHyXBGDHYUSuIr7ByQ/WxS9nQaWW1UKk9WFC/XtUFND bHMfL+DidkUxMnZBe7Snz6gb16oEr0DsrsSyHe/J2dWrid6QJVA= =aSvT -----END PGP SIGNATURE----- Merge tag 'pull-block-jobs-2024-04-29-v2' of https://gitlab.com/vsementsov/qemu into staging Block jobs patches for 2024-04-29 v2: add "iotests/pylintrc: allow up to 10 similar lines" to fix check-python-minreqs - backup: discard-source parameter - blockcommit: Reopen base image as RO after abort # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCgAdFiEEi5wmzbL9FHyIDoahVh8kwfGfefsFAmZV4UwACgkQVh8kwfGf # eftBIA/9Em1xR7yEK5gE9kiGc+qSBsRPB8sJZ/JB+GukDPvzQ+/CktIJJgTryI/q # QC08KyHnuE6WknUfJPkV5kfINj8vTDtkMjwgccrMu8enc9W5wnRfVBQomS8qWpZY # maJhyW+Sva7k82v/U1mpdur5cTF1cu8VmwMSNurBYVd84E33KHkgQikEbXSLzFBu # N8dG4WOgtwuLmP5BMgg5ftzwC3W7qv+sq1DhnZwDATUKVbjX1lLtKAYwu66bH8du # ekZtWqtJNJqRTcOIiSyl52lPm3xo9+U8khXWQ/lmq1jjvdKcC90y76bT16yIQw98 # 74aBiKSRu2MO/EraEgPQKU2LpSzbzr4Eu1kRjmDXcVDAB183vaFW3Ogym8BuGJ9n # ZiNFYLZqOqUL4RkyaXEwci6THEyjHqQvK2HYGmjoidZPvATf5G52FWrKZT3S9LVT # Q4oUhb6dQW4EtU4WoVJpqSg7xozVI/swJ04+gLTjQskitXQm2jX8ifD6MI+85tVp # nntS5BtMfTe/z5K4L7bv8KOe7J+gK0NUo3YCdw3zQKa+u7tX/QQKnPmNtUK8ohjO # g6wIuwrxn/GsHxvXaeOKftHyXBGDHYUSuIr7ByQ/WxS9nQaWW1UKk9WFC/XtUFND # bHMfL+DidkUxMnZBe7Snz6gb16oEr0DsrsSyHe/J2dWrid6QJVA= # =aSvT # -----END PGP SIGNATURE----- # gpg: Signature made Tue 28 May 2024 06:51:08 AM PDT # gpg: using RSA key 8B9C26CDB2FD147C880E86A1561F24C1F19F79FB # gpg: Good signature from "Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>" [unknown] # gpg: aka "Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 8B9C 26CD B2FD 147C 880E 86A1 561F 24C1 F19F 79FB * tag 'pull-block-jobs-2024-04-29-v2' of https://gitlab.com/vsementsov/qemu: iotests/pylintrc: allow up to 10 similar lines iotests: add backup-discard-source qapi: blockdev-backup: add discard-source parameter block/copy-before-write: create block_copy bitmap in filter node block/copy-before-write: support unligned snapshot-discard block/copy-before-write: fix permission blockcommit: Reopen base image as RO after abort Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
79d7475f39
@ -356,7 +356,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||||||
BlockDriverState *target, int64_t speed,
|
BlockDriverState *target, int64_t speed,
|
||||||
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
|
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
|
||||||
BitmapSyncMode bitmap_mode,
|
BitmapSyncMode bitmap_mode,
|
||||||
bool compress,
|
bool compress, bool discard_source,
|
||||||
const char *filter_node_name,
|
const char *filter_node_name,
|
||||||
BackupPerf *perf,
|
BackupPerf *perf,
|
||||||
BlockdevOnError on_source_error,
|
BlockdevOnError on_source_error,
|
||||||
@ -457,7 +457,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
cbw = bdrv_cbw_append(bs, target, filter_node_name, &bcs, errp);
|
cbw = bdrv_cbw_append(bs, target, filter_node_name, discard_source,
|
||||||
|
&bcs, errp);
|
||||||
if (!cbw) {
|
if (!cbw) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@ typedef struct BlockCopyState {
|
|||||||
CoMutex lock;
|
CoMutex lock;
|
||||||
int64_t in_flight_bytes;
|
int64_t in_flight_bytes;
|
||||||
BlockCopyMethod method;
|
BlockCopyMethod method;
|
||||||
|
bool discard_source;
|
||||||
BlockReqList reqs;
|
BlockReqList reqs;
|
||||||
QLIST_HEAD(, BlockCopyCallState) calls;
|
QLIST_HEAD(, BlockCopyCallState) calls;
|
||||||
/*
|
/*
|
||||||
@ -351,7 +352,9 @@ static int64_t block_copy_calculate_cluster_size(BlockDriverState *target,
|
|||||||
}
|
}
|
||||||
|
|
||||||
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||||
|
BlockDriverState *copy_bitmap_bs,
|
||||||
const BdrvDirtyBitmap *bitmap,
|
const BdrvDirtyBitmap *bitmap,
|
||||||
|
bool discard_source,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
ERRP_GUARD();
|
ERRP_GUARD();
|
||||||
@ -367,7 +370,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL,
|
copy_bitmap = bdrv_create_dirty_bitmap(copy_bitmap_bs, cluster_size, NULL,
|
||||||
errp);
|
errp);
|
||||||
if (!copy_bitmap) {
|
if (!copy_bitmap) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -417,6 +420,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
|||||||
cluster_size),
|
cluster_size),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
s->discard_source = discard_source;
|
||||||
block_copy_set_copy_opts(s, false, false);
|
block_copy_set_copy_opts(s, false, false);
|
||||||
|
|
||||||
ratelimit_init(&s->rate_limit);
|
ratelimit_init(&s->rate_limit);
|
||||||
@ -588,6 +592,12 @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
|
|||||||
co_put_to_shres(s->mem, t->req.bytes);
|
co_put_to_shres(s->mem, t->req.bytes);
|
||||||
block_copy_task_end(t, ret);
|
block_copy_task_end(t, ret);
|
||||||
|
|
||||||
|
if (s->discard_source && ret == 0) {
|
||||||
|
int64_t nbytes =
|
||||||
|
MIN(t->req.offset + t->req.bytes, s->len) - t->req.offset;
|
||||||
|
bdrv_co_pdiscard(s->source, t->req.offset, nbytes);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ typedef struct BDRVCopyBeforeWriteState {
|
|||||||
BdrvChild *target;
|
BdrvChild *target;
|
||||||
OnCbwError on_cbw_error;
|
OnCbwError on_cbw_error;
|
||||||
uint32_t cbw_timeout_ns;
|
uint32_t cbw_timeout_ns;
|
||||||
|
bool discard_source;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @lock: protects access to @access_bitmap, @done_bitmap and
|
* @lock: protects access to @access_bitmap, @done_bitmap and
|
||||||
@ -325,14 +326,24 @@ static int coroutine_fn GRAPH_RDLOCK
|
|||||||
cbw_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes)
|
cbw_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes)
|
||||||
{
|
{
|
||||||
BDRVCopyBeforeWriteState *s = bs->opaque;
|
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||||
|
uint32_t cluster_size = block_copy_cluster_size(s->bcs);
|
||||||
|
int64_t aligned_offset = QEMU_ALIGN_UP(offset, cluster_size);
|
||||||
|
int64_t aligned_end = QEMU_ALIGN_DOWN(offset + bytes, cluster_size);
|
||||||
|
int64_t aligned_bytes;
|
||||||
|
|
||||||
|
if (aligned_end <= aligned_offset) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
aligned_bytes = aligned_end - aligned_offset;
|
||||||
|
|
||||||
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
||||||
bdrv_reset_dirty_bitmap(s->access_bitmap, offset, bytes);
|
bdrv_reset_dirty_bitmap(s->access_bitmap, aligned_offset,
|
||||||
|
aligned_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
block_copy_reset(s->bcs, offset, bytes);
|
block_copy_reset(s->bcs, aligned_offset, aligned_bytes);
|
||||||
|
|
||||||
return bdrv_co_pdiscard(s->target, offset, bytes);
|
return bdrv_co_pdiscard(s->target, aligned_offset, aligned_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GRAPH_RDLOCK cbw_refresh_filename(BlockDriverState *bs)
|
static void GRAPH_RDLOCK cbw_refresh_filename(BlockDriverState *bs)
|
||||||
@ -347,6 +358,8 @@ cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role,
|
|||||||
uint64_t perm, uint64_t shared,
|
uint64_t perm, uint64_t shared,
|
||||||
uint64_t *nperm, uint64_t *nshared)
|
uint64_t *nperm, uint64_t *nshared)
|
||||||
{
|
{
|
||||||
|
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||||
|
|
||||||
if (!(role & BDRV_CHILD_FILTERED)) {
|
if (!(role & BDRV_CHILD_FILTERED)) {
|
||||||
/*
|
/*
|
||||||
* Target child
|
* Target child
|
||||||
@ -364,9 +377,17 @@ cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role,
|
|||||||
perm, shared, nperm, nshared);
|
perm, shared, nperm, nshared);
|
||||||
|
|
||||||
if (!QLIST_EMPTY(&bs->parents)) {
|
if (!QLIST_EMPTY(&bs->parents)) {
|
||||||
if (perm & BLK_PERM_WRITE) {
|
/*
|
||||||
|
* Note, that source child may be shared with backup job. Backup job
|
||||||
|
* does create own blk parent on copy-before-write node, so this
|
||||||
|
* works even if source node does not have any parents before backup
|
||||||
|
* start
|
||||||
|
*/
|
||||||
*nperm = *nperm | BLK_PERM_CONSISTENT_READ;
|
*nperm = *nperm | BLK_PERM_CONSISTENT_READ;
|
||||||
|
if (s->discard_source) {
|
||||||
|
*nperm = *nperm | BLK_PERM_WRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
*nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
*nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,7 +475,9 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||||
bs->file->bs->supported_zero_flags);
|
bs->file->bs->supported_zero_flags);
|
||||||
|
|
||||||
s->bcs = block_copy_state_new(bs->file, s->target, bitmap, errp);
|
s->discard_source = flags & BDRV_O_CBW_DISCARD_SOURCE;
|
||||||
|
s->bcs = block_copy_state_new(bs->file, s->target, bs, bitmap,
|
||||||
|
flags & BDRV_O_CBW_DISCARD_SOURCE, errp);
|
||||||
if (!s->bcs) {
|
if (!s->bcs) {
|
||||||
error_prepend(errp, "Cannot create block-copy-state: ");
|
error_prepend(errp, "Cannot create block-copy-state: ");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -521,12 +544,14 @@ static BlockDriver bdrv_cbw_filter = {
|
|||||||
BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
|
BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
|
||||||
BlockDriverState *target,
|
BlockDriverState *target,
|
||||||
const char *filter_node_name,
|
const char *filter_node_name,
|
||||||
|
bool discard_source,
|
||||||
BlockCopyState **bcs,
|
BlockCopyState **bcs,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVCopyBeforeWriteState *state;
|
BDRVCopyBeforeWriteState *state;
|
||||||
BlockDriverState *top;
|
BlockDriverState *top;
|
||||||
QDict *opts;
|
QDict *opts;
|
||||||
|
int flags = BDRV_O_RDWR | (discard_source ? BDRV_O_CBW_DISCARD_SOURCE : 0);
|
||||||
|
|
||||||
assert(source->total_sectors == target->total_sectors);
|
assert(source->total_sectors == target->total_sectors);
|
||||||
GLOBAL_STATE_CODE();
|
GLOBAL_STATE_CODE();
|
||||||
@ -539,7 +564,7 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
|
|||||||
qdict_put_str(opts, "file", bdrv_get_node_name(source));
|
qdict_put_str(opts, "file", bdrv_get_node_name(source));
|
||||||
qdict_put_str(opts, "target", bdrv_get_node_name(target));
|
qdict_put_str(opts, "target", bdrv_get_node_name(target));
|
||||||
|
|
||||||
top = bdrv_insert_node(source, opts, BDRV_O_RDWR, errp);
|
top = bdrv_insert_node(source, opts, flags, errp);
|
||||||
if (!top) {
|
if (!top) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
|
BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
|
||||||
BlockDriverState *target,
|
BlockDriverState *target,
|
||||||
const char *filter_node_name,
|
const char *filter_node_name,
|
||||||
|
bool discard_source,
|
||||||
BlockCopyState **bcs,
|
BlockCopyState **bcs,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
void bdrv_cbw_drop(BlockDriverState *bs);
|
void bdrv_cbw_drop(BlockDriverState *bs);
|
||||||
|
@ -93,6 +93,7 @@ typedef struct MirrorBlockJob {
|
|||||||
int64_t active_write_bytes_in_flight;
|
int64_t active_write_bytes_in_flight;
|
||||||
bool prepared;
|
bool prepared;
|
||||||
bool in_drain;
|
bool in_drain;
|
||||||
|
bool base_ro;
|
||||||
} MirrorBlockJob;
|
} MirrorBlockJob;
|
||||||
|
|
||||||
typedef struct MirrorBDSOpaque {
|
typedef struct MirrorBDSOpaque {
|
||||||
@ -794,6 +795,10 @@ static int mirror_exit_common(Job *job)
|
|||||||
bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort);
|
bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort);
|
||||||
bdrv_graph_wrunlock();
|
bdrv_graph_wrunlock();
|
||||||
|
|
||||||
|
if (abort && s->base_ro && !bdrv_is_read_only(target_bs)) {
|
||||||
|
bdrv_reopen_set_read_only(target_bs, true, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
bdrv_drained_end(target_bs);
|
bdrv_drained_end(target_bs);
|
||||||
bdrv_unref(target_bs);
|
bdrv_unref(target_bs);
|
||||||
|
|
||||||
@ -1717,6 +1722,7 @@ static BlockJob *mirror_start_job(
|
|||||||
bool is_none_mode, BlockDriverState *base,
|
bool is_none_mode, BlockDriverState *base,
|
||||||
bool auto_complete, const char *filter_node_name,
|
bool auto_complete, const char *filter_node_name,
|
||||||
bool is_mirror, MirrorCopyMode copy_mode,
|
bool is_mirror, MirrorCopyMode copy_mode,
|
||||||
|
bool base_ro,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s;
|
MirrorBlockJob *s;
|
||||||
@ -1800,6 +1806,7 @@ static BlockJob *mirror_start_job(
|
|||||||
bdrv_unref(mirror_top_bs);
|
bdrv_unref(mirror_top_bs);
|
||||||
|
|
||||||
s->mirror_top_bs = mirror_top_bs;
|
s->mirror_top_bs = mirror_top_bs;
|
||||||
|
s->base_ro = base_ro;
|
||||||
|
|
||||||
/* No resize for the target either; while the mirror is still running, a
|
/* No resize for the target either; while the mirror is still running, a
|
||||||
* consistent read isn't necessarily possible. We could possibly allow
|
* consistent read isn't necessarily possible. We could possibly allow
|
||||||
@ -2029,7 +2036,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
|||||||
speed, granularity, buf_size, backing_mode, zero_target,
|
speed, granularity, buf_size, backing_mode, zero_target,
|
||||||
on_source_error, on_target_error, unmap, NULL, NULL,
|
on_source_error, on_target_error, unmap, NULL, NULL,
|
||||||
&mirror_job_driver, is_none_mode, base, false,
|
&mirror_job_driver, is_none_mode, base, false,
|
||||||
filter_node_name, true, copy_mode, errp);
|
filter_node_name, true, copy_mode, false, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||||
@ -2058,7 +2065,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
|||||||
on_error, on_error, true, cb, opaque,
|
on_error, on_error, true, cb, opaque,
|
||||||
&commit_active_job_driver, false, base, auto_complete,
|
&commit_active_job_driver, false, base, auto_complete,
|
||||||
filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND,
|
filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND,
|
||||||
errp);
|
base_read_only, errp);
|
||||||
if (!job) {
|
if (!job) {
|
||||||
goto error_restore_flags;
|
goto error_restore_flags;
|
||||||
}
|
}
|
||||||
|
@ -582,8 +582,8 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||||||
|
|
||||||
s->backup_job = backup_job_create(
|
s->backup_job = backup_job_create(
|
||||||
NULL, s->secondary_disk->bs, s->hidden_disk->bs,
|
NULL, s->secondary_disk->bs, s->hidden_disk->bs,
|
||||||
0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL,
|
0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, false,
|
||||||
&perf,
|
NULL, &perf,
|
||||||
BLOCKDEV_ON_ERROR_REPORT,
|
BLOCKDEV_ON_ERROR_REPORT,
|
||||||
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
|
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
|
||||||
backup_job_completed, bs, NULL, &local_err);
|
backup_job_completed, bs, NULL, &local_err);
|
||||||
|
@ -2728,7 +2728,7 @@ static BlockJob *do_backup_common(BackupCommon *backup,
|
|||||||
|
|
||||||
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
||||||
backup->sync, bmap, backup->bitmap_mode,
|
backup->sync, bmap, backup->bitmap_mode,
|
||||||
backup->compress,
|
backup->compress, backup->discard_source,
|
||||||
backup->filter_node_name,
|
backup->filter_node_name,
|
||||||
&perf,
|
&perf,
|
||||||
backup->on_source_error,
|
backup->on_source_error,
|
||||||
|
@ -243,6 +243,8 @@ typedef enum {
|
|||||||
read-write fails */
|
read-write fails */
|
||||||
#define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */
|
#define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */
|
||||||
|
|
||||||
|
#define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */
|
||||||
|
|
||||||
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH)
|
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH)
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +25,9 @@ typedef struct BlockCopyState BlockCopyState;
|
|||||||
typedef struct BlockCopyCallState BlockCopyCallState;
|
typedef struct BlockCopyCallState BlockCopyCallState;
|
||||||
|
|
||||||
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||||
|
BlockDriverState *copy_bitmap_bs,
|
||||||
const BdrvDirtyBitmap *bitmap,
|
const BdrvDirtyBitmap *bitmap,
|
||||||
|
bool discard_source,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
/* Function should be called prior any actual copy request */
|
/* Function should be called prior any actual copy request */
|
||||||
|
@ -193,7 +193,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||||||
MirrorSyncMode sync_mode,
|
MirrorSyncMode sync_mode,
|
||||||
BdrvDirtyBitmap *sync_bitmap,
|
BdrvDirtyBitmap *sync_bitmap,
|
||||||
BitmapSyncMode bitmap_mode,
|
BitmapSyncMode bitmap_mode,
|
||||||
bool compress,
|
bool compress, bool discard_source,
|
||||||
const char *filter_node_name,
|
const char *filter_node_name,
|
||||||
BackupPerf *perf,
|
BackupPerf *perf,
|
||||||
BlockdevOnError on_source_error,
|
BlockdevOnError on_source_error,
|
||||||
|
@ -1610,6 +1610,9 @@
|
|||||||
# node specified by @drive. If this option is not given, a node
|
# node specified by @drive. If this option is not given, a node
|
||||||
# name is autogenerated. (Since: 4.2)
|
# name is autogenerated. (Since: 4.2)
|
||||||
#
|
#
|
||||||
|
# @discard-source: Discard blocks on source which have already been
|
||||||
|
# copied to the target. (Since 9.1)
|
||||||
|
#
|
||||||
# @x-perf: Performance options. (Since 6.0)
|
# @x-perf: Performance options. (Since 6.0)
|
||||||
#
|
#
|
||||||
# Features:
|
# Features:
|
||||||
@ -1631,6 +1634,7 @@
|
|||||||
'*on-target-error': 'BlockdevOnError',
|
'*on-target-error': 'BlockdevOnError',
|
||||||
'*auto-finalize': 'bool', '*auto-dismiss': 'bool',
|
'*auto-finalize': 'bool', '*auto-dismiss': 'bool',
|
||||||
'*filter-node-name': 'str',
|
'*filter-node-name': 'str',
|
||||||
|
'*discard-source': 'bool',
|
||||||
'*x-perf': { 'type': 'BackupPerf',
|
'*x-perf': { 'type': 'BackupPerf',
|
||||||
'features': [ 'unstable' ] } } }
|
'features': [ 'unstable' ] } } }
|
||||||
|
|
||||||
|
@ -120,16 +120,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -596,16 +596,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -865,16 +865,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -1341,16 +1341,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -1610,16 +1610,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -2086,16 +2086,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -2355,16 +2355,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -2831,16 +2831,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -3100,16 +3100,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -3576,16 +3576,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -3845,16 +3845,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -4321,16 +4321,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -4590,16 +4590,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
@ -5066,16 +5066,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
}
|
},
|
||||||
],
|
|
||||||
"drive0": [
|
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"granularity": 65536,
|
"granularity": 65536,
|
||||||
"persistent": false,
|
"persistent": false,
|
||||||
"recording": false
|
"recording": false
|
||||||
},
|
}
|
||||||
|
],
|
||||||
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
"count": 458752,
|
"count": 458752,
|
||||||
|
@ -55,4 +55,4 @@ max-line-length=79
|
|||||||
|
|
||||||
[SIMILARITIES]
|
[SIMILARITIES]
|
||||||
|
|
||||||
min-similarity-lines=6
|
min-similarity-lines=10
|
||||||
|
152
tests/qemu-iotests/tests/backup-discard-source
Executable file
152
tests/qemu-iotests/tests/backup-discard-source
Executable file
@ -0,0 +1,152 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Test backup discard-source parameter
|
||||||
|
#
|
||||||
|
# Copyright (c) Virtuozzo International GmbH.
|
||||||
|
# Copyright (c) Yandex
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import iotests
|
||||||
|
from iotests import qemu_img_create, qemu_img_map, qemu_io
|
||||||
|
|
||||||
|
|
||||||
|
temp_img = os.path.join(iotests.test_dir, 'temp')
|
||||||
|
source_img = os.path.join(iotests.test_dir, 'source')
|
||||||
|
target_img = os.path.join(iotests.test_dir, 'target')
|
||||||
|
size = '1M'
|
||||||
|
|
||||||
|
|
||||||
|
def get_actual_size(vm, node_name):
|
||||||
|
nodes = vm.cmd('query-named-block-nodes', flat=True)
|
||||||
|
node = next(n for n in nodes if n['node-name'] == node_name)
|
||||||
|
return node['image']['actual-size']
|
||||||
|
|
||||||
|
|
||||||
|
class TestBackup(iotests.QMPTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
qemu_img_create('-f', iotests.imgfmt, source_img, size)
|
||||||
|
qemu_img_create('-f', iotests.imgfmt, temp_img, size)
|
||||||
|
qemu_img_create('-f', iotests.imgfmt, target_img, size)
|
||||||
|
qemu_io('-c', 'write 0 1M', source_img)
|
||||||
|
|
||||||
|
self.vm = iotests.VM()
|
||||||
|
self.vm.launch()
|
||||||
|
|
||||||
|
self.vm.cmd('blockdev-add', {
|
||||||
|
'node-name': 'cbw',
|
||||||
|
'driver': 'copy-before-write',
|
||||||
|
'file': {
|
||||||
|
'driver': iotests.imgfmt,
|
||||||
|
'file': {
|
||||||
|
'driver': 'file',
|
||||||
|
'filename': source_img,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'target': {
|
||||||
|
'driver': iotests.imgfmt,
|
||||||
|
'discard': 'unmap',
|
||||||
|
'node-name': 'temp',
|
||||||
|
'file': {
|
||||||
|
'driver': 'file',
|
||||||
|
'filename': temp_img
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self.vm.cmd('blockdev-add', {
|
||||||
|
'node-name': 'access',
|
||||||
|
'discard': 'unmap',
|
||||||
|
'driver': 'snapshot-access',
|
||||||
|
'file': 'cbw'
|
||||||
|
})
|
||||||
|
|
||||||
|
self.vm.cmd('blockdev-add', {
|
||||||
|
'driver': iotests.imgfmt,
|
||||||
|
'node-name': 'target',
|
||||||
|
'file': {
|
||||||
|
'driver': 'file',
|
||||||
|
'filename': target_img
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertLess(get_actual_size(self.vm, 'temp'), 512 * 1024)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
# That should fail, because region is discarded
|
||||||
|
self.vm.hmp_qemu_io('access', 'read 0 1M')
|
||||||
|
|
||||||
|
self.vm.shutdown()
|
||||||
|
|
||||||
|
self.assertTrue('read failed: Permission denied' in self.vm.get_log())
|
||||||
|
|
||||||
|
# Final check that temp image is empty
|
||||||
|
mapping = qemu_img_map(temp_img)
|
||||||
|
self.assertEqual(len(mapping), 1)
|
||||||
|
self.assertEqual(mapping[0]['start'], 0)
|
||||||
|
self.assertEqual(mapping[0]['length'], 1024 * 1024)
|
||||||
|
self.assertEqual(mapping[0]['data'], False)
|
||||||
|
|
||||||
|
os.remove(temp_img)
|
||||||
|
os.remove(source_img)
|
||||||
|
os.remove(target_img)
|
||||||
|
|
||||||
|
def do_backup(self):
|
||||||
|
self.vm.cmd('blockdev-backup', device='access',
|
||||||
|
sync='full', target='target',
|
||||||
|
job_id='backup0',
|
||||||
|
discard_source=True)
|
||||||
|
|
||||||
|
self.vm.event_wait(name='BLOCK_JOB_COMPLETED')
|
||||||
|
|
||||||
|
def test_discard_written(self):
|
||||||
|
"""
|
||||||
|
1. Guest writes
|
||||||
|
2. copy-before-write operation, data is stored to temp
|
||||||
|
3. start backup(discard_source=True), check that data is
|
||||||
|
removed from temp
|
||||||
|
"""
|
||||||
|
# Trigger copy-before-write operation
|
||||||
|
result = self.vm.hmp_qemu_io('cbw', 'write 0 1M')
|
||||||
|
self.assert_qmp(result, 'return', '')
|
||||||
|
|
||||||
|
# Check that data is written to temporary image
|
||||||
|
self.assertGreater(get_actual_size(self.vm, 'temp'), 1024 * 1024)
|
||||||
|
|
||||||
|
self.do_backup()
|
||||||
|
|
||||||
|
def test_discard_cbw(self):
|
||||||
|
"""
|
||||||
|
1. do backup(discard_source=True), which should inform
|
||||||
|
copy-before-write that data is not needed anymore
|
||||||
|
2. Guest writes
|
||||||
|
3. Check that copy-before-write operation is not done
|
||||||
|
"""
|
||||||
|
self.do_backup()
|
||||||
|
|
||||||
|
# Try trigger copy-before-write operation
|
||||||
|
result = self.vm.hmp_qemu_io('cbw', 'write 0 1M')
|
||||||
|
self.assert_qmp(result, 'return', '')
|
||||||
|
|
||||||
|
# Check that data is not written to temporary image, as region
|
||||||
|
# is discarded from copy-before-write process
|
||||||
|
self.assertLess(get_actual_size(self.vm, 'temp'), 512 * 1024)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
iotests.main(supported_fmts=['qcow2'],
|
||||||
|
supported_protocols=['file'])
|
5
tests/qemu-iotests/tests/backup-discard-source.out
Normal file
5
tests/qemu-iotests/tests/backup-discard-source.out
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
..
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 2 tests
|
||||||
|
|
||||||
|
OK
|
Loading…
x
Reference in New Issue
Block a user