Skip to content

Commit dd53059

Browse files
committed
Merge tag 'exfat-for-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
Pull exfat updates from Namjae Jeon: - Improve error code handling and four cleanups - Reduce unnecessary valid_size extension during mmap write to avoid over-extending writes - Optimize consecutive FAT entry reads by caching buffer heads in __exfat_ent_get to significantly reduce sb_bread() calls - Add multi-cluster (contiguous cluster) support to exfat_get_cluster() and exfat_map_cluster() for better sequential read performance, especially on small cluster sizes * tag 'exfat-for-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: add blank line after declarations exfat: remove unnecessary else after return statement exfat: support multi-cluster for exfat_get_cluster exfat: return the start of next cache in exfat_cache_lookup exfat: tweak cluster cache to support zero offset exfat: support multi-cluster for exfat_map_cluster exfat: remove handling of non-file types in exfat_map_cluster exfat: reuse cache to improve exfat_get_cluster exfat: reduce the number of parameters for exfat_get_cluster() exfat: remove the unreachable warning for cache miss cases exfat: remove the check for infinite cluster chain loop exfat: improve exfat_find_last_cluster exfat: improve exfat_count_num_clusters exfat: support reuse buffer head for exfat_ent_get exfat: add cache option for __exfat_ent_get exfat: reduce unnecessary writes during mmap write exfat: improve error code handling in exfat_find_empty_entry()
2 parents 3e48a11 + c1f5740 commit dd53059

6 files changed

Lines changed: 170 additions & 125 deletions

File tree

fs/exfat/cache.c

Lines changed: 96 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -80,41 +80,66 @@ static inline void exfat_cache_update_lru(struct inode *inode,
8080
list_move(&cache->cache_list, &ei->cache_lru);
8181
}
8282

83-
static unsigned int exfat_cache_lookup(struct inode *inode,
84-
unsigned int fclus, struct exfat_cache_id *cid,
83+
/*
84+
* Find the cache that covers or precedes 'fclus' and return the last
85+
* cluster before the next cache range.
86+
*/
87+
static inline unsigned int
88+
exfat_cache_lookup(struct inode *inode, struct exfat_cache_id *cid,
89+
unsigned int fclus, unsigned int end,
8590
unsigned int *cached_fclus, unsigned int *cached_dclus)
8691
{
8792
struct exfat_inode_info *ei = EXFAT_I(inode);
8893
static struct exfat_cache nohit = { .fcluster = 0, };
8994
struct exfat_cache *hit = &nohit, *p;
90-
unsigned int offset = EXFAT_EOF_CLUSTER;
95+
unsigned int tail = 0; /* End boundary of hit cache */
9196

97+
/*
98+
* Search range [fclus, end]. Stop early if:
99+
* 1. Cache covers entire range, or
100+
* 2. Next cache starts at current cache tail
101+
*/
92102
spin_lock(&ei->cache_lru_lock);
93103
list_for_each_entry(p, &ei->cache_lru, cache_list) {
94104
/* Find the cache of "fclus" or nearest cache. */
95-
if (p->fcluster <= fclus && hit->fcluster < p->fcluster) {
105+
if (p->fcluster <= fclus) {
106+
if (p->fcluster < hit->fcluster)
107+
continue;
108+
96109
hit = p;
97-
if (hit->fcluster + hit->nr_contig < fclus) {
98-
offset = hit->nr_contig;
99-
} else {
100-
offset = fclus - hit->fcluster;
110+
tail = hit->fcluster + hit->nr_contig;
111+
112+
/* Current cache covers [fclus, end] completely */
113+
if (tail >= end)
114+
break;
115+
} else if (p->fcluster <= end) {
116+
end = p->fcluster - 1;
117+
118+
/*
119+
* If we have a hit and next cache starts within/at
120+
* its tail, caches are contiguous, stop searching.
121+
*/
122+
if (tail && tail >= end)
101123
break;
102-
}
103124
}
104125
}
105126
if (hit != &nohit) {
106-
exfat_cache_update_lru(inode, hit);
127+
unsigned int offset;
107128

129+
exfat_cache_update_lru(inode, hit);
108130
cid->id = ei->cache_valid_id;
109131
cid->nr_contig = hit->nr_contig;
110132
cid->fcluster = hit->fcluster;
111133
cid->dcluster = hit->dcluster;
134+
135+
offset = min(cid->nr_contig, fclus - cid->fcluster);
112136
*cached_fclus = cid->fcluster + offset;
113137
*cached_dclus = cid->dcluster + offset;
114138
}
115139
spin_unlock(&ei->cache_lru_lock);
116140

117-
return offset;
141+
/* Return next cache start or 'end' if no more caches */
142+
return end;
118143
}
119144

120145
static struct exfat_cache *exfat_cache_merge(struct inode *inode,
@@ -234,15 +259,15 @@ static inline void cache_init(struct exfat_cache_id *cid,
234259
}
235260

236261
int exfat_get_cluster(struct inode *inode, unsigned int cluster,
237-
unsigned int *fclus, unsigned int *dclus,
238-
unsigned int *last_dclus, int allow_eof)
262+
unsigned int *dclus, unsigned int *count,
263+
unsigned int *last_dclus)
239264
{
240265
struct super_block *sb = inode->i_sb;
241-
struct exfat_sb_info *sbi = EXFAT_SB(sb);
242-
unsigned int limit = sbi->num_clusters;
243266
struct exfat_inode_info *ei = EXFAT_I(inode);
267+
struct buffer_head *bh = NULL;
244268
struct exfat_cache_id cid;
245-
unsigned int content;
269+
unsigned int content, fclus;
270+
unsigned int end = cluster + *count - 1;
246271

247272
if (ei->start_clu == EXFAT_FREE_CLUSTER) {
248273
exfat_fs_error(sb,
@@ -251,64 +276,82 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster,
251276
return -EIO;
252277
}
253278

254-
*fclus = 0;
279+
fclus = 0;
255280
*dclus = ei->start_clu;
256281
*last_dclus = *dclus;
257282

258283
/*
259-
* Don`t use exfat_cache if zero offset or non-cluster allocation
284+
* This case should not exist, as exfat_map_cluster function doesn't
285+
* call this routine when start_clu == EXFAT_EOF_CLUSTER.
286+
* This case is retained here for routine completeness.
260287
*/
261-
if (cluster == 0 || *dclus == EXFAT_EOF_CLUSTER)
288+
if (*dclus == EXFAT_EOF_CLUSTER) {
289+
*count = 0;
262290
return 0;
263-
264-
cache_init(&cid, EXFAT_EOF_CLUSTER, EXFAT_EOF_CLUSTER);
265-
266-
if (exfat_cache_lookup(inode, cluster, &cid, fclus, dclus) ==
267-
EXFAT_EOF_CLUSTER) {
268-
/*
269-
* dummy, always not contiguous
270-
* This is reinitialized by cache_init(), later.
271-
*/
272-
WARN_ON(cid.id != EXFAT_CACHE_VALID ||
273-
cid.fcluster != EXFAT_EOF_CLUSTER ||
274-
cid.dcluster != EXFAT_EOF_CLUSTER ||
275-
cid.nr_contig != 0);
276291
}
277292

278-
if (*fclus == cluster)
293+
/* If only the first cluster is needed, return now. */
294+
if (fclus == cluster && *count == 1)
279295
return 0;
280296

281-
while (*fclus < cluster) {
282-
/* prevent the infinite loop of cluster chain */
283-
if (*fclus > limit) {
284-
exfat_fs_error(sb,
285-
"detected the cluster chain loop (i_pos %u)",
286-
(*fclus));
287-
return -EIO;
288-
}
297+
cache_init(&cid, fclus, *dclus);
298+
/*
299+
* Update the 'end' to exclude the next cache range, as clusters in
300+
* different cache are typically not contiguous.
301+
*/
302+
end = exfat_cache_lookup(inode, &cid, cluster, end, &fclus, dclus);
289303

290-
if (exfat_ent_get(sb, *dclus, &content))
304+
/* Return if the cache covers the entire range. */
305+
if (cid.fcluster + cid.nr_contig >= end) {
306+
*count = end - cluster + 1;
307+
return 0;
308+
}
309+
310+
/* Find the first cluster we need. */
311+
while (fclus < cluster) {
312+
if (exfat_ent_get(sb, *dclus, &content, &bh))
291313
return -EIO;
292314

293315
*last_dclus = *dclus;
294316
*dclus = content;
295-
(*fclus)++;
296-
297-
if (content == EXFAT_EOF_CLUSTER) {
298-
if (!allow_eof) {
299-
exfat_fs_error(sb,
300-
"invalid cluster chain (i_pos %u, last_clus 0x%08x is EOF)",
301-
*fclus, (*last_dclus));
302-
return -EIO;
303-
}
317+
fclus++;
304318

319+
if (content == EXFAT_EOF_CLUSTER)
305320
break;
306-
}
307321

308322
if (!cache_contiguous(&cid, *dclus))
309-
cache_init(&cid, *fclus, *dclus);
323+
cache_init(&cid, fclus, *dclus);
310324
}
311325

326+
/*
327+
* Now the cid cache contains the first cluster requested, collect
328+
* the remaining clusters of this contiguous extent.
329+
*/
330+
if (*dclus != EXFAT_EOF_CLUSTER) {
331+
unsigned int clu = *dclus;
332+
333+
while (fclus < end) {
334+
if (exfat_ent_get(sb, clu, &content, &bh))
335+
return -EIO;
336+
if (++clu != content)
337+
break;
338+
fclus++;
339+
}
340+
cid.nr_contig = fclus - cid.fcluster;
341+
*count = fclus - cluster + 1;
342+
343+
/*
344+
* Cache this discontiguous cluster, we'll definitely need
345+
* it later
346+
*/
347+
if (fclus < end && content != EXFAT_EOF_CLUSTER) {
348+
exfat_cache_add(inode, &cid);
349+
cache_init(&cid, fclus + 1, content);
350+
}
351+
} else {
352+
*count = 0;
353+
}
354+
brelse(bh);
312355
exfat_cache_add(inode, &cid);
313356
return 0;
314357
}

fs/exfat/exfat_fs.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -432,13 +432,13 @@ int exfat_set_volume_dirty(struct super_block *sb);
432432
int exfat_clear_volume_dirty(struct super_block *sb);
433433

434434
/* fatent.c */
435-
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
435+
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu, NULL)
436436

437437
int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
438438
struct exfat_chain *p_chain, bool sync_bmap);
439439
int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain);
440440
int exfat_ent_get(struct super_block *sb, unsigned int loc,
441-
unsigned int *content);
441+
unsigned int *content, struct buffer_head **last);
442442
int exfat_ent_set(struct super_block *sb, unsigned int loc,
443443
unsigned int content);
444444
int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
@@ -486,8 +486,7 @@ int exfat_cache_init(void);
486486
void exfat_cache_shutdown(void);
487487
void exfat_cache_inval_inode(struct inode *inode);
488488
int exfat_get_cluster(struct inode *inode, unsigned int cluster,
489-
unsigned int *fclus, unsigned int *dclus,
490-
unsigned int *last_dclus, int allow_eof);
489+
unsigned int *dclus, unsigned int *count, unsigned int *last_dclus);
491490

492491
/* dir.c */
493492
extern const struct inode_operations exfat_dir_inode_operations;

0 commit comments

Comments
 (0)