block: Introduce op_blockers to BlockDriverState
BlockDriverState.op_blockers is an array of lists with BLOCK_OP_TYPE_MAX elements. Each list is a list of blockers of an operation type (BlockOpType), that marks this BDS as currently blocked for a certain type of operation with reason errors stored in the list. The rule of usage is: * BDS user who wants to take an operation should check if there's any blocker of the type with bdrv_op_is_blocked(). * BDS user who wants to block certain types of operation, should call bdrv_op_block (or bdrv_op_block_all to block all types of operations, which is similar to the existing bdrv_set_in_use()). * A blocker is only referenced by op_blockers, so the lifecycle is managed by caller, and shouldn't be lost until unblock, so typically a caller does these: - Allocate a blocker with error_setg or similar, call bdrv_op_block() to block some operations. - Hold the blocker, do his job. - Unblock operations that it blocked, with the same reason pointer passed to bdrv_op_unblock(). - Release the blocker with error_free(). Signed-off-by: Fam Zheng <famz@redhat.com> Reviewed-by: Benoit Canet <benoit@irqsave.net> Reviewed-by: Jeff Cody <jcody@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
8574575f90
commit
fbe40ff780
76
block.c
76
block.c
|
@ -335,6 +335,7 @@ void bdrv_register(BlockDriver *bdrv)
|
||||||
BlockDriverState *bdrv_new(const char *device_name, Error **errp)
|
BlockDriverState *bdrv_new(const char *device_name, Error **errp)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (bdrv_find(device_name)) {
|
if (bdrv_find(device_name)) {
|
||||||
error_setg(errp, "Device with id '%s' already exists",
|
error_setg(errp, "Device with id '%s' already exists",
|
||||||
|
@ -353,6 +354,9 @@ BlockDriverState *bdrv_new(const char *device_name, Error **errp)
|
||||||
if (device_name[0] != '\0') {
|
if (device_name[0] != '\0') {
|
||||||
QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list);
|
QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list);
|
||||||
}
|
}
|
||||||
|
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
|
||||||
|
QLIST_INIT(&bs->op_blockers[i]);
|
||||||
|
}
|
||||||
bdrv_iostatus_disable(bs);
|
bdrv_iostatus_disable(bs);
|
||||||
notifier_list_init(&bs->close_notifiers);
|
notifier_list_init(&bs->close_notifiers);
|
||||||
notifier_with_return_list_init(&bs->before_write_notifiers);
|
notifier_with_return_list_init(&bs->before_write_notifiers);
|
||||||
|
@ -1952,6 +1956,8 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
||||||
pstrcpy(bs_dest->device_name, sizeof(bs_dest->device_name),
|
pstrcpy(bs_dest->device_name, sizeof(bs_dest->device_name),
|
||||||
bs_src->device_name);
|
bs_src->device_name);
|
||||||
bs_dest->device_list = bs_src->device_list;
|
bs_dest->device_list = bs_src->device_list;
|
||||||
|
memcpy(bs_dest->op_blockers, bs_src->op_blockers,
|
||||||
|
sizeof(bs_dest->op_blockers));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -5325,6 +5331,76 @@ void bdrv_unref(BlockDriverState *bs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BdrvOpBlocker {
|
||||||
|
Error *reason;
|
||||||
|
QLIST_ENTRY(BdrvOpBlocker) list;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp)
|
||||||
|
{
|
||||||
|
BdrvOpBlocker *blocker;
|
||||||
|
assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX);
|
||||||
|
if (!QLIST_EMPTY(&bs->op_blockers[op])) {
|
||||||
|
blocker = QLIST_FIRST(&bs->op_blockers[op]);
|
||||||
|
if (errp) {
|
||||||
|
error_setg(errp, "Device '%s' is busy: %s",
|
||||||
|
bs->device_name, error_get_pretty(blocker->reason));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason)
|
||||||
|
{
|
||||||
|
BdrvOpBlocker *blocker;
|
||||||
|
assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX);
|
||||||
|
|
||||||
|
blocker = g_malloc0(sizeof(BdrvOpBlocker));
|
||||||
|
blocker->reason = reason;
|
||||||
|
QLIST_INSERT_HEAD(&bs->op_blockers[op], blocker, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason)
|
||||||
|
{
|
||||||
|
BdrvOpBlocker *blocker, *next;
|
||||||
|
assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX);
|
||||||
|
QLIST_FOREACH_SAFE(blocker, &bs->op_blockers[op], list, next) {
|
||||||
|
if (blocker->reason == reason) {
|
||||||
|
QLIST_REMOVE(blocker, list);
|
||||||
|
g_free(blocker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_op_block_all(BlockDriverState *bs, Error *reason)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
|
||||||
|
bdrv_op_block(bs, i, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
|
||||||
|
bdrv_op_unblock(bs, i, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bdrv_op_blocker_is_empty(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
|
||||||
|
if (!QLIST_EMPTY(&bs->op_blockers[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
||||||
{
|
{
|
||||||
assert(bs->in_use != in_use);
|
assert(bs->in_use != in_use);
|
||||||
|
|
|
@ -475,6 +475,13 @@ void bdrv_unref(BlockDriverState *bs);
|
||||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use);
|
void bdrv_set_in_use(BlockDriverState *bs, int in_use);
|
||||||
int bdrv_in_use(BlockDriverState *bs);
|
int bdrv_in_use(BlockDriverState *bs);
|
||||||
|
|
||||||
|
bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp);
|
||||||
|
void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason);
|
||||||
|
void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason);
|
||||||
|
void bdrv_op_block_all(BlockDriverState *bs, Error *reason);
|
||||||
|
void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason);
|
||||||
|
bool bdrv_op_blocker_is_empty(BlockDriverState *bs);
|
||||||
|
|
||||||
#ifdef CONFIG_LINUX_AIO
|
#ifdef CONFIG_LINUX_AIO
|
||||||
int raw_get_aio_fd(BlockDriverState *bs);
|
int raw_get_aio_fd(BlockDriverState *bs);
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -270,6 +270,8 @@ typedef struct BlockLimits {
|
||||||
size_t opt_mem_alignment;
|
size_t opt_mem_alignment;
|
||||||
} BlockLimits;
|
} BlockLimits;
|
||||||
|
|
||||||
|
typedef struct BdrvOpBlocker BdrvOpBlocker;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: the function bdrv_append() copies and swaps contents of
|
* Note: the function bdrv_append() copies and swaps contents of
|
||||||
* BlockDriverStates, so if you add new fields to this struct, please
|
* BlockDriverStates, so if you add new fields to this struct, please
|
||||||
|
@ -360,6 +362,9 @@ struct BlockDriverState {
|
||||||
|
|
||||||
QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
|
QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
|
||||||
|
|
||||||
|
/* operation blockers */
|
||||||
|
QLIST_HEAD(, BdrvOpBlocker) op_blockers[BLOCK_OP_TYPE_MAX];
|
||||||
|
|
||||||
/* long-running background operation */
|
/* long-running background operation */
|
||||||
BlockJob *job;
|
BlockJob *job;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue