* [PATCH] dm-crypt: unlock allocation mutex before retry
@ 2026-08-09 5:18 Runyu Xiao
2026-08-09 13:20 ` Bart Van Assche
2026-08-10 9:46 ` [PATCH] dm-crypt: unlock allocation mutex before retry Mikulas Patocka
0 siblings, 2 replies; 6+ messages in thread
From: Runyu Xiao @ 2026-08-09 5:18 UTC (permalink / raw)
To: agk, snitzer, mpatocka
Cc: dm-devel, linux-kernel, stable, runyu.xiao, jianhao.xu
If the initial GFP_NOWAIT page allocation fails, crypt_alloc_buffer()
sets __GFP_DIRECT_RECLAIM and retries while holding bio_alloc_lock. If a
later page allocation fails, the retry edge bypasses the common unlock and
reacquires the same mutex. This can deadlock the dm-crypt I/O path and
leave I/O for the affected mapping stalled.
Release the mutex before retrying, but only when this attempt took the
direct-reclaim path; the first attempt did not acquire it.
This issue was identified by a static-analysis checker and manually
confirmed by following the retry control flow in v6.1.66 and current
mainline. A source-level control-flow check verified the vulnerable
ordering and the unlock-before-retry ordering after this change. A small
POSIX-thread model checked only the mutex re-acquisition condition; it does
not exercise dm-crypt.
Fixes: 7145c241a1bf ("dm crypt: avoid deadlock in mempools")
Cc: stable@vger.kernel.org
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
drivers/md/dm-crypt.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 608b617fb817..aabb9a5f85a7 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1674,6 +1674,8 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size)
if (!pages) {
crypt_free_buffer_pages(cc, clone);
bio_put(clone);
+ if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM))
+ mutex_unlock(&cc->bio_alloc_lock);
gfp_mask |= __GFP_DIRECT_RECLAIM;
order = 0;
goto retry;
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] dm-crypt: unlock allocation mutex before retry 2026-08-09 5:18 [PATCH] dm-crypt: unlock allocation mutex before retry Runyu Xiao @ 2026-08-09 13:20 ` Bart Van Assche 2026-08-11 3:14 ` [PATCH v2] dm-crypt: refactor buffer allocation retry handling Runyu Xiao 2026-08-10 9:46 ` [PATCH] dm-crypt: unlock allocation mutex before retry Mikulas Patocka 1 sibling, 1 reply; 6+ messages in thread From: Bart Van Assche @ 2026-08-09 13:20 UTC (permalink / raw) To: Runyu Xiao, agk, snitzer, mpatocka Cc: dm-devel, linux-kernel, stable, jianhao.xu On 8/8/26 10:18 PM, Runyu Xiao wrote: > diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c > index 608b617fb817..aabb9a5f85a7 100644 > --- a/drivers/md/dm-crypt.c > +++ b/drivers/md/dm-crypt.c > @@ -1674,6 +1674,8 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) > if (!pages) { > crypt_free_buffer_pages(cc, clone); > bio_put(clone); > + if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) > + mutex_unlock(&cc->bio_alloc_lock); > gfp_mask |= __GFP_DIRECT_RECLAIM; > order = 0; > goto retry; Please refactor crypt_alloc_buffer() such that context analysis can be enabled instead of making this function more complex. One way to do this is by moving the crypt_alloc_buffer() code that occurs between the mutex_lock() and mutex_unlock() calls into a helper function. See also https://docs.kernel.org/dev-tools/context-analysis.html. Thanks, Bart. ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] dm-crypt: refactor buffer allocation retry handling 2026-08-09 13:20 ` Bart Van Assche @ 2026-08-11 3:14 ` Runyu Xiao 2026-08-11 11:55 ` Mikulas Patocka 0 siblings, 1 reply; 6+ messages in thread From: Runyu Xiao @ 2026-08-11 3:14 UTC (permalink / raw) To: Alasdair Kergon, Mike Snitzer, Mikulas Patocka Cc: Bart Van Assche, dm-devel, linux-kernel, Runyu Xiao, Jianhao Xu crypt_alloc_buffer() conditionally acquires bio_alloc_lock around the allocation path and also contains the retry logic for the reclaim fallback. Move one allocation attempt into crypt_alloc_buffer_try() so that bio_alloc_lock is always acquired and released in crypt_alloc_buffer(), and the retry decision is made only after the mutex has been dropped. This keeps the retry path outside the locked region and makes the locking context easier to analyze. Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn> --- Changes in v2: - Move one allocation attempt into crypt_alloc_buffer_try(), as suggested by Bart Van Assche. - Keep bio_alloc_lock acquisition and release in crypt_alloc_buffer(), and make the retry decision only after releasing the lock. - Preserve the distinction between a page-pool retry and a final integrity allocation failure. v1: https://lore.kernel.org/r/20260809051839.3504080-1-runyu.xiao@seu.edu.cn diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 608b617fb817..7c68fad12960 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1627,18 +1627,16 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone); * In order to reduce allocation overhead, we try to allocate compound pages in * the first pass. If they are not available, we fall back to the mempool. */ -static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) +static struct bio *crypt_alloc_buffer_try(struct dm_crypt_io *io, + unsigned int size, gfp_t gfp_mask, + unsigned int order, bool *retry) { struct crypt_config *cc = io->cc; struct bio *clone; unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; - gfp_t gfp_mask = GFP_NOWAIT | __GFP_HIGHMEM; unsigned int remaining_size; - unsigned int order = MAX_PAGE_ORDER; -retry: - if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) - mutex_lock(&cc->bio_alloc_lock); + *retry = false; clone = bio_alloc_bioset(cc->dev->bdev, nr_iovecs, io->base_bio->bi_opf, GFP_NOIO, &cc->bs); @@ -1674,9 +1672,8 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) if (!pages) { crypt_free_buffer_pages(cc, clone); bio_put(clone); - gfp_mask |= __GFP_DIRECT_RECLAIM; - order = 0; - goto retry; + *retry = true; + return NULL; } have_pages: @@ -1692,12 +1689,34 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) clone = NULL; } - if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) - mutex_unlock(&cc->bio_alloc_lock); - return clone; } +static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) +{ + struct crypt_config *cc = io->cc; + struct bio *clone; + gfp_t gfp_mask = GFP_NOWAIT | __GFP_HIGHMEM; + unsigned int order = MAX_PAGE_ORDER; + bool retry; + + for (;;) { + if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) + mutex_lock(&cc->bio_alloc_lock); + + clone = crypt_alloc_buffer_try(io, size, gfp_mask, order, &retry); + + if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) + mutex_unlock(&cc->bio_alloc_lock); + + if (!retry) + return clone; + + gfp_mask |= __GFP_DIRECT_RECLAIM; + order = 0; + } +} + static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone) { struct folio_iter fi; -- 2.34.1 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2] dm-crypt: refactor buffer allocation retry handling 2026-08-11 3:14 ` [PATCH v2] dm-crypt: refactor buffer allocation retry handling Runyu Xiao @ 2026-08-11 11:55 ` Mikulas Patocka 2026-08-11 14:08 ` Runyu Xiao 0 siblings, 1 reply; 6+ messages in thread From: Mikulas Patocka @ 2026-08-11 11:55 UTC (permalink / raw) To: Runyu Xiao Cc: Alasdair Kergon, Mike Snitzer, Bart Van Assche, dm-devel, linux-kernel, Jianhao Xu On Tue, 11 Aug 2026, Runyu Xiao wrote: > crypt_alloc_buffer() conditionally acquires bio_alloc_lock around the > allocation path and also contains the retry logic for the reclaim > fallback. > > Move one allocation attempt into crypt_alloc_buffer_try() so that > bio_alloc_lock is always acquired and released in crypt_alloc_buffer(), > and the retry decision is made only after the mutex has been dropped. > > This keeps the retry path outside the locked region and makes the > locking context easier to analyze. > > Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn> Hi I wouldn't do this. The patch just moves code around and increases code size with no benefit. Mikulas > --- > Changes in v2: > - Move one allocation attempt into crypt_alloc_buffer_try(), as suggested > by Bart Van Assche. > - Keep bio_alloc_lock acquisition and release in crypt_alloc_buffer(), and > make the retry decision only after releasing the lock. > - Preserve the distinction between a page-pool retry and a final integrity > allocation failure. > > v1: https://lore.kernel.org/r/20260809051839.3504080-1-runyu.xiao@seu.edu.cn > > diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c > index 608b617fb817..7c68fad12960 100644 > --- a/drivers/md/dm-crypt.c > +++ b/drivers/md/dm-crypt.c > @@ -1627,18 +1627,16 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone); > * In order to reduce allocation overhead, we try to allocate compound pages in > * the first pass. If they are not available, we fall back to the mempool. > */ > -static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) > +static struct bio *crypt_alloc_buffer_try(struct dm_crypt_io *io, > + unsigned int size, gfp_t gfp_mask, > + unsigned int order, bool *retry) > { > struct crypt_config *cc = io->cc; > struct bio *clone; > unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; > - gfp_t gfp_mask = GFP_NOWAIT | __GFP_HIGHMEM; > unsigned int remaining_size; > - unsigned int order = MAX_PAGE_ORDER; > > -retry: > - if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) > - mutex_lock(&cc->bio_alloc_lock); > + *retry = false; > > clone = bio_alloc_bioset(cc->dev->bdev, nr_iovecs, io->base_bio->bi_opf, > GFP_NOIO, &cc->bs); > @@ -1674,9 +1672,8 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) > if (!pages) { > crypt_free_buffer_pages(cc, clone); > bio_put(clone); > - gfp_mask |= __GFP_DIRECT_RECLAIM; > - order = 0; > - goto retry; > + *retry = true; > + return NULL; > } > > have_pages: > @@ -1692,12 +1689,34 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) > clone = NULL; > } > > - if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) > - mutex_unlock(&cc->bio_alloc_lock); > - > return clone; > } > > +static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) > +{ > + struct crypt_config *cc = io->cc; > + struct bio *clone; > + gfp_t gfp_mask = GFP_NOWAIT | __GFP_HIGHMEM; > + unsigned int order = MAX_PAGE_ORDER; > + bool retry; > + > + for (;;) { > + if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) > + mutex_lock(&cc->bio_alloc_lock); > + > + clone = crypt_alloc_buffer_try(io, size, gfp_mask, order, &retry); > + > + if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) > + mutex_unlock(&cc->bio_alloc_lock); > + > + if (!retry) > + return clone; > + > + gfp_mask |= __GFP_DIRECT_RECLAIM; > + order = 0; > + } > +} > + > static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone) > { > struct folio_iter fi; > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re:Re: [PATCH v2] dm-crypt: refactor buffer allocation retry handling 2026-08-11 11:55 ` Mikulas Patocka @ 2026-08-11 14:08 ` Runyu Xiao 0 siblings, 0 replies; 6+ messages in thread From: Runyu Xiao @ 2026-08-11 14:08 UTC (permalink / raw) To: Mikulas Patocka Cc: Alasdair Kergon, Mike Snitzer, Bart Van Assche, dm-devel, linux-kernel, Jianhao Xu Hi Mikulas, Thanks for reviewing this. I initially interpreted the goto retry path as being reachable after bio_alloc_lock was acquired. After rechecking mempool_alloc() in v6.1.66, I found that it returns NULL only without __GFP_DIRECT_RECLAIM. With that flag set, it waits and retries when the pool is empty. The first allocation failure happens before bio_alloc_lock is acquired, while the later allocation is performed with the mutex held and does not take the NULL retry path in normal process context. Therefore, I cannot justify the suspected double acquisition, and the refactoring provides no functional benefit. I will drop this patch. Regards, Runyu ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] dm-crypt: unlock allocation mutex before retry 2026-08-09 5:18 [PATCH] dm-crypt: unlock allocation mutex before retry Runyu Xiao 2026-08-09 13:20 ` Bart Van Assche @ 2026-08-10 9:46 ` Mikulas Patocka 1 sibling, 0 replies; 6+ messages in thread From: Mikulas Patocka @ 2026-08-10 9:46 UTC (permalink / raw) To: Runyu Xiao; +Cc: agk, snitzer, dm-devel, linux-kernel, stable, jianhao.xu On Sun, 9 Aug 2026, Runyu Xiao wrote: > If the initial GFP_NOWAIT page allocation fails, crypt_alloc_buffer() > sets __GFP_DIRECT_RECLAIM and retries while holding bio_alloc_lock. If a > later page allocation fails, the retry edge bypasses the common unlock and > reacquires the same mutex. This can deadlock the dm-crypt I/O path and > leave I/O for the affected mapping stalled. Hi When __GFP_DIRECT_RECLAIM is set, mempool_alloc can't return NULL. So, the bug can't happen. Mikulas > Release the mutex before retrying, but only when this attempt took the > direct-reclaim path; the first attempt did not acquire it. > > This issue was identified by a static-analysis checker and manually > confirmed by following the retry control flow in v6.1.66 and current > mainline. A source-level control-flow check verified the vulnerable > ordering and the unlock-before-retry ordering after this change. A small > POSIX-thread model checked only the mutex re-acquisition condition; it does > not exercise dm-crypt. > > Fixes: 7145c241a1bf ("dm crypt: avoid deadlock in mempools") > Cc: stable@vger.kernel.org > Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn> > --- > drivers/md/dm-crypt.c | 2 ++ > 1 file changed, 2 insertions(+) > > diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c > index 608b617fb817..aabb9a5f85a7 100644 > --- a/drivers/md/dm-crypt.c > +++ b/drivers/md/dm-crypt.c > @@ -1674,6 +1674,8 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) > if (!pages) { > crypt_free_buffer_pages(cc, clone); > bio_put(clone); > + if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) > + mutex_unlock(&cc->bio_alloc_lock); > gfp_mask |= __GFP_DIRECT_RECLAIM; > order = 0; > goto retry; > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-11 15:24 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-08-09 5:18 [PATCH] dm-crypt: unlock allocation mutex before retry Runyu Xiao 2026-08-09 13:20 ` Bart Van Assche 2026-08-11 3:14 ` [PATCH v2] dm-crypt: refactor buffer allocation retry handling Runyu Xiao 2026-08-11 11:55 ` Mikulas Patocka 2026-08-11 14:08 ` Runyu Xiao 2026-08-10 9:46 ` [PATCH] dm-crypt: unlock allocation mutex before retry Mikulas Patocka
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®