6.1-stable review patch. If anyone has any objections, please let me know. ------------------ From: Theodore Ts'o <tytso@xxxxxxx> [ Upstream commit 62913ae96de747091c4dacd06d158e7729c1a76d ] The generic bmap() function exported by the VFS takes locks and does checks that are not necessary for the journal inode. So allow the file system to set a journal-optimized bmap function in journal->j_bmap. Reported-by: syzbot+9543479984ae9e576000@xxxxxxxxxxxxxxxxxxxxxxxxx Link: https://syzkaller.appspot.com/bug?id=e4aaa78795e490421c79f76ec3679006c8ff4cf0 Signed-off-by: Theodore Ts'o <tytso@xxxxxxx> Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx> --- fs/ext4/super.c | 23 +++++++++++++++++++++++ fs/jbd2/journal.c | 9 ++++++--- include/linux/jbd2.h | 8 ++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 274542d869d0c..3db39758486e9 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -5752,6 +5752,28 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb, return journal_inode; } +static int ext4_journal_bmap(journal_t *journal, sector_t *block) +{ + struct ext4_map_blocks map; + int ret; + + if (journal->j_inode == NULL) + return 0; + + map.m_lblk = *block; + map.m_len = 1; + ret = ext4_map_blocks(NULL, journal->j_inode, &map, 0); + if (ret <= 0) { + ext4_msg(journal->j_inode->i_sb, KERN_CRIT, + "journal bmap failed: block %llu ret %d\n", + *block, ret); + jbd2_journal_abort(journal, ret ? ret : -EIO); + return ret; + } + *block = map.m_pblk; + return 0; +} + static journal_t *ext4_get_journal(struct super_block *sb, unsigned int journal_inum) { @@ -5772,6 +5794,7 @@ static journal_t *ext4_get_journal(struct super_block *sb, return NULL; } journal->j_private = sb; + journal->j_bmap = ext4_journal_bmap; ext4_init_journal_params(sb, journal); return journal; } diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index c8d59f7c47453..d3d3ea439d29b 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -971,10 +971,13 @@ int jbd2_journal_bmap(journal_t *journal, unsigned long blocknr, { int err = 0; unsigned long long ret; - sector_t block = 0; + sector_t block = blocknr; - if (journal->j_inode) { - block = blocknr; + if (journal->j_bmap) { + err = journal->j_bmap(journal, &block); + if (err == 0) + *retp = block; + } else if (journal->j_inode) { ret = bmap(journal->j_inode, &block); if (ret || !block) { diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index e301d323108d1..5bf7ada754d79 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1302,6 +1302,14 @@ struct journal_s struct buffer_head *bh, enum passtype pass, int off, tid_t expected_commit_id); + + /** + * @j_bmap: + * + * Bmap function that should be used instead of the generic + * VFS bmap function. + */ + int (*j_bmap)(struct journal_s *journal, sector_t *block); }; #define jbd2_might_wait_for_commit(j) \ -- 2.43.0