__get_metapage() holds the folio lock when it finds a cached metapage flagged META_discard on a non-new lookup. It reports the corruption and calls discard_metapage(), which goes through release_metapage() and tries to take the same folio lock again: release_metapage+0x103/0xab0 fs/jfs/jfs_metapage.c:868 __get_metapage+0x9a0/0xde0 fs/jfs/jfs_metapage.c:751 jfs_readdir+0x1e01/0x3ae0 fs/jfs/jfs_dtree.c:3041 The task then sleeps forever in D state with i_rwsem held, which syzbot reports as a hung task. Split release_metapage() into a locked variant and use it here. The locked variant also unlocks and puts the folio, so return NULL directly instead of going through the unlock label. Reported-by: syzbot+5570e0eb2edddd308514@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=5570e0eb2edddd308514 Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Nguyen Ngoc Thang --- fs/jfs/jfs_metapage.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 41fe12e641ce..b3585df259cd 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -681,6 +681,8 @@ const struct address_space_operations jfs_metapage_aops = { .migrate_folio = metapage_migrate_folio, }; +static void release_metapage_locked(struct metapage *mp); + struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, unsigned int size, int absolute, unsigned long new) @@ -751,8 +753,9 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, if (!new) { jfs_error(inode->i_sb, "using a discarded metapage\n"); - discard_metapage(mp); - goto unlock; + clear_bit(META_dirty, &mp->flag); + release_metapage_locked(mp); + return NULL; } clear_bit(META_discard, &mp->flag); } @@ -863,12 +866,11 @@ void put_metapage(struct metapage *mp) release_metapage(mp); } -void release_metapage(struct metapage * mp) +/* Caller holds the folio lock; it is dropped along with the folio ref. */ +static void release_metapage_locked(struct metapage *mp) { struct folio *folio = mp->folio; - jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag); - folio_lock(folio); unlock_metapage(mp); assert(mp->count); @@ -896,6 +898,14 @@ void release_metapage(struct metapage * mp) folio_put(folio); } +void release_metapage(struct metapage *mp) +{ + jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag); + + folio_lock(mp->folio); + release_metapage_locked(mp); +} + void __invalidate_metapages(struct inode *ip, s64 addr, int len) { sector_t lblock; -- 2.43.0