drbd: fix potential deadlock during bitmap (re-)allocation
authorLars Ellenberg <lars.ellenberg@linbit.com>
Thu, 20 Sep 2012 12:05:39 +0000 (14:05 +0200)
committerJens Axboe <axboe@kernel.dk>
Tue, 30 Oct 2012 07:39:18 +0000 (08:39 +0100)
The former comment arguing that GFP_KERNEL was good enough was wrong: it
did not take resize into account at all, and assumed the only path
leading here was the normal attach on a still secondary device, so no
deadlock would be possible.

Both resize on a Primary, or attach on a diskless Primary,
could potentially deadlock.

drbd_bm_resize() is called while IO to the respective device is
suspended, so we must use GFP_NOIO to avoid potential deadlock.

Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/block/drbd/drbd_bitmap.c

index d845664..dda4e38 100644 (file)
@@ -373,14 +373,16 @@ static struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want)
                return old_pages;
 
        /* Trying kmalloc first, falling back to vmalloc.
-        * GFP_KERNEL is ok, as this is done when a lower level disk is
-        * "attached" to the drbd.  Context is receiver thread or cqueue
-        * thread.  As we have no disk yet, we are not in the IO path,
-        * not even the IO path of the peer. */
+        * GFP_NOIO, as this is called while drbd IO is "suspended",
+        * and during resize or attach on diskless Primary,
+        * we must not block on IO to ourselves.
+        * Context is receiver thread or cqueue thread/dmsetup.  */
        bytes = sizeof(struct page *)*want;
-       new_pages = kzalloc(bytes, GFP_KERNEL);
+       new_pages = kzalloc(bytes, GFP_NOIO);
        if (!new_pages) {
-               new_pages = vzalloc(bytes);
+               new_pages = __vmalloc(bytes,
+                               GFP_NOIO | __GFP_HIGHMEM | __GFP_ZERO,
+                               PAGE_KERNEL);
                if (!new_pages)
                        return NULL;
                vmalloced = 1;
@@ -390,7 +392,7 @@ static struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want)
                for (i = 0; i < have; i++)
                        new_pages[i] = old_pages[i];
                for (; i < want; i++) {
-                       page = alloc_page(GFP_HIGHUSER);
+                       page = alloc_page(GFP_NOIO | __GFP_HIGHMEM);
                        if (!page) {
                                bm_free_pages(new_pages + have, i - have);
                                bm_vk_free(new_pages, vmalloced);