Skip to content

Commit 9276e33

Browse files
Chi Zhilingnamjaejeon
authored andcommitted
exfat: support multi-cluster for exfat_get_cluster
This patch introduces a count parameter to exfat_get_cluster, which serves as an input parameter for the caller to specify the desired number of clusters, and as an output parameter to store the length of consecutive clusters. This patch can improve read performance by reducing the number of get_block calls in sequential read scenarios. speacially in small cluster size. According to my test data, the performance improvement is approximately 10% when read FAT_CHAIN file with 512 bytes of cluster size. 454 MB/s -> 511 MB/s Suggested-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Chi Zhiling <chizhiling@kylinos.cn> Reviewed-by: Yuezhang Mo <Yuezhang.Mo@sony.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
1 parent 9fb696a commit 9276e33

3 files changed

Lines changed: 53 additions & 8 deletions

File tree

fs/exfat/cache.c

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,15 @@ static inline void cache_init(struct exfat_cache_id *cid,
259259
}
260260

261261
int exfat_get_cluster(struct inode *inode, unsigned int cluster,
262-
unsigned int *dclus, unsigned int *last_dclus)
262+
unsigned int *dclus, unsigned int *count,
263+
unsigned int *last_dclus)
263264
{
264265
struct super_block *sb = inode->i_sb;
265266
struct exfat_inode_info *ei = EXFAT_I(inode);
266267
struct buffer_head *bh = NULL;
267268
struct exfat_cache_id cid;
268269
unsigned int content, fclus;
270+
unsigned int end = cluster + *count - 1;
269271

270272
if (ei->start_clu == EXFAT_FREE_CLUSTER) {
271273
exfat_fs_error(sb,
@@ -279,17 +281,33 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster,
279281
*last_dclus = *dclus;
280282

281283
/*
282-
* 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.
283287
*/
284-
if (cluster == 0 || *dclus == EXFAT_EOF_CLUSTER)
288+
if (*dclus == EXFAT_EOF_CLUSTER) {
289+
*count = 0;
290+
return 0;
291+
}
292+
293+
/* If only the first cluster is needed, return now. */
294+
if (fclus == cluster && *count == 1)
285295
return 0;
286296

287297
cache_init(&cid, fclus, *dclus);
288-
exfat_cache_lookup(inode, &cid, cluster, cluster, &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 (fclus == cluster)
304+
/* Return if the cache covers the entire range. */
305+
if (cid.fcluster + cid.nr_contig >= end) {
306+
*count = end - cluster + 1;
291307
return 0;
308+
}
292309

310+
/* Find the first cluster we need. */
293311
while (fclus < cluster) {
294312
if (exfat_ent_get(sb, *dclus, &content, &bh))
295313
return -EIO;
@@ -305,6 +323,34 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster,
305323
cache_init(&cid, fclus, *dclus);
306324
}
307325

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+
}
308354
brelse(bh);
309355
exfat_cache_add(inode, &cid);
310356
return 0;

fs/exfat/exfat_fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +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 *dclus, unsigned int *last_dclus);
489+
unsigned int *dclus, unsigned int *count, unsigned int *last_dclus);
490490

491491
/* dir.c */
492492
extern const struct inode_operations exfat_dir_inode_operations;

fs/exfat/inode.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
160160
}
161161
} else {
162162
int err = exfat_get_cluster(inode, clu_offset,
163-
clu, &last_clu);
163+
clu, count, &last_clu);
164164
if (err)
165165
return -EIO;
166-
*count = (*clu == EXFAT_EOF_CLUSTER) ? 0 : 1;
167166
}
168167

169168
if (*clu == EXFAT_EOF_CLUSTER) {

0 commit comments

Comments
 (0)