Skip to content

Commit 8ae8932

Browse files
committed
Merge tag 'exfat-for-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
Pull exfat updates from Namjae Jeon: - Improve write performance with dirsync mount option - Improve lookup performance - Add support for FITRIM ioctl - Fix a bug with discard option * tag 'exfat-for-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: speed up iterate/lookup by fixing start point of traversing cluster chain exfat: improve write performance when dirsync enabled exfat: add support ioctl and FITRIM function exfat: introduce bitmap_lock for cluster bitmap access exfat: fix erroneous discard when clear cluster bit
2 parents d72cd4a + c6e2f52 commit 8ae8932

File tree

8 files changed

+206
-35
lines changed

8 files changed

+206
-35
lines changed

fs/exfat/balloc.c

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,7 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi)
141141
kfree(sbi->vol_amap);
142142
}
143143

144-
/*
145-
* If the value of "clu" is 0, it means cluster 2 which is the first cluster of
146-
* the cluster heap.
147-
*/
148-
int exfat_set_bitmap(struct inode *inode, unsigned int clu)
144+
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
149145
{
150146
int i, b;
151147
unsigned int ent_idx;
@@ -158,14 +154,10 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu)
158154
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
159155

160156
set_bit_le(b, sbi->vol_amap[i]->b_data);
161-
exfat_update_bh(sbi->vol_amap[i], IS_DIRSYNC(inode));
157+
exfat_update_bh(sbi->vol_amap[i], sync);
162158
return 0;
163159
}
164160

165-
/*
166-
* If the value of "clu" is 0, it means cluster 2 which is the first cluster of
167-
* the cluster heap.
168-
*/
169161
void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
170162
{
171163
int i, b;
@@ -186,8 +178,7 @@ void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
186178
int ret_discard;
187179

188180
ret_discard = sb_issue_discard(sb,
189-
exfat_cluster_to_sector(sbi, clu +
190-
EXFAT_RESERVED_CLUSTERS),
181+
exfat_cluster_to_sector(sbi, clu),
191182
(1 << sbi->sect_per_clus_bits), GFP_NOFS, 0);
192183

193184
if (ret_discard == -EOPNOTSUPP) {
@@ -273,3 +264,83 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
273264
*ret_count = count;
274265
return 0;
275266
}
267+
268+
int exfat_trim_fs(struct inode *inode, struct fstrim_range *range)
269+
{
270+
unsigned int trim_begin, trim_end, count, next_free_clu;
271+
u64 clu_start, clu_end, trim_minlen, trimmed_total = 0;
272+
struct super_block *sb = inode->i_sb;
273+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
274+
int err = 0;
275+
276+
clu_start = max_t(u64, range->start >> sbi->cluster_size_bits,
277+
EXFAT_FIRST_CLUSTER);
278+
clu_end = clu_start + (range->len >> sbi->cluster_size_bits) - 1;
279+
trim_minlen = range->minlen >> sbi->cluster_size_bits;
280+
281+
if (clu_start >= sbi->num_clusters || range->len < sbi->cluster_size)
282+
return -EINVAL;
283+
284+
if (clu_end >= sbi->num_clusters)
285+
clu_end = sbi->num_clusters - 1;
286+
287+
mutex_lock(&sbi->bitmap_lock);
288+
289+
trim_begin = trim_end = exfat_find_free_bitmap(sb, clu_start);
290+
if (trim_begin == EXFAT_EOF_CLUSTER)
291+
goto unlock;
292+
293+
next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1);
294+
if (next_free_clu == EXFAT_EOF_CLUSTER)
295+
goto unlock;
296+
297+
do {
298+
if (next_free_clu == trim_end + 1) {
299+
/* extend trim range for continuous free cluster */
300+
trim_end++;
301+
} else {
302+
/* trim current range if it's larger than trim_minlen */
303+
count = trim_end - trim_begin + 1;
304+
if (count >= trim_minlen) {
305+
err = sb_issue_discard(sb,
306+
exfat_cluster_to_sector(sbi, trim_begin),
307+
count * sbi->sect_per_clus, GFP_NOFS, 0);
308+
if (err)
309+
goto unlock;
310+
311+
trimmed_total += count;
312+
}
313+
314+
/* set next start point of the free hole */
315+
trim_begin = trim_end = next_free_clu;
316+
}
317+
318+
if (next_free_clu >= clu_end)
319+
break;
320+
321+
if (fatal_signal_pending(current)) {
322+
err = -ERESTARTSYS;
323+
goto unlock;
324+
}
325+
326+
next_free_clu = exfat_find_free_bitmap(sb, next_free_clu + 1);
327+
} while (next_free_clu != EXFAT_EOF_CLUSTER &&
328+
next_free_clu > trim_end);
329+
330+
/* try to trim remainder */
331+
count = trim_end - trim_begin + 1;
332+
if (count >= trim_minlen) {
333+
err = sb_issue_discard(sb, exfat_cluster_to_sector(sbi, trim_begin),
334+
count * sbi->sect_per_clus, GFP_NOFS, 0);
335+
if (err)
336+
goto unlock;
337+
338+
trimmed_total += count;
339+
}
340+
341+
unlock:
342+
mutex_unlock(&sbi->bitmap_lock);
343+
range->len = trimmed_total << sbi->cluster_size_bits;
344+
345+
return err;
346+
}

fs/exfat/dir.c

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
#include <linux/slab.h>
7+
#include <linux/compat.h>
78
#include <linux/bio.h>
89
#include <linux/buffer_head.h>
910

@@ -146,7 +147,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
146147
0);
147148

148149
*uni_name.name = 0x0;
149-
exfat_get_uniname_from_ext_entry(sb, &dir, dentry,
150+
exfat_get_uniname_from_ext_entry(sb, &clu, i,
150151
uni_name.name);
151152
exfat_utf16_to_nls(sb, &uni_name,
152153
dir_entry->namebuf.lfn,
@@ -306,6 +307,10 @@ const struct file_operations exfat_dir_operations = {
306307
.llseek = generic_file_llseek,
307308
.read = generic_read_dir,
308309
.iterate = exfat_iterate,
310+
.unlocked_ioctl = exfat_ioctl,
311+
#ifdef CONFIG_COMPAT
312+
.compat_ioctl = exfat_compat_ioctl,
313+
#endif
309314
.fsync = exfat_file_fsync,
310315
};
311316

@@ -315,7 +320,7 @@ int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu)
315320

316321
exfat_chain_set(clu, EXFAT_EOF_CLUSTER, 0, ALLOC_NO_FAT_CHAIN);
317322

318-
ret = exfat_alloc_cluster(inode, 1, clu);
323+
ret = exfat_alloc_cluster(inode, 1, clu, IS_DIRSYNC(inode));
319324
if (ret)
320325
return ret;
321326

@@ -906,14 +911,19 @@ enum {
906911
};
907912

908913
/*
909-
* return values:
910-
* >= 0 : return dir entiry position with the name in dir
911-
* -ENOENT : entry with the name does not exist
912-
* -EIO : I/O error
914+
* @ei: inode info of parent directory
915+
* @p_dir: directory structure of parent directory
916+
* @num_entries:entry size of p_uniname
917+
* @hint_opt: If p_uniname is found, filled with optimized dir/entry
918+
* for traversing cluster chain.
919+
* @return:
920+
* >= 0: file directory entry position where the name exists
921+
* -ENOENT: entry with the name does not exist
922+
* -EIO: I/O error
913923
*/
914924
int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
915925
struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
916-
int num_entries, unsigned int type)
926+
int num_entries, unsigned int type, struct exfat_hint *hint_opt)
917927
{
918928
int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len;
919929
int order, step, name_len = 0;
@@ -990,6 +1000,8 @@ int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
9901000

9911001
if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) {
9921002
step = DIRENT_STEP_FILE;
1003+
hint_opt->clu = clu.dir;
1004+
hint_opt->eidx = i;
9931005
if (type == TYPE_ALL || type == entry_type) {
9941006
num_ext = ep->dentry.file.num_ext;
9951007
step = DIRENT_STEP_STRM;

fs/exfat/exfat_fs.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ struct exfat_sb_info {
238238
unsigned int used_clusters; /* number of used clusters */
239239

240240
struct mutex s_lock; /* superblock lock */
241+
struct mutex bitmap_lock; /* bitmap lock */
241242
struct exfat_mount_options options;
242243
struct nls_table *nls_io; /* Charset used for input and display */
243244
struct ratelimit_state ratelimit;
@@ -388,7 +389,7 @@ int exfat_clear_volume_dirty(struct super_block *sb);
388389
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
389390

390391
int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
391-
struct exfat_chain *p_chain);
392+
struct exfat_chain *p_chain, bool sync_bmap);
392393
int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain);
393394
int exfat_ent_get(struct super_block *sb, unsigned int loc,
394395
unsigned int *content);
@@ -407,10 +408,11 @@ int exfat_count_num_clusters(struct super_block *sb,
407408
/* balloc.c */
408409
int exfat_load_bitmap(struct super_block *sb);
409410
void exfat_free_bitmap(struct exfat_sb_info *sbi);
410-
int exfat_set_bitmap(struct inode *inode, unsigned int clu);
411+
int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync);
411412
void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync);
412413
unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu);
413414
int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
415+
int exfat_trim_fs(struct inode *inode, struct fstrim_range *range);
414416

415417
/* file.c */
416418
extern const struct file_operations exfat_file_operations;
@@ -422,6 +424,9 @@ int exfat_getattr(struct user_namespace *mnt_userns, const struct path *path,
422424
struct kstat *stat, unsigned int request_mask,
423425
unsigned int query_flags);
424426
int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
427+
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
428+
long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
429+
unsigned long arg);
425430

426431
/* namei.c */
427432
extern const struct dentry_operations exfat_dentry_ops;
@@ -452,7 +457,7 @@ void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es);
452457
int exfat_calc_num_entries(struct exfat_uni_name *p_uniname);
453458
int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
454459
struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
455-
int num_entries, unsigned int type);
460+
int num_entries, unsigned int type, struct exfat_hint *hint_opt);
456461
int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu);
457462
int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
458463
int entry, sector_t *sector, int *offset);

fs/exfat/fatent.c

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,14 @@ int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
151151
return 0;
152152
}
153153

154-
int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
154+
/* This function must be called with bitmap_lock held */
155+
static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
155156
{
156-
unsigned int num_clusters = 0;
157-
unsigned int clu;
158157
struct super_block *sb = inode->i_sb;
159158
struct exfat_sb_info *sbi = EXFAT_SB(sb);
160159
int cur_cmap_i, next_cmap_i;
160+
unsigned int num_clusters = 0;
161+
unsigned int clu;
161162

162163
/* invalid cluster number */
163164
if (p_chain->dir == EXFAT_FREE_CLUSTER ||
@@ -230,6 +231,17 @@ int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
230231
return 0;
231232
}
232233

234+
int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain)
235+
{
236+
int ret = 0;
237+
238+
mutex_lock(&EXFAT_SB(inode->i_sb)->bitmap_lock);
239+
ret = __exfat_free_cluster(inode, p_chain);
240+
mutex_unlock(&EXFAT_SB(inode->i_sb)->bitmap_lock);
241+
242+
return ret;
243+
}
244+
233245
int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
234246
unsigned int *ret_clu)
235247
{
@@ -308,7 +320,7 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
308320
}
309321

310322
int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
311-
struct exfat_chain *p_chain)
323+
struct exfat_chain *p_chain, bool sync_bmap)
312324
{
313325
int ret = -ENOSPC;
314326
unsigned int num_clusters = 0, total_cnt;
@@ -328,6 +340,8 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
328340
if (num_alloc > total_cnt - sbi->used_clusters)
329341
return -ENOSPC;
330342

343+
mutex_lock(&sbi->bitmap_lock);
344+
331345
hint_clu = p_chain->dir;
332346
/* find new cluster */
333347
if (hint_clu == EXFAT_EOF_CLUSTER) {
@@ -338,8 +352,10 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
338352
}
339353

340354
hint_clu = exfat_find_free_bitmap(sb, sbi->clu_srch_ptr);
341-
if (hint_clu == EXFAT_EOF_CLUSTER)
342-
return -ENOSPC;
355+
if (hint_clu == EXFAT_EOF_CLUSTER) {
356+
ret = -ENOSPC;
357+
goto unlock;
358+
}
343359
}
344360

345361
/* check cluster validation */
@@ -349,8 +365,10 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
349365
hint_clu = EXFAT_FIRST_CLUSTER;
350366
if (p_chain->flags == ALLOC_NO_FAT_CHAIN) {
351367
if (exfat_chain_cont_cluster(sb, p_chain->dir,
352-
num_clusters))
353-
return -EIO;
368+
num_clusters)) {
369+
ret = -EIO;
370+
goto unlock;
371+
}
354372
p_chain->flags = ALLOC_FAT_CHAIN;
355373
}
356374
}
@@ -370,7 +388,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
370388
}
371389

372390
/* update allocation bitmap */
373-
if (exfat_set_bitmap(inode, new_clu)) {
391+
if (exfat_set_bitmap(inode, new_clu, sync_bmap)) {
374392
ret = -EIO;
375393
goto free_cluster;
376394
}
@@ -400,6 +418,7 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
400418
sbi->used_clusters += num_clusters;
401419

402420
p_chain->size += num_clusters;
421+
mutex_unlock(&sbi->bitmap_lock);
403422
return 0;
404423
}
405424

@@ -419,7 +438,9 @@ int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
419438
}
420439
free_cluster:
421440
if (num_clusters)
422-
exfat_free_cluster(inode, p_chain);
441+
__exfat_free_cluster(inode, p_chain);
442+
unlock:
443+
mutex_unlock(&sbi->bitmap_lock);
423444
return ret;
424445
}
425446

0 commit comments

Comments
 (0)