block: Mark drain related functions GRAPH_RDLOCK
Draining recursively traverses the graph, therefore we need to make sure that also such accesses to the graph are protected by the graph rdlock. There are 3 different drain callers to consider: 1. drain in the main loop: no issue at all, rdlock is nop. 2. drain in an iothread: rdlock only works in main loop or coroutines, so disallow it. 3. drain in a coroutine (regardless of AioContext): the drain mechanism takes care of scheduling a BH in the bs->aio_context that will then take care of perform the actual draining. This is wrong, because as pointed in (2) if bs->aio_context is an iothread then rdlock won't work. Therefore change bdrv_co_yield_to_drain to schedule the BH in the main loop. Caller (2) also implies that we need to modify test-bdrv-drain.c to disallow draining in the iothreads. For some places, we know that they will hold the lock, but we don't have the GRAPH_RDLOCK annotations yet. In this case, add assume_graph_lock() with a FIXME comment. These places will be removed once everything is properly annotated. Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <20230929145157.45443-6-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
2b3912f135
commit
d05ab380db
6
block.c
6
block.c
@ -1192,19 +1192,19 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c)
|
|||||||
return g_strdup_printf("node '%s'", bdrv_get_node_name(parent));
|
return g_strdup_printf("node '%s'", bdrv_get_node_name(parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdrv_child_cb_drained_begin(BdrvChild *child)
|
static void GRAPH_RDLOCK bdrv_child_cb_drained_begin(BdrvChild *child)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = child->opaque;
|
BlockDriverState *bs = child->opaque;
|
||||||
bdrv_do_drained_begin_quiesce(bs, NULL);
|
bdrv_do_drained_begin_quiesce(bs, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool bdrv_child_cb_drained_poll(BdrvChild *child)
|
static bool GRAPH_RDLOCK bdrv_child_cb_drained_poll(BdrvChild *child)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = child->opaque;
|
BlockDriverState *bs = child->opaque;
|
||||||
return bdrv_drain_poll(bs, NULL, false);
|
return bdrv_drain_poll(bs, NULL, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdrv_child_cb_drained_end(BdrvChild *child)
|
static void GRAPH_RDLOCK bdrv_child_cb_drained_end(BdrvChild *child)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = child->opaque;
|
BlockDriverState *bs = child->opaque;
|
||||||
bdrv_drained_end(bs);
|
bdrv_drained_end(bs);
|
||||||
|
30
block/io.c
30
block/io.c
@ -46,9 +46,12 @@ static void bdrv_parent_cb_resize(BlockDriverState *bs);
|
|||||||
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||||
int64_t offset, int64_t bytes, BdrvRequestFlags flags);
|
int64_t offset, int64_t bytes, BdrvRequestFlags flags);
|
||||||
|
|
||||||
static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
|
static void GRAPH_RDLOCK
|
||||||
|
bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
|
||||||
{
|
{
|
||||||
BdrvChild *c, *next;
|
BdrvChild *c, *next;
|
||||||
|
IO_OR_GS_CODE();
|
||||||
|
assert_bdrv_graph_readable();
|
||||||
|
|
||||||
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
|
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
|
||||||
if (c == ignore) {
|
if (c == ignore) {
|
||||||
@ -70,9 +73,12 @@ void bdrv_parent_drained_end_single(BdrvChild *c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
|
static void GRAPH_RDLOCK
|
||||||
|
bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
|
||||||
{
|
{
|
||||||
BdrvChild *c;
|
BdrvChild *c;
|
||||||
|
IO_OR_GS_CODE();
|
||||||
|
assert_bdrv_graph_readable();
|
||||||
|
|
||||||
QLIST_FOREACH(c, &bs->parents, next_parent) {
|
QLIST_FOREACH(c, &bs->parents, next_parent) {
|
||||||
if (c == ignore) {
|
if (c == ignore) {
|
||||||
@ -84,17 +90,22 @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
|
|||||||
|
|
||||||
bool bdrv_parent_drained_poll_single(BdrvChild *c)
|
bool bdrv_parent_drained_poll_single(BdrvChild *c)
|
||||||
{
|
{
|
||||||
|
IO_OR_GS_CODE();
|
||||||
|
|
||||||
if (c->klass->drained_poll) {
|
if (c->klass->drained_poll) {
|
||||||
return c->klass->drained_poll(c);
|
return c->klass->drained_poll(c);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore,
|
static bool GRAPH_RDLOCK
|
||||||
|
bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore,
|
||||||
bool ignore_bds_parents)
|
bool ignore_bds_parents)
|
||||||
{
|
{
|
||||||
BdrvChild *c, *next;
|
BdrvChild *c, *next;
|
||||||
bool busy = false;
|
bool busy = false;
|
||||||
|
IO_OR_GS_CODE();
|
||||||
|
assert_bdrv_graph_readable();
|
||||||
|
|
||||||
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
|
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
|
||||||
if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) {
|
if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) {
|
||||||
@ -114,6 +125,7 @@ void bdrv_parent_drained_begin_single(BdrvChild *c)
|
|||||||
c->quiesced_parent = true;
|
c->quiesced_parent = true;
|
||||||
|
|
||||||
if (c->klass->drained_begin) {
|
if (c->klass->drained_begin) {
|
||||||
|
/* called with rdlock taken, but it doesn't really need it. */
|
||||||
c->klass->drained_begin(c);
|
c->klass->drained_begin(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,6 +275,9 @@ bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent,
|
|||||||
static bool bdrv_drain_poll_top_level(BlockDriverState *bs,
|
static bool bdrv_drain_poll_top_level(BlockDriverState *bs,
|
||||||
BdrvChild *ignore_parent)
|
BdrvChild *ignore_parent)
|
||||||
{
|
{
|
||||||
|
GLOBAL_STATE_CODE();
|
||||||
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
||||||
|
|
||||||
return bdrv_drain_poll(bs, ignore_parent, false);
|
return bdrv_drain_poll(bs, ignore_parent, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,6 +377,7 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent,
|
|||||||
|
|
||||||
/* Stop things in parent-to-child order */
|
/* Stop things in parent-to-child order */
|
||||||
if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) {
|
if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) {
|
||||||
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
||||||
bdrv_parent_drained_begin(bs, parent);
|
bdrv_parent_drained_begin(bs, parent);
|
||||||
if (bs->drv && bs->drv->bdrv_drain_begin) {
|
if (bs->drv && bs->drv->bdrv_drain_begin) {
|
||||||
bs->drv->bdrv_drain_begin(bs);
|
bs->drv->bdrv_drain_begin(bs);
|
||||||
@ -408,12 +424,16 @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
|
|||||||
bdrv_co_yield_to_drain(bs, false, parent, false);
|
bdrv_co_yield_to_drain(bs, false, parent, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* At this point, we should be always running in the main loop. */
|
||||||
|
GLOBAL_STATE_CODE();
|
||||||
assert(bs->quiesce_counter > 0);
|
assert(bs->quiesce_counter > 0);
|
||||||
GLOBAL_STATE_CODE();
|
GLOBAL_STATE_CODE();
|
||||||
|
|
||||||
/* Re-enable things in child-to-parent order */
|
/* Re-enable things in child-to-parent order */
|
||||||
old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter);
|
old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter);
|
||||||
if (old_quiesce_counter == 1) {
|
if (old_quiesce_counter == 1) {
|
||||||
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
||||||
if (bs->drv && bs->drv->bdrv_drain_end) {
|
if (bs->drv && bs->drv->bdrv_drain_end) {
|
||||||
bs->drv->bdrv_drain_end(bs);
|
bs->drv->bdrv_drain_end(bs);
|
||||||
}
|
}
|
||||||
@ -437,6 +457,8 @@ void bdrv_drain(BlockDriverState *bs)
|
|||||||
static void bdrv_drain_assert_idle(BlockDriverState *bs)
|
static void bdrv_drain_assert_idle(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BdrvChild *child, *next;
|
BdrvChild *child, *next;
|
||||||
|
GLOBAL_STATE_CODE();
|
||||||
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
||||||
|
|
||||||
assert(qatomic_read(&bs->in_flight) == 0);
|
assert(qatomic_read(&bs->in_flight) == 0);
|
||||||
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
|
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
|
||||||
@ -450,7 +472,9 @@ static bool bdrv_drain_all_poll(void)
|
|||||||
{
|
{
|
||||||
BlockDriverState *bs = NULL;
|
BlockDriverState *bs = NULL;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
GLOBAL_STATE_CODE();
|
GLOBAL_STATE_CODE();
|
||||||
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
||||||
|
|
||||||
/* bdrv_drain_poll() can't make changes to the graph and we are holding the
|
/* bdrv_drain_poll() can't make changes to the graph and we are holding the
|
||||||
* main AioContext lock, so iterating bdrv_next_all_states() is safe. */
|
* main AioContext lock, so iterating bdrv_next_all_states() is safe. */
|
||||||
|
@ -370,7 +370,7 @@ bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
|
|||||||
*
|
*
|
||||||
* Begin a quiesced section for the parent of @c.
|
* Begin a quiesced section for the parent of @c.
|
||||||
*/
|
*/
|
||||||
void bdrv_parent_drained_begin_single(BdrvChild *c);
|
void GRAPH_RDLOCK bdrv_parent_drained_begin_single(BdrvChild *c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bdrv_parent_drained_poll_single:
|
* bdrv_parent_drained_poll_single:
|
||||||
@ -378,14 +378,14 @@ void bdrv_parent_drained_begin_single(BdrvChild *c);
|
|||||||
* Returns true if there is any pending activity to cease before @c can be
|
* Returns true if there is any pending activity to cease before @c can be
|
||||||
* called quiesced, false otherwise.
|
* called quiesced, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool bdrv_parent_drained_poll_single(BdrvChild *c);
|
bool GRAPH_RDLOCK bdrv_parent_drained_poll_single(BdrvChild *c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bdrv_parent_drained_end_single:
|
* bdrv_parent_drained_end_single:
|
||||||
*
|
*
|
||||||
* End a quiesced section for the parent of @c.
|
* End a quiesced section for the parent of @c.
|
||||||
*/
|
*/
|
||||||
void bdrv_parent_drained_end_single(BdrvChild *c);
|
void GRAPH_RDLOCK bdrv_parent_drained_end_single(BdrvChild *c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bdrv_drain_poll:
|
* bdrv_drain_poll:
|
||||||
@ -398,7 +398,8 @@ void bdrv_parent_drained_end_single(BdrvChild *c);
|
|||||||
*
|
*
|
||||||
* This is part of bdrv_drained_begin.
|
* This is part of bdrv_drained_begin.
|
||||||
*/
|
*/
|
||||||
bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent,
|
bool GRAPH_RDLOCK
|
||||||
|
bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent,
|
||||||
bool ignore_bds_parents);
|
bool ignore_bds_parents);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -407,6 +408,12 @@ bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent,
|
|||||||
* Begin a quiesced section for exclusive access to the BDS, by disabling
|
* Begin a quiesced section for exclusive access to the BDS, by disabling
|
||||||
* external request sources including NBD server, block jobs, and device model.
|
* external request sources including NBD server, block jobs, and device model.
|
||||||
*
|
*
|
||||||
|
* This function can only be invoked by the main loop or a coroutine
|
||||||
|
* (regardless of the AioContext where it is running).
|
||||||
|
* If the coroutine is running in an Iothread AioContext, this function will
|
||||||
|
* just schedule a BH to run in the main loop.
|
||||||
|
* However, it cannot be directly called by an Iothread.
|
||||||
|
*
|
||||||
* This function can be recursive.
|
* This function can be recursive.
|
||||||
*/
|
*/
|
||||||
void bdrv_drained_begin(BlockDriverState *bs);
|
void bdrv_drained_begin(BlockDriverState *bs);
|
||||||
@ -423,6 +430,12 @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent);
|
|||||||
* bdrv_drained_end:
|
* bdrv_drained_end:
|
||||||
*
|
*
|
||||||
* End a quiescent section started by bdrv_drained_begin().
|
* End a quiescent section started by bdrv_drained_begin().
|
||||||
|
*
|
||||||
|
* This function can only be invoked by the main loop or a coroutine
|
||||||
|
* (regardless of the AioContext where it is running).
|
||||||
|
* If the coroutine is running in an Iothread AioContext, this function will
|
||||||
|
* just schedule a BH to run in the main loop.
|
||||||
|
* However, it cannot be directly called by an Iothread.
|
||||||
*/
|
*/
|
||||||
void bdrv_drained_end(BlockDriverState *bs);
|
void bdrv_drained_end(BlockDriverState *bs);
|
||||||
|
|
||||||
|
@ -963,15 +963,15 @@ struct BdrvChildClass {
|
|||||||
* Note that this can be nested. If drained_begin() was called twice, new
|
* Note that this can be nested. If drained_begin() was called twice, new
|
||||||
* I/O is allowed only after drained_end() was called twice, too.
|
* I/O is allowed only after drained_end() was called twice, too.
|
||||||
*/
|
*/
|
||||||
void (*drained_begin)(BdrvChild *child);
|
void GRAPH_RDLOCK_PTR (*drained_begin)(BdrvChild *child);
|
||||||
void (*drained_end)(BdrvChild *child);
|
void GRAPH_RDLOCK_PTR (*drained_end)(BdrvChild *child);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns whether the parent has pending requests for the child. This
|
* Returns whether the parent has pending requests for the child. This
|
||||||
* callback is polled after .drained_begin() has been called until all
|
* callback is polled after .drained_begin() has been called until all
|
||||||
* activity on the child has stopped.
|
* activity on the child has stopped.
|
||||||
*/
|
*/
|
||||||
bool (*drained_poll)(BdrvChild *child);
|
bool GRAPH_RDLOCK_PTR (*drained_poll)(BdrvChild *child);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Notifies the parent that the filename of its child has changed (e.g.
|
* Notifies the parent that the filename of its child has changed (e.g.
|
||||||
|
@ -1196,7 +1196,7 @@ static void coroutine_mixed_fn detach_by_parent_aio_cb(void *opaque, int ret)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void detach_by_driver_cb_drained_begin(BdrvChild *child)
|
static void GRAPH_RDLOCK detach_by_driver_cb_drained_begin(BdrvChild *child)
|
||||||
{
|
{
|
||||||
struct detach_by_parent_data *data = &detach_by_parent_data;
|
struct detach_by_parent_data *data = &detach_by_parent_data;
|
||||||
|
|
||||||
@ -1233,7 +1233,7 @@ static BdrvChildClass detach_by_driver_cb_class;
|
|||||||
* state is messed up, but if it is only polled in the single
|
* state is messed up, but if it is only polled in the single
|
||||||
* BDRV_POLL_WHILE() at the end of the drain, this should work fine.
|
* BDRV_POLL_WHILE() at the end of the drain, this should work fine.
|
||||||
*/
|
*/
|
||||||
static void test_detach_indirect(bool by_parent_cb)
|
static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb)
|
||||||
{
|
{
|
||||||
BlockBackend *blk;
|
BlockBackend *blk;
|
||||||
BlockDriverState *parent_a, *parent_b, *a, *b, *c;
|
BlockDriverState *parent_a, *parent_b, *a, *b, *c;
|
||||||
|
Loading…
Reference in New Issue
Block a user