mirror of https://github.com/xemu-project/xemu.git
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,
|
||||
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
|
||||
BitmapSyncMode bitmap_mode,
|
||||
bool compress,
|
||||
bool compress, bool discard_source,
|
||||
const char *filter_node_name,
|
||||
BackupPerf *perf,
|
||||
BlockdevOnError on_source_error,
|
||||
|
@ -457,7 +457,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||
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) {
|
||||
goto error;
|
||||
}
|
||||
|
|
|
@ -137,6 +137,7 @@ typedef struct BlockCopyState {
|
|||
CoMutex lock;
|
||||
int64_t in_flight_bytes;
|
||||
BlockCopyMethod method;
|
||||
bool discard_source;
|
||||
BlockReqList reqs;
|
||||
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,
|
||||
BlockDriverState *copy_bitmap_bs,
|
||||
const BdrvDirtyBitmap *bitmap,
|
||||
bool discard_source,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
|
@ -367,7 +370,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
|||
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);
|
||||
if (!copy_bitmap) {
|
||||
return NULL;
|
||||
|
@ -417,6 +420,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
|||
cluster_size),
|
||||
};
|
||||
|
||||
s->discard_source = discard_source;
|
||||
block_copy_set_copy_opts(s, false, false);
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ typedef struct BDRVCopyBeforeWriteState {
|
|||
BdrvChild *target;
|
||||
OnCbwError on_cbw_error;
|
||||
uint32_t cbw_timeout_ns;
|
||||
bool discard_source;
|
||||
|
||||
/*
|
||||
* @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)
|
||||
{
|
||||
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) {
|
||||
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)
|
||||
|
@ -347,6 +358,8 @@ cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role,
|
|||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||
|
||||
if (!(role & BDRV_CHILD_FILTERED)) {
|
||||
/*
|
||||
* Target child
|
||||
|
@ -364,9 +377,17 @@ cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role,
|
|||
perm, shared, nperm, nshared);
|
||||
|
||||
if (!QLIST_EMPTY(&bs->parents)) {
|
||||
if (perm & BLK_PERM_WRITE) {
|
||||
*nperm = *nperm | BLK_PERM_CONSISTENT_READ;
|
||||
/*
|
||||
* 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;
|
||||
if (s->discard_source) {
|
||||
*nperm = *nperm | BLK_PERM_WRITE;
|
||||
}
|
||||
|
||||
*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) &
|
||||
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) {
|
||||
error_prepend(errp, "Cannot create block-copy-state: ");
|
||||
return -EINVAL;
|
||||
|
@ -521,12 +544,14 @@ static BlockDriver bdrv_cbw_filter = {
|
|||
BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
|
||||
BlockDriverState *target,
|
||||
const char *filter_node_name,
|
||||
bool discard_source,
|
||||
BlockCopyState **bcs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVCopyBeforeWriteState *state;
|
||||
BlockDriverState *top;
|
||||
QDict *opts;
|
||||
int flags = BDRV_O_RDWR | (discard_source ? BDRV_O_CBW_DISCARD_SOURCE : 0);
|
||||
|
||||
assert(source->total_sectors == target->total_sectors);
|
||||
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, "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) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
|
||||
BlockDriverState *target,
|
||||
const char *filter_node_name,
|
||||
bool discard_source,
|
||||
BlockCopyState **bcs,
|
||||
Error **errp);
|
||||
void bdrv_cbw_drop(BlockDriverState *bs);
|
||||
|
|
|
@ -93,6 +93,7 @@ typedef struct MirrorBlockJob {
|
|||
int64_t active_write_bytes_in_flight;
|
||||
bool prepared;
|
||||
bool in_drain;
|
||||
bool base_ro;
|
||||
} MirrorBlockJob;
|
||||
|
||||
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_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_unref(target_bs);
|
||||
|
||||
|
@ -1717,6 +1722,7 @@ static BlockJob *mirror_start_job(
|
|||
bool is_none_mode, BlockDriverState *base,
|
||||
bool auto_complete, const char *filter_node_name,
|
||||
bool is_mirror, MirrorCopyMode copy_mode,
|
||||
bool base_ro,
|
||||
Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s;
|
||||
|
@ -1800,6 +1806,7 @@ static BlockJob *mirror_start_job(
|
|||
bdrv_unref(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
|
||||
* 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,
|
||||
on_source_error, on_target_error, unmap, NULL, NULL,
|
||||
&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,
|
||||
|
@ -2058,7 +2065,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
|||
on_error, on_error, true, cb, opaque,
|
||||
&commit_active_job_driver, false, base, auto_complete,
|
||||
filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND,
|
||||
errp);
|
||||
base_read_only, errp);
|
||||
if (!job) {
|
||||
goto error_restore_flags;
|
||||
}
|
||||
|
|
|
@ -582,8 +582,8 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||
|
||||
s->backup_job = backup_job_create(
|
||||
NULL, s->secondary_disk->bs, s->hidden_disk->bs,
|
||||
0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL,
|
||||
&perf,
|
||||
0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, false,
|
||||
NULL, &perf,
|
||||
BLOCKDEV_ON_ERROR_REPORT,
|
||||
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
|
||||
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,
|
||||
backup->sync, bmap, backup->bitmap_mode,
|
||||
backup->compress,
|
||||
backup->compress, backup->discard_source,
|
||||
backup->filter_node_name,
|
||||
&perf,
|
||||
backup->on_source_error,
|
||||
|
|
|
@ -243,6 +243,8 @@ typedef enum {
|
|||
read-write fails */
|
||||
#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)
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ typedef struct BlockCopyState BlockCopyState;
|
|||
typedef struct BlockCopyCallState BlockCopyCallState;
|
||||
|
||||
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||
BlockDriverState *copy_bitmap_bs,
|
||||
const BdrvDirtyBitmap *bitmap,
|
||||
bool discard_source,
|
||||
Error **errp);
|
||||
|
||||
/* 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,
|
||||
BdrvDirtyBitmap *sync_bitmap,
|
||||
BitmapSyncMode bitmap_mode,
|
||||
bool compress,
|
||||
bool compress, bool discard_source,
|
||||
const char *filter_node_name,
|
||||
BackupPerf *perf,
|
||||
BlockdevOnError on_source_error,
|
||||
|
|
|
@ -1610,6 +1610,9 @@
|
|||
# node specified by @drive. If this option is not given, a node
|
||||
# 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)
|
||||
#
|
||||
# Features:
|
||||
|
@ -1631,6 +1634,7 @@
|
|||
'*on-target-error': 'BlockdevOnError',
|
||||
'*auto-finalize': 'bool', '*auto-dismiss': 'bool',
|
||||
'*filter-node-name': 'str',
|
||||
'*discard-source': 'bool',
|
||||
'*x-perf': { 'type': 'BackupPerf',
|
||||
'features': [ 'unstable' ] } } }
|
||||
|
||||
|
|
|
@ -120,16 +120,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -596,16 +596,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -865,16 +865,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -1341,16 +1341,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -1610,16 +1610,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -2086,16 +2086,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -2355,16 +2355,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -2831,16 +2831,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -3100,16 +3100,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -3576,16 +3576,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -3845,16 +3845,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -4321,16 +4321,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -4590,16 +4590,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
@ -5066,16 +5066,16 @@ write -P0x67 0x3fe0000 0x20000
|
|||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
},
|
||||
{
|
||||
"busy": false,
|
||||
"count": 0,
|
||||
"granularity": 65536,
|
||||
"persistent": false,
|
||||
"recording": false
|
||||
},
|
||||
}
|
||||
],
|
||||
"drive0": [
|
||||
{
|
||||
"busy": false,
|
||||
"count": 458752,
|
||||
|
|
|
@ -55,4 +55,4 @@ max-line-length=79
|
|||
|
||||
[SIMILARITIES]
|
||||
|
||||
min-similarity-lines=6
|
||||
min-similarity-lines=10
|
||||
|
|
|
@ -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'])
|
|
@ -0,0 +1,5 @@
|
|||
..
|
||||
----------------------------------------------------------------------
|
||||
Ran 2 tests
|
||||
|
||||
OK
|
Loading…
Reference in New Issue