Skip to content

Commit

Permalink
block: use safe iteration over AioContext notifiers
Browse files Browse the repository at this point in the history
It's possible that an AioContext notifier user was close to finishing
when .detach_aio_context() or .attached_aio_context() is called.  In
that case they may call bdrv_remove_aio_context_notifier() during the
callback.

Use safe iteration to avoid crashing when the notifier list is modified
during iteration.  We must not only handle the case where the current
aio notifier is removed during a callback but also the one where any
other aio notifier is removed.

The next patch adds an AioContext notifier for block jobs and they
really could be terminating just as .detach_aio_context() is invoked.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Fam Zheng <famz@redhat.com>
Message-id: 1466096189-6477-6-git-send-email-stefanha@redhat.com
  • Loading branch information
stefanhaRH committed Jun 20, 2016
1 parent 9f6bc64 commit e8a095d
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 10 deletions.
46 changes: 36 additions & 10 deletions block.c
Expand Up @@ -3609,18 +3609,34 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs)
return bs->aio_context;
}

static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban)
{
QLIST_REMOVE(ban, list);
g_free(ban);
}

void bdrv_detach_aio_context(BlockDriverState *bs)
{
BdrvAioNotifier *baf;
BdrvAioNotifier *baf, *baf_tmp;
BdrvChild *child;

if (!bs->drv) {
return;
}

QLIST_FOREACH(baf, &bs->aio_notifiers, list) {
baf->detach_aio_context(baf->opaque);
assert(!bs->walking_aio_notifiers);
bs->walking_aio_notifiers = true;
QLIST_FOREACH_SAFE(baf, &bs->aio_notifiers, list, baf_tmp) {
if (baf->deleted) {
bdrv_do_remove_aio_context_notifier(baf);
} else {
baf->detach_aio_context(baf->opaque);
}
}
/* Never mind iterating again to check for ->deleted. bdrv_close() will
* remove remaining aio notifiers if we aren't called again.
*/
bs->walking_aio_notifiers = false;

if (bs->drv->bdrv_detach_aio_context) {
bs->drv->bdrv_detach_aio_context(bs);
Expand All @@ -3635,7 +3651,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
void bdrv_attach_aio_context(BlockDriverState *bs,
AioContext *new_context)
{
BdrvAioNotifier *ban;
BdrvAioNotifier *ban, *ban_tmp;
BdrvChild *child;

if (!bs->drv) {
Expand All @@ -3651,9 +3667,16 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
bs->drv->bdrv_attach_aio_context(bs, new_context);
}

QLIST_FOREACH(ban, &bs->aio_notifiers, list) {
ban->attached_aio_context(new_context, ban->opaque);
assert(!bs->walking_aio_notifiers);
bs->walking_aio_notifiers = true;
QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_tmp) {
if (ban->deleted) {
bdrv_do_remove_aio_context_notifier(ban);
} else {
ban->attached_aio_context(new_context, ban->opaque);
}
}
bs->walking_aio_notifiers = false;
}

void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
Expand Down Expand Up @@ -3695,11 +3718,14 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) {
if (ban->attached_aio_context == attached_aio_context &&
ban->detach_aio_context == detach_aio_context &&
ban->opaque == opaque)
ban->opaque == opaque &&
ban->deleted == false)
{
QLIST_REMOVE(ban, list);
g_free(ban);

if (bs->walking_aio_notifiers) {
ban->deleted = true;
} else {
bdrv_do_remove_aio_context_notifier(ban);
}
return;
}
}
Expand Down
2 changes: 2 additions & 0 deletions include/block/block_int.h
Expand Up @@ -361,6 +361,7 @@ typedef struct BdrvAioNotifier {
void (*detach_aio_context)(void *opaque);

void *opaque;
bool deleted;

QLIST_ENTRY(BdrvAioNotifier) list;
} BdrvAioNotifier;
Expand Down Expand Up @@ -427,6 +428,7 @@ struct BlockDriverState {
* BDS may register themselves in this list to be notified of changes
* regarding this BDS's context */
QLIST_HEAD(, BdrvAioNotifier) aio_notifiers;
bool walking_aio_notifiers; /* to make removal during iteration safe */

char filename[PATH_MAX];
char backing_file[PATH_MAX]; /* if non zero, the image is a diff of
Expand Down

0 comments on commit e8a095d

Please sign in to comment.