mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Chi Zhiling <chizhiling@163.com>
To: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Namjae Jeon <linkinjeon@kernel.org>,
	Sungjong Seo <sj1557.seo@samsung.com>,
	Yuezhang Mo <yuezhang.mo@sony.com>,
	Chi Zhiling <chizhiling@kylinos.cn>,
	Yuezhang Mo <Yuezhang.Mo@sony.com>
Subject: [PATCH v2 13/13] exfat: support multi-cluster for exfat_get_cluster
Date: Thu,  8 Jan 2026 15:59:38 +0800	[thread overview]
Message-ID: <20260108075938.360282-3-chizhiling@163.com> (raw)
In-Reply-To: <20260108075938.360282-1-chizhiling@163.com>

From: Chi Zhiling <chizhiling@kylinos.cn>

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>
---
 fs/exfat/cache.c    | 56 +++++++++++++++++++++++++++++++++++++++++----
 fs/exfat/exfat_fs.h |  2 +-
 fs/exfat/inode.c    |  3 +--
 3 files changed, 53 insertions(+), 8 deletions(-)

diff --git a/fs/exfat/cache.c b/fs/exfat/cache.c
index 5cdeac014a3d..18d304d1d4cc 100644
--- a/fs/exfat/cache.c
+++ b/fs/exfat/cache.c
@@ -259,13 +259,15 @@ static inline void cache_init(struct exfat_cache_id *cid,
 }
 
 int exfat_get_cluster(struct inode *inode, unsigned int cluster,
-		unsigned int *dclus, unsigned int *last_dclus)
+		unsigned int *dclus, unsigned int *count,
+		unsigned int *last_dclus)
 {
 	struct super_block *sb = inode->i_sb;
 	struct exfat_inode_info *ei = EXFAT_I(inode);
 	struct buffer_head *bh = NULL;
 	struct exfat_cache_id cid;
 	unsigned int content, fclus;
+	unsigned int end = cluster + *count - 1;
 
 	if (ei->start_clu == EXFAT_FREE_CLUSTER) {
 		exfat_fs_error(sb,
@@ -279,17 +281,33 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster,
 	*last_dclus = *dclus;
 
 	/*
-	 * Don`t use exfat_cache if zero offset or non-cluster allocation
+	 * This case should not exist, as exfat_map_cluster function doesn't
+	 * call this routine when start_clu == EXFAT_EOF_CLUSTER.
+	 * This case is retained here for routine completeness.
 	 */
-	if (cluster == 0 || *dclus == EXFAT_EOF_CLUSTER)
+	if (*dclus == EXFAT_EOF_CLUSTER) {
+		*count = 0;
+		return 0;
+	}
+
+	/* If only the first cluster is needed, return now. */
+	if (fclus == cluster && *count == 1)
 		return 0;
 
 	cache_init(&cid, fclus, *dclus);
-	exfat_cache_lookup(inode, &cid, cluster, cluster, &fclus, dclus);
+	/*
+	 * Update the 'end' to exclude the next cache range, as clusters in
+	 * different cache are typically not contiguous.
+	 */
+	end = exfat_cache_lookup(inode, &cid, cluster, end, &fclus, dclus);
 
-	if (fclus == cluster)
+	/* Return if the cache covers the entire range. */
+	if (cid.fcluster + cid.nr_contig >= end) {
+		*count = cid.fcluster + cid.nr_contig - cluster + 1;
 		return 0;
+	}
 
+	/* Find the first cluster we need. */
 	while (fclus < cluster) {
 		if (exfat_ent_get(sb, *dclus, &content, &bh))
 			return -EIO;
@@ -305,6 +323,34 @@ int exfat_get_cluster(struct inode *inode, unsigned int cluster,
 			cache_init(&cid, fclus, *dclus);
 	}
 
+	/*
+	 * Now the cid cache contains the first cluster requested, collect
+	 * the remaining clusters of this contiguous extent.
+	 */
+	if (*dclus != EXFAT_EOF_CLUSTER) {
+		unsigned int clu = *dclus;
+
+		while (fclus < end) {
+			if (exfat_ent_get(sb, clu, &content, &bh))
+				return -EIO;
+			if (++clu != content)
+				break;
+			fclus++;
+		}
+		cid.nr_contig = fclus - cid.fcluster;
+		*count = fclus - cluster + 1;
+
+		/*
+		 * Cache this discontiguous cluster, we'll definitely need
+		 * it later
+		 */
+		if (fclus < end && content != EXFAT_EOF_CLUSTER) {
+			exfat_cache_add(inode, &cid);
+			cache_init(&cid, fclus + 1, content);
+		}
+	} else {
+		*count = 0;
+	}
 	brelse(bh);
 	exfat_cache_add(inode, &cid);
 	return 0;
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
index e58d8eed5495..2dbed5f8ec26 100644
--- a/fs/exfat/exfat_fs.h
+++ b/fs/exfat/exfat_fs.h
@@ -486,7 +486,7 @@ int exfat_cache_init(void);
 void exfat_cache_shutdown(void);
 void exfat_cache_inval_inode(struct inode *inode);
 int exfat_get_cluster(struct inode *inode, unsigned int cluster,
-		unsigned int *dclus, unsigned int *last_dclus);
+		unsigned int *dclus, unsigned int *count, unsigned int *last_dclus);
 
 /* dir.c */
 extern const struct inode_operations exfat_dir_inode_operations;
diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c
index 00ff6c7ed935..e8b74185b0ad 100644
--- a/fs/exfat/inode.c
+++ b/fs/exfat/inode.c
@@ -160,10 +160,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
 		}
 	} else {
 		int err = exfat_get_cluster(inode, clu_offset,
-				clu, &last_clu);
+				clu, count, &last_clu);
 		if (err)
 			return -EIO;
-		*count = (*clu == EXFAT_EOF_CLUSTER) ? 0 : 1;
 	}
 
 	if (*clu == EXFAT_EOF_CLUSTER) {
-- 
2.43.0


      parent reply	other threads:[~2026-01-08  8:00 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-08  7:49 [PATCH v2 00/13] Enable multi-cluster fetching for exfat_get_block Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 01/13] exfat: add cache option for __exfat_ent_get Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 02/13] exfat: support reuse buffer head for exfat_ent_get Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 03/13] exfat: improve exfat_count_num_clusters Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 04/13] exfat: improve exfat_find_last_cluster Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 05/13] exfat: remove the check for infinite cluster chain loop Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 06/13] exfat: remove the unreachable warning for cache miss cases Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 07/13] exfat: reduce the number of parameters for exfat_get_cluster() Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 08/13] exfat: reuse cache to improve exfat_get_cluster Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 09/13] exfat: remove handling of non-file types in exfat_map_cluster Chi Zhiling
2026-01-08  7:49 ` [PATCH v2 10/13] exfat: support multi-cluster for exfat_map_cluster Chi Zhiling
2026-01-13  6:37   ` Yuezhang.Mo
2026-01-13  8:53     ` Chi Zhiling
2026-01-13 10:26       ` Yuezhang.Mo
2026-01-14  3:07         ` Chi Zhiling
2026-01-08  7:59 ` [PATCH v2 11/13] exfat: tweak cluster cache to support zero offset Chi Zhiling
2026-01-08  7:59   ` [PATCH v2 12/13] exfat: return the start of next cache in exfat_cache_lookup Chi Zhiling
2026-01-08  7:59   ` Chi Zhiling [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260108075938.360282-3-chizhiling@163.com \
    --to=chizhiling@163.com \
    --cc=chizhiling@kylinos.cn \
    --cc=linkinjeon@kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sj1557.seo@samsung.com \
    --cc=yuezhang.mo@sony.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®