Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

jbd2: fix deadlock in jbd2_journal_cancel_revoke()

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, 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@huawei.com>
Link: https://patch.msgid.link/20260409114204.917154-1-yi.zhang@huaweicloud.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@kernel.org

authored by

Zhang Yi and committed by
Theodore Ts'o
981fcc56 77d05951

+5 -3
+5 -3
fs/jbd2/revoke.c
··· 428 428 journal_t *journal = handle->h_transaction->t_journal; 429 429 int need_cancel; 430 430 struct buffer_head *bh = jh2bh(jh); 431 + struct address_space *bh_mapping = bh->b_folio->mapping; 431 432 432 433 jbd2_debug(4, "journal_head %p, cancelling revoke\n", jh); 433 434 ··· 465 464 * buffer_head? If so, we'd better make sure we clear the 466 465 * revoked status on any hashed alias too, otherwise the revoke 467 466 * state machine will get very upset later on. */ 468 - if (need_cancel) { 467 + if (need_cancel && !sb_is_blkdev_sb(bh_mapping->host->i_sb)) { 469 468 struct buffer_head *bh2; 469 + 470 470 bh2 = __find_get_block_nonatomic(bh->b_bdev, bh->b_blocknr, 471 471 bh->b_size); 472 472 if (bh2) { 473 - if (bh2 != bh) 474 - clear_buffer_revoked(bh2); 473 + WARN_ON_ONCE(bh2 == bh); 474 + clear_buffer_revoked(bh2); 475 475 __brelse(bh2); 476 476 } 477 477 }