All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chi Zhiling <chizhiling@163.com>
To: exfat@lists.linux.dev, linux-kernel@vger.kernel.org
Cc: Namjae Jeon <linkinjeon@kernel.org>,
	Sungjong Seo <sj1557.seo@samsung.com>,
	Yuezhang Mo <yuezhang.mo@sony.com>,
	Chi Zhiling <chizhiling@kylinos.cn>
Subject: [RFC PATCH v1 3/6] exfat: use atomic bit ops for volume dirty flag
Date: Fri, 21 Aug 2026 18:05:28 +0800	[thread overview]
Message-ID: <20260821100531.998196-4-chizhiling@163.com> (raw)
In-Reply-To: <20260821100531.998196-1-chizhiling@163.com>

From: Chi Zhiling <chizhiling@kylinos.cn>

After converting s_lock to a reader-writer lock, multiple operations can
execute concurrently under the read lock. As a result, multiple threads may
concurrently update sbi->vol_flags, causing a race in the existing
read-modify-write sequence.

Convert sbi->vol_flags to an unsigned long bitmap and use
test_and_set_bit() / test_and_clear_bit() to atomically update the volume
dirty bit. Only the thread performing the 0 -> 1 transition updates the
boot sector, avoiding concurrent unsynchronized modifications of vol_flags
and the boot-sector buffer.

Remove vol_flags_persistent, since the bitmap can directly retain the
MEDIA_FAILURE state while preserving the complete 16-bit on-disk volume
flags field when writing it back.

Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn>
---
 fs/exfat/exfat_fs.h  |  3 +--
 fs/exfat/exfat_raw.h |  4 ++--
 fs/exfat/super.c     | 28 ++++++++++++----------------
 3 files changed, 15 insertions(+), 20 deletions(-)

diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index a9131fe03302..f1505c013248 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -232,8 +232,7 @@ struct exfat_sb_info {
 	unsigned int num_FAT_sectors; /* num of FAT sectors */
 	unsigned int root_dir; /* root dir cluster */
 	unsigned int dentries_per_clu; /* num of dentries per cluster */
-	unsigned int vol_flags; /* volume flags */
-	unsigned int vol_flags_persistent; /* volume flags to retain */
+	unsigned long vol_flags; /* volume flags (bitmap) */
 	struct buffer_head *boot_bh; /* buffer_head of BOOT sector */
 
 	unsigned int map_clu; /* allocation bitmap start cluster */
diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
index ec70cd35bba0..222fee5c2adf 100644
--- a/fs/exfat/exfat_raw.h
+++ b/fs/exfat/exfat_raw.h
@@ -14,8 +14,8 @@
 
 #define EXFAT_MAX_FILE_LEN	255
 
-#define VOLUME_DIRTY		0x0002
-#define MEDIA_FAILURE		0x0004
+#define VOLUME_DIRTY_BIT	1
+#define MEDIA_FAILURE_BIT	2
 
 #define EXFAT_EOF_CLUSTER	0xFFFFFFFFu
 #define EXFAT_BAD_CLUSTER	0xFFFFFFF7u
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
index a9ea36ba2693..491273d8eeb6 100644
--- a/fs/exfat/super.c
+++ b/fs/exfat/super.c
@@ -69,27 +69,18 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
 	return 0;
 }
 
-static int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags)
+static int exfat_sync_vol_flags(struct super_block *sb)
 {
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
 	struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
 
-	/* retain persistent-flags */
-	new_flags |= sbi->vol_flags_persistent;
-
-	/* flags are not changed */
-	if (sbi->vol_flags == new_flags)
-		return 0;
-
-	sbi->vol_flags = new_flags;
-
 	/* skip updating volume dirty flag,
 	 * if this volume has been mounted with read-only
 	 */
 	if (sb_rdonly(sb))
 		return 0;
 
-	p_boot->vol_flags = cpu_to_le16(new_flags);
+	p_boot->vol_flags = cpu_to_le16((unsigned short)READ_ONCE(sbi->vol_flags));
 
 	set_buffer_uptodate(sbi->boot_bh);
 	mark_buffer_dirty(sbi->boot_bh);
@@ -103,14 +94,20 @@ int exfat_set_volume_dirty(struct super_block *sb)
 {
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
 
-	return exfat_set_vol_flags(sb, sbi->vol_flags | VOLUME_DIRTY);
+	if (test_and_set_bit(VOLUME_DIRTY_BIT, &sbi->vol_flags))
+		return 0;
+
+	return exfat_sync_vol_flags(sb);
 }
 
 int exfat_clear_volume_dirty(struct super_block *sb)
 {
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
 
-	return exfat_set_vol_flags(sb, sbi->vol_flags & ~VOLUME_DIRTY);
+	if (!test_and_clear_bit(VOLUME_DIRTY_BIT, &sbi->vol_flags))
+		return 0;
+
+	return exfat_sync_vol_flags(sb);
 }
 
 static int exfat_show_options(struct seq_file *m, struct dentry *root)
@@ -509,7 +506,6 @@ static int exfat_read_boot_sector(struct super_block *sb)
 		(sbi->cluster_size_bits - DENTRY_SIZE_BITS);
 
 	sbi->vol_flags = le16_to_cpu(p_boot->vol_flags);
-	sbi->vol_flags_persistent = sbi->vol_flags & (VOLUME_DIRTY | MEDIA_FAILURE);
 	sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER;
 
 	/* check consistencies */
@@ -526,9 +522,9 @@ static int exfat_read_boot_sector(struct super_block *sb)
 		return -EINVAL;
 	}
 
-	if (sbi->vol_flags & VOLUME_DIRTY)
+	if (test_bit(VOLUME_DIRTY_BIT, &sbi->vol_flags))
 		exfat_warn(sb, "Volume was not properly unmounted. Some data may be corrupt. Please run fsck.");
-	if (sbi->vol_flags & MEDIA_FAILURE)
+	if (test_bit(MEDIA_FAILURE_BIT, &sbi->vol_flags))
 		exfat_warn(sb, "Medium has reported failures. Some data may be lost.");
 
 	/*
-- 
2.53.0


  parent reply	other threads:[~2026-08-21 10:06 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-21 10:05 [RFC PATCH 0/6] exfat: take s_lock in read mode for iomap mapping paths Chi Zhiling
2026-08-21 10:05 ` [RFC PATCH v1 1/6] exfat: remove dead hint_bmap updates in I/O and truncate paths Chi Zhiling
2026-08-21 10:05 ` [RFC PATCH v1 2/6] exfat: take bitmap_lock at the start of exfat_alloc_cluster() Chi Zhiling
2026-08-21 10:05 ` Chi Zhiling [this message]
2026-08-21 10:05 ` [RFC PATCH v1 4/6] exfat: lock FAT2 buffer while copying mirrored FAT entries Chi Zhiling
2026-08-21 10:05 ` [RFC PATCH v1 5/6] exfat: convert s_lock mutex to rw_semaphore using write lock Chi Zhiling
2026-08-21 10:05 ` [RFC PATCH v1 6/6] exfat: take s_lock in read mode for iomap mapping paths Chi Zhiling

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260821100531.998196-4-chizhiling@163.com \
    --to=chizhiling@163.com \
    --cc=chizhiling@kylinos.cn \
    --cc=exfat@lists.linux.dev \
    --cc=linkinjeon@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sj1557.seo@samsung.com \
    --cc=yuezhang.mo@sony.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.