Commit 151ff932 authored by Zhang Yi's avatar Zhang Yi Committed by Theodore Ts'o
Browse files

ext4: prevent stale extent cache entries caused by concurrent fiemap



The ext4_fiemap() currently invokes ext4_ext_precache() and
iomap_fiemap() to preload the extent cache and query mapping information
without holding the inode's i_rwsem. This can result in stale extent
cache entries when competing with operations such as
ext4_collapse_range() which calls ext4_ext_remove_space() or
ext4_ext_shift_extents().

The problem arises when ext4_ext_remove_space() temporarily releases
i_data_sem due to insufficient journal credits. During this interval, a
concurrent ext4_fiemap() may cache extent entries that are about to be
deleted. As a result, these cached entries become stale and inconsistent
with the actual extents.

Loading the extents cache without holding the inode's i_rwsem or the
mapping's invalidate_lock is not permitted besides during the writeback.
Fix this by holding the i_rwsem in ext4_fiemap().

Signed-off-by: default avatarZhang Yi <yi.zhang@huawei.com>
Link: https://patch.msgid.link/20250423085257.122685-5-yi.zhang@huaweicloud.com


Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 402e38e6
Loading
Loading
Loading
Loading
+11 −6
Original line number Diff line number Diff line
@@ -4963,10 +4963,11 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
{
	int error = 0;

	inode_lock_shared(inode);
	if (fieinfo->fi_flags & FIEMAP_FLAG_CACHE) {
		error = ext4_ext_precache(inode);
		if (error)
			return error;
			goto unlock;
		fieinfo->fi_flags &= ~FIEMAP_FLAG_CACHE;
	}

@@ -4977,15 +4978,19 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
	 */
	error = ext4_fiemap_check_ranges(inode, start, &len);
	if (error)
		return error;
		goto unlock;

	if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
		fieinfo->fi_flags &= ~FIEMAP_FLAG_XATTR;
		return iomap_fiemap(inode, fieinfo, start, len,
		error = iomap_fiemap(inode, fieinfo, start, len,
				     &ext4_iomap_xattr_ops);
	} else {
		error = iomap_fiemap(inode, fieinfo, start, len,
				     &ext4_iomap_report_ops);
	}

	return iomap_fiemap(inode, fieinfo, start, len, &ext4_iomap_report_ops);
unlock:
	inode_unlock_shared(inode);
	return error;
}

int ext4_get_es_cache(struct inode *inode, struct fiemap_extent_info *fieinfo,