aboutsummaryrefslogtreecommitdiffstats
path: root/fs/f2fs/dir.c
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/dir.c
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/dir.c')
-rw-r--r--fs/f2fs/dir.c21
1 files changed, 12 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);