]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
exfat: support batch discard of clusters when freeing clusters
authorYuezhang Mo <Yuezhang.Mo@sony.com>
Wed, 12 Feb 2025 06:18:12 +0000 (14:18 +0800)
committerNamjae Jeon <linkinjeon@kernel.org>
Thu, 27 Mar 2025 12:18:02 +0000 (21:18 +0900)
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>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
fs/exfat/balloc.c
fs/exfat/fatent.c

index 9ff825f1502d5e69427ce17acecdcfcc0d3b5c3f..cc01556c9d9b37afb3982b26803e8868b2e8d61d 100644 (file)
@@ -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;
 }
 
index 6f3651c6ca91ef8a3f9fa34fece61241e45f6bcc..b9473a69f104d5624f16d003923909fd7c9ed3cd 100644 (file)
@@ -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++;