ext4_get_inode_loc_noio() looks the inode table block up with sb_find_get_block(). That's the atomic lookup. It serializes against try_to_free_buffers() with i_private_lock instead of the folio lock and gives up if the folio is being migrated. So it can report the block as not cached while it is. For ext4_fc_snapshot_inode() that only means falling back to a full commit. But ext4_sync_inode_metadata() is going to use ext4_get_inode_loc_noio() to find the inode block that fsync() has to write out and there a dirty block must not be skipped. ext4_fc_snapshot_inode() is the only caller and it can sleep. It runs under jbd2_journal_lock_updates() and allocates the snapshot with GFP_NOFS. So use sb_find_get_block_nonatomic() which takes the folio lock and waits for the migration instead. Suggested-by: Jan Kara Cc: stable@vger.kernel.org # v7.3 Reviewed-by: Jan Kara Signed-off-by: Christian Brauner (Amutable) --- fs/ext4/inode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index b4d581aa5ee1..4484c2383eaf 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5876,7 +5876,8 @@ int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc) /* * ext4_get_inode_loc_noio() is a best-effort variant of ext4_get_inode_loc(). * It looks up the inode table block in the buffer cache and returns -EAGAIN if - * the block is not present or not uptodate, without starting any I/O. + * the block is not present or not uptodate, without starting any I/O. It may + * sleep on the folio lock of the block. */ int ext4_get_inode_loc_noio(struct inode *inode, struct ext4_iloc *iloc) { @@ -5912,7 +5913,7 @@ int ext4_get_inode_loc_noio(struct inode *inode, struct ext4_iloc *iloc) } block += inode_offset / inodes_per_block; - bh = sb_find_get_block(sb, block); + bh = sb_find_get_block_nonatomic(sb, block); if (!bh) return -EAGAIN; if (!ext4_buffer_uptodate(bh)) { -- 2.53.0