diren scan profiling report
As I said in "[RFC] FAT dirent scan with hint"
<[email protected]>, we realized that FAT/VFAT has
poor performance with scanning directory entries.
Per discussions with Ogawa-san, VFAT maintainer, I took profiling data to seek better solution. Here are results attached.
In short, I would say we need to reduce following factors.
a) number of iterations inside fat_search_long()
b) number of callings to uni16_to_x8()
c) number of callings to fat__get_entry(), for short name scan.
In another E-mail, I'll send revised version patch which use hint values to scan dirent. That patch would reduce number of iterations inside fat_search_long() and fat_scan(). Those contributes reductions above a)-c) factors.
RESULTS:
1-1) Top 10 function consuming time on long file name dir scan (vfat_lookup) for 4095th LFN entry.
% kd -n 10 /tmp/lfn_kft-a.log
Function Count Time Average Local
----------------------------------- ----- -------- -------- --------
vfat_lookup 1 1242285 1242285 705 vfat_find 1 1241522 1241522 629 fat_search_long 1 1240893 1240893 887490 uni16_to_x8 4209 250222 59 249143 fat_get_entry 765 69908 91 3306 fat__get_entry 762 66602 87 50796 fat_shortname2uni 593 33158 55 11393 fat_short2lower_uni 414 21765 52 20860 fat_bmap 202 13425 66 857 fat_bmap_cluster 201 12568 62 1015
*)To exclude profiling overhead, doesn't count functions < 50usec **) Remove "inline" from fat/dir.c to count up inline funcs.
1-2) Top 10 function consuming time on short file name dir scan (fat_scan) for 4095th short file name entry.
% kd -n 10 /tmp/kft-a.log
Function Count Time Average Local
----------------------------------- ----- -------- -------- --------fat_scan 1 149743 149743 68069
fat_get_short_entry 812 81512 100 11706 fat_get_entry 765 69806 91 3252 fat__get_entry 762 66554 87 50425 fat_bmap 199 13181 66 838 fat_bmap_cluster 198 12343 62 1055 fat_get_cluster 194 11288 58 8481 fat_ent_read 52 2807 53 2583 __getblk 32 2339 73 326 __bread 29 2145 73 598
*)To exclude profiling overhead, doesn't count functions < 50usec **) Remove "inline" from fat/dir.c to count up inline funcs.
2-1) how to get result 1-1)
% ( cat <<__CONF
new
begin
trigger start entry 0xc00cd904 # vfat_lookup
trigger stop exit 0xc00cd904 # vfat_lookup
filter mintime 50
filter maxtime 0
filter noints
logentries 5000000end
__CONF
) > /proc/kft
% echo prime > /proc/kft
# mount vtat
% time stat 4095th-shortfilename-entry
real 0m1.351s user 0m0.007s sys 0m1.295s
# umount
# get data from /proc/kft_data
2-2) how to get result 1-2)
% ( cat <<__CONF
new
begin
trigger start entry 0xc00c36dc # fat_scan
trigger stop exit 0xc00c36dc # fat_scan
filter mintime 50
filter maxtime 0
filter noints
logentries 5000000end__CONF
) > /proc/kft
% echo prime > /proc/kft
# mount msdos
% time stat 4095th-shortname-entry
real 0m0.216s user 0m0.002s sys 0m0.200s
# umount
# get data from /proc/kft_data
Signature · 2 lines
Hiroyuki Machida [email protected] SSW Dept. HENC, Sony Corp.
PATCH 1/1 FAT dirent scan with hin take #2
Here is a revised version of dirent scan patch, mentioned at following E-mail.
This patch addresses performance damages on "ls | xargs xxx" and reverse order scan which are reported to the previous patch.
With this patch, fat_search_long() and fat_scan() use hint value as start of scan. For each directory holds multiple hint value entries. The entry would be selected by hash value based on scan target name and PID. Hint value would be calculated based on the entry previously found entry, so that the hint can cover backward neighborhood.
This patch is for 2.6.12, because I tested it at the last weekend...
Quoted 13 lines · Hiroyuki
As I said in "[RFC] FAT dirent scan with hint"
<[email protected]>, we realized that FAT/VFAT haspoor performance with scanning directory entries.
Per discussions with Ogawa-san, VFAT maintainer, I took profiling data to seek better solution. Here are results attached.
In short, I would say we need to reduce following factors.
a) number of iterations insidefat_search_long()
b) number of callings touni16_to_x8()
c) number of callings tofat__get_entry(), for short name scan.
Quoted 5 lines
In another E-mail, I'll send revised version patch which use hint values to scan dirent. That patch would reduce number of iterations inside
fat_search_long()andfat_scan(). Those contributes reductions above a)-c) factors.
: snip :
Signature · 2 lines
Hiroyuki Machida [email protected] SSW Dept. HENC, Sony Corp.
This patch enables using hint nformation on scanning dir. It reaches excelent performance with "ls -l" for over 1000 entries.
* fat-dirscan-with-hint.patch
fs/fat/dir.c | 133 ++++++++++++++++++++++++++++++++++++++++++++--- fs/fat/inode.c | 13 ++++ include/linux/msdos_fs.h | 2 3 files changed, 140 insertions(+), 8 deletions(-)
* modified files
include/linux/msdos_fs.h +2 -0
@@ -255,6 +255,8 @@ struct msdos_inode_info {/* for avoiding the race between fat_free() and fat_get_cluster() */ unsigned int cache_valid_id; +struct semaphore *scan_lock;/* lock for dirscan hints */ +loff_t *scan_hints;/* dirscan hints */ loff_t mmu_private;int i_start;/* first cluster or 0 */ int i_logstart;/* logical first cluster */
fs/fat/dir.c +125 -8
175 lines not on this page · raw message
fs/fat/inode.c +13 -0
@@ -242,6 +242,8 @@ static int fat_fill_inode(struct inode * inode->i_version++; inode->i_generation =get_seconds (); + init_MUTEX(&(MSDOS_I(inode)->scan_lock); +MSDOS_I (inode)->scan_hints =0 ;if ((de->attr &ATTR_DIR ) && !IS_FREE (de->name)) { inode->i_generation &= ~1 ; inode->i_mode = MSDOS_MKMODE(de->attr,
@@ -345,6 +347,15 @@ static void fat_delete_inode(struct inodstatic void fat_clear_inode (struct inode *inode) {struct msdos_sb_info *sbi =MSDOS_SB (inode->i_sb); +void *hints; + + down(&(MSDOS_I(inode)->scan_lock); + hints = (void *)(MSDOS_I (inode)->scan_hints); +if (hints) { +MSDOS_I (inode)->scan_hints =0 ; + } + up(&(MSDOS_I(inode)->scan_lock); +if (hints)kfree (hints);if (is_bad_inode (inode))return ;
@@ -1011,6 +1022,8 @@ static int fat_read_root(struct inode *istruct msdos_sb_info *sbi =MSDOS_SB (sb);int error; + init_MUTEX(&(MSDOS_I(inode)->scan_lock); +MSDOS_I (inode)->scan_hints =0 ;MSDOS_I (inode)->i_pos =0 ; inode->i_uid = sbi->options.fs_uid; inode->i_gid = sbi->options.fs_gid;
Re: [PATCH][FAT] FAT dirent scan with hin take #2
Hi,
Some coding style nitpicks.
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 222
Quoted 12 lines · Hiroyuki
fs/fat/dir.c +12
-0+inline +static int hint_allocate (struct inode *dir) +{ +void *hints; +int err =0 ; + +if (!MSDOS_I (dir)->scan_hints) { + hints =kmalloc (FAT_SCAN_NWAY *sizeof (loff_t),GFP_KERNEL ); +if (hints) +memset (hints,0 ,FAT_SCAN_NWAY *sizeof (loff_t)); +else + err = -ENOMEM ;
Please consider using kcalloc().
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 225
Quoted 3 lines
fs/fat/dir.c +3
-0+ +down (&MSDOS_I (dir)->scan_lock); +if (MSDOS_I (dir)->scan_hints) err = -EINVAL ;
Please put the statement after if clause to a separate line. The above makes code very hard to read.
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 229
Quoted 4 lines
fs/fat/dir.c +4
-0+if (!err)MSDOS_I (dir)->scan_hints = hints; +up (&MSDOS_I (dir)->scan_lock); +if (err == -EINVAL ) { +if (hints)kfree (hints);
kfree() can handle NULLs just fine so please drop the redundant check.
Pekka
Re: [PATCH][FAT] FAT dirent scan with hin take #2
In reply to Machida, HiroyukiHi,
Some more.
Quoted 5 lines · Hiroyuki
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 renamed from fs/fat/inode.c 2005-08-29 09:38:53.308587787 +0900 +0
-0@@ -345,6 +347,15 @@ static void fat_delete_inode(struct inod static void fat_clear_inode(struct inode *inode) {
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 static void fat_delete_inode(struct inod · line 358
Quoted 10 lines
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 renamed from fs/fat/inode.c 2005-08-29 09:38:53.308587787 +0900 +9
-0struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); + void *hints; + + down(&(MSDOS_I(inode)->scan_lock); + hints = (void *)(MSDOS_I(inode)->scan_hints); + if (hints) { + MSDOS_I(inode)->scan_hints = 0; + } + up(&(MSDOS_I(inode)->scan_lock); + if (hints) kfree(hints);
Please just make the local variable hints of type loff_t * to get rid of the pointless casting.
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 static int fat_read_root(struct inode *i · line 1026
Quoted 9 lines
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 renamed from fs/fat/inode.c 2005-08-29 09:38:53.308587787 +0900 +2
-0if (is_bad_inode(inode)) return;@@ -1011,6 +1022,8 @@ static int fat_read_root(struct inode *i struct msdos_sb_info *sbi = MSDOS_SB(sb); int error; + init_MUTEX(&(MSDOS_I(inode)->scan_lock); + MSDOS_I(inode)->scan_hints = 0;
Use NULLs instead of 0 for pointers to keep new code sparse-clean.
Re: [PATCH][FAT] FAT dirent scan with hin take #2
Quoted 15 lines · Pekka Enberg
Hi,
Some more.
Quoted 10 lines · Hiroyuki
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 renamed from fs/fat/inode.c 2005-08-29 09:38:53.308587787 +0900 +4
-0@@ -345,6 +347,15 @@ static void fat_delete_inode(struct inod static void fat_clear_inode(struct inode *inode) { struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); + void *hints; + + down(&(MSDOS_I(inode)->scan_lock); + hints = (void *)(MSDOS_I(inode)->scan_hints);
Quoted 8 lines
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 static void fat_delete_inode(struct inod · line 358
Quoted 5 lines
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 renamed from fs/fat/inode.c 2005-08-29 09:38:53.308587787 +0900 +5
-0+ if (hints) { + MSDOS_I(inode)->scan_hints = 0; + } + up(&(MSDOS_I(inode)->scan_lock); + if (hints) kfree(hints);Please just make the local variable hints of type
loff_t* to get rid of the pointless casting.
For voids no casting is needed anyway, the type exists for the very reason of being compatible with any other.
Quoted 10 lines
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 static int fat_read_root(struct inode *i · line 1026
Quoted 8 lines
fs/fat/inode.c 2005-08-29 09:39:33.889555606 +0900 renamed from fs/fat/inode.c 2005-08-29 09:38:53.308587787 +0900 +2
-0if (is_bad_inode(inode)) return;@@ -1011,6 +1022,8 @@ static int fat_read_root(struct inode *i struct msdos_sb_info *sbi = MSDOS_SB(sb); int error; + init_MUTEX(&(MSDOS_I(inode)->scan_lock); + MSDOS_I(inode)->scan_hints = 0;Use NULLs instead of 0 for pointers to keep new code sparse-clean.
It's a good idea for readability, too.
Signature · 2 lines
Vojtech Pavlik SuSE Labs, SuSE CR
OGAWA Hirofumi
maintainerRe: [PATCH][FAT] FAT dirent scan with hin take #2
In reply to Machida, HiroyukiQuoted 11 lines · Hiroyuki
Here is a revised version of dirent scan patch, mentioned at following E-mail.
This patch addresses performance damages on "ls | xargs xxx" and reverse order scan which are reported to the previous patch.
With this patch,
fat_search_long()andfat_scan()use hint value as start of scan. For each directory holds multiple hint value entries. The entry would be selected by hash value based on scan target name and PID. Hint value would be calculated based on the entry previously found entry, so that the hint can cover backward neighborhood.
This patch couldn't compile. I assume you post a wrong patch...?
The code is strange... Is the hint value related to the previously accessed entry?
This seems to be randomly cacheing the recent access position... Is it your intention of this patch?
Signature · 1 line
OGAWA Hirofumi <[email protected]>
OGAWA Hirofumi
maintainerRe: [PATCH][FAT] FAT dirent scan with hin take #2
Quoted 1 line · OGAWA Hirofumi
This patch couldn't compile. I assume you post a wrong patch...?
^^^^^
versionSignature · 1 line
OGAWA Hirofumi <[email protected]>
PATCH 1/1 FAT dirent scan with hin take #3
In reply to OGAWA HirofumiSorry, I send out wrong version. I attached the latest patch to 2.6.13.
Quoted 16 lines · OGAWA Hirofumi
Quoted 11 lines · Hiroyuki
Here is a revised version of dirent scan patch, mentioned at following E-mail.
This patch addresses performance damages on "ls | xargs xxx" and reverse order scan which are reported to the previous patch.
With this patch,
fat_search_long()andfat_scan()use hint value as start of scan. For each directory holds multiple hint value entries. The entry would be selected by hash value based on scan target name and PID. Hint value would be calculated based on the entry previously found entry, so that the hint can cover backward neighborhood.
Quoted 7 lines
This patch couldn't compile. I assume you post a wrong patch...?
The code is strange... Is the hint value related to the previously accessed entry?
This seems to be randomly cacheing the recent access position... Is it your intention of this patch?
Right, it looks like TLB, which holds cache "Physical addres" correponding to "Logical address". In this case, PID and file name to be looked up, perform role of "Logical address".
I prepared vfat16 fs where over 4000 files in /foo and 200 files in root, then checked with following cases;
without patch with patch
ls-vfat 59.0 2.49
xargs-vfat 61.3 11.9
stat-vfat 41 17
stat-vfat-reverse 56 32
ls-msdos 14.2 0.62
xargs-msdos 16.8 16.7
stat-msdos 21 15
stat-msdos-reverse 22 16
- all valuses have sec unit
1-1 ls-vat)
mount vfat to /a/usr/bin/time -p sh -c "/usr/bin/ls -Rl /a > /dev/null" umount /a
1-2 ls-msdos)
mount msdos to /a/usr/bin/time -p sh -c "/usr/bin/ls -Rl /a > /dev/null" umount /a
2-1 xargs-vfat)
mount vfat to /a
cd /a/foo/usr/bin/time -p sh -c "(/usr/bin/ls | /usr/in/xargs stat) > /dev/null" umount /a
2-2 xargs-msdos)
mount msdos to /a
cd /a/foo/usr/bin/time -p sh -c "(/usr/bin/ls | /usr/in/xargs stat) > /dev/null" umount /a
3-1 stat-vfat)
mount vfat to /a
cd /a/foo
repeat stat files which have located in the last 1024 dir entries
with incremental order.
umount /a
3-2 stat-vfat-reverse)
do 3-1 with decremental order
3-3 stat-msdos)
do 3-1 with msdos fs
3-4 stat-msdos-reverse)
do 3-2 with msdos fs
Signature · 1 line
Hiroyuki Machida
This patch enables using hint information on scanning dir. It achieves excellent performance with "ls -l" for over 1000 entries.
* fat-dirscan-with-hint_3.patch for linux 2.6.13
fs/fat/dir.c | 130 ++++++++++++++++++++++++++++++++++++++++++++--- fs/fat/inode.c | 13 ++++ include/linux/msdos_fs.h | 2 3 files changed, 137 insertions(+), 8 deletions(-)
* modified files
fs/fat/dir.c +122 -8
@@ -201,6 +201,88 @@ fat_shortname2uni(struct nls_table *nls,* Return values: negative -> error, 0 -> not found, positive -> found, * value is the total amount of slots, including the shortname entry. */ + +#define FAT_SCAN_SHIFT 4/* 2x8 way scan hints */ +#define FAT_SCAN_NWAY (1<<FAT_SCAN_SHIFT) + +inline +static int hint_allocate (struct inode *dir) +{ +loff_t *hints; +int err =0 ; + +if (!MSDOS_I (dir)->scan_hints) { + hints =kcalloc (FAT_SCAN_NWAY ,sizeof (loff_t),GFP_KERNEL ); +if (!hints) + err = -ENOMEM ; + +down (&MSDOS_I (dir)->scan_lock); +if (MSDOS_I (dir)->scan_hints) + err = -EINVAL ; +if (!err) +MSDOS_I (dir)->scan_hints = hints; +up (&MSDOS_I (dir)->scan_lock); +if (err == -EINVAL ) { +kfree (hints); + err =0 ; + } + } +return err; +} + + +inline +static void hint_record (struct inode *dir,struct fat_slot_info *sinfo, +int hindex) +{ +loff_t under_scan_off, nent; + + nent = (dir->i_size >PAGE_SIZE ? dir->i_size :PAGE_SIZE ) + /sizeof (struct msdos_dir_entry ); + +/* educational guess; try to cover 1/4 previous range */ + nent >>= (FAT_SCAN_SHIFT +2 ); + under_scan_off = nent *sizeof (struct msdos_dir_entry ); + +if (sinfo->slot_off > under_scan_off) +MSDOS_I (dir)->scan_hints[hindex] = + sinfo->slot_off - under_scan_off; +else +MSDOS_I (dir)->scan_hints[hindex] =0 ; +} + + +inline +static int hint_index_body (const unsigned char *name,int name_len,int check_null) +{ +int i; +int val =0 ; +unsigned char *p = (unsigned char *) name; +int id = current->pid; + +for (i=0 ; i<name_len; i++) { +if (check_null && !*p)break ; + val = ((val <<1 ) &0xfe ) | ((val &0x80 ) ?1 :0 ); + val ^= *p; + p ++; + } + id = ((id >>8 ) &0xf ) ^ (id &0xf ); + val = (val <<1 ) | (id &1 ); +return val & (FAT_SCAN_NWAY -1 ); +} + +inline +static int lfn_hint_index (const unsigned char *name,int name_len) +{ +return hint_index_body (name, name_len,0 ); +} + +inline +static int hint_index (const unsigned char *name) +{ +return hint_index_body (name,MSDOS_NAME ,1 ); +} +int fat_search_long (struct inode *inode,const unsigned char *name,int name_len,struct fat_slot_info *sinfo) {
@@ -218,13 +300,26 @@ int fat_search_long(struct inode *inode,int utf8 = sbi->options.utf8;int anycase = (sbi->options.name_check !='s' );unsigned short opt_shortname = sbi->options.shortname; -loff_t cpos =0 ;int chl, i, j, last_u, err; +loff_t cpos, start_off; +int reach_bottom =0 ; +int hindex; +int ret; + + ret =hint_allocate (inode); +if (ret <0 )return ret; + hindex =lfn_hint_index (name, name_len); + start_off = cpos =MSDOS_I (inode)->scan_hints[hindex]; err = -ENOENT ;while (1 ) { -if (fat_get_entry (inode, &cpos, &bh, &de) ==-1 ) - goto EODir; +Top: +if (reach_bottom && cpos >= start_off) goto EODir; +if (fat_get_entry (inode, &cpos, &bh, &de) ==-1 ) { +if (!start_off) goto EODir; + reach_bottom ++; cpos =0 ; +continue ; + } parse_record: nr_slots =0 ;if (de->name[0 ] ==DELETED_FLAG )
@@ -274,8 +369,11 @@ parse_long:if (ds->id &0x40 ) { unicode[offset +13 ] =0 ; } -if (fat_get_entry (inode, &cpos, &bh, &de) <0 ) - goto EODir; +if (fat_get_entry (inode, &cpos, &bh, &de) <0 ) { +if (!start_off) goto EODir; + reach_bottom ++; cpos =0 ; + goto Top; + }if (slot ==0 )break ; ds = (struct msdos_dir_slot *) de;
@@ -363,6 +461,7 @@ Found: sinfo->de = de; sinfo->bh = bh; sinfo->i_pos =fat_make_i_pos (sb, sinfo->bh, sinfo->de); +hint_record (inode, sinfo, hindex); err =0 ; EODir:if (unicode)
@@ -838,17 +937,32 @@ int fat_scan(struct inode *dir, const un struct fat_slot_info *sinfo) {struct super_block *sb = dir->i_sb; +loff_t start_off; +int hindex; +int ret; +int reach_bottom =0 ; + + ret =hint_allocate (dir); +if (ret <0 )return ret; + hindex =hint_index (name); - sinfo->slot_off =0 ; + sinfo->slot_off = start_off =MSDOS_I (dir)->scan_hints[hindex]; sinfo->bh =NULL ; -while (fat_get_short_entry (dir, &sinfo->slot_off, &sinfo->bh, - &sinfo->de) >=0 ) { +while (1 ) { +if (fat_get_short_entry (dir, &sinfo->slot_off, + &sinfo->bh, &sinfo->de) <0 ) { +if (!start_off)break ; + sinfo->slot_off =0LL ; reach_bottom ++; +continue ; + }if (!strncmp (sinfo->de->name, name,MSDOS_NAME )) { sinfo->slot_off -=sizeof (*sinfo->de); sinfo->nr_slots =1 ; sinfo->i_pos =fat_make_i_pos (sb, sinfo->bh, sinfo->de); +hint_record (dir, sinfo, hindex);return 0 ; } +if (reach_bottom && (start_off <= sinfo->slot_off))break ; }return -ENOENT ; }
fs/fat/inode.c +13 -0
@@ -242,6 +242,8 @@ static int fat_fill_inode(struct inode * inode->i_version++; inode->i_generation =get_seconds (); +init_MUTEX (&MSDOS_I (inode)->scan_lock); +MSDOS_I (inode)->scan_hints =0 ;if ((de->attr &ATTR_DIR ) && !IS_FREE (de->name)) { inode->i_generation &= ~1 ; inode->i_mode = MSDOS_MKMODE(de->attr,
@@ -345,6 +347,15 @@ static void fat_delete_inode(struct inodstatic void fat_clear_inode (struct inode *inode) {struct msdos_sb_info *sbi =MSDOS_SB (inode->i_sb); +loff_t *hints; + +down (&MSDOS_I (inode)->scan_lock); + hints = (MSDOS_I (inode)->scan_hints); +if (hints) { +MSDOS_I (inode)->scan_hints =NULL ; + } +up (&MSDOS_I (inode)->scan_lock); +kfree (hints);if (is_bad_inode (inode))return ;
@@ -1011,6 +1022,8 @@ static int fat_read_root(struct inode *istruct msdos_sb_info *sbi =MSDOS_SB (sb);int error; +init_MUTEX (&MSDOS_I (inode)->scan_lock); +MSDOS_I (inode)->scan_hints =0 ;MSDOS_I (inode)->i_pos =0 ; inode->i_uid = sbi->options.fs_uid; inode->i_gid = sbi->options.fs_gid;
include/linux/msdos_fs.h +2 -0
@@ -255,6 +255,8 @@ struct msdos_inode_info {/* for avoiding the race between fat_free() and fat_get_cluster() */ unsigned int cache_valid_id; +struct semaphore scan_lock;/* lock for dirscan hints */ +loff_t *scan_hints;/* dirscan hints */ loff_t mmu_private;int i_start;/* first cluster or 0 */ int i_logstart;/* logical first cluster */
Re: [PATCH][FAT] FAT dirent scan with hin take #3
Hi,
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 217
Quoted 10 lines · Hiroyuki
fs/fat/dir.c +10
-0+inline +static int hint_allocate (struct inode *dir) +{ +loff_t *hints; +int err =0 ; + +if (!MSDOS_I (dir)->scan_hints) { + hints =kcalloc (FAT_SCAN_NWAY ,sizeof (loff_t),GFP_KERNEL ); +if (!hints) + err = -ENOMEM ;
Better to bail out here as...
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 221
Quoted 4 lines
fs/fat/dir.c +4
-0+ +down (&MSDOS_I (dir)->scan_lock); +if (MSDOS_I (dir)->scan_hints) + err = -EINVAL ;
...you might overwrite -ENOMEM here masking the real problem.
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 225
Quoted 4 lines
fs/fat/dir.c +4
-0+if (!err) +MSDOS_I (dir)->scan_hints = hints; +up (&MSDOS_I (dir)->scan_lock); +if (err == -EINVAL ) {
Gotos would make error handling much cleaner.
Review
Quoted 2 lines
(quoted diff) +2
-0+inline +static int hint_index_body(const unsigned char *name, int name_len, int check_null)
Please consider calling this __hint_index() instead as per normal naming conventions.
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 264
Quoted 8 lines
fs/fat/dir.c +8
-0+{ +int i; +int val =0 ; +unsigned char *p = (unsigned char *) name; +int id = current->pid; + +for (i=0 ; i<name_len; i++) { +if (check_null && !*p)break ;
Please put break on separate line. You still have quite a few of these.
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 272
Quoted 8 lines
fs/fat/dir.c +8
-0+ val = ((val <<1 ) &0xfe ) | ((val &0x80 ) ?1 :0 ); + val ^= *p; + p ++; + } + id = ((id >>8 ) &0xf ) ^ (id &0xf ); + val = (val <<1 ) | (id &1 ); +return val & (FAT_SCAN_NWAY -1 ); +}
Pekka
Re: [PATCH][FAT] FAT dirent scan with hin take #3
Hi,
Quoted 19 lines · Pekka Enberg
Hi,
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 217
Quoted 10 lines · Hiroyuki
fs/fat/dir.c +10
-0+inline +static int hint_allocate (struct inode *dir) +{ +loff_t *hints; +int err =0 ; + +if (!MSDOS_I (dir)->scan_hints) { + hints =kcalloc (FAT_SCAN_NWAY ,sizeof (loff_t),GFP_KERNEL ); +if (!hints) + err = -ENOMEM ;Better to bail out here as...
Quoted 8 lines
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 221
Quoted 4 lines
fs/fat/dir.c +4
-0+ +down (&MSDOS_I (dir)->scan_lock); +if (MSDOS_I (dir)->scan_hints) + err = -EINVAL ;...you might overwrite
-ENOMEMhere masking the real problem.
It's ok. If MSDOS_I(dir)->scan_hints isn't NULL, it's not error case. We need just kfree(hints) and return 0.(Assuming kfree() accepts NULL) I think EINVAL confuse you.
Quoted 9 lines
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 225
Quoted 5 lines
fs/fat/dir.c +4
-0+if (!err) +MSDOS_I (dir)->scan_hints = hints; +up (&MSDOS_I (dir)->scan_lock); +if (err == -EINVAL ) {Gotos would make error handling much cleaner.
How about this ?
if (!MSDOS_I(dir)->scan_hints) {
hints = kcalllo(....);
down
if (MSDOS_I(dir)->scan_hints) {
up
goto already_allocated;
}
if (hints)
MSDOS_I(dir)->scan_hints = hints;
up
}
return (hints == 0) ? -ENOMEM : 0;already_allocated:
kfree(hints); /* kfree accepts NULL */ return 0;
Quoted 7 lines
Review
Quoted 3 lines
(quoted diff) +2
-0+inline +static int hint_index_body(const unsigned char *name, int name_len, int check_null)Please consider calling this
__hint_index()instead as per normal naming conventions.
Agree.
Quoted 12 lines
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 264
Quoted 9 lines
fs/fat/dir.c +8
-0+{ +int i; +int val =0 ; +unsigned char *p = (unsigned char *) name; +int id = current->pid; + +for (i=0 ; i<name_len; i++) { +if (check_null && !*p)break ;Please put break on separate line. You still have quite a few of these.
Agree
Quoted 13 lines
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 272
Quoted 10 lines
fs/fat/dir.c +8
-0+ val = ((val <<1 ) &0xfe ) | ((val &0x80 ) ?1 :0 ); + val ^= *p; + p ++; + } + id = ((id >>8 ) &0xf ) ^ (id &0xf ); + val = (val <<1 ) | (id &1 ); +return val & (FAT_SCAN_NWAY -1 ); +}Pekka
Signature · 2 lines
Hiroyuki Machida [email protected] SSW Dept. HENC, Sony Corp.
Re: [PATCH][FAT] FAT dirent scan with hin take #3
Quoted 16 lines · Hiroyuki
How about this ?
if (!MSDOS_I(dir)->scan_hints) { hints = kcalllo(....); down if (MSDOS_I(dir)->scan_hints) { up goto already_allocated; } if (hints) MSDOS_I(dir)->scan_hints = hints; up } return (hints == 0) ? -ENOMEM : 0;
Quoted 3 lines
already_allocated:
kfree(hints); /* kfree accepts NULL */
return 0;
After finally understanding what you're doing, how about:
static inline int hint_allocate(struct inode *dir)
{
loff_t *hints;
int err = 0;
if (!MSDOS_I(dir)->scan_hints)
return 0;
hints = kcalloc(FAT_SCAN_NWAY, sizeof(loff_t), GFP_KERNEL);
if (!hints)
err = -ENOMEM;
down(&MSDOS_I(dir)->scan_lock);
/*
* We allocated memory without scan_lock so lets make sure
* no other thread completed hint_allocate() before us.
*/
if (!MSDOS_I(dir)->scan_hints) {
MSDOS_I(dir)->scan_hints = hints;
hints = NULL;
}
up(&MSDOS_I(dir)->scan_lock);
kfree(hints);
return err;
}Pekka
Re: [PATCH][FAT] FAT dirent scan with hin take #3
Quoted 8 lines · Pekka Enberg
After finally understanding what you're doing, how about:
static inline int
hint_allocate(struct inode *dir){ loff_t *hints; int err = 0; if (!MSDOS_I(dir)->scan_hints)
Should read:
if (MSDOS_I(dir)->scan_hints)
Quoted 23 lines
return 0; hints = kcalloc(FAT_SCAN_NWAY, sizeof(loff_t), GFP_KERNEL); if (!hints) err = -ENOMEM; down(&MSDOS_I(dir)->scan_lock); /* * We allocated memory without scan_lock so lets make sure * no other thread completed hint_allocate() before us. */ if (!MSDOS_I(dir)->scan_hints) { MSDOS_I(dir)->scan_hints = hints; hints = NULL; } up(&MSDOS_I(dir)->scan_lock); kfree(hints); return err; }Pekka
Re: [PATCH][FAT] FAT dirent scan with hin take #3
In reply to Machida, HiroyukiQuoted 7 lines · Hiroyuki
fs/fat/dir.c +7
-0+inline +static int hint_index_body (const unsigned char *name,int name_len,int check_null) +{ +int i; +int val =0 ; +unsigned char *p = (unsigned char *) name; +int id = current->pid;
fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 271
Quoted 10 lines
fs/fat/dir.c +10
-0+ +for (i=0 ; i<name_len; i++) { +if (check_null && !*p)break ; + val = ((val <<1 ) &0xfe ) | ((val &0x80 ) ?1 :0 ); + val ^= *p; + p ++; + } + id = ((id >>8 ) &0xf ) ^ (id &0xf ); + val = (val <<1 ) | (id &1 ); +return val & (FAT_SCAN_NWAY -1 );
Couldn't you use jhash() from <linux/jhash.h> here?
Pekka
Re: [PATCH][FAT] FAT dirent scan with hin take #3
Quoted 23 lines · Pekka Enberg
Quoted 7 lines · Hiroyuki
fs/fat/dir.c +7
-0+inline +static int hint_index_body (const unsigned char *name,int name_len,int check_null) +{ +int i; +int val =0 ; +unsigned char *p = (unsigned char *) name; +int id = current->pid;fs/fat/dir.c fat_shortname2uni(struct nls_table *nls, · line 271
Quoted 10 lines
fs/fat/dir.c +10
-0+ +for (i=0 ; i<name_len; i++) { +if (check_null && !*p)break ; + val = ((val <<1 ) &0xfe ) | ((val &0x80 ) ?1 :0 ); + val ^= *p; + p ++; + } + id = ((id >>8 ) &0xf ) ^ (id &0xf ); + val = (val <<1 ) | (id &1 ); +return val & (FAT_SCAN_NWAY -1 );Couldn't you use
jhash()from <linux/jhash.h> here?
Quoted 1 line
Pekka
Thanks, I'll replace it with functions in jhash.h, then check performance again.
Signature · 1 line
Hiroyuki Machida [email protected]
Re: [PATCH][FAT] FAT dirent scan with hin take #3
In reply to Machida, HiroyukiHi,
Quoted 113 lines · Hiroyuki
Sorry, I send out wrong version. I attached the latest patch to 2.6.13.
Quoted 16 lines · OGAWA Hirofumi
Quoted 11 lines · Hiroyuki
Here is a revised version of dirent scan patch, mentioned at following E-mail.
This patch addresses performance damages on "ls | xargs xxx" and reverse order scan which are reported to the previous patch.
With this patch,
fat_search_long()andfat_scan()use hint value as start of scan. For each directory holds multiple hint value entries. The entry would be selected by hash value based on scan target name and PID. Hint value would be calculated based on the entry previously found entry, so that the hint can cover backward neighborhood.Quoted 7 lines
This patch couldn't compile. I assume you post a wrong patch...?
The code is strange... Is the hint value related to the previously accessed entry?
This seems to be randomly cacheing the recent access position... Is it your intention of this patch?
Right, it looks like TLB, which holds cache "Physical addres" correponding to "Logical address". In this case, PID and file name to be looked up, perform role of "Logical address".
I prepared vfat16 fs where over 4000 files in /foo and 200 files in root, then checked with following cases;
without patch with patch ls-vfat 59.0 2.49 xargs-vfat 61.3 11.9 stat-vfat 41 17 stat-vfat-reverse 56 32ls-msdos 14.2 0.62 xargs-msdos 16.8 16.7 stat-msdos 21 15 stat-msdos-reverse 22 16- all valuses have sec unit
1-1 ls-vat)
mount vfat to /a/usr/bin/time-p sh -c "/usr/bin/ls-Rl /a >/dev/null" umount /a1-2 ls-msdos)
mount msdos to /a/usr/bin/time-p sh -c "/usr/bin/ls-Rl /a >/dev/null" umount /a2-1 xargs-vfat)
mount vfat to /a
cd /a/foo/usr/bin/time-p sh -c "(/usr/bin/ls|/usr/in/xargsstat) >/dev/null" umount /a2-2 xargs-msdos)
mount msdos to /a
cd /a/foo/usr/bin/time-p sh -c "(/usr/bin/ls|/usr/in/xargsstat) >/dev/null" umount /a3-1 stat-vfat)
mount vfat to /a
cd /a/foo
repeat stat files which have located in the last 1024 dir entries
with incremental order.
umount /a3-2 stat-vfat-reverse)
do 3-1 with decremental order3-3 stat-msdos)
do 3-1 with msdos fs3-4 stat-msdos-reverse)
do 3-2 with msdos fs--
Hiroyuki MachidaThis patch enables using hint information on scanning dir. It achieves excellent performance with "ls -l" for over 1000 entries.
* fat-dirscan-with-hint_3.patch for linux 2.6.13
fs/fat/dir.c | 130 ++++++++++++++++++++++++++++++++++++++++++++--- fs/fat/inode.c | 13 ++++ include/linux/msdos_fs.h | 2 3 files changed, 137 insertions(+), 8 deletions(-)Signed-off-by Hiroyuki Machida <[email protected]> for CELF* modified files
fs/fat/dir.c 2005-08-31 13:48:01.001119437 +0900 renamed from fs/fat/dir.c 2005-08-29 08:41:01.000000000 +0900 +1
-0@@ -201,6 +201,88 @@ fat_shortname2uni(struct nls_table *nls, * Return values: negative -> error, 0 -> not found, positive -> found, * value is the total amount of slots, including the shortname entry. */ +
fs/fat/dir.c 2005-08-31 13:48:01.001119437 +0900 fat_shortname2uni(struct nls_table *nls, · line 214
Quoted 10 lines
fs/fat/dir.c 2005-08-31 13:48:01.001119437 +0900 renamed from fs/fat/dir.c 2005-08-29 08:41:01.000000000 +0900 +10
-0+#define FAT_SCAN_SHIFT 4 /* 2x8 way scan hints */ +#define FAT_SCAN_NWAY (1<<FAT_SCAN_SHIFT) + +inline +static int hint_allocate(struct inode *dir) +{ + loff_t *hints; + int err = 0; + + if (!MSDOS_I(dir)->scan_hints) {
Please consider moving this check to callers. Conditional allocation makes this bit strange API-wise. Or alternatively, give hint_allocate() a better name.
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 static int fat_fill_inode(struct inode * · line 246
Quoted 8 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 renamed from fs/fat/inode.c 2005-08-29 08:41:01.000000000 +0900 +2
-0@@ -242,6 +242,8 @@ static int fat_fill_inode(struct inode * inode->i_version++; inode->i_generation = get_seconds(); + init_MUTEX(&MSDOS_I(inode)->scan_lock); + MSDOS_I(inode)->scan_hints = 0;
Please use NULL.
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 static void fat_delete_inode(struct inod · line 353
Quoted 11 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 renamed from fs/fat/inode.c 2005-08-29 08:41:01.000000000 +0900 +4
-0if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) { inode->i_generation &= ~1; inode->i_mode = MSDOS_MKMODE(de->attr,@@ -345,6 +347,15 @@ static void fat_delete_inode(struct inod static void fat_clear_inode(struct inode *inode) { struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); + loff_t *hints; + + down(&MSDOS_I(inode)->scan_lock); + hints = (MSDOS_I(inode)->scan_hints);
Pleas drop redundant parenthesis.
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 static void fat_delete_inode(struct inod · line 358
Quoted 5 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 renamed from fs/fat/inode.c 2005-08-29 08:41:01.000000000 +0900 +5
-0+ if (hints) { + MSDOS_I(inode)->scan_hints = NULL; + } + up(&MSDOS_I(inode)->scan_lock); + kfree(hints);
Why can't you do kfree() under scan_lock?
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 static int fat_read_root(struct inode *i · line 1026
Quoted 6 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 renamed from fs/fat/inode.c 2005-08-29 08:41:01.000000000 +0900 +2
-0@@ -1011,6 +1022,8 @@ static int fat_read_root(struct inode *i struct msdos_sb_info *sbi = MSDOS_SB(sb); int error; + init_MUTEX(&MSDOS_I(inode)->scan_lock); + MSDOS_I(inode)->scan_hints = 0;
Please use NULL here.
Pekka
Re: [PATCH][FAT] FAT dirent scan with hin take #3
Hi,
Thank you checking code...
Quoted 2 lines · Pekka Enberg
Hi,
: snip
Quoted 30 lines
Quoted 30 lines
This patch enables using hint information on scanning dir. It achieves excellent performance with "ls -l" for over 1000 entries.
* fat-dirscan-with-hint_3.patch for linux 2.6.13
fs/fat/dir.c | 130 ++++++++++++++++++++++++++++++++++++++++++++--- fs/fat/inode.c | 13 ++++ include/linux/msdos_fs.h | 2 3 files changed, 137 insertions(+), 8 deletions(-)Signed-off-by Hiroyuki Machida <[email protected]> for CELF* modified files
fs/fat/dir.c 2005-08-31 13:48:01.001119437 +0900 renamed from fs/fat/dir.c 2005-08-29 08:41:01.000000000 +0900 +8
-0@@ -201,6 +201,88 @@ fat_shortname2uni(struct nls_table *nls, * Return values: negative -> error, 0 -> not found, positive -> found, * value is the total amount of slots, including the shortname entry. */ + +#define FAT_SCAN_SHIFT 4 /* 2x8 way scan hints */ +#define FAT_SCAN_NWAY (1<<FAT_SCAN_SHIFT) + +inline +static int hint_allocate(struct inode *dir) +{ + loff_t *hints;
Quoted 8 lines
fs/fat/dir.c 2005-08-31 13:48:01.001119437 +0900 fat_shortname2uni(struct nls_table *nls, · line 214
Quoted 3 lines
fs/fat/dir.c 2005-08-31 13:48:01.001119437 +0900 renamed from fs/fat/dir.c 2005-08-29 08:41:01.000000000 +0900 +3
-0+ int err = 0; + + if (!MSDOS_I(dir)->scan_hints) {Please consider moving this check to callers. Conditional allocation makes this bit strange API-wise. Or alternatively, give
hint_allocate()a better name.
How about hint_allocate_conditional() ?
Quoted 7 lines
Quoted 7 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 renamed from fs/fat/inode.c 2005-08-29 08:41:01.000000000 +0900 +0
-0@@ -242,6 +242,8 @@ static int fat_fill_inode(struct inode * inode->i_version++; inode->i_generation = get_seconds();
Quoted 5 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 static int fat_fill_inode(struct inode * · line 246
Quoted 2 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 renamed from fs/fat/inode.c 2005-08-29 08:41:01.000000000 +0900 +2
-0+ init_MUTEX(&MSDOS_I(inode)->scan_lock); + MSDOS_I(inode)->scan_hints = 0;Please use NULL.
Ok,
Quoted 15 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 static void fat_delete_inode(struct inod · line 353
Quoted 12 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 renamed from fs/fat/inode.c 2005-08-29 08:41:01.000000000 +0900 +4
-0if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) { inode->i_generation &= ~1; inode->i_mode = MSDOS_MKMODE(de->attr,@@ -345,6 +347,15 @@ static void fat_delete_inode(struct inod static void fat_clear_inode(struct inode *inode) { struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); + loff_t *hints; + + down(&MSDOS_I(inode)->scan_lock); + hints = (MSDOS_I(inode)->scan_hints);Pleas drop redundant parenthesis.
Ok,
Quoted 10 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 static void fat_delete_inode(struct inod · line 358
Quoted 6 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 renamed from fs/fat/inode.c 2005-08-29 08:41:01.000000000 +0900 +5
-0+ if (hints) { + MSDOS_I(inode)->scan_hints = NULL; + } + up(&MSDOS_I(inode)->scan_lock); + kfree(hints);Why can't you do
kfree()underscan_lock?
Just try to minimize blocked period.
Quoted 10 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 static int fat_read_root(struct inode *i · line 1026
Quoted 7 lines
fs/fat/inode.c 2005-08-31 12:59:54.292274060 +0900 renamed from fs/fat/inode.c 2005-08-29 08:41:01.000000000 +0900 +2
-0@@ -1011,6 +1022,8 @@ static int fat_read_root(struct inode *i struct msdos_sb_info *sbi = MSDOS_SB(sb); int error; + init_MUTEX(&MSDOS_I(inode)->scan_lock); + MSDOS_I(inode)->scan_hints = 0;Please use NULL here.
Ok.
Quoted 6 lines
Pekka
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Signature · 2 lines
Hiroyuki Machida [email protected] SSW Dept. HENC, Sony Corp.
Re: [PATCH][FAT] FAT dirent scan with hin take #3
Quoted 5 lines · Hiroyuki
Quoted 3 lines
Please consider moving this check to callers. Conditional allocation makes this bit strange API-wise. Or alternatively, give
hint_allocate()a better name.How about
hint_allocate_conditional()?
hint_get() sounds better to me.
Pekka
OGAWA Hirofumi
maintainerRe: [PATCH][FAT] FAT dirent scan with hin take #3
In reply to Machida, HiroyukiQuoted 3 lines · Hiroyuki
Right, it looks like TLB, which holds cache "Physical addres" correponding to "Logical address". In this case, PID and file name to be looked up, perform role of "Logical address".
But, there is the big difference between hint table and TLB. TLB is just the cache, and TLB hit is perfectly good, because kernel is flushing the wrong values.
But this hint table is just collecting the recent access, it's not cache, and it's not tracking the process's access at all. So, since the hint value is really random, the hint value may be bad.
I worry bad cases of this.
Umm... How about tracking the access pattern of process? If that seems randomly access, just give up tracking and return no hint. And, probably, I think it would be easy to improve the behavior later.
What do you think?
e.g.
#define FAT_LOOKUP_HINT_MAX 16
/* this data per task */
struct fat_lookup_hint {
struct list_head lru;
pid_t pid;
struct super_block *sb;
struct inode *dir;
loff_t last_pos;/* int state;*/
};
static void fat_lkup_hint_inval(struct super_block *, struct inode *); static loff_t fat_lkup_hint_get(struct super_block *, struct inode *); static void fat_lkup_hint_add(struct super_block *, struct inode *, loff_t); static int fat_lkup_hint_init(void);
Signature · 1 line
OGAWA Hirofumi <[email protected]>
Re: [PATCH][FAT] FAT dirent scan with hin take #3
Quoted 16 lines · OGAWA Hirofumi
Quoted 3 lines · Hiroyuki
Right, it looks like TLB, which holds cache "Physical addres" correponding to "Logical address". In this case, PID and file name to be looked up, perform role of "Logical address".
But, there is the big difference between hint table and TLB. TLB is just the cache, and TLB hit is perfectly good, because kernel is flushing the wrong values.
But this hint table is just collecting the recent access, it's not cache, and it's not tracking the process's access at all. So, since the hint value is really random, the hint value may be bad.
Quoted 8 lines
I worry bad cases of this.
Umm... How about tracking the access pattern of process? If that seems randomly access, just give up tracking and return no hint. And, probably, I think it would be easy to improve the behavior later.
What do you think?
Sounds interesting...
Once concern about global URL in general, it tends to be occupied by specific pattern, like accesses from one process or to on dir. It prevents to realize locality.
I think it's better to have limitations like;
entries for same process would be limited to 2/3 entries for same dir would be limited to 1/3
Quoted 5 lines
e.g.
#define
FAT_LOOKUP_HINT_MAX16/* this data per task */
Quoted 8 lines
struct fat_lookup_hint { struct list_head lru; pid_t pid; struct super_block *sb; struct inode *dir; loff_t last_pos;/* int state;*/
};
Does this mean for each process recording last recent 16 accesses to FAT file system ? If true, pid would be eliminated.
I guess it's better to record nr_slots for this entry.
As implementation issue, if number of entires is small enough, we can use an array, not a list.
Quoted 4 lines
static void
fat_lkup_hint_inval(struct super_block *, struct inode *); staticloff_tfat_lkup_hint_get(struct super_block *, struct inode *); static voidfat_lkup_hint_add(struct super_block *, struct inode *, loff_t); static intfat_lkup_hint_init(void);
I think super_block can be retrieved from inode, any other intention do you have?
In addtion, we can do follwoing to check the exact match case;
0. Record hash value of file name in struct fat_lookup_hint
1. Check hash value to find exact match case, 1-1. If matched entry is found, check if file name and file name retieved from dirent corresponding
1-2. We found the entry
2. Get hint value, if there seem to have locality 2-1. Check locality of access pattern for this PID and this DIR.
2-2. If we relize access locality, return hit value so that
it covers a potential working set.
2-3. Use hint value as start position of dirscan.
Signature · 1 line
Hiroyuki Machida
Re: [PATCH][FAT] FAT dirent scan with hin take #3
Quoted 26 lines · Hiroyuki
Quoted 24 lines · OGAWA Hirofumi
Quoted 3 lines · Hiroyuki
Right, it looks like TLB, which holds cache "Physical addres" correponding to "Logical address". In this case, PID and file name to be looked up, perform role of "Logical address".
But, there is the big difference between hint table and TLB. TLB is just the cache, and TLB hit is perfectly good, because kernel is flushing the wrong values.
But this hint table is just collecting the recent access, it's not cache, and it's not tracking the process's access at all. So, since the hint value is really random, the hint value may be bad.
I worry bad cases of this.
Umm... How about tracking the access pattern of process? If that seems randomly access, just give up tracking and return no hint. And, probably, I think it would be easy to improve the behavior later.
Quoted 6 lines
Quoted 1 line
What do you think?
Sounds interesting...
Once concern about global URL in general, it tends to be occupied
^^^
sorry, LRU not URL.
Quoted 1 line
by specific pattern, like accesses from one process or to on dir.
one dir.
Quoted 57 lines
It prevents to realize locality.
I think it's better to have limitations like;
entries for same process would be limited to 2/3 entries for same dir would be limited to 1/3Quoted 5 lines
e.g.
#define
FAT_LOOKUP_HINT_MAX16/* this data per task */
Quoted 8 lines
struct fat_lookup_hint { struct list_head lru; pid_t pid; struct super_block *sb; struct inode *dir; loff_t last_pos;/* int state;*/
};Does this mean for each process recording last recent 16 accesses to FAT file system ? If true, pid would be eliminated.
I guess it's better to record
nr_slotsfor this entry.As implementation issue, if number of entires is small enough, we can use an array, not a list.
Quoted 4 lines
static void
fat_lkup_hint_inval(struct super_block *, struct inode *); staticloff_tfat_lkup_hint_get(struct super_block *, struct inode *); static voidfat_lkup_hint_add(struct super_block *, struct inode *, loff_t); static intfat_lkup_hint_init(void);I think
super_blockcan be retrieved from inode, any other intention do you have?In addtion, we can do follwoing to check the exact match case;
0. Record hash value of file name in struct
fat_lookup_hint1. Check hash value to find exact match case, 1-1. If matched entry is found, check if file name and file name retieved from dirent corresponding
1-2. We found the entry2. Get hint value, if there seem to have locality 2-1. Check locality of access pattern for this PID and this DIR.
2-2. If we relize access locality, return hit value so that
it covers a potential working set.
2-3. Use hint value as start position of dirscan.
Signature · 1 line
Hiroyuki Machida
OGAWA Hirofumi
maintainerRe: [PATCH][FAT] FAT dirent scan with hin take #3
In reply to Machida, HiroyukiQuoted 7 lines · Hiroyuki
Once concern about global URL in general, it tends to be occupied by specific pattern, like accesses from one process or to on dir. It prevents to realize locality.
I think it's better to have limitations like;
entries for same process would be limited to 2/3 entries for same dir would be limited to 1/3
[...]
Quoted 4 lines
Does this mean for each process recording last recent 16 accesses to FAT file system ? If true, pid would be eliminated.
I guess it's better to record
nr_slotsfor this entry.
The fat_lookup_hint is one per task. And fat_lookup_hint is tracking the task's access. If access is sequential, it returns the hint, and if not, it returns 0. I made the dirty patch just for explaining what I said.
However, these lookup-hint approaches seems the hack after all.... Hhmmm...
Signature · 1 line
OGAWA Hirofumi <[email protected]>
---
fs/fat/dir.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- fs/fat/inode.c | 2 2 files changed, 164 insertions(+), 4 deletions(-)
fs/fat/dir.c renamed from fs/fat/dir.c~fat_lookup-hint +162 -4
@@ -22,6 +22,148 @@#include <linux/buffer_head.h> #include <asm/uaccess.h> +#define FAT_LOOKUP_HINT_MAX 16 + +struct fat_lookup_hint { +struct list_head lru_list; +pid_t pid; +struct super_block *sb; +struct inode *dir; +loff_t last_pos; +#define HINT_STATE_RANDOM 1 +int state; +}; + +static spinlock_t fat_lookup_hint_lock =SPIN_LOCK_UNLOCKED ; +static LIST_HEAD (fat_lookup_hint_head); +static int fat_lookup_hint_count; + +#if 0 +static int fat_lkup_hint_init (void ) +{ +/* replace with kmem_cache */ +return 0 ; +} +#endif + +static inline struct fat_lookup_hint *fat_lkup_hint_alloc (void ) +{ +struct fat_lookup_hint *hint; + +/* replace with kmem_cache */ + hint =kmalloc (sizeof (*hint),GFP_KERNEL ); +if (hint) { +INIT_LIST_HEAD (&hint->lru_list); + hint->pid =0 ; + hint->sb =NULL ; + hint->dir =NULL ; + hint->last_pos =0 ; + } +return hint; +} + +static inline void fat_lkup_hint_free (struct fat_lookup_hint *hint) +{ +/* replace with kmem_cache */ +BUG_ON (!list_empty (&hint->lru_list)); +kfree (hint); +} + +void fat_lkup_hint_inval (struct inode *dir) +{ +struct super_block *sb = dir->i_sb; +struct fat_lookup_hint *hint, *n; + +spin_lock (&fat_lookup_hint_lock); +list_for_each_entry_safe (hint, n, &fat_lookup_hint_head, lru_list) { +if (hint->sb == sb && hint->dir == dir) { +list_del_init (&hint->lru_list); +fat_lkup_hint_free (hint); + fat_lookup_hint_count--; + } + } +spin_unlock (&fat_lookup_hint_lock); +} + +#define WIN_TOP (2 * MSDOS_SLOTS * sizeof(struct msdos_dir_entry)) +#define WIN_BOTTOM (5 * MSDOS_SLOTS * sizeof(struct msdos_dir_entry)) +static loff_t fat_lkup_hint_get (struct inode *dir) +{ +struct super_block *sb = dir->i_sb; +struct fat_lookup_hint *hint; +loff_t pos =0 ; + +spin_lock (&fat_lookup_hint_lock); +list_for_each_entry (hint, &fat_lookup_hint_head, lru_list) { +if (hint->pid == current->pid && + hint->sb == sb && + hint->dir == dir) { +if (!(hint->state &HINT_STATE_RANDOM )) + pos = hint->last_pos; +break ; + } + } +spin_unlock (&fat_lookup_hint_lock); +return max (0LL , pos -WIN_TOP ); +} + +/* caller must hold dir->i_sem */ +static void fat_lkup_hint_add (struct inode *dir,loff_t pos) +{ +struct super_block *sb = dir->i_sb; +struct fat_lookup_hint *p, *hint =NULL ; +loff_t win_start, win_end; + +spin_lock (&fat_lookup_hint_lock); +list_for_each_entry (p, &fat_lookup_hint_head, lru_list) { +if (p->pid == current->pid) { + hint = p; +break ; + } + } +if (hint ==NULL ) { +if (fat_lookup_hint_count <FAT_LOOKUP_HINT_MAX ) { + fat_lookup_hint_count++; +spin_unlock (&fat_lookup_hint_lock); + + hint =fat_lkup_hint_alloc (); +if (hint ==NULL ) +return ; +spin_lock (&fat_lookup_hint_lock); +/* don't need to recheck hint */ + }else { +struct list_head *p = fat_lookup_hint_head.prev; + hint = list_entry(p, struct fat_lookup_hint, lru_list); + } + hint->pid = current->pid; + hint->sb = sb; + hint->dir = dir; + } + +if (hint->sb != sb || hint->dir != dir) { + hint->sb = sb; + hint->dir = dir; + hint->state |=HINT_STATE_RANDOM ; + }else { + win_start = hint->last_pos -WIN_TOP ; + win_end = hint->last_pos +WIN_BOTTOM ; +if (win_start <= pos && pos <= win_end) { + hint->state &= ~HINT_STATE_RANDOM ; + hint->last_pos = pos; + }else { +/* seems random access */ + hint->last_pos = pos; + hint->state |=HINT_STATE_RANDOM ; + } + } + hint->last_pos = pos; + +/* update LRU */ +list_del (&hint->lru_list); +list_add (&hint->lru_list, &fat_lookup_hint_head); +spin_unlock (&fat_lookup_hint_lock); +} +static inline loff_t fat_make_i_pos (struct super_block *sb,struct buffer_head *bh,struct msdos_dir_entry *de)
@@ -243,13 +385,23 @@ int fat_search_long(struct inode *inode,int utf8 = sbi->options.utf8;int anycase = (sbi->options.name_check !='s' );unsigned short opt_shortname = sbi->options.shortname; -loff_t cpos =0 ;int chl, i, j, last_u, err; +loff_t cpos, start_off; +int reach_bottom =0 ; + start_off = cpos =fat_lkup_hint_get (inode); err = -ENOENT ;while (1 ) { -if (fat_get_entry (inode, &cpos, &bh, &de) ==-1 ) +top: +if (reach_bottom && cpos >= start_off) goto EODir; +if (fat_get_entry (inode, &cpos, &bh, &de) ==-1 ) { +if (!start_off) + goto EODir; + reach_bottom++; + cpos =0 ; +continue ; + } parse_record: nr_slots =0 ;if (de->name[0 ] ==DELETED_FLAG )
@@ -298,8 +450,13 @@ parse_long:if (ds->id &0x40 ) { unicode[offset +13 ] =0 ; } -if (fat_get_entry (inode, &cpos, &bh, &de) <0 ) - goto EODir; +if (fat_get_entry (inode, &cpos, &bh, &de) <0 ) { +if (!start_off) + goto EODir; + reach_bottom++; + cpos =0 ; + goto top; + }if (slot ==0 )break ; ds = (struct msdos_dir_slot *) de;
@@ -385,6 +542,7 @@ Found: sinfo->de = de; sinfo->bh = bh; sinfo->i_pos =fat_make_i_pos (sb, sinfo->bh, sinfo->de); +fat_lkup_hint_add (inode, sinfo->slot_off); err =0 ; EODir:if (unicode)
fs/fat/inode.c renamed from fs/fat/inode.c~fat_lookup-hint +2 -0
@@ -342,6 +342,7 @@ static void fat_delete_inode(struct inodclear_inode (inode); } +extern void fat_lkup_hint_inval (struct inode *);static void fat_clear_inode (struct inode *inode) {struct msdos_sb_info *sbi =MSDOS_SB (inode->i_sb);
@@ -349,6 +350,7 @@ static void fat_clear_inode(struct inodeif (is_bad_inode (inode))return ;lock_kernel (); +fat_lkup_hint_inval (inode);spin_lock (&sbi->inode_hash_lock);fat_cache_inval_inode (inode);hlist_del_init (&MSDOS_I (inode)->i_fat_hash);
_
Re: [PATCH][FAT] FAT dirent scan with hin take #3
OGAWA Hirofumi wrote:
:
Quoted 6 lines
The
fat_lookup_hintis one per task. Andfat_lookup_hintis tracking the task's access. If access is sequential, it returns the hint, and if not, it returns 0. I made the dirty patch just for explaining what I said.
Sorry late, at the last weekend I looked into the patch you post and run it. I got your idea, now. I would propose following changes on it and attached modified patch into this mail.
1. The original patch has system wide 16 entries access-tracking buffer. I modified system has one access-tracking buffer for each mounted FS instance. And it allocates the buffer on mounting as a part of FS dependent super block.
2. On looking up hints, the original patch try to find the entry which has same pid and dir. If matched entry is not found, it uses always zero. However we can expect locality beyond process, so I changes a little bit, behavior of fail to find the matched entry.
try to find the entry which has same pid and dir.
if matched entry is not found, try to find recent access to same dir.
3. With the original code, the access-tracking buffer can hold just one entry for one pid. I think it is better to have few entries for one pid. I modified patch to have up to 3 entires (less than 1/4 of total entries) for each pid.
4. Originally window size to determine locality seems wide due to using max len of LFN(MSDOS_SLOTS), like;
WIN_BOTTOM = 5 * MSDOS_SLOTS * size of entry
We can use the value based on statistic instead of MSDOS_SLOTS. I checked with my FC3 installed system, most files(over 99%) have less than or equal to 48 chars file name length. So use 9 instead of 21(MSDOS_SLOTS)
5. Adding short name entry support.
And I have fixed a bug which makes infinite loop on lookup empty dir after all files were removed.
Quoted 2 lines
However, these lookup-hint approaches seems the hack after all.... Hhmmm...
I have another idea as following;
On reading blocks ahead, try to build dentry caches for all FAT dir entries which reside in those blocks. And start dir-scan at some position close to bottom of the latest read-ahead block, instead of zero.
However, I guess this tends to consume dentry caches and it may have affects on other area.
As you said, I think "lookup-hint" is hack, but not so bad. Because it has no side effect to other area and memory consumption is predictable.
Signature · 1 line
Hiroyuki Machida
---
fs/fat/dir.c | 232 ++++++++++++++++++++++++++++++++++++++++++++--- fs/fat/inode.c | 7 + include/linux/msdos_fs.h | 19 +++
3 files changed, 246 insertions(+), 12 deletions(-)
fs/fat/dir.c +220 -12
@@ -22,6 +22,185 @@#include <linux/buffer_head.h> #include <asm/uaccess.h> +void fat_lkup_hint_init_sb (struct super_block *sb) +{ +struct msdos_sb_info *sbi =MSDOS_SB (sb); + + sbi->lkup_hint_lock =SPIN_LOCK_UNLOCKED ; +INIT_LIST_HEAD (&sbi->lkup_hint_head); + sbi->lkup_hint_freemap =0 ; + sbi->lkup_hint_freemap = ~sbi->lkup_hint_freemap; +} + +void fat_lkup_hint_inval (struct inode *dir) +{ +struct super_block *sb = dir->i_sb; +struct msdos_sb_info *sbi =MSDOS_SB (sb); +struct fat_lookup_hint *hint, *n; +int i; + +spin_lock (&sbi->lkup_hint_lock); +list_for_each_entry_safe (hint, n, &sbi->lkup_hint_head, lru_list) { +if (hint->dir == dir) { +list_del_init (&hint->lru_list); + i = hint - &(sbi->lkup_hint[0 ]); + sbi->lkup_hint_freemap |= (1 << i); + } + } +spin_unlock (&sbi->lkup_hint_lock); +} + +/* +* AVG_MSDOS_SLOTS is selected so that it can store 48 chars file name length. +* +* According raugh observation, most files in Linux installed system have +* less than or equal to 48 chars file name length. +* +* Following is one of statistic information; +* +* total # of files(*1) 9193 +* max length of file name 52 chars +* +* # of files over 48 chars name length 36 +* % covered by 48 chars name length 99.6% +* +* # of files over 32 chars name length 517 +* % covered by 32 chars name length 94.3% +* +* *1) # of file, dir and symlink which has over 2 chars +* file name length. +* +*/ +#define AVG_MSDOS_SLOTS (8+1) +#define WIN_LFN_TOP (3 * AVG_MSDOS_SLOTS * sizeof(struct msdos_dir_entry)) +#define WIN_LFN_BOTTOM (5 * AVG_MSDOS_SLOTS * sizeof(struct msdos_dir_entry)) + +#define WIN_TOP (3 * sizeof(struct msdos_dir_entry)) +#define WIN_BOTTOM (5 * sizeof(struct msdos_dir_entry)) + +static inline +loff_t get_syswide_hint (loff_t pos,struct fat_lookup_hint *hint) +{ +/* +* NOTE: If you don't expect locality beyond process, +* make this return 0 always, instead of following. +*/ +return (pos ==0 ) ? hint->last_pos : pos; +} + +static loff_t fat_lkup_hint_get (struct inode *dir,int is_lfn) +{ +struct super_block *sb = dir->i_sb; +struct fat_lookup_hint *hint; +struct msdos_sb_info *sbi =MSDOS_SB (sb); +loff_t win_top; +unsigned int rnd_flg; +loff_t pos =0 ; + +if (is_lfn) { + rnd_flg =FAT_LKUP_ST_LFN_RANDOM ; + win_top =WIN_LFN_TOP ; + }else { + rnd_flg =FAT_LKUP_ST_RANDOM ; + win_top =WIN_TOP ; + } + +spin_lock (&sbi->lkup_hint_lock); +list_for_each_entry (hint, &sbi->lkup_hint_head, lru_list) { +if (hint->dir == dir) { +if (!(hint->state & rnd_flg)) { +if (hint->pid == current->pid) { + pos = hint->last_pos; +break ; + } +/* expect locality beyond process */ + pos =get_syswide_hint (pos, hint); + } + } + } +spin_unlock (&sbi->lkup_hint_lock); +return max (0LL , pos - win_top); +} + +/* +* NOTE: If you don't want multiple entries for +* same pid, set this to 1. +*/ +#define LKUP_MULTIENT_MAX (FAT_LKUP_HINT_MAX/4-1) + + +/* caller must hold dir->i_sem */ +static void fat_lkup_hint_add (struct inode *dir,loff_t pos) +{ +struct super_block *sb = dir->i_sb; +struct fat_lookup_hint *p, *hint =NULL ; +loff_t win_start, win_end; +struct msdos_sb_info *sbi =MSDOS_SB (sb); +int nr_pid_match =0 ; +unsigned int state; + +spin_lock (&sbi->lkup_hint_lock); +/* find out same pid entry */ +list_for_each_entry (p, &sbi->lkup_hint_head, lru_list) { +if (p->pid == current->pid) { + nr_pid_match ++; +if (p->dir == dir) { + hint = p; + goto match; + } +if (!hint) + hint = p; + } + } + +if (nr_pid_match <LKUP_MULTIENT_MAX ) + hint =NULL ; + +if (hint ==NULL ) { +int free_hint; + free_hint =ffs (sbi->lkup_hint_freemap); +if (free_hint) { +/* add the new entry */ + free_hint --; + sbi->lkup_hint_freemap &= ~ (1 <<(free_hint)); + hint = &sbi->lkup_hint[free_hint]; +INIT_LIST_HEAD (&hint->lru_list); +/* don't need to recheck hint */ + }else { +/* replace the last entry */ +struct list_head *p = sbi->lkup_hint_head.prev; + hint = list_entry(p, struct fat_lookup_hint, lru_list); + } + hint->pid = current->pid; + hint->dir = dir; + hint->last_pos =0 ; + } + +match: + state = (FAT_LKUP_ST_RANDOM |FAT_LKUP_ST_LFN_RANDOM ); + hint->last_pos = pos; +if (hint->dir != dir) { + hint->dir = dir; + }else { + win_start = hint->last_pos -WIN_TOP ; + win_end = hint->last_pos +WIN_BOTTOM ; +if (win_start <= pos && pos <= win_end) { + state &= ~FAT_LKUP_ST_RANDOM ; + } + win_start = hint->last_pos -WIN_LFN_TOP ; + win_end = hint->last_pos +WIN_LFN_BOTTOM ; +if (win_start <= pos && pos <= win_end) { + state &= ~FAT_LKUP_ST_LFN_RANDOM ; + } + } + hint->state = state; + +/* update LRU list */ +list_del (&hint->lru_list); +list_add (&hint->lru_list, &sbi->lkup_hint_head);/* put hint to head */ +spin_unlock (&sbi->lkup_hint_lock); +} +static inline loff_t fat_make_i_pos (struct super_block *sb,struct buffer_head *bh,struct msdos_dir_entry *de)
@@ -218,13 +397,23 @@ int fat_search_long(struct inode *inode,int utf8 = sbi->options.utf8;int anycase = (sbi->options.name_check !='s' );unsigned short opt_shortname = sbi->options.shortname; -loff_t cpos =0 ;int chl, i, j, last_u, err; +loff_t cpos, start_off; +int reach_bottom =0 ; + start_off = cpos =fat_lkup_hint_get (inode,1 ); err = -ENOENT ;while (1 ) { -if (fat_get_entry (inode, &cpos, &bh, &de) ==-1 ) +top: +if (reach_bottom && cpos >= start_off) goto EODir; +if (fat_get_entry (inode, &cpos, &bh, &de) ==-1 ) { +if (!start_off || reach_bottom) + goto EODir; + reach_bottom++; + cpos =0 ; +continue ; + } parse_record: nr_slots =0 ;if (de->name[0 ] ==DELETED_FLAG )
@@ -274,8 +463,13 @@ parse_long:if (ds->id &0x40 ) { unicode[offset +13 ] =0 ; } -if (fat_get_entry (inode, &cpos, &bh, &de) <0 ) - goto EODir; +if (fat_get_entry (inode, &cpos, &bh, &de) <0 ) { +if (!start_off || reach_bottom) + goto EODir; + reach_bottom++; + cpos =0 ; + goto top; + }if (slot ==0 )break ; ds = (struct msdos_dir_slot *) de;
@@ -363,6 +557,7 @@ Found: sinfo->de = de; sinfo->bh = bh; sinfo->i_pos =fat_make_i_pos (sb, sinfo->bh, sinfo->de); +fat_lkup_hint_add (inode, sinfo->slot_off); err =0 ; EODir:if (unicode)
@@ -838,16 +1033,29 @@ int fat_scan(struct inode *dir, const un struct fat_slot_info *sinfo) {struct super_block *sb = dir->i_sb; +int reach_bottom =0 ; +loff_t start_off; - sinfo->slot_off =0 ; + start_off = sinfo->slot_off =fat_lkup_hint_get (dir,0 ); sinfo->bh =NULL ; -while (fat_get_short_entry (dir, &sinfo->slot_off, &sinfo->bh, - &sinfo->de) >=0 ) { -if (!strncmp (sinfo->de->name, name,MSDOS_NAME )) { - sinfo->slot_off -=sizeof (*sinfo->de); - sinfo->nr_slots =1 ; - sinfo->i_pos =fat_make_i_pos (sb, sinfo->bh, sinfo->de); -return 0 ; +while (1 ) { +if (fat_get_short_entry (dir, &sinfo->slot_off, &sinfo->bh, + &sinfo->de) >=0 ) { +if (!strncmp (sinfo->de->name, name,MSDOS_NAME )) { + sinfo->slot_off -=sizeof (*sinfo->de); + sinfo->nr_slots =1 ; + sinfo->i_pos =fat_make_i_pos (sb, sinfo->bh, + sinfo->de); +fat_lkup_hint_add (dir, sinfo->slot_off); +return 0 ; + } +if (reach_bottom && (start_off <= sinfo->slot_off)) +break ; + }else { +if (!start_off || reach_bottom) +break ; + reach_bottom ++; + sinfo->slot_off =0 ; } }return -ENOENT ;
fs/fat/inode.c +7 -0
@@ -342,6 +342,8 @@ static void fat_delete_inode(struct inodclear_inode (inode); } +extern void fat_lkup_hint_inval (struct inode *); +static void fat_clear_inode (struct inode *inode) {struct msdos_sb_info *sbi =MSDOS_SB (inode->i_sb);
@@ -349,6 +351,7 @@ static void fat_clear_inode(struct inodeif (is_bad_inode (inode))return ;lock_kernel (); +fat_lkup_hint_inval (inode);spin_lock (&sbi->inode_hash_lock);fat_cache_inval_inode (inode);hlist_del_init (&MSDOS_I (inode)->i_fat_hash);
@@ -1042,6 +1045,8 @@ static int fat_read_root(struct inode *ireturn 0 ; } +extern void fat_lkup_hint_init_sb (struct super_block *sb); +/* * Read the super block of an MS-DOS FS. */
@@ -1302,6 +1307,8 @@ int fat_fill_super(struct super_block *s goto out_fail; } +fat_lkup_hint_init_sb (sb); +return 0 ; out_invalid:
include/linux/msdos_fs.h +19 -0
@@ -185,6 +185,19 @@ struct fat_slot_info {#include <linux/nls.h> #include <linux/fs.h> +#define FAT_LKUP_HINT_MAX 16 +/* assume FAT_LKUP_HINT_MAX <= sizeof(long)*8 */ + +struct fat_lookup_hint { +struct list_head lru_list; +pid_t pid; +struct inode *dir; +loff_t last_pos; +#define FAT_LKUP_ST_RANDOM 1 +#define FAT_LKUP_ST_LFN_RANDOM 2 +unsigned int state; +}; +struct fat_mount_options {uid_t fs_uid;gid_t fs_gid;
@@ -241,6 +254,12 @@ struct msdos_sb_info {spinlock_t inode_hash_lock;struct hlist_head inode_hashtable[FAT_HASH_SIZE ]; + +/* dirent lookup hints */ +spinlock_t lkup_hint_lock; +struct list_head lkup_hint_head; +struct fat_lookup_hint lkup_hint[FAT_LKUP_HINT_MAX ]; + unsigned lkup_hint_freemap:FAT_LKUP_HINT_MAX; };#define FAT_CACHE_VALID 0/* special case for valid cache */