mirror of https://github.com/xemu-project/xemu.git
qapi: blockdev-backup: add discard-source parameter
Add a parameter that enables discard-after-copy. That is mostly useful in "push backup with fleecing" scheme, when source is snapshot-access format driver node, based on copy-before-write filter snapshot-access API: [guest] [snapshot-access] ~~ blockdev-backup ~~> [backup target] | | | root | file v v [copy-before-write] | | | file | target v v [active disk] [temp.img] In this case discard-after-copy does two things: - discard data in temp.img to save disk space - avoid further copy-before-write operation in discarded area Note that we have to declare WRITE permission on source in copy-before-write filter, for discard to work. Still we can't take it unconditionally, as it will break normal backup from RO source. So, we have to add a parameter and pass it thorough bdrv_open flags. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Reviewed-by: Fiona Ebner <f.ebner@proxmox.com> Tested-by: Fiona Ebner <f.ebner@proxmox.com> Acked-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20240313152822.626493-5-vsementsov@yandex-team.ru> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
This commit is contained in:
parent
006e845b5a
commit
0fd05c8d80
|
@ -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;
|
||||||
/*
|
/*
|
||||||
|
@ -353,6 +354,7 @@ 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,
|
BlockDriverState *copy_bitmap_bs,
|
||||||
const BdrvDirtyBitmap *bitmap,
|
const BdrvDirtyBitmap *bitmap,
|
||||||
|
bool discard_source,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
ERRP_GUARD();
|
ERRP_GUARD();
|
||||||
|
@ -418,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);
|
||||||
|
@ -589,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
|
||||||
|
@ -357,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
|
||||||
|
@ -381,6 +384,10 @@ cbw_child_perm(BlockDriverState *bs, BdrvChild *c, BdrvChildRole role,
|
||||||
* start
|
* 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -468,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, bs, 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;
|
||||||
|
@ -535,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();
|
||||||
|
@ -553,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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ 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,
|
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' ] } } }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue