Skip to content

Commit e9a0d5d

Browse files
Jaegeuk KimHashcode
authored andcommitted
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>
1 parent 12052ad commit e9a0d5d

File tree

5 files changed

+20
-12
lines changed

5 files changed

+20
-12
lines changed

Documentation/filesystems/f2fs.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,11 @@ The number of blocks and buckets are determined by,
436436
# of blocks in level #n = |
437437
`- 4, Otherwise
438438

439-
,- 2^n, if n < MAX_DIR_HASH_DEPTH / 2,
439+
,- 2^ (n + dir_level),
440+
| if n < MAX_DIR_HASH_DEPTH / 2,
440441
# of buckets in level #n = |
441-
`- 2^((MAX_DIR_HASH_DEPTH / 2) - 1), Otherwise
442+
`- 2^((MAX_DIR_HASH_DEPTH / 2 + dir_level) - 1),
443+
Otherwise
442444

443445
When F2FS finds a file name in a directory, at first a hash value of the file
444446
name is calculated. Then, F2FS scans the hash table in level #0 to find the

fs/f2fs/dir.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ static unsigned long dir_blocks(struct inode *inode)
2121
>> PAGE_CACHE_SHIFT;
2222
}
2323

24-
static unsigned int dir_buckets(unsigned int level)
24+
static unsigned int dir_buckets(unsigned int level, int dir_level)
2525
{
2626
if (level < MAX_DIR_HASH_DEPTH / 2)
27-
return 1 << level;
27+
return 1 << (level + dir_level);
2828
else
29-
return 1 << ((MAX_DIR_HASH_DEPTH / 2) - 1);
29+
return 1 << ((MAX_DIR_HASH_DEPTH / 2 + dir_level) - 1);
3030
}
3131

3232
static unsigned int bucket_blocks(unsigned int level)
@@ -65,13 +65,14 @@ static void set_de_type(struct f2fs_dir_entry *de, struct inode *inode)
6565
de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
6666
}
6767

68-
static unsigned long dir_block_index(unsigned int level, unsigned int idx)
68+
static unsigned long dir_block_index(unsigned int level,
69+
int dir_level, unsigned int idx)
6970
{
7071
unsigned long i;
7172
unsigned long bidx = 0;
7273

7374
for (i = 0; i < level; i++)
74-
bidx += dir_buckets(i) * bucket_blocks(i);
75+
bidx += dir_buckets(i, dir_level) * bucket_blocks(i);
7576
bidx += idx * bucket_blocks(level);
7677
return bidx;
7778
}
@@ -143,10 +144,11 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
143144

144145
f2fs_bug_on(level > MAX_DIR_HASH_DEPTH);
145146

146-
nbucket = dir_buckets(level);
147+
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
147148
nblock = bucket_blocks(level);
148149

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);
150152
end_block = bidx + nblock;
151153

152154
for (; bidx < end_block; bidx++) {
@@ -467,10 +469,11 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
467469
if (level == current_depth)
468470
++current_depth;
469471

470-
nbucket = dir_buckets(level);
472+
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
471473
nblock = bucket_blocks(level);
472474

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));
474477

475478
for (block = bidx; block <= (bidx + nblock - 1); block++) {
476479
dentry_page = get_new_data_page(dir, NULL, block, true);

fs/f2fs/f2fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ struct f2fs_inode_info {
200200
struct inode vfs_inode; /* serve a vfs inode */
201201
unsigned long i_flags; /* keep an inode flags for ioctl */
202202
unsigned char i_advise; /* use to give file attribute hints */
203+
unsigned char i_dir_level; /* use for dentry level for large dir */
203204
unsigned int i_current_depth; /* use only in directory structure */
204205
unsigned int i_pino; /* parent inode number */
205206
umode_t i_acl_mode; /* keep file acl mode temporarily */

fs/f2fs/inode.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ static int do_read_inode(struct inode *inode)
107107
fi->flags = 0;
108108
fi->i_advise = ri->i_advise;
109109
fi->i_pino = le32_to_cpu(ri->i_pino);
110+
fi->i_dir_level = ri->i_dir_level;
110111

111112
get_extent_info(&fi->ext, ri->i_ext);
112113
get_inline_info(fi, ri);
@@ -204,6 +205,7 @@ void update_inode(struct inode *inode, struct page *node_page)
204205
ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags);
205206
ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino);
206207
ri->i_generation = cpu_to_le32(inode->i_generation);
208+
ri->i_dir_level = F2FS_I(inode)->i_dir_level;
207209

208210
__set_inode_rdev(inode, ri);
209211
set_cold_node(inode, node_page);

include/linux/f2fs_fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ struct f2fs_inode {
183183
__le32 i_pino; /* parent inode number */
184184
__le32 i_namelen; /* file name length */
185185
__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 */
187187

188188
struct f2fs_extent i_ext; /* caching a largest extent */
189189

0 commit comments

Comments
 (0)