Re: [PATCH] jbd2: fix deadlock in jbd2_journal_cancel_revoke()
From: Zhang Yi
Date: Wed Apr 08 2026 - 22:45:53 EST
Please ignore this patch; this solution does not correct.
Best Regards.
Yi.
On 4/9/2026 9:07 AM, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@xxxxxxxxxx>
>
> Commit f76d4c28a46a ("fs/jbd2: use sleeping version of
> __find_get_block()") changed jbd2_journal_cancel_revoke() to use
> __find_get_block_nonatomic() which holds the folio lock instead of
> i_private_lock. This breaks the lock ordering (folio -> buffer) and
> causes an ABBA deadlock when the filesystem blocksize < pagesize:
>
> T1 T2
> ext4_mkdir()
> ext4_init_new_dir()
> ext4_append()
> ext4_getblk()
> lock_buffer() <- A
> sync_blockdev()
> blkdev_writepages()
> writeback_iter()
> writeback_get_folio()
> folio_lock() <- B
> ext4_journal_get_create_access()
> jbd2_journal_cancel_revoke()
> __find_get_block_nonatomic()
> folio_lock() <- B
> block_write_full_folio()
> lock_buffer() <- A
>
> This can occasionally cause generic/013 to hang.
>
> Fix by only calling __find_get_block_nonatomic() when the passed
> buffer_head doesn't belong to the bdev (i.e., !bh->b_bdev), which is the
> only case that we need to look up its bdev alias. Otherwise, the lookup
> is redundant since the found buffer_head is equal to the one we passed
> in.
>
> Fixes: f76d4c28a46a ("fs/jbd2: use sleeping version of __find_get_block()")
> Signed-off-by: Zhang Yi <yi.zhang@xxxxxxxxxx>
> ---
> fs/jbd2/revoke.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c
> index 9016ddb82447..8b52d40c27c9 100644
> --- a/fs/jbd2/revoke.c
> +++ b/fs/jbd2/revoke.c
> @@ -464,13 +464,14 @@ void jbd2_journal_cancel_revoke(handle_t *handle, struct journal_head *jh)
> * buffer_head? If so, we'd better make sure we clear the
> * revoked status on any hashed alias too, otherwise the revoke
> * state machine will get very upset later on. */
> - if (need_cancel) {
> + if (need_cancel && !bh->b_bdev) {
> struct buffer_head *bh2;
> +
> bh2 = __find_get_block_nonatomic(bh->b_bdev, bh->b_blocknr,
> bh->b_size);
> if (bh2) {
> - if (bh2 != bh)
> - clear_buffer_revoked(bh2);
> + WARN_ON_ONCE(bh2 == bh);
> + clear_buffer_revoked(bh2);
> __brelse(bh2);
> }
> }