Skip to content

Commit

Permalink
exfat: support batch discard of clusters when freeing clusters
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yuezhang Mo authored and Namjae Jeon committed Mar 27, 2025
1 parent 1a9239b commit a36e0ab
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 14 deletions.
14 changes: 0 additions & 14 deletions fs/exfat/balloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down
29 changes: 29 additions & 0 deletions fs/exfat/fatent.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -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++;

Expand Down

0 comments on commit a36e0ab

Please sign in to comment.