[RFC PATCH v1 6/6] exfat: take s_lock in read mode for iomap mapping paths

From: Chi Zhiling

Date: Fri Aug 21 2026 - 06:47:48 EST


From: Chi Zhiling <chizhiling@xxxxxxxxxx>

The iomap mapping paths mainly access the FAT chain of the file itself.
Concurrent mappings of distinct inodes can therefore proceed in
parallel by taking s_lock in read mode instead of write mode. The
superblock-wide state shared between files is already protected by the
preceding patches: the allocation bitmap, used_clusters and clu_srch_ptr
by bitmap_lock, and the volume dirty flag / boot sector by atomic bit
ops with a single writer on the 0 -> 1 transition.

Concurrent access to the same inode stays serialized by the exclusive
inode_lock held in exfat_file_write_iter().

Writeback takes the read lock even though it does not hold inode_lock.
Folios under writeback are marked writeback, and truncate first flushes
and truncates the page cache, so truncate cannot run concurrently with
writeback. Writeback therefore only maps clusters that are still
committed or owned by the inode and cannot race with cluster freeing.

The lock ordering remains inode_lock -> s_lock -> bitmap_lock, so this
change does not introduce any new deadlock scenarios.

Signed-off-by: Chi Zhiling <chizhiling@xxxxxxxxxx>
---
fs/exfat/iomap.c | 4 ++--
fs/exfat/super.c | 4 ++++
2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c
index bc8bdfa9bb80..147e9da01a47 100644
--- a/fs/exfat/iomap.c
+++ b/fs/exfat/iomap.c
@@ -68,7 +68,7 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length
num_clusters = exfat_bytes_to_cluster_round_up(sbi,
offset + length) - exfat_bytes_to_cluster(sbi, offset);

- down_write(&sbi->s_lock);
+ down_read(&sbi->s_lock);
iomap->bdev = inode->i_sb->s_bdev;
iomap->offset = offset;

@@ -135,7 +135,7 @@ static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length

iomap->flags |= IOMAP_F_MERGED;
out:
- up_write(&sbi->s_lock);
+ up_read(&sbi->s_lock);
return err;
}

diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 72a35f4079b4..64ec4d2d1bf6 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -94,6 +94,8 @@ int exfat_set_volume_dirty(struct super_block *sb)
{
struct exfat_sb_info *sbi = EXFAT_SB(sb);

+ lockdep_assert_held(&sbi->s_lock);
+
if (test_and_set_bit(VOLUME_DIRTY_BIT, &sbi->vol_flags))
return 0;

@@ -104,6 +106,8 @@ int exfat_clear_volume_dirty(struct super_block *sb)
{
struct exfat_sb_info *sbi = EXFAT_SB(sb);

+ lockdep_assert_held_write(&sbi->s_lock);
+
if (!test_and_clear_bit(VOLUME_DIRTY_BIT, &sbi->vol_flags))
return 0;

--
2.53.0