Blame SOURCES/0029-exfat-optimize-dir-cache.patch

Kmods SIG 50e2b3
From 943af1fdacfebe4ff13430655abc460a5e1072f7 Mon Sep 17 00:00:00 2001
Kmods SIG 50e2b3
From: Tetsuhiro Kohada <kohada.tetsuhiro@dc.mitsubishielectric.co.jp>
Kmods SIG 50e2b3
Date: Wed, 20 May 2020 16:56:41 +0900
Kmods SIG 50e2b3
Subject: [Backport 943af1fdacfe] exfat: optimize dir-cache
Kmods SIG 50e2b3
Kmods SIG 50e2b3
Optimize directory access based on exfat_entry_set_cache.
Kmods SIG 50e2b3
 - Hold bh instead of copied d-entry.
Kmods SIG 50e2b3
 - Modify bh->data directly instead of the copied d-entry.
Kmods SIG 50e2b3
 - Write back the retained bh instead of rescanning the d-entry-set.
Kmods SIG 50e2b3
And
Kmods SIG 50e2b3
 - Remove unused cache related definitions.
Kmods SIG 50e2b3
Kmods SIG 50e2b3
Signed-off-by: Tetsuhiro Kohada <kohada.tetsuhiro@dc.mitsubishielectric.co.jp>
Kmods SIG 50e2b3
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Kmods SIG 50e2b3
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
Kmods SIG 50e2b3
---
Kmods SIG 50e2b3
 src/dir.c      | 197 +++++++++++++++++---------------------------
Kmods SIG 50e2b3
 src/exfat_fs.h |  27 +++---
Kmods SIG 50e2b3
 src/file.c     |  15 ++--
Kmods SIG 50e2b3
 src/inode.c    |  53 +++++-------
Kmods SIG 50e2b3
 src/namei.c    |  14 ++--
Kmods SIG 50e2b3
 5 files changed, 124 insertions(+), 182 deletions(-)
Kmods SIG 50e2b3
Kmods SIG 50e2b3
diff --git a/src/dir.c b/src/dir.c
Kmods SIG 50e2b3
index b5a237c33d50c02c632ec82627f78b4e70507d7a..2902d285bf20412af3a57b0e96a6768e49d6c5ac 100644
Kmods SIG 50e2b3
--- a/src/dir.c
Kmods SIG 50e2b3
+++ b/src/dir.c
Kmods SIG 50e2b3
@@ -32,35 +32,30 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
Kmods SIG 50e2b3
 		struct exfat_chain *p_dir, int entry, unsigned short *uniname)
Kmods SIG 50e2b3
 {
Kmods SIG 50e2b3
 	int i;
Kmods SIG 50e2b3
-	struct exfat_dentry *ep;
Kmods SIG 50e2b3
 	struct exfat_entry_set_cache *es;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-	es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES, &ep);
Kmods SIG 50e2b3
+	es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
Kmods SIG 50e2b3
 	if (!es)
Kmods SIG 50e2b3
 		return;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-	if (es->num_entries < 3)
Kmods SIG 50e2b3
-		goto free_es;
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-	ep += 2;
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
 	/*
Kmods SIG 50e2b3
 	 * First entry  : file entry
Kmods SIG 50e2b3
 	 * Second entry : stream-extension entry
Kmods SIG 50e2b3
 	 * Third entry  : first file-name entry
Kmods SIG 50e2b3
 	 * So, the index of first file-name dentry should start from 2.
Kmods SIG 50e2b3
 	 */
Kmods SIG 50e2b3
-	for (i = 2; i < es->num_entries; i++, ep++) {
Kmods SIG 50e2b3
+	for (i = 2; i < es->num_entries; i++) {
Kmods SIG 50e2b3
+		struct exfat_dentry *ep = exfat_get_dentry_cached(es, i);
Kmods SIG 50e2b3
+
Kmods SIG 50e2b3
 		/* end of name entry */
Kmods SIG 50e2b3
 		if (exfat_get_entry_type(ep) != TYPE_EXTEND)
Kmods SIG 50e2b3
-			goto free_es;
Kmods SIG 50e2b3
+			break;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 		exfat_extract_uni_name(ep, uniname);
Kmods SIG 50e2b3
 		uniname += EXFAT_FILE_NAME_LEN;
Kmods SIG 50e2b3
 	}
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-free_es:
Kmods SIG 50e2b3
-	kfree(es);
Kmods SIG 50e2b3
+	exfat_free_dentry_set(es, false);
Kmods SIG 50e2b3
 }
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 /* read a directory entry from the opened directory */
Kmods SIG 50e2b3
@@ -590,62 +585,33 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
Kmods SIG 50e2b3
 	return 0;
Kmods SIG 50e2b3
 }
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-int exfat_update_dir_chksum_with_entry_set(struct super_block *sb,
Kmods SIG 50e2b3
-		struct exfat_entry_set_cache *es, int sync)
Kmods SIG 50e2b3
+void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
Kmods SIG 50e2b3
 {
Kmods SIG 50e2b3
-	struct exfat_sb_info *sbi = EXFAT_SB(sb);
Kmods SIG 50e2b3
-	struct buffer_head *bh;
Kmods SIG 50e2b3
-	sector_t sec = es->sector;
Kmods SIG 50e2b3
-	unsigned int off = es->offset;
Kmods SIG 50e2b3
-	int chksum_type = CS_DIR_ENTRY, i, num_entries = es->num_entries;
Kmods SIG 50e2b3
-	unsigned int buf_off = (off - es->offset);
Kmods SIG 50e2b3
-	unsigned int remaining_byte_in_sector, copy_entries, clu;
Kmods SIG 50e2b3
+	int chksum_type = CS_DIR_ENTRY, i;
Kmods SIG 50e2b3
 	unsigned short chksum = 0;
Kmods SIG 50e2b3
+	struct exfat_dentry *ep;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-	for (i = 0; i < num_entries; i++) {
Kmods SIG 50e2b3
-		chksum = exfat_calc_chksum_2byte(&es->entries[i], DENTRY_SIZE,
Kmods SIG 50e2b3
-			chksum, chksum_type);
Kmods SIG 50e2b3
+	for (i = 0; i < es->num_entries; i++) {
Kmods SIG 50e2b3
+		ep = exfat_get_dentry_cached(es, i);
Kmods SIG 50e2b3
+		chksum = exfat_calc_chksum_2byte(ep, DENTRY_SIZE, chksum,
Kmods SIG 50e2b3
+						 chksum_type);
Kmods SIG 50e2b3
 		chksum_type = CS_DEFAULT;
Kmods SIG 50e2b3
 	}
Kmods SIG 50e2b3
+	ep = exfat_get_dentry_cached(es, 0);
Kmods SIG 50e2b3
+	ep->dentry.file.checksum = cpu_to_le16(chksum);
Kmods SIG 50e2b3
+	es->modified = true;
Kmods SIG 50e2b3
+}
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-	es->entries[0].dentry.file.checksum = cpu_to_le16(chksum);
Kmods SIG 50e2b3
+void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
Kmods SIG 50e2b3
+{
Kmods SIG 50e2b3
+	int i;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-	while (num_entries) {
Kmods SIG 50e2b3
-		/* write per sector base */
Kmods SIG 50e2b3
-		remaining_byte_in_sector = (1 << sb->s_blocksize_bits) - off;
Kmods SIG 50e2b3
-		copy_entries = min_t(int,
Kmods SIG 50e2b3
-			EXFAT_B_TO_DEN(remaining_byte_in_sector),
Kmods SIG 50e2b3
-			num_entries);
Kmods SIG 50e2b3
-		bh = sb_bread(sb, sec);
Kmods SIG 50e2b3
-		if (!bh)
Kmods SIG 50e2b3
-			goto err_out;
Kmods SIG 50e2b3
-		memcpy(bh->b_data + off,
Kmods SIG 50e2b3
-			(unsigned char *)&es->entries[0] + buf_off,
Kmods SIG 50e2b3
-			EXFAT_DEN_TO_B(copy_entries));
Kmods SIG 50e2b3
-		exfat_update_bh(sb, bh, sync);
Kmods SIG 50e2b3
-		brelse(bh);
Kmods SIG 50e2b3
-		num_entries -= copy_entries;
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-		if (num_entries) {
Kmods SIG 50e2b3
-			/* get next sector */
Kmods SIG 50e2b3
-			if (exfat_is_last_sector_in_cluster(sbi, sec)) {
Kmods SIG 50e2b3
-				clu = exfat_sector_to_cluster(sbi, sec);
Kmods SIG 50e2b3
-				if (es->alloc_flag == ALLOC_NO_FAT_CHAIN)
Kmods SIG 50e2b3
-					clu++;
Kmods SIG 50e2b3
-				else if (exfat_get_next_cluster(sb, &clu))
Kmods SIG 50e2b3
-					goto err_out;
Kmods SIG 50e2b3
-				sec = exfat_cluster_to_sector(sbi, clu);
Kmods SIG 50e2b3
-			} else {
Kmods SIG 50e2b3
-				sec++;
Kmods SIG 50e2b3
-			}
Kmods SIG 50e2b3
-			off = 0;
Kmods SIG 50e2b3
-			buf_off += EXFAT_DEN_TO_B(copy_entries);
Kmods SIG 50e2b3
-		}
Kmods SIG 50e2b3
+	for (i = 0; i < es->num_bh; i++) {
Kmods SIG 50e2b3
+		if (es->modified)
Kmods SIG 50e2b3
+			exfat_update_bh(es->sb, es->bh[i], sync);
Kmods SIG 50e2b3
+		brelse(es->bh[i]);
Kmods SIG 50e2b3
 	}
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-	return 0;
Kmods SIG 50e2b3
-err_out:
Kmods SIG 50e2b3
-	return -EIO;
Kmods SIG 50e2b3
+	kfree(es);
Kmods SIG 50e2b3
 }
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 static int exfat_walk_fat_chain(struct super_block *sb,
Kmods SIG 50e2b3
@@ -820,34 +786,40 @@ static bool exfat_validate_entry(unsigned int type,
Kmods SIG 50e2b3
 	}
Kmods SIG 50e2b3
 }
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
+struct exfat_dentry *exfat_get_dentry_cached(
Kmods SIG 50e2b3
+	struct exfat_entry_set_cache *es, int num)
Kmods SIG 50e2b3
+{
Kmods SIG 50e2b3
+	int off = es->start_off + num * DENTRY_SIZE;
Kmods SIG 50e2b3
+	struct buffer_head *bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)];
Kmods SIG 50e2b3
+	char *p = bh->b_data + EXFAT_BLK_OFFSET(off, es->sb);
Kmods SIG 50e2b3
+
Kmods SIG 50e2b3
+	return (struct exfat_dentry *)p;
Kmods SIG 50e2b3
+}
Kmods SIG 50e2b3
+
Kmods SIG 50e2b3
 /*
Kmods SIG 50e2b3
  * Returns a set of dentries for a file or dir.
Kmods SIG 50e2b3
  *
Kmods SIG 50e2b3
- * Note that this is a copy (dump) of dentries so that user should
Kmods SIG 50e2b3
- * call write_entry_set() to apply changes made in this entry set
Kmods SIG 50e2b3
- * to the real device.
Kmods SIG 50e2b3
+ * Note It provides a direct pointer to bh->data via exfat_get_dentry_cached().
Kmods SIG 50e2b3
+ * User should call exfat_get_dentry_set() after setting 'modified' to apply
Kmods SIG 50e2b3
+ * changes made in this entry set to the real device.
Kmods SIG 50e2b3
  *
Kmods SIG 50e2b3
  * in:
Kmods SIG 50e2b3
  *   sb+p_dir+entry: indicates a file/dir
Kmods SIG 50e2b3
  *   type:  specifies how many dentries should be included.
Kmods SIG 50e2b3
- * out:
Kmods SIG 50e2b3
- *   file_ep: will point the first dentry(= file dentry) on success
Kmods SIG 50e2b3
  * return:
Kmods SIG 50e2b3
  *   pointer of entry set on success,
Kmods SIG 50e2b3
  *   NULL on failure.
Kmods SIG 50e2b3
  */
Kmods SIG 50e2b3
 struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
Kmods SIG 50e2b3
-		struct exfat_chain *p_dir, int entry, unsigned int type,
Kmods SIG 50e2b3
-		struct exfat_dentry **file_ep)
Kmods SIG 50e2b3
+		struct exfat_chain *p_dir, int entry, unsigned int type)
Kmods SIG 50e2b3
 {
Kmods SIG 50e2b3
-	int ret;
Kmods SIG 50e2b3
+	int ret, i, num_bh;
Kmods SIG 50e2b3
 	unsigned int off, byte_offset, clu = 0;
Kmods SIG 50e2b3
-	unsigned int entry_type;
Kmods SIG 50e2b3
 	sector_t sec;
Kmods SIG 50e2b3
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
Kmods SIG 50e2b3
 	struct exfat_entry_set_cache *es;
Kmods SIG 50e2b3
-	struct exfat_dentry *ep, *pos;
Kmods SIG 50e2b3
-	unsigned char num_entries;
Kmods SIG 50e2b3
+	struct exfat_dentry *ep;
Kmods SIG 50e2b3
+	int num_entries;
Kmods SIG 50e2b3
 	enum exfat_validate_dentry_mode mode = ES_MODE_STARTED;
Kmods SIG 50e2b3
 	struct buffer_head *bh;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
@@ -861,11 +833,18 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
Kmods SIG 50e2b3
 	if (ret)
Kmods SIG 50e2b3
 		return NULL;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
+	es = kzalloc(sizeof(*es), GFP_KERNEL);
Kmods SIG 50e2b3
+	if (!es)
Kmods SIG 50e2b3
+		return NULL;
Kmods SIG 50e2b3
+	es->sb = sb;
Kmods SIG 50e2b3
+	es->modified = false;
Kmods SIG 50e2b3
+
Kmods SIG 50e2b3
 	/* byte offset in cluster */
Kmods SIG 50e2b3
 	byte_offset = EXFAT_CLU_OFFSET(byte_offset, sbi);
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 	/* byte offset in sector */
Kmods SIG 50e2b3
 	off = EXFAT_BLK_OFFSET(byte_offset, sb);
Kmods SIG 50e2b3
+	es->start_off = off;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 	/* sector offset in cluster */
Kmods SIG 50e2b3
 	sec = EXFAT_B_TO_BLK(byte_offset, sb);
Kmods SIG 50e2b3
@@ -873,72 +852,46 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 	bh = sb_bread(sb, sec);
Kmods SIG 50e2b3
 	if (!bh)
Kmods SIG 50e2b3
-		return NULL;
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-	ep = (struct exfat_dentry *)(bh->b_data + off);
Kmods SIG 50e2b3
-	entry_type = exfat_get_entry_type(ep);
Kmods SIG 50e2b3
+		goto free_es;
Kmods SIG 50e2b3
+	es->bh[es->num_bh++] = bh;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-	if (entry_type != TYPE_FILE && entry_type != TYPE_DIR)
Kmods SIG 50e2b3
-		goto release_bh;
Kmods SIG 50e2b3
+	ep = exfat_get_dentry_cached(es, 0);
Kmods SIG 50e2b3
+	if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
Kmods SIG 50e2b3
+		goto free_es;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 	num_entries = type == ES_ALL_ENTRIES ?
Kmods SIG 50e2b3
 		ep->dentry.file.num_ext + 1 : type;
Kmods SIG 50e2b3
-	es = kmalloc(struct_size(es, entries, num_entries), GFP_KERNEL);
Kmods SIG 50e2b3
-	if (!es)
Kmods SIG 50e2b3
-		goto release_bh;
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
 	es->num_entries = num_entries;
Kmods SIG 50e2b3
-	es->sector = sec;
Kmods SIG 50e2b3
-	es->offset = off;
Kmods SIG 50e2b3
-	es->alloc_flag = p_dir->flags;
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-	pos = &es->entries[0];
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-	while (num_entries) {
Kmods SIG 50e2b3
-		if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
Kmods SIG 50e2b3
-			goto free_es;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-		/* copy dentry */
Kmods SIG 50e2b3
-		memcpy(pos, ep, sizeof(struct exfat_dentry));
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-		if (--num_entries == 0)
Kmods SIG 50e2b3
-			break;
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-		if (((off + DENTRY_SIZE) & (sb->s_blocksize - 1)) <
Kmods SIG 50e2b3
-		    (off & (sb->s_blocksize - 1))) {
Kmods SIG 50e2b3
-			/* get the next sector */
Kmods SIG 50e2b3
-			if (exfat_is_last_sector_in_cluster(sbi, sec)) {
Kmods SIG 50e2b3
-				if (es->alloc_flag == ALLOC_NO_FAT_CHAIN)
Kmods SIG 50e2b3
-					clu++;
Kmods SIG 50e2b3
-				else if (exfat_get_next_cluster(sb, &clu))
Kmods SIG 50e2b3
-					goto free_es;
Kmods SIG 50e2b3
-				sec = exfat_cluster_to_sector(sbi, clu);
Kmods SIG 50e2b3
-			} else {
Kmods SIG 50e2b3
-				sec++;
Kmods SIG 50e2b3
-			}
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-			brelse(bh);
Kmods SIG 50e2b3
-			bh = sb_bread(sb, sec);
Kmods SIG 50e2b3
-			if (!bh)
Kmods SIG 50e2b3
+	num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb);
Kmods SIG 50e2b3
+	for (i = 1; i < num_bh; i++) {
Kmods SIG 50e2b3
+		/* get the next sector */
Kmods SIG 50e2b3
+		if (exfat_is_last_sector_in_cluster(sbi, sec)) {
Kmods SIG 50e2b3
+			if (p_dir->flags == ALLOC_NO_FAT_CHAIN)
Kmods SIG 50e2b3
+				clu++;
Kmods SIG 50e2b3
+			else if (exfat_get_next_cluster(sb, &clu))
Kmods SIG 50e2b3
 				goto free_es;
Kmods SIG 50e2b3
-			off = 0;
Kmods SIG 50e2b3
-			ep = (struct exfat_dentry *)bh->b_data;
Kmods SIG 50e2b3
+			sec = exfat_cluster_to_sector(sbi, clu);
Kmods SIG 50e2b3
 		} else {
Kmods SIG 50e2b3
-			ep++;
Kmods SIG 50e2b3
-			off += DENTRY_SIZE;
Kmods SIG 50e2b3
+			sec++;
Kmods SIG 50e2b3
 		}
Kmods SIG 50e2b3
-		pos++;
Kmods SIG 50e2b3
+
Kmods SIG 50e2b3
+		bh = sb_bread(sb, sec);
Kmods SIG 50e2b3
+		if (!bh)
Kmods SIG 50e2b3
+			goto free_es;
Kmods SIG 50e2b3
+		es->bh[es->num_bh++] = bh;
Kmods SIG 50e2b3
 	}
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-	if (file_ep)
Kmods SIG 50e2b3
-		*file_ep = &es->entries[0];
Kmods SIG 50e2b3
-	brelse(bh);
Kmods SIG 50e2b3
+	/* validiate cached dentries */
Kmods SIG 50e2b3
+	for (i = 1; i < num_entries; i++) {
Kmods SIG 50e2b3
+		ep = exfat_get_dentry_cached(es, i);
Kmods SIG 50e2b3
+		if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
Kmods SIG 50e2b3
+			goto free_es;
Kmods SIG 50e2b3
+	}
Kmods SIG 50e2b3
 	return es;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 free_es:
Kmods SIG 50e2b3
-	kfree(es);
Kmods SIG 50e2b3
-release_bh:
Kmods SIG 50e2b3
-	brelse(bh);
Kmods SIG 50e2b3
+	exfat_free_dentry_set(es, false);
Kmods SIG 50e2b3
 	return NULL;
Kmods SIG 50e2b3
 }
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
diff --git a/src/exfat_fs.h b/src/exfat_fs.h
Kmods SIG 50e2b3
index 294aa7792bc3ca6c7e334110cba9160577d9affe..c84ae9e605085e0a5b19a2dbe8a9c36ab7efdce6 100644
Kmods SIG 50e2b3
--- a/src/exfat_fs.h
Kmods SIG 50e2b3
+++ b/src/exfat_fs.h
Kmods SIG 50e2b3
@@ -71,10 +71,8 @@ enum {
Kmods SIG 50e2b3
 #define MAX_NAME_LENGTH		255 /* max len of file name excluding NULL */
Kmods SIG 50e2b3
 #define MAX_VFSNAME_BUF_SIZE	((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE)
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-#define FAT_CACHE_SIZE		128
Kmods SIG 50e2b3
-#define FAT_CACHE_HASH_SIZE	64
Kmods SIG 50e2b3
-#define BUF_CACHE_SIZE		256
Kmods SIG 50e2b3
-#define BUF_CACHE_HASH_SIZE	64
Kmods SIG 50e2b3
+/* Enough size to hold 256 dentry (even 512 Byte sector) */
Kmods SIG 50e2b3
+#define DIR_CACHE_SIZE		(256*sizeof(struct exfat_dentry)/512+1)
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 #define EXFAT_HINT_NONE		-1
Kmods SIG 50e2b3
 #define EXFAT_MIN_SUBDIR	2
Kmods SIG 50e2b3
@@ -170,14 +168,12 @@ struct exfat_hint {
Kmods SIG 50e2b3
 };
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 struct exfat_entry_set_cache {
Kmods SIG 50e2b3
-	/* sector number that contains file_entry */
Kmods SIG 50e2b3
-	sector_t sector;
Kmods SIG 50e2b3
-	/* byte offset in the sector */
Kmods SIG 50e2b3
-	unsigned int offset;
Kmods SIG 50e2b3
-	/* flag in stream entry. 01 for cluster chain, 03 for contig. */
Kmods SIG 50e2b3
-	int alloc_flag;
Kmods SIG 50e2b3
+	struct super_block *sb;
Kmods SIG 50e2b3
+	bool modified;
Kmods SIG 50e2b3
+	unsigned int start_off;
Kmods SIG 50e2b3
+	int num_bh;
Kmods SIG 50e2b3
+	struct buffer_head *bh[DIR_CACHE_SIZE];
Kmods SIG 50e2b3
 	unsigned int num_entries;
Kmods SIG 50e2b3
-	struct exfat_dentry entries[];
Kmods SIG 50e2b3
 };
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 struct exfat_dir_entry {
Kmods SIG 50e2b3
@@ -451,8 +447,7 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
Kmods SIG 50e2b3
 		int entry, int order, int num_entries);
Kmods SIG 50e2b3
 int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
Kmods SIG 50e2b3
 		int entry);
Kmods SIG 50e2b3
-int exfat_update_dir_chksum_with_entry_set(struct super_block *sb,
Kmods SIG 50e2b3
-		struct exfat_entry_set_cache *es, int sync);
Kmods SIG 50e2b3
+void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es);
Kmods SIG 50e2b3
 int exfat_calc_num_entries(struct exfat_uni_name *p_uniname);
Kmods SIG 50e2b3
 int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
Kmods SIG 50e2b3
 		struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
Kmods SIG 50e2b3
@@ -463,9 +458,11 @@ int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
Kmods SIG 50e2b3
 struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
Kmods SIG 50e2b3
 		struct exfat_chain *p_dir, int entry, struct buffer_head **bh,
Kmods SIG 50e2b3
 		sector_t *sector);
Kmods SIG 50e2b3
+struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es,
Kmods SIG 50e2b3
+		int num);
Kmods SIG 50e2b3
 struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
Kmods SIG 50e2b3
-		struct exfat_chain *p_dir, int entry, unsigned int type,
Kmods SIG 50e2b3
-		struct exfat_dentry **file_ep);
Kmods SIG 50e2b3
+		struct exfat_chain *p_dir, int entry, unsigned int type);
Kmods SIG 50e2b3
+void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync);
Kmods SIG 50e2b3
 int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 /* inode.c */
Kmods SIG 50e2b3
diff --git a/src/file.c b/src/file.c
Kmods SIG 50e2b3
index 84f3d31a3a555729b5d2e655f354929018b6b290..8e3f0eef45d77345e6f57ef58f3e86c51c69e905 100644
Kmods SIG 50e2b3
--- a/src/file.c
Kmods SIG 50e2b3
+++ b/src/file.c
Kmods SIG 50e2b3
@@ -96,11 +96,9 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
Kmods SIG 50e2b3
 	unsigned int num_clusters_new, num_clusters_phys;
Kmods SIG 50e2b3
 	unsigned int last_clu = EXFAT_FREE_CLUSTER;
Kmods SIG 50e2b3
 	struct exfat_chain clu;
Kmods SIG 50e2b3
-	struct exfat_dentry *ep, *ep2;
Kmods SIG 50e2b3
 	struct super_block *sb = inode->i_sb;
Kmods SIG 50e2b3
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
Kmods SIG 50e2b3
 	struct exfat_inode_info *ei = EXFAT_I(inode);
Kmods SIG 50e2b3
-	struct exfat_entry_set_cache *es = NULL;
Kmods SIG 50e2b3
 	int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 	/* check if the given file ID is opened */
Kmods SIG 50e2b3
@@ -153,12 +151,15 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
Kmods SIG 50e2b3
 	/* update the directory entry */
Kmods SIG 50e2b3
 	if (!evict) {
Kmods SIG 50e2b3
 		struct timespec64 ts;
Kmods SIG 50e2b3
+		struct exfat_dentry *ep, *ep2;
Kmods SIG 50e2b3
+		struct exfat_entry_set_cache *es;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 		es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry,
Kmods SIG 50e2b3
-				ES_ALL_ENTRIES, &ep);
Kmods SIG 50e2b3
+				ES_ALL_ENTRIES);
Kmods SIG 50e2b3
 		if (!es)
Kmods SIG 50e2b3
 			return -EIO;
Kmods SIG 50e2b3
-		ep2 = ep + 1;
Kmods SIG 50e2b3
+		ep = exfat_get_dentry_cached(es, 0);
Kmods SIG 50e2b3
+		ep2 = exfat_get_dentry_cached(es, 1);
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 		ts = current_time(inode);
Kmods SIG 50e2b3
 		exfat_set_entry_time(sbi, &ts,
Kmods SIG 50e2b3
@@ -185,10 +186,8 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
Kmods SIG 50e2b3
 			ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
Kmods SIG 50e2b3
 		}
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-		if (exfat_update_dir_chksum_with_entry_set(sb, es,
Kmods SIG 50e2b3
-		    inode_needs_sync(inode)))
Kmods SIG 50e2b3
-			return -EIO;
Kmods SIG 50e2b3
-		kfree(es);
Kmods SIG 50e2b3
+		exfat_update_dir_chksum_with_entry_set(es);
Kmods SIG 50e2b3
+		exfat_free_dentry_set(es, inode_needs_sync(inode));
Kmods SIG 50e2b3
 	}
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 	/* cut off from the FAT chain */
Kmods SIG 50e2b3
diff --git a/src/inode.c b/src/inode.c
Kmods SIG 50e2b3
index 3f367d081cd6dfe6c7cdb475d77e130b64d14d6d..ef7cf7a6d187b8d806383ba7b94daa06342edd22 100644
Kmods SIG 50e2b3
--- a/src/inode.c
Kmods SIG 50e2b3
+++ b/src/inode.c
Kmods SIG 50e2b3
@@ -19,7 +19,6 @@
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 static int __exfat_write_inode(struct inode *inode, int sync)
Kmods SIG 50e2b3
 {
Kmods SIG 50e2b3
-	int ret = -EIO;
Kmods SIG 50e2b3
 	unsigned long long on_disk_size;
Kmods SIG 50e2b3
 	struct exfat_dentry *ep, *ep2;
Kmods SIG 50e2b3
 	struct exfat_entry_set_cache *es = NULL;
Kmods SIG 50e2b3
@@ -43,11 +42,11 @@ static int __exfat_write_inode(struct inode *inode, int sync)
Kmods SIG 50e2b3
 	exfat_set_vol_flags(sb, VOL_DIRTY);
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 	/* get the directory entry of given file or directory */
Kmods SIG 50e2b3
-	es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES,
Kmods SIG 50e2b3
-		&ep);
Kmods SIG 50e2b3
+	es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES);
Kmods SIG 50e2b3
 	if (!es)
Kmods SIG 50e2b3
 		return -EIO;
Kmods SIG 50e2b3
-	ep2 = ep + 1;
Kmods SIG 50e2b3
+	ep = exfat_get_dentry_cached(es, 0);
Kmods SIG 50e2b3
+	ep2 = exfat_get_dentry_cached(es, 1);
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 	ep->dentry.file.attr = cpu_to_le16(exfat_make_attr(inode));
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
@@ -77,9 +76,9 @@ static int __exfat_write_inode(struct inode *inode, int sync)
Kmods SIG 50e2b3
 	ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size);
Kmods SIG 50e2b3
 	ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-	ret = exfat_update_dir_chksum_with_entry_set(sb, es, sync);
Kmods SIG 50e2b3
-	kfree(es);
Kmods SIG 50e2b3
-	return ret;
Kmods SIG 50e2b3
+	exfat_update_dir_chksum_with_entry_set(es);
Kmods SIG 50e2b3
+	exfat_free_dentry_set(es, sync);
Kmods SIG 50e2b3
+	return 0;
Kmods SIG 50e2b3
 }
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
Kmods SIG 50e2b3
@@ -110,8 +109,6 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
Kmods SIG 50e2b3
 	int ret, modified = false;
Kmods SIG 50e2b3
 	unsigned int last_clu;
Kmods SIG 50e2b3
 	struct exfat_chain new_clu;
Kmods SIG 50e2b3
-	struct exfat_dentry *ep;
Kmods SIG 50e2b3
-	struct exfat_entry_set_cache *es = NULL;
Kmods SIG 50e2b3
 	struct super_block *sb = inode->i_sb;
Kmods SIG 50e2b3
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
Kmods SIG 50e2b3
 	struct exfat_inode_info *ei = EXFAT_I(inode);
Kmods SIG 50e2b3
@@ -222,34 +219,28 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
Kmods SIG 50e2b3
 		num_clusters += num_to_be_allocated;
Kmods SIG 50e2b3
 		*clu = new_clu.dir;
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
-		if (ei->dir.dir != DIR_DELETED) {
Kmods SIG 50e2b3
+		if (ei->dir.dir != DIR_DELETED && modified) {
Kmods SIG 50e2b3
+			struct exfat_dentry *ep;
Kmods SIG 50e2b3
+			struct exfat_entry_set_cache *es;
Kmods SIG 50e2b3
+
Kmods SIG 50e2b3
 			es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry,
Kmods SIG 50e2b3
-				ES_ALL_ENTRIES, &ep);
Kmods SIG 50e2b3
+				ES_ALL_ENTRIES);
Kmods SIG 50e2b3
 			if (!es)
Kmods SIG 50e2b3
 				return -EIO;
Kmods SIG 50e2b3
 			/* get stream entry */
Kmods SIG 50e2b3
-			ep++;
Kmods SIG 50e2b3
+			ep = exfat_get_dentry_cached(es, 1);
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 			/* update directory entry */
Kmods SIG 50e2b3
-			if (modified) {
Kmods SIG 50e2b3
-				if (ep->dentry.stream.flags != ei->flags)
Kmods SIG 50e2b3
-					ep->dentry.stream.flags = ei->flags;
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-				if (le32_to_cpu(ep->dentry.stream.start_clu) !=
Kmods SIG 50e2b3
-						ei->start_clu)
Kmods SIG 50e2b3
-					ep->dentry.stream.start_clu =
Kmods SIG 50e2b3
-						cpu_to_le32(ei->start_clu);
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-				ep->dentry.stream.valid_size =
Kmods SIG 50e2b3
-					cpu_to_le64(i_size_read(inode));
Kmods SIG 50e2b3
-				ep->dentry.stream.size =
Kmods SIG 50e2b3
-					ep->dentry.stream.valid_size;
Kmods SIG 50e2b3
-			}
Kmods SIG 50e2b3
-
Kmods SIG 50e2b3
-			if (exfat_update_dir_chksum_with_entry_set(sb, es,
Kmods SIG 50e2b3
-			    inode_needs_sync(inode)))
Kmods SIG 50e2b3
-				return -EIO;
Kmods SIG 50e2b3
-			kfree(es);
Kmods SIG 50e2b3
+			ep->dentry.stream.flags = ei->flags;
Kmods SIG 50e2b3
+			ep->dentry.stream.start_clu =
Kmods SIG 50e2b3
+				cpu_to_le32(ei->start_clu);
Kmods SIG 50e2b3
+			ep->dentry.stream.valid_size =
Kmods SIG 50e2b3
+				cpu_to_le64(i_size_read(inode));
Kmods SIG 50e2b3
+			ep->dentry.stream.size =
Kmods SIG 50e2b3
+				ep->dentry.stream.valid_size;
Kmods SIG 50e2b3
+
Kmods SIG 50e2b3
+			exfat_update_dir_chksum_with_entry_set(es);
Kmods SIG 50e2b3
+			exfat_free_dentry_set(es, inode_needs_sync(inode));
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 		} /* end of if != DIR_DELETED */
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
diff --git a/src/namei.c b/src/namei.c
Kmods SIG 50e2b3
index 48f4df883f3ba5a30219f8f038a025530a8f8fa2..5b0f35329d63e0b9ab52162e4fbad129601c458d 100644
Kmods SIG 50e2b3
--- a/src/namei.c
Kmods SIG 50e2b3
+++ b/src/namei.c
Kmods SIG 50e2b3
@@ -600,8 +600,6 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
Kmods SIG 50e2b3
 	int ret, dentry, num_entries, count;
Kmods SIG 50e2b3
 	struct exfat_chain cdir;
Kmods SIG 50e2b3
 	struct exfat_uni_name uni_name;
Kmods SIG 50e2b3
-	struct exfat_dentry *ep, *ep2;
Kmods SIG 50e2b3
-	struct exfat_entry_set_cache *es = NULL;
Kmods SIG 50e2b3
 	struct super_block *sb = dir->i_sb;
Kmods SIG 50e2b3
 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
Kmods SIG 50e2b3
 	struct exfat_inode_info *ei = EXFAT_I(dir);
Kmods SIG 50e2b3
@@ -660,10 +658,14 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 		info->num_subdirs = count;
Kmods SIG 50e2b3
 	} else {
Kmods SIG 50e2b3
-		es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES, &ep);
Kmods SIG 50e2b3
+		struct exfat_dentry *ep, *ep2;
Kmods SIG 50e2b3
+		struct exfat_entry_set_cache *es;
Kmods SIG 50e2b3
+
Kmods SIG 50e2b3
+		es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES);
Kmods SIG 50e2b3
 		if (!es)
Kmods SIG 50e2b3
 			return -EIO;
Kmods SIG 50e2b3
-		ep2 = ep + 1;
Kmods SIG 50e2b3
+		ep = exfat_get_dentry_cached(es, 0);
Kmods SIG 50e2b3
+		ep2 = exfat_get_dentry_cached(es, 1);
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 		info->type = exfat_get_entry_type(ep);
Kmods SIG 50e2b3
 		info->attr = le16_to_cpu(ep->dentry.file.attr);
Kmods SIG 50e2b3
@@ -681,7 +683,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
Kmods SIG 50e2b3
 			exfat_fs_error(sb,
Kmods SIG 50e2b3
 				"non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)",
Kmods SIG 50e2b3
 				i_size_read(dir), ei->dir.dir, ei->entry);
Kmods SIG 50e2b3
-			kfree(es);
Kmods SIG 50e2b3
+			exfat_free_dentry_set(es, false);
Kmods SIG 50e2b3
 			return -EIO;
Kmods SIG 50e2b3
 		}
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
@@ -700,7 +702,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
Kmods SIG 50e2b3
 				ep->dentry.file.access_time,
Kmods SIG 50e2b3
 				ep->dentry.file.access_date,
Kmods SIG 50e2b3
 				0);
Kmods SIG 50e2b3
-		kfree(es);
Kmods SIG 50e2b3
+		exfat_free_dentry_set(es, false);
Kmods SIG 50e2b3
 
Kmods SIG 50e2b3
 		if (info->type == TYPE_DIR) {
Kmods SIG 50e2b3
 			exfat_chain_set(&cdir, info->start_clu,
Kmods SIG 50e2b3
-- 
Kmods SIG 50e2b3
2.31.1
Kmods SIG 50e2b3