qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Stefan Hajnoczi <stefanha@redhat.com>
To: qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>,
	jjherne@linux.vnet.ibm.com, Fam Zheng <famz@redhat.com>,
	Paolo Bonzini <pbonzini@redhat.com>, Jeff Cody <jcody@redhat.com>,
	mreitz@redhat.com, Stefan Hajnoczi <stefanha@redhat.com>
Subject: [Qemu-devel] [PATCH v5 5/8] block: use safe iteration over AioContext notifiers
Date: Thu, 16 Jun 2016 17:56:26 +0100	[thread overview]
Message-ID: <1466096189-6477-6-git-send-email-stefanha@redhat.com> (raw)
In-Reply-To: <1466096189-6477-1-git-send-email-stefanha@redhat.com>

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>
---
 block.c                   | 46 ++++++++++++++++++++++++++++++++++++----------
 include/block/block_int.h |  2 ++
 2 files changed, 38 insertions(+), 10 deletions(-)

diff --git a/block.c b/block.c
index f54bc25..691b201 100644
--- a/block.c
+++ b/block.c
@@ -3601,18 +3601,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);
@@ -3627,7 +3643,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) {
@@ -3643,9 +3659,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)
@@ -3687,11 +3710,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;
         }
     }
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 8a4963c..647e97b 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -359,6 +359,7 @@ typedef struct BdrvAioNotifier {
     void (*detach_aio_context)(void *opaque);
 
     void *opaque;
+    bool deleted;
 
     QLIST_ENTRY(BdrvAioNotifier) list;
 } BdrvAioNotifier;
@@ -425,6 +426,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
-- 
2.5.5

  parent reply	other threads:[~2016-06-16 16:56 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-06-16 16:56 [Qemu-devel] [PATCH v5 0/8] blockjob: AioContext change support for mirror and backup Stefan Hajnoczi
2016-06-16 16:56 ` [Qemu-devel] [PATCH v5 1/8] blockjob: move iostatus reset out of block_job_enter() Stefan Hajnoczi
2016-06-16 16:56 ` [Qemu-devel] [PATCH v5 2/8] blockjob: rename block_job_is_paused() Stefan Hajnoczi
2016-06-16 16:56 ` [Qemu-devel] [PATCH v5 3/8] blockjob: add pause points Stefan Hajnoczi
2016-06-16 16:56 ` [Qemu-devel] [PATCH v5 4/8] blockjob: add block_job_get_aio_context() Stefan Hajnoczi
2016-06-16 16:56 ` Stefan Hajnoczi [this message]
2016-06-16 16:56 ` [Qemu-devel] [PATCH v5 6/8] blockjob: add AioContext attached callback Stefan Hajnoczi
2016-06-16 16:56 ` [Qemu-devel] [PATCH v5 7/8] mirror: follow AioContext change gracefully Stefan Hajnoczi
2016-06-16 16:56 ` [Qemu-devel] [PATCH v5 8/8] backup: " Stefan Hajnoczi
2016-06-16 17:06 ` [Qemu-devel] [PATCH v5 0/8] blockjob: AioContext change support for mirror and backup Paolo Bonzini
2016-06-17 12:11 ` Stefan Hajnoczi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1466096189-6477-6-git-send-email-stefanha@redhat.com \
    --to=stefanha@redhat.com \
    --cc=famz@redhat.com \
    --cc=jcody@redhat.com \
    --cc=jjherne@linux.vnet.ibm.com \
    --cc=kwolf@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).