Skip to content

Commit 943af1f

Browse files
kohada-t2namjaejeon
authored andcommitted
exfat: optimize dir-cache
Optimize directory access based on exfat_entry_set_cache. - Hold bh instead of copied d-entry. - Modify bh->data directly instead of the copied d-entry. - Write back the retained bh instead of rescanning the d-entry-set. And - Remove unused cache related definitions. Signed-off-by: Tetsuhiro Kohada <[email protected]> Reviewed-by: Sungjong Seo <[email protected]> Signed-off-by: Namjae Jeon <[email protected]>
1 parent ed0f84d commit 943af1f

File tree

5 files changed

+124
-182
lines changed

5 files changed

+124
-182
lines changed

fs/exfat/dir.c

Lines changed: 75 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -32,35 +32,30 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
3232
struct exfat_chain *p_dir, int entry, unsigned short *uniname)
3333
{
3434
int i;
35-
struct exfat_dentry *ep;
3635
struct exfat_entry_set_cache *es;
3736

38-
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES, &ep);
37+
es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES);
3938
if (!es)
4039
return;
4140

42-
if (es->num_entries < 3)
43-
goto free_es;
44-
45-
ep += 2;
46-
4741
/*
4842
* First entry : file entry
4943
* Second entry : stream-extension entry
5044
* Third entry : first file-name entry
5145
* So, the index of first file-name dentry should start from 2.
5246
*/
53-
for (i = 2; i < es->num_entries; i++, ep++) {
47+
for (i = 2; i < es->num_entries; i++) {
48+
struct exfat_dentry *ep = exfat_get_dentry_cached(es, i);
49+
5450
/* end of name entry */
5551
if (exfat_get_entry_type(ep) != TYPE_EXTEND)
56-
goto free_es;
52+
break;
5753

5854
exfat_extract_uni_name(ep, uniname);
5955
uniname += EXFAT_FILE_NAME_LEN;
6056
}
6157

62-
free_es:
63-
kfree(es);
58+
exfat_free_dentry_set(es, false);
6459
}
6560

6661
/* read a directory entry from the opened directory */
@@ -590,62 +585,33 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
590585
return 0;
591586
}
592587

593-
int exfat_update_dir_chksum_with_entry_set(struct super_block *sb,
594-
struct exfat_entry_set_cache *es, int sync)
588+
void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es)
595589
{
596-
struct exfat_sb_info *sbi = EXFAT_SB(sb);
597-
struct buffer_head *bh;
598-
sector_t sec = es->sector;
599-
unsigned int off = es->offset;
600-
int chksum_type = CS_DIR_ENTRY, i, num_entries = es->num_entries;
601-
unsigned int buf_off = (off - es->offset);
602-
unsigned int remaining_byte_in_sector, copy_entries, clu;
590+
int chksum_type = CS_DIR_ENTRY, i;
603591
unsigned short chksum = 0;
592+
struct exfat_dentry *ep;
604593

605-
for (i = 0; i < num_entries; i++) {
606-
chksum = exfat_calc_chksum_2byte(&es->entries[i], DENTRY_SIZE,
607-
chksum, chksum_type);
594+
for (i = 0; i < es->num_entries; i++) {
595+
ep = exfat_get_dentry_cached(es, i);
596+
chksum = exfat_calc_chksum_2byte(ep, DENTRY_SIZE, chksum,
597+
chksum_type);
608598
chksum_type = CS_DEFAULT;
609599
}
600+
ep = exfat_get_dentry_cached(es, 0);
601+
ep->dentry.file.checksum = cpu_to_le16(chksum);
602+
es->modified = true;
603+
}
610604

611-
es->entries[0].dentry.file.checksum = cpu_to_le16(chksum);
605+
void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync)
606+
{
607+
int i;
612608

613-
while (num_entries) {
614-
/* write per sector base */
615-
remaining_byte_in_sector = (1 << sb->s_blocksize_bits) - off;
616-
copy_entries = min_t(int,
617-
EXFAT_B_TO_DEN(remaining_byte_in_sector),
618-
num_entries);
619-
bh = sb_bread(sb, sec);
620-
if (!bh)
621-
goto err_out;
622-
memcpy(bh->b_data + off,
623-
(unsigned char *)&es->entries[0] + buf_off,
624-
EXFAT_DEN_TO_B(copy_entries));
625-
exfat_update_bh(sb, bh, sync);
626-
brelse(bh);
627-
num_entries -= copy_entries;
628-
629-
if (num_entries) {
630-
/* get next sector */
631-
if (exfat_is_last_sector_in_cluster(sbi, sec)) {
632-
clu = exfat_sector_to_cluster(sbi, sec);
633-
if (es->alloc_flag == ALLOC_NO_FAT_CHAIN)
634-
clu++;
635-
else if (exfat_get_next_cluster(sb, &clu))
636-
goto err_out;
637-
sec = exfat_cluster_to_sector(sbi, clu);
638-
} else {
639-
sec++;
640-
}
641-
off = 0;
642-
buf_off += EXFAT_DEN_TO_B(copy_entries);
643-
}
609+
for (i = 0; i < es->num_bh; i++) {
610+
if (es->modified)
611+
exfat_update_bh(es->sb, es->bh[i], sync);
612+
brelse(es->bh[i]);
644613
}
645-
646-
return 0;
647-
err_out:
648-
return -EIO;
614+
kfree(es);
649615
}
650616

651617
static int exfat_walk_fat_chain(struct super_block *sb,
@@ -820,34 +786,40 @@ static bool exfat_validate_entry(unsigned int type,
820786
}
821787
}
822788

789+
struct exfat_dentry *exfat_get_dentry_cached(
790+
struct exfat_entry_set_cache *es, int num)
791+
{
792+
int off = es->start_off + num * DENTRY_SIZE;
793+
struct buffer_head *bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)];
794+
char *p = bh->b_data + EXFAT_BLK_OFFSET(off, es->sb);
795+
796+
return (struct exfat_dentry *)p;
797+
}
798+
823799
/*
824800
* Returns a set of dentries for a file or dir.
825801
*
826-
* Note that this is a copy (dump) of dentries so that user should
827-
* call write_entry_set() to apply changes made in this entry set
828-
* to the real device.
802+
* Note It provides a direct pointer to bh->data via exfat_get_dentry_cached().
803+
* User should call exfat_get_dentry_set() after setting 'modified' to apply
804+
* changes made in this entry set to the real device.
829805
*
830806
* in:
831807
* sb+p_dir+entry: indicates a file/dir
832808
* type: specifies how many dentries should be included.
833-
* out:
834-
* file_ep: will point the first dentry(= file dentry) on success
835809
* return:
836810
* pointer of entry set on success,
837811
* NULL on failure.
838812
*/
839813
struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
840-
struct exfat_chain *p_dir, int entry, unsigned int type,
841-
struct exfat_dentry **file_ep)
814+
struct exfat_chain *p_dir, int entry, unsigned int type)
842815
{
843-
int ret;
816+
int ret, i, num_bh;
844817
unsigned int off, byte_offset, clu = 0;
845-
unsigned int entry_type;
846818
sector_t sec;
847819
struct exfat_sb_info *sbi = EXFAT_SB(sb);
848820
struct exfat_entry_set_cache *es;
849-
struct exfat_dentry *ep, *pos;
850-
unsigned char num_entries;
821+
struct exfat_dentry *ep;
822+
int num_entries;
851823
enum exfat_validate_dentry_mode mode = ES_MODE_STARTED;
852824
struct buffer_head *bh;
853825

@@ -861,84 +833,65 @@ struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
861833
if (ret)
862834
return NULL;
863835

836+
es = kzalloc(sizeof(*es), GFP_KERNEL);
837+
if (!es)
838+
return NULL;
839+
es->sb = sb;
840+
es->modified = false;
841+
864842
/* byte offset in cluster */
865843
byte_offset = EXFAT_CLU_OFFSET(byte_offset, sbi);
866844

867845
/* byte offset in sector */
868846
off = EXFAT_BLK_OFFSET(byte_offset, sb);
847+
es->start_off = off;
869848

870849
/* sector offset in cluster */
871850
sec = EXFAT_B_TO_BLK(byte_offset, sb);
872851
sec += exfat_cluster_to_sector(sbi, clu);
873852

874853
bh = sb_bread(sb, sec);
875854
if (!bh)
876-
return NULL;
877-
878-
ep = (struct exfat_dentry *)(bh->b_data + off);
879-
entry_type = exfat_get_entry_type(ep);
855+
goto free_es;
856+
es->bh[es->num_bh++] = bh;
880857

881-
if (entry_type != TYPE_FILE && entry_type != TYPE_DIR)
882-
goto release_bh;
858+
ep = exfat_get_dentry_cached(es, 0);
859+
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
860+
goto free_es;
883861

884862
num_entries = type == ES_ALL_ENTRIES ?
885863
ep->dentry.file.num_ext + 1 : type;
886-
es = kmalloc(struct_size(es, entries, num_entries), GFP_KERNEL);
887-
if (!es)
888-
goto release_bh;
889-
890864
es->num_entries = num_entries;
891-
es->sector = sec;
892-
es->offset = off;
893-
es->alloc_flag = p_dir->flags;
894-
895-
pos = &es->entries[0];
896-
897-
while (num_entries) {
898-
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
899-
goto free_es;
900865

901-
/* copy dentry */
902-
memcpy(pos, ep, sizeof(struct exfat_dentry));
903-
904-
if (--num_entries == 0)
905-
break;
906-
907-
if (((off + DENTRY_SIZE) & (sb->s_blocksize - 1)) <
908-
(off & (sb->s_blocksize - 1))) {
909-
/* get the next sector */
910-
if (exfat_is_last_sector_in_cluster(sbi, sec)) {
911-
if (es->alloc_flag == ALLOC_NO_FAT_CHAIN)
912-
clu++;
913-
else if (exfat_get_next_cluster(sb, &clu))
914-
goto free_es;
915-
sec = exfat_cluster_to_sector(sbi, clu);
916-
} else {
917-
sec++;
918-
}
919-
920-
brelse(bh);
921-
bh = sb_bread(sb, sec);
922-
if (!bh)
866+
num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb);
867+
for (i = 1; i < num_bh; i++) {
868+
/* get the next sector */
869+
if (exfat_is_last_sector_in_cluster(sbi, sec)) {
870+
if (p_dir->flags == ALLOC_NO_FAT_CHAIN)
871+
clu++;
872+
else if (exfat_get_next_cluster(sb, &clu))
923873
goto free_es;
924-
off = 0;
925-
ep = (struct exfat_dentry *)bh->b_data;
874+
sec = exfat_cluster_to_sector(sbi, clu);
926875
} else {
927-
ep++;
928-
off += DENTRY_SIZE;
876+
sec++;
929877
}
930-
pos++;
878+
879+
bh = sb_bread(sb, sec);
880+
if (!bh)
881+
goto free_es;
882+
es->bh[es->num_bh++] = bh;
931883
}
932884

933-
if (file_ep)
934-
*file_ep = &es->entries[0];
935-
brelse(bh);
885+
/* validiate cached dentries */
886+
for (i = 1; i < num_entries; i++) {
887+
ep = exfat_get_dentry_cached(es, i);
888+
if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode))
889+
goto free_es;
890+
}
936891
return es;
937892

938893
free_es:
939-
kfree(es);
940-
release_bh:
941-
brelse(bh);
894+
exfat_free_dentry_set(es, false);
942895
return NULL;
943896
}
944897

fs/exfat/exfat_fs.h

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,8 @@ enum {
7171
#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */
7272
#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE)
7373

74-
#define FAT_CACHE_SIZE 128
75-
#define FAT_CACHE_HASH_SIZE 64
76-
#define BUF_CACHE_SIZE 256
77-
#define BUF_CACHE_HASH_SIZE 64
74+
/* Enough size to hold 256 dentry (even 512 Byte sector) */
75+
#define DIR_CACHE_SIZE (256*sizeof(struct exfat_dentry)/512+1)
7876

7977
#define EXFAT_HINT_NONE -1
8078
#define EXFAT_MIN_SUBDIR 2
@@ -170,14 +168,12 @@ struct exfat_hint {
170168
};
171169

172170
struct exfat_entry_set_cache {
173-
/* sector number that contains file_entry */
174-
sector_t sector;
175-
/* byte offset in the sector */
176-
unsigned int offset;
177-
/* flag in stream entry. 01 for cluster chain, 03 for contig. */
178-
int alloc_flag;
171+
struct super_block *sb;
172+
bool modified;
173+
unsigned int start_off;
174+
int num_bh;
175+
struct buffer_head *bh[DIR_CACHE_SIZE];
179176
unsigned int num_entries;
180-
struct exfat_dentry entries[];
181177
};
182178

183179
struct exfat_dir_entry {
@@ -451,8 +447,7 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
451447
int entry, int order, int num_entries);
452448
int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
453449
int entry);
454-
int exfat_update_dir_chksum_with_entry_set(struct super_block *sb,
455-
struct exfat_entry_set_cache *es, int sync);
450+
void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es);
456451
int exfat_calc_num_entries(struct exfat_uni_name *p_uniname);
457452
int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
458453
struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
@@ -463,9 +458,11 @@ int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
463458
struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
464459
struct exfat_chain *p_dir, int entry, struct buffer_head **bh,
465460
sector_t *sector);
461+
struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es,
462+
int num);
466463
struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
467-
struct exfat_chain *p_dir, int entry, unsigned int type,
468-
struct exfat_dentry **file_ep);
464+
struct exfat_chain *p_dir, int entry, unsigned int type);
465+
void exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync);
469466
int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
470467

471468
/* inode.c */

fs/exfat/file.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,9 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
9696
unsigned int num_clusters_new, num_clusters_phys;
9797
unsigned int last_clu = EXFAT_FREE_CLUSTER;
9898
struct exfat_chain clu;
99-
struct exfat_dentry *ep, *ep2;
10099
struct super_block *sb = inode->i_sb;
101100
struct exfat_sb_info *sbi = EXFAT_SB(sb);
102101
struct exfat_inode_info *ei = EXFAT_I(inode);
103-
struct exfat_entry_set_cache *es = NULL;
104102
int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0;
105103

106104
/* check if the given file ID is opened */
@@ -153,12 +151,15 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
153151
/* update the directory entry */
154152
if (!evict) {
155153
struct timespec64 ts;
154+
struct exfat_dentry *ep, *ep2;
155+
struct exfat_entry_set_cache *es;
156156

157157
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry,
158-
ES_ALL_ENTRIES, &ep);
158+
ES_ALL_ENTRIES);
159159
if (!es)
160160
return -EIO;
161-
ep2 = ep + 1;
161+
ep = exfat_get_dentry_cached(es, 0);
162+
ep2 = exfat_get_dentry_cached(es, 1);
162163

163164
ts = current_time(inode);
164165
exfat_set_entry_time(sbi, &ts,
@@ -185,10 +186,8 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
185186
ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
186187
}
187188

188-
if (exfat_update_dir_chksum_with_entry_set(sb, es,
189-
inode_needs_sync(inode)))
190-
return -EIO;
191-
kfree(es);
189+
exfat_update_dir_chksum_with_entry_set(es);
190+
exfat_free_dentry_set(es, inode_needs_sync(inode));
192191
}
193192

194193
/* cut off from the FAT chain */

0 commit comments

Comments
 (0)