qapi: copy-on-read filter: add 'bottom' option
Add an option to limit copy-on-read operations to specified sub-chain of backing-chain, to make copy-on-read filter useful for block-stream job. Suggested-by: Max Reitz <mreitz@redhat.com> Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> [vsementsov: change subject, modified to freeze the chain, do some fixes] Message-Id: <20201216061703.70908-6-vsementsov@virtuozzo.com> Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
880747a887
commit
e4c8fddde7
@ -24,18 +24,24 @@
|
|||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "block/copy-on-read.h"
|
#include "block/copy-on-read.h"
|
||||||
|
|
||||||
|
|
||||||
typedef struct BDRVStateCOR {
|
typedef struct BDRVStateCOR {
|
||||||
bool active;
|
bool active;
|
||||||
|
BlockDriverState *bottom_bs;
|
||||||
|
bool chain_frozen;
|
||||||
} BDRVStateCOR;
|
} BDRVStateCOR;
|
||||||
|
|
||||||
|
|
||||||
static int cor_open(BlockDriverState *bs, QDict *options, int flags,
|
static int cor_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
BlockDriverState *bottom_bs = NULL;
|
||||||
BDRVStateCOR *state = bs->opaque;
|
BDRVStateCOR *state = bs->opaque;
|
||||||
|
/* Find a bottom node name, if any */
|
||||||
|
const char *bottom_node = qdict_get_try_str(options, "bottom");
|
||||||
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
|
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
|
||||||
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
|
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
|
||||||
@ -51,7 +57,38 @@ static int cor_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);
|
||||||
|
|
||||||
|
if (bottom_node) {
|
||||||
|
bottom_bs = bdrv_find_node(bottom_node);
|
||||||
|
if (!bottom_bs) {
|
||||||
|
error_setg(errp, "Bottom node '%s' not found", bottom_node);
|
||||||
|
qdict_del(options, "bottom");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
qdict_del(options, "bottom");
|
||||||
|
|
||||||
|
if (!bottom_bs->drv) {
|
||||||
|
error_setg(errp, "Bottom node '%s' not opened", bottom_node);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bottom_bs->drv->is_filter) {
|
||||||
|
error_setg(errp, "Bottom node '%s' is a filter", bottom_node);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bdrv_freeze_backing_chain(bs, bottom_bs, errp) < 0) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
state->chain_frozen = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We do freeze the chain, so it shouldn't be removed. Still, storing a
|
||||||
|
* pointer worth bdrv_ref().
|
||||||
|
*/
|
||||||
|
bdrv_ref(bottom_bs);
|
||||||
|
}
|
||||||
state->active = true;
|
state->active = true;
|
||||||
|
state->bottom_bs = bottom_bs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We don't need to call bdrv_child_refresh_perms() now as the permissions
|
* We don't need to call bdrv_child_refresh_perms() now as the permissions
|
||||||
@ -107,8 +144,46 @@ static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs,
|
|||||||
size_t qiov_offset,
|
size_t qiov_offset,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
|
int64_t n;
|
||||||
flags | BDRV_REQ_COPY_ON_READ);
|
int local_flags;
|
||||||
|
int ret;
|
||||||
|
BDRVStateCOR *state = bs->opaque;
|
||||||
|
|
||||||
|
if (!state->bottom_bs) {
|
||||||
|
return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
|
||||||
|
flags | BDRV_REQ_COPY_ON_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (bytes) {
|
||||||
|
local_flags = flags;
|
||||||
|
|
||||||
|
/* In case of failure, try to copy-on-read anyway */
|
||||||
|
ret = bdrv_is_allocated(bs->file->bs, offset, bytes, &n);
|
||||||
|
if (ret <= 0) {
|
||||||
|
ret = bdrv_is_allocated_above(bdrv_backing_chain_next(bs->file->bs),
|
||||||
|
state->bottom_bs, true, offset,
|
||||||
|
n, &n);
|
||||||
|
if (ret > 0 || ret < 0) {
|
||||||
|
local_flags |= BDRV_REQ_COPY_ON_READ;
|
||||||
|
}
|
||||||
|
/* Finish earlier if the end of a backing file has been reached */
|
||||||
|
if (n == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bdrv_co_preadv_part(bs->file, offset, n, qiov, qiov_offset,
|
||||||
|
local_flags);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += n;
|
||||||
|
qiov_offset += n;
|
||||||
|
bytes -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -160,11 +235,25 @@ static void cor_lock_medium(BlockDriverState *bs, bool locked)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cor_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVStateCOR *s = bs->opaque;
|
||||||
|
|
||||||
|
if (s->chain_frozen) {
|
||||||
|
s->chain_frozen = false;
|
||||||
|
bdrv_unfreeze_backing_chain(bs, s->bottom_bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bdrv_unref(s->bottom_bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static BlockDriver bdrv_copy_on_read = {
|
static BlockDriver bdrv_copy_on_read = {
|
||||||
.format_name = "copy-on-read",
|
.format_name = "copy-on-read",
|
||||||
.instance_size = sizeof(BDRVStateCOR),
|
.instance_size = sizeof(BDRVStateCOR),
|
||||||
|
|
||||||
.bdrv_open = cor_open,
|
.bdrv_open = cor_open,
|
||||||
|
.bdrv_close = cor_close,
|
||||||
.bdrv_child_perm = cor_child_perm,
|
.bdrv_child_perm = cor_child_perm,
|
||||||
|
|
||||||
.bdrv_getlength = cor_getlength,
|
.bdrv_getlength = cor_getlength,
|
||||||
@ -201,6 +290,11 @@ void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs)
|
|||||||
bdrv_drained_begin(bs);
|
bdrv_drained_begin(bs);
|
||||||
/* Drop permissions before the graph change. */
|
/* Drop permissions before the graph change. */
|
||||||
s->active = false;
|
s->active = false;
|
||||||
|
/* unfreeze, as otherwise bdrv_replace_node() will fail */
|
||||||
|
if (s->chain_frozen) {
|
||||||
|
s->chain_frozen = false;
|
||||||
|
bdrv_unfreeze_backing_chain(cor_filter_bs, s->bottom_bs);
|
||||||
|
}
|
||||||
bdrv_child_refresh_perms(cor_filter_bs, child, &error_abort);
|
bdrv_child_refresh_perms(cor_filter_bs, child, &error_abort);
|
||||||
bdrv_replace_node(cor_filter_bs, bs, &error_abort);
|
bdrv_replace_node(cor_filter_bs, bs, &error_abort);
|
||||||
|
|
||||||
|
@ -3959,6 +3959,24 @@
|
|||||||
'data': { 'throttle-group': 'str',
|
'data': { 'throttle-group': 'str',
|
||||||
'file' : 'BlockdevRef'
|
'file' : 'BlockdevRef'
|
||||||
} }
|
} }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevOptionsCor:
|
||||||
|
#
|
||||||
|
# Driver specific block device options for the copy-on-read driver.
|
||||||
|
#
|
||||||
|
# @bottom: The name of a non-filter node (allocation-bearing layer) that
|
||||||
|
# limits the COR operations in the backing chain (inclusive), so
|
||||||
|
# that no data below this node will be copied by this filter.
|
||||||
|
# If option is absent, the limit is not applied, so that data
|
||||||
|
# from all backing layers may be copied.
|
||||||
|
#
|
||||||
|
# Since: 6.0
|
||||||
|
##
|
||||||
|
{ 'struct': 'BlockdevOptionsCor',
|
||||||
|
'base': 'BlockdevOptionsGenericFormat',
|
||||||
|
'data': { '*bottom': 'str' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevOptions:
|
# @BlockdevOptions:
|
||||||
#
|
#
|
||||||
@ -4011,7 +4029,7 @@
|
|||||||
'bochs': 'BlockdevOptionsGenericFormat',
|
'bochs': 'BlockdevOptionsGenericFormat',
|
||||||
'cloop': 'BlockdevOptionsGenericFormat',
|
'cloop': 'BlockdevOptionsGenericFormat',
|
||||||
'compress': 'BlockdevOptionsGenericFormat',
|
'compress': 'BlockdevOptionsGenericFormat',
|
||||||
'copy-on-read':'BlockdevOptionsGenericFormat',
|
'copy-on-read':'BlockdevOptionsCor',
|
||||||
'dmg': 'BlockdevOptionsGenericFormat',
|
'dmg': 'BlockdevOptionsGenericFormat',
|
||||||
'file': 'BlockdevOptionsFile',
|
'file': 'BlockdevOptionsFile',
|
||||||
'ftp': 'BlockdevOptionsCurlFtp',
|
'ftp': 'BlockdevOptionsCurlFtp',
|
||||||
|
Loading…
Reference in New Issue
Block a user