dm bufio: fix deadlock with loop device [Linux 4.9.187]

This Linux kernel change "dm bufio: fix deadlock with loop device" is included in the Linux 4.9.187 release. This change is authored by Junxiao Bi <junxiao.bi [at] oracle.com> on Tue Jul 9 17:17:19 2019 -0700. The commit for this change in Linux stable tree is 0d78b27 (patch) which is from upstream commit bd293d0. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream bd293d0.

dm bufio: fix deadlock with loop device

commit bd293d071ffe65e645b4d8104f9d8fe15ea13862 upstream.

When thin-volume is built on loop device, if available memory is low,
the following deadlock can be triggered:

One process P1 allocates memory with GFP_FS flag, direct alloc fails,
memory reclaim invokes memory shrinker in dm_bufio, dm_bufio_shrink_scan()
runs, mutex dm_bufio_client->lock is acquired, then P1 waits for dm_buffer
IO to complete in __try_evict_buffer().

But this IO may never complete if issued to an underlying loop device
that forwards it using direct-IO, which allocates memory using
GFP_KERNEL (see: do_blockdev_direct_IO()).  If allocation fails, memory
reclaim will invoke memory shrinker in dm_bufio, dm_bufio_shrink_scan()
will be invoked, and since the mutex is already held by P1 the loop
thread will hang, and IO will never complete.  Resulting in ABBA
deadlock.

Cc: stable@vger.kernel.org
Signed-off-by: Junxiao Bi <junxiao.bi@oracle.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

There are 4 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 drivers/md/dm-bufio.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index c837def..673ce38 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -1585,9 +1585,7 @@ static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
    unsigned long freed;

    c = container_of(shrink, struct dm_bufio_client, shrinker);
-   if (sc->gfp_mask & __GFP_FS)
-       dm_bufio_lock(c);
-   else if (!dm_bufio_trylock(c))
+   if (!dm_bufio_trylock(c))
        return SHRINK_STOP;

    freed  = __scan(c, sc->nr_to_scan, sc->gfp_mask);

Leave a Reply

Your email address will not be published. Required fields are marked *