[RFC PATCH v1 6/6] exfat: take s_lock in read mode for iomap mapping paths
Chi Zhiling <[email protected]>
| Newsgroups | dev.linux.lists.exfat,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Chi Zhiling <[email protected]> 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 <[email protected]> --- 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