[RFC PATCH v1 5/6] exfat: convert s_lock mutex to rw_semaphore using write lock

From: Chi Zhiling

Date: Fri Aug 21 2026 - 06:45:42 EST


From: Chi Zhiling <chizhiling@xxxxxxxxxx>

Convert sbi->s_lock from a mutex to an rw_semaphore and acquire it in
write mode at every existing use site. This is a mechanical conversion
with no behavioral change, preparing the lock for read-mode access in
the iomap mapping paths so that concurrent mappings of distinct inodes
can proceed in parallel.

Lock ordering stays consistent (inode_lock -> s_lock -> bitmap_lock),
so no deadlock is introduced.

Signed-off-by: Chi Zhiling <chizhiling@xxxxxxxxxx>
---
fs/exfat/dir.c | 14 +++++++-------
fs/exfat/exfat_fs.h | 2 +-
fs/exfat/file.c | 4 ++--
fs/exfat/inode.c | 10 +++++-----
fs/exfat/iomap.c | 4 ++--
fs/exfat/namei.c | 26 +++++++++++++-------------
fs/exfat/super.c | 10 +++++-----
7 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
index fe73b1380c5d..ea454574260e 100644
--- a/fs/exfat/dir.c
+++ b/fs/exfat/dir.c
@@ -223,7 +223,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
if (err)
goto out;
get_new:
- mutex_lock(&EXFAT_SB(sb)->s_lock);
+ down_write(&EXFAT_SB(sb)->s_lock);

if (ei->flags == ALLOC_NO_FAT_CHAIN && cpos >= i_size_read(inode))
goto end_of_dir;
@@ -255,7 +255,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
inum = iunique(sb, EXFAT_ROOT_INO);
}

- mutex_unlock(&EXFAT_SB(sb)->s_lock);
+ up_write(&EXFAT_SB(sb)->s_lock);
if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum,
(de.attr & EXFAT_ATTR_SUBDIR) ? DT_DIR : DT_REG))
goto out;
@@ -266,7 +266,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
if (!cpos && fake_offset)
cpos = ITER_POS_FILLED_DOTS;
ctx->pos = cpos;
- mutex_unlock(&EXFAT_SB(sb)->s_lock);
+ up_write(&EXFAT_SB(sb)->s_lock);
out:
/*
* To improve performance, free namebuf after unlock sb_lock.
@@ -1294,7 +1294,7 @@ int exfat_read_volume_label(struct super_block *sb, struct exfat_uni_name *label
struct exfat_entry_set_cache es;
struct exfat_dentry *ep;

- mutex_lock(&sbi->s_lock);
+ down_write(&sbi->s_lock);

memset(label_out, 0, sizeof(*label_out));
ret = exfat_get_volume_label_dentry(sb, &es);
@@ -1322,7 +1322,7 @@ int exfat_read_volume_label(struct super_block *sb, struct exfat_uni_name *label

exfat_put_dentry_set(&es, false);
unlock:
- mutex_unlock(&sbi->s_lock);
+ up_write(&sbi->s_lock);
return ret;
}

@@ -1339,7 +1339,7 @@ int exfat_write_volume_label(struct super_block *sb,
if (label->name_len > EXFAT_VOLUME_LABEL_LEN)
return -EINVAL;

- mutex_lock(&sbi->s_lock);
+ down_write(&sbi->s_lock);

ret = exfat_get_volume_label_dentry(sb, &es);
if (ret == -ENOENT) {
@@ -1376,6 +1376,6 @@ int exfat_write_volume_label(struct super_block *sb,
ret = exfat_put_dentry_set(&es, IS_DIRSYNC(root_inode));

unlock:
- mutex_unlock(&sbi->s_lock);
+ up_write(&sbi->s_lock);
return ret;
}
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index f1505c013248..16ce508e0946 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -246,7 +246,7 @@ struct exfat_sb_info {

unsigned long s_exfat_flags; /* Exfat superblock flags */

- struct mutex s_lock; /* superblock lock */
+ struct rw_semaphore s_lock; /* superblock lock */
struct mutex bitmap_lock; /* bitmap lock */
struct exfat_mount_options options;
struct nls_table *nls_io; /* Charset used for input and display */
diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index 29c5db12288e..8abef3d2294a 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -297,7 +297,7 @@ static void exfat_truncate(struct inode *inode)
struct exfat_inode_info *ei = EXFAT_I(inode);
int err;

- mutex_lock(&sbi->s_lock);
+ down_write(&sbi->s_lock);
if (ei->start_clu == 0) {
/*
* Empty start_clu != ~0 (not allocated)
@@ -312,7 +312,7 @@ static void exfat_truncate(struct inode *inode)

inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
write_size:
- mutex_unlock(&sbi->s_lock);
+ up_write(&sbi->s_lock);
}

int exfat_getattr(struct mnt_idmap *idmap, const struct path *path,
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index d041f893b1c3..a89826e08e1c 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -116,16 +116,16 @@ int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
return -EIO;

- mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
+ down_write(&EXFAT_SB(inode->i_sb)->s_lock);
ret = __exfat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
- mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
+ up_write(&EXFAT_SB(inode->i_sb)->s_lock);

return ret;
}

void exfat_sync_inode(struct inode *inode)
{
- lockdep_assert_held(&EXFAT_SB(inode->i_sb)->s_lock);
+ lockdep_assert_held_write(&EXFAT_SB(inode->i_sb)->s_lock);
__exfat_write_inode(inode, 1);
}

@@ -443,9 +443,9 @@ void exfat_evict_inode(struct inode *inode)

if (!inode->i_nlink) {
i_size_write(inode, 0);
- mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
+ down_write(&EXFAT_SB(inode->i_sb)->s_lock);
__exfat_truncate(inode);
- mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
+ up_write(&EXFAT_SB(inode->i_sb)->s_lock);
}

clear_inode(inode);
diff --git a/fs/exfat/iomap.c b/fs/exfat/iomap.c
index 0c805bf6676a..bc8bdfa9bb80 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);

- mutex_lock(&sbi->s_lock);
+ down_write(&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:
- mutex_unlock(&sbi->s_lock);
+ up_write(&sbi->s_lock);
return err;
}

diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
index f26f987a34cf..0c28040e97d6 100644
--- a/fs/exfat/namei.c
+++ b/fs/exfat/namei.c
@@ -565,7 +565,7 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,
if (unlikely(exfat_forced_shutdown(sb)))
return -EIO;

- mutex_lock(&EXFAT_SB(sb)->s_lock);
+ down_write(&EXFAT_SB(sb)->s_lock);
exfat_set_volume_dirty(sb);
err = exfat_add_entry(dir, dentry->d_name.name, TYPE_FILE, &info);
if (err)
@@ -592,7 +592,7 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,

d_instantiate(dentry, inode);
unlock:
- mutex_unlock(&EXFAT_SB(sb)->s_lock);
+ up_write(&EXFAT_SB(sb)->s_lock);
return err;
}

@@ -732,7 +732,7 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
int err;
loff_t i_pos;

- mutex_lock(&EXFAT_SB(sb)->s_lock);
+ down_write(&EXFAT_SB(sb)->s_lock);
err = exfat_find(dir, &dentry->d_name, &info);
if (err) {
if (unlikely(err != -ENOENT))
@@ -761,12 +761,12 @@ static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry,
*/
d_move(alias, dentry);
iput(inode);
- mutex_unlock(&EXFAT_SB(sb)->s_lock);
+ up_write(&EXFAT_SB(sb)->s_lock);
return alias;
}
dput(alias);
out:
- mutex_unlock(&EXFAT_SB(sb)->s_lock);
+ up_write(&EXFAT_SB(sb)->s_lock);
if (!inode)
exfat_d_version_set(dentry, inode_query_iversion(dir));

@@ -785,7 +785,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
if (unlikely(exfat_forced_shutdown(sb)))
return -EIO;

- mutex_lock(&EXFAT_SB(sb)->s_lock);
+ down_write(&EXFAT_SB(sb)->s_lock);
if (ei->dir.dir == DIR_DELETED) {
exfat_err(sb, "abnormal access to deleted dentry");
err = -ENOENT;
@@ -821,7 +821,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
exfat_unhash_inode(inode);
exfat_d_version_set(dentry, inode_query_iversion(dir));
unlock:
- mutex_unlock(&EXFAT_SB(sb)->s_lock);
+ up_write(&EXFAT_SB(sb)->s_lock);
return err;
}

@@ -838,7 +838,7 @@ static struct dentry *exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
if (unlikely(exfat_forced_shutdown(sb)))
return ERR_PTR(-EIO);

- mutex_lock(&EXFAT_SB(sb)->s_lock);
+ down_write(&EXFAT_SB(sb)->s_lock);
exfat_set_volume_dirty(sb);
err = exfat_add_entry(dir, dentry->d_name.name, TYPE_DIR, &info);
if (err)
@@ -867,7 +867,7 @@ static struct dentry *exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
d_instantiate(dentry, inode);

unlock:
- mutex_unlock(&EXFAT_SB(sb)->s_lock);
+ up_write(&EXFAT_SB(sb)->s_lock);
return err ? ERR_PTR(err) : NULL;
}

@@ -929,7 +929,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
if (unlikely(exfat_forced_shutdown(sb)))
return -EIO;

- mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
+ down_write(&EXFAT_SB(inode->i_sb)->s_lock);

if (ei->dir.dir == DIR_DELETED) {
exfat_err(sb, "abnormal access to deleted dentry");
@@ -979,7 +979,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
exfat_unhash_inode(inode);
exfat_d_version_set(dentry, inode_query_iversion(dir));
unlock:
- mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
+ up_write(&EXFAT_SB(inode->i_sb)->s_lock);
return err;
}

@@ -1282,7 +1282,7 @@ static int exfat_rename(struct mnt_idmap *idmap,
if (flags & ~RENAME_NOREPLACE)
return -EINVAL;

- mutex_lock(&EXFAT_SB(sb)->s_lock);
+ down_write(&EXFAT_SB(sb)->s_lock);
old_inode = old_dentry->d_inode;
new_inode = new_dentry->d_inode;

@@ -1334,7 +1334,7 @@ static int exfat_rename(struct mnt_idmap *idmap,
}

unlock:
- mutex_unlock(&EXFAT_SB(sb)->s_lock);
+ up_write(&EXFAT_SB(sb)->s_lock);
return err;
}

diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index 491273d8eeb6..72a35f4079b4 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -45,11 +45,11 @@ static void exfat_put_super(struct super_block *sb)
{
struct exfat_sb_info *sbi = EXFAT_SB(sb);

- mutex_lock(&sbi->s_lock);
+ down_write(&sbi->s_lock);
exfat_clear_volume_dirty(sb);
exfat_free_bitmap(sbi);
brelse(sbi->boot_bh);
- mutex_unlock(&sbi->s_lock);
+ up_write(&sbi->s_lock);
}

static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
@@ -771,9 +771,9 @@ static int exfat_reconfigure(struct fs_context *fc)
fc->sb_flags |= SB_NODIRATIME;

sync_filesystem(sb);
- mutex_lock(&sbi->s_lock);
+ down_write(&sbi->s_lock);
exfat_clear_volume_dirty(sb);
- mutex_unlock(&sbi->s_lock);
+ up_write(&sbi->s_lock);

if (new_opts->allow_utime == (unsigned short)-1)
new_opts->allow_utime = ~new_opts->fs_dmask & 0022;
@@ -820,7 +820,7 @@ static int exfat_init_fs_context(struct fs_context *fc)
if (!sbi)
return -ENOMEM;

- mutex_init(&sbi->s_lock);
+ init_rwsem(&sbi->s_lock);
mutex_init(&sbi->bitmap_lock);
ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
--
2.53.0