aboutsummaryrefslogtreecommitdiffstats
path: root/fs/f2fs
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 /fs/f2fs
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>
Diffstat (limited to 'fs/f2fs')
-rw-r--r--fs/f2fs/dir.c21
-rw-r--r--fs/f2fs/f2fs.h1
-rw-r--r--fs/f2fs/inode.c2
3 files changed, 15 insertions, 9 deletions
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);