aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaegeuk Kim <jaegeuk.kim@samsung.com>2014-02-27 04:20:00 -0500
committerJaegeuk Kim <jaegeuk.kim@samsung.com>2014-02-27 05:56:09 -0500
commit3843154598a00408f4214a68bd536fdf27b1df10 (patch)
treee4e89b1bc769996bf72851baffd62194d9a48385
parent5d0c667121bfc8be76d1580f485bddbe73465d1a (diff)
f2fs: introduce large directory support
This patch introduces an i_dir_level field to support large directory. Previously, f2fs maintains multi-level hash tables to find a dentry quickly from a bunch of chiild dentries in a directory, and the hash tables consist of the following tree structure as below. In Documentation/filesystems/f2fs.txt, ---------------------- A : bucket B : block N : MAX_DIR_HASH_DEPTH ---------------------- level #0 | A(2B) | level #1 | A(2B) - A(2B) | level #2 | A(2B) - A(2B) - A(2B) - A(2B) . | . . . . level #N/2 | A(2B) - A(2B) - A(2B) - A(2B) - A(2B) - ... - A(2B) . | . . . . level #N | A(4B) - A(4B) - A(4B) - A(4B) - A(4B) - ... - A(4B) But, if we can guess that a directory will handle a number of child files, we don't need to traverse the tree from level #0 to #N all the time. Since the lower level tables contain relatively small number of dentries, the miss ratio of the target dentry is likely to be high. In order to avoid that, we can configure the hash tables sparsely from level #0 like this. level #0 | A(2B) - A(2B) - A(2B) - A(2B) level #1 | A(2B) - A(2B) - A(2B) - A(2B) - A(2B) - ... - A(2B) . | . . . . level #N/2 | A(2B) - A(2B) - A(2B) - A(2B) - A(2B) - ... - A(2B) . | . . . . level #N | A(4B) - A(4B) - A(4B) - A(4B) - A(4B) - ... - A(4B) With this structure, we can skip the ineffective tree searches in lower level hash tables. This patch adds just a facility for this by introducing i_dir_level in f2fs_inode. Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
-rw-r--r--Documentation/filesystems/f2fs.txt6
-rw-r--r--fs/f2fs/dir.c21
-rw-r--r--fs/f2fs/f2fs.h1
-rw-r--r--fs/f2fs/inode.c2
-rw-r--r--include/linux/f2fs_fs.h2
5 files changed, 20 insertions, 12 deletions
diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
index b8d284975f0f..8eb06b0a7d2b 100644
--- a/Documentation/filesystems/f2fs.txt
+++ b/Documentation/filesystems/f2fs.txt
@@ -444,9 +444,11 @@ The number of blocks and buckets are determined by,
444 # of blocks in level #n = | 444 # of blocks in level #n = |
445 `- 4, Otherwise 445 `- 4, Otherwise
446 446
447 ,- 2^n, if n < MAX_DIR_HASH_DEPTH / 2, 447 ,- 2^ (n + dir_level),
448 | if n < MAX_DIR_HASH_DEPTH / 2,
448 # of buckets in level #n = | 449 # of buckets in level #n = |
449 `- 2^((MAX_DIR_HASH_DEPTH / 2) - 1), Otherwise 450 `- 2^((MAX_DIR_HASH_DEPTH / 2 + dir_level) - 1),
451 Otherwise
450 452
451When F2FS finds a file name in a directory, at first a hash value of the file 453When F2FS finds a file name in a directory, at first a hash value of the file
452name is calculated. Then, F2FS scans the hash table in level #0 to find the 454name is calculated. Then, F2FS scans the hash table in level #0 to find the
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index c3ea8f8cc80a..582fa00f3597 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -21,12 +21,12 @@ static unsigned long dir_blocks(struct inode *inode)
21 >> PAGE_CACHE_SHIFT; 21 >> PAGE_CACHE_SHIFT;
22} 22}
23 23
24static unsigned int dir_buckets(unsigned int level) 24static unsigned int dir_buckets(unsigned int level, int dir_level)
25{ 25{
26 if (level < MAX_DIR_HASH_DEPTH / 2) 26 if (level < MAX_DIR_HASH_DEPTH / 2)
27 return 1 << level; 27 return 1 << (level + dir_level);
28 else 28 else
29 return 1 << ((MAX_DIR_HASH_DEPTH / 2) - 1); 29 return 1 << ((MAX_DIR_HASH_DEPTH / 2 + dir_level) - 1);
30} 30}
31 31
32static unsigned int bucket_blocks(unsigned int level) 32static unsigned int bucket_blocks(unsigned int level)
@@ -65,13 +65,14 @@ static void set_de_type(struct f2fs_dir_entry *de, struct inode *inode)
65 de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; 65 de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
66} 66}
67 67
68static unsigned long dir_block_index(unsigned int level, unsigned int idx) 68static unsigned long dir_block_index(unsigned int level,
69 int dir_level, unsigned int idx)
69{ 70{
70 unsigned long i; 71 unsigned long i;
71 unsigned long bidx = 0; 72 unsigned long bidx = 0;
72 73
73 for (i = 0; i < level; i++) 74 for (i = 0; i < level; i++)
74 bidx += dir_buckets(i) * bucket_blocks(i); 75 bidx += dir_buckets(i, dir_level) * bucket_blocks(i);
75 bidx += idx * bucket_blocks(level); 76 bidx += idx * bucket_blocks(level);
76 return bidx; 77 return bidx;
77} 78}
@@ -143,10 +144,11 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
143 144
144 f2fs_bug_on(level > MAX_DIR_HASH_DEPTH); 145 f2fs_bug_on(level > MAX_DIR_HASH_DEPTH);
145 146
146 nbucket = dir_buckets(level); 147 nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
147 nblock = bucket_blocks(level); 148 nblock = bucket_blocks(level);
148 149
149 bidx = dir_block_index(level, le32_to_cpu(namehash) % nbucket); 150 bidx = dir_block_index(level, F2FS_I(dir)->i_dir_level,
151 le32_to_cpu(namehash) % nbucket);
150 end_block = bidx + nblock; 152 end_block = bidx + nblock;
151 153
152 for (; bidx < end_block; bidx++) { 154 for (; bidx < end_block; bidx++) {
@@ -467,10 +469,11 @@ start:
467 if (level == current_depth) 469 if (level == current_depth)
468 ++current_depth; 470 ++current_depth;
469 471
470 nbucket = dir_buckets(level); 472 nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
471 nblock = bucket_blocks(level); 473 nblock = bucket_blocks(level);
472 474
473 bidx = dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket)); 475 bidx = dir_block_index(level, F2FS_I(dir)->i_dir_level,
476 (le32_to_cpu(dentry_hash) % nbucket));
474 477
475 for (block = bidx; block <= (bidx + nblock - 1); block++) { 478 for (block = bidx; block <= (bidx + nblock - 1); block++) {
476 dentry_page = get_new_data_page(dir, NULL, block, true); 479 dentry_page = get_new_data_page(dir, NULL, block, true);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 4beedccc28a0..a82691674918 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -200,6 +200,7 @@ struct f2fs_inode_info {
200 struct inode vfs_inode; /* serve a vfs inode */ 200 struct inode vfs_inode; /* serve a vfs inode */
201 unsigned long i_flags; /* keep an inode flags for ioctl */ 201 unsigned long i_flags; /* keep an inode flags for ioctl */
202 unsigned char i_advise; /* use to give file attribute hints */ 202 unsigned char i_advise; /* use to give file attribute hints */
203 unsigned char i_dir_level; /* use for dentry level for large dir */
203 unsigned int i_current_depth; /* use only in directory structure */ 204 unsigned int i_current_depth; /* use only in directory structure */
204 unsigned int i_pino; /* parent inode number */ 205 unsigned int i_pino; /* parent inode number */
205 umode_t i_acl_mode; /* keep file acl mode temporarily */ 206 umode_t i_acl_mode; /* keep file acl mode temporarily */
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 08d69c94ab8b..d518e37df3a7 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -107,6 +107,7 @@ static int do_read_inode(struct inode *inode)
107 fi->flags = 0; 107 fi->flags = 0;
108 fi->i_advise = ri->i_advise; 108 fi->i_advise = ri->i_advise;
109 fi->i_pino = le32_to_cpu(ri->i_pino); 109 fi->i_pino = le32_to_cpu(ri->i_pino);
110 fi->i_dir_level = ri->i_dir_level;
110 111
111 get_extent_info(&fi->ext, ri->i_ext); 112 get_extent_info(&fi->ext, ri->i_ext);
112 get_inline_info(fi, ri); 113 get_inline_info(fi, ri);
@@ -204,6 +205,7 @@ void update_inode(struct inode *inode, struct page *node_page)
204 ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags); 205 ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags);
205 ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino); 206 ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino);
206 ri->i_generation = cpu_to_le32(inode->i_generation); 207 ri->i_generation = cpu_to_le32(inode->i_generation);
208 ri->i_dir_level = F2FS_I(inode)->i_dir_level;
207 209
208 __set_inode_rdev(inode, ri); 210 __set_inode_rdev(inode, ri);
209 set_cold_node(inode, node_page); 211 set_cold_node(inode, node_page);
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index da74d878dc4f..df53e1753a76 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -183,7 +183,7 @@ struct f2fs_inode {
183 __le32 i_pino; /* parent inode number */ 183 __le32 i_pino; /* parent inode number */
184 __le32 i_namelen; /* file name length */ 184 __le32 i_namelen; /* file name length */
185 __u8 i_name[F2FS_NAME_LEN]; /* file name for SPOR */ 185 __u8 i_name[F2FS_NAME_LEN]; /* file name for SPOR */
186 __u8 i_reserved2; /* for backward compatibility */ 186 __u8 i_dir_level; /* dentry_level for large dir */
187 187
188 struct f2fs_extent i_ext; /* caching a largest extent */ 188 struct f2fs_extent i_ext; /* caching a largest extent */
189 189