diff mbox series

[v1] exfat: support batch discard of clusters when freeing clusters

Message ID PUZPR04MB63166087C78BBC8ECA38B0EC81C42@PUZPR04MB6316.apcprd04.prod.outlook.com (mailing list archive)
State New
Headers show
Series [v1] exfat: support batch discard of clusters when freeing clusters | expand

Commit Message

Yuezhang.Mo@sony.com Feb. 20, 2025, 6:21 a.m. UTC
If the discard mount option is enabled, the file's clusters are
discarded when the clusters are freed. Discarding clusters one by
one will significantly reduce performance. Poor performance may
cause soft lockup when lots of clusters are freed.

This commit improves performance by discarding contiguous clusters
in batches.

Measure the performance by:

  # truncate -s 80G /mnt/file
  # time rm /mnt/file

Without this commit:

  real    4m46.183s
  user    0m0.000s
  sys     0m12.863s

With this commit:

  real    0m1.661s
  user    0m0.000s
  sys     0m0.017s

Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
---
 fs/exfat/balloc.c | 14 --------------
 fs/exfat/fatent.c | 29 +++++++++++++++++++++++++++++
 2 files changed, 29 insertions(+), 14 deletions(-)
diff mbox series

Patch

diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
index 9ff825f1502d..cc01556c9d9b 100644
--- a/fs/exfat/balloc.c
+++ b/fs/exfat/balloc.c
@@ -147,7 +147,6 @@  int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
 	unsigned int ent_idx;
 	struct super_block *sb = inode->i_sb;
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
-	struct exfat_mount_options *opts = &sbi->options;
 
 	if (!is_valid_cluster(sbi, clu))
 		return -EIO;
@@ -163,19 +162,6 @@  int exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
 
 	exfat_update_bh(sbi->vol_amap[i], sync);
 
-	if (opts->discard) {
-		int ret_discard;
-
-		ret_discard = sb_issue_discard(sb,
-			exfat_cluster_to_sector(sbi, clu),
-			(1 << sbi->sect_per_clus_bits), GFP_NOFS, 0);
-
-		if (ret_discard == -EOPNOTSUPP) {
-			exfat_err(sb, "discard not supported by device, disabling");
-			opts->discard = 0;
-		}
-	}
-
 	return 0;
 }
 
diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c
index 6f3651c6ca91..b9473a69f104 100644
--- a/fs/exfat/fatent.c
+++ b/fs/exfat/fatent.c
@@ -144,6 +144,20 @@  int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
 	return 0;
 }
 
+static inline void exfat_discard_cluster(struct super_block *sb,
+		unsigned int clu, unsigned int num_clusters)
+{
+	int ret;
+	struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+	ret = sb_issue_discard(sb, exfat_cluster_to_sector(sbi, clu),
+			sbi->sect_per_clus * num_clusters, GFP_NOFS, 0);
+	if (ret == -EOPNOTSUPP) {
+		exfat_err(sb, "discard not supported by device, disabling");
+		sbi->options.discard = 0;
+	}
+}
+
 /* This function must be called with bitmap_lock held */
 static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
 {
@@ -196,7 +210,12 @@  static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
 			clu++;
 			num_clusters++;
 		} while (num_clusters < p_chain->size);
+
+		if (sbi->options.discard)
+			exfat_discard_cluster(sb, p_chain->dir, p_chain->size);
 	} else {
+		unsigned int nr_clu = 1;
+
 		do {
 			bool sync = false;
 			unsigned int n_clu = clu;
@@ -215,6 +234,16 @@  static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain
 
 			if (exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))))
 				break;
+
+			if (sbi->options.discard) {
+				if (n_clu == clu + 1)
+					nr_clu++;
+				else {
+					exfat_discard_cluster(sb, clu - nr_clu + 1, nr_clu);
+					nr_clu = 1;
+				}
+			}
+
 			clu = n_clu;
 			num_clusters++;