Commit f9a03ae1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-f2fs-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs

Pull f2fs updates from Jaegeuk Kim:
 "This series adds two ioctls to control cached data and fragmented
  files.  Most of the rest fixes missing error cases and bugs that we
  have not covered so far.  Summary:

  Enhancements:
   - support an ioctl to execute online file defragmentation
   - support an ioctl to flush cached data
   - speed up shrinking of extent_cache entries
   - handle broken superblock
   - refector dirty inode management infra
   - revisit f2fs_map_blocks to handle more cases
   - reduce global lock coverage
   - add detecting user's idle time

  Major bug fixes:
   - fix data race condition on cached nat entries
   - fix error cases of volatile and atomic writes"

* tag 'for-f2fs-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (87 commits)
  f2fs: should unset atomic flag after successful commit
  f2fs: fix wrong memory condition check
  f2fs: monitor the number of background checkpoint
  f2fs: detect idle time depending on user behavior
  f2fs: introduce time and interval facility
  f2fs: skip releasing nodes in chindless extent tree
  f2fs: use atomic type for node count in extent tree
  f2fs: recognize encrypted data in f2fs_fiemap
  f2fs: clean up f2fs_balance_fs
  f2fs: remove redundant calls
  f2fs: avoid unnecessary f2fs_balance_fs calls
  f2fs: check the page status filled from disk
  f2fs: introduce __get_node_page to reuse common code
  f2fs: check node id earily when readaheading node page
  f2fs: read isize while holding i_mutex in fiemap
  Revert "f2fs: check the node block address of newly allocated nid"
  f2fs: cover more area with nat_tree_lock
  f2fs: introduce max_file_blocks in sbi
  f2fs crypto: check CONFIG_F2FS_FS_XATTR for encrypted symlink
  f2fs: introduce zombie list for fast shrinking extent trees
  ...
parents 1289ace5 447135a8
...@@ -87,6 +87,12 @@ Contact: "Jaegeuk Kim" <jaegeuk@kernel.org> ...@@ -87,6 +87,12 @@ Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
Description: Description:
Controls the checkpoint timing. Controls the checkpoint timing.
What: /sys/fs/f2fs/<disk>/idle_interval
Date: January 2016
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
Description:
Controls the idle timing.
What: /sys/fs/f2fs/<disk>/ra_nid_pages What: /sys/fs/f2fs/<disk>/ra_nid_pages
Date: October 2015 Date: October 2015
Contact: "Chao Yu" <chao2.yu@samsung.com> Contact: "Chao Yu" <chao2.yu@samsung.com>
......
...@@ -102,7 +102,7 @@ background_gc=%s Turn on/off cleaning operations, namely garbage ...@@ -102,7 +102,7 @@ background_gc=%s Turn on/off cleaning operations, namely garbage
collection, triggered in background when I/O subsystem is collection, triggered in background when I/O subsystem is
idle. If background_gc=on, it will turn on the garbage idle. If background_gc=on, it will turn on the garbage
collection and if background_gc=off, garbage collection collection and if background_gc=off, garbage collection
will be truned off. If background_gc=sync, it will turn will be turned off. If background_gc=sync, it will turn
on synchronous garbage collection running in background. on synchronous garbage collection running in background.
Default value for this option is on. So garbage Default value for this option is on. So garbage
collection is on by default. collection is on by default.
...@@ -145,10 +145,12 @@ extent_cache Enable an extent cache based on rb-tree, it can cache ...@@ -145,10 +145,12 @@ extent_cache Enable an extent cache based on rb-tree, it can cache
as many as extent which map between contiguous logical as many as extent which map between contiguous logical
address and physical address per inode, resulting in address and physical address per inode, resulting in
increasing the cache hit ratio. Set by default. increasing the cache hit ratio. Set by default.
noextent_cache Diable an extent cache based on rb-tree explicitly, see noextent_cache Disable an extent cache based on rb-tree explicitly, see
the above extent_cache mount option. the above extent_cache mount option.
noinline_data Disable the inline data feature, inline data feature is noinline_data Disable the inline data feature, inline data feature is
enabled by default. enabled by default.
data_flush Enable data flushing before checkpoint in order to
persist data of regular and symlink.
================================================================================ ================================================================================
DEBUGFS ENTRIES DEBUGFS ENTRIES
...@@ -192,7 +194,7 @@ Files in /sys/fs/f2fs/<devname> ...@@ -192,7 +194,7 @@ Files in /sys/fs/f2fs/<devname>
policy for garbage collection. Setting gc_idle = 0 policy for garbage collection. Setting gc_idle = 0
(default) will disable this option. Setting (default) will disable this option. Setting
gc_idle = 1 will select the Cost Benefit approach gc_idle = 1 will select the Cost Benefit approach
& setting gc_idle = 2 will select the greedy aproach. & setting gc_idle = 2 will select the greedy approach.
reclaim_segments This parameter controls the number of prefree reclaim_segments This parameter controls the number of prefree
segments to be reclaimed. If the number of prefree segments to be reclaimed. If the number of prefree
...@@ -298,7 +300,7 @@ The dump.f2fs shows the information of specific inode and dumps SSA and SIT to ...@@ -298,7 +300,7 @@ The dump.f2fs shows the information of specific inode and dumps SSA and SIT to
file. Each file is dump_ssa and dump_sit. file. Each file is dump_ssa and dump_sit.
The dump.f2fs is used to debug on-disk data structures of the f2fs filesystem. The dump.f2fs is used to debug on-disk data structures of the f2fs filesystem.
It shows on-disk inode information reconized by a given inode number, and is It shows on-disk inode information recognized by a given inode number, and is
able to dump all the SSA and SIT entries into predefined files, ./dump_ssa and able to dump all the SSA and SIT entries into predefined files, ./dump_ssa and
./dump_sit respectively. ./dump_sit respectively.
......
This diff is collapsed.
This diff is collapsed.
...@@ -38,12 +38,15 @@ static void update_general_status(struct f2fs_sb_info *sbi) ...@@ -38,12 +38,15 @@ static void update_general_status(struct f2fs_sb_info *sbi)
si->hit_rbtree = atomic64_read(&sbi->read_hit_rbtree); si->hit_rbtree = atomic64_read(&sbi->read_hit_rbtree);
si->hit_total = si->hit_largest + si->hit_cached + si->hit_rbtree; si->hit_total = si->hit_largest + si->hit_cached + si->hit_rbtree;
si->total_ext = atomic64_read(&sbi->total_hit_ext); si->total_ext = atomic64_read(&sbi->total_hit_ext);
si->ext_tree = sbi->total_ext_tree; si->ext_tree = atomic_read(&sbi->total_ext_tree);
si->zombie_tree = atomic_read(&sbi->total_zombie_tree);
si->ext_node = atomic_read(&sbi->total_ext_node); si->ext_node = atomic_read(&sbi->total_ext_node);
si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES); si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES);
si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS);
si->ndirty_dirs = sbi->n_dirty_dirs;
si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META); si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META);
si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA);
si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE];
si->ndirty_files = sbi->ndirty_inode[FILE_INODE];
si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES);
si->wb_pages = get_pages(sbi, F2FS_WRITEBACK); si->wb_pages = get_pages(sbi, F2FS_WRITEBACK);
si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg;
...@@ -105,7 +108,7 @@ static void update_sit_info(struct f2fs_sb_info *sbi) ...@@ -105,7 +108,7 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
bimodal = 0; bimodal = 0;
total_vblocks = 0; total_vblocks = 0;
blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg); blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg;
hblks_per_sec = blks_per_sec / 2; hblks_per_sec = blks_per_sec / 2;
for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec); vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
...@@ -189,10 +192,10 @@ get_cache: ...@@ -189,10 +192,10 @@ get_cache:
si->cache_mem += NM_I(sbi)->dirty_nat_cnt * si->cache_mem += NM_I(sbi)->dirty_nat_cnt *
sizeof(struct nat_entry_set); sizeof(struct nat_entry_set);
si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages); si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages);
si->cache_mem += sbi->n_dirty_dirs * sizeof(struct inode_entry);
for (i = 0; i <= UPDATE_INO; i++) for (i = 0; i <= UPDATE_INO; i++)
si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry); si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
si->cache_mem += sbi->total_ext_tree * sizeof(struct extent_tree); si->cache_mem += atomic_read(&sbi->total_ext_tree) *
sizeof(struct extent_tree);
si->cache_mem += atomic_read(&sbi->total_ext_node) * si->cache_mem += atomic_read(&sbi->total_ext_node) *
sizeof(struct extent_node); sizeof(struct extent_node);
...@@ -267,7 +270,8 @@ static int stat_show(struct seq_file *s, void *v) ...@@ -267,7 +270,8 @@ static int stat_show(struct seq_file *s, void *v)
si->dirty_count); si->dirty_count);
seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n", seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n",
si->prefree_count, si->free_segs, si->free_secs); si->prefree_count, si->free_segs, si->free_secs);
seq_printf(s, "CP calls: %d\n", si->cp_count); seq_printf(s, "CP calls: %d (BG: %d)\n",
si->cp_count, si->bg_cp_count);
seq_printf(s, "GC calls: %d (BG: %d)\n", seq_printf(s, "GC calls: %d (BG: %d)\n",
si->call_count, si->bg_gc); si->call_count, si->bg_gc);
seq_printf(s, " - data segments : %d (%d)\n", seq_printf(s, " - data segments : %d (%d)\n",
...@@ -288,8 +292,8 @@ static int stat_show(struct seq_file *s, void *v) ...@@ -288,8 +292,8 @@ static int stat_show(struct seq_file *s, void *v)
!si->total_ext ? 0 : !si->total_ext ? 0 :
div64_u64(si->hit_total * 100, si->total_ext), div64_u64(si->hit_total * 100, si->total_ext),
si->hit_total, si->total_ext); si->hit_total, si->total_ext);
seq_printf(s, " - Inner Struct Count: tree: %d, node: %d\n", seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n",
si->ext_tree, si->ext_node); si->ext_tree, si->zombie_tree, si->ext_node);
seq_puts(s, "\nBalancing F2FS Async:\n"); seq_puts(s, "\nBalancing F2FS Async:\n");
seq_printf(s, " - inmem: %4d, wb: %4d\n", seq_printf(s, " - inmem: %4d, wb: %4d\n",
si->inmem_pages, si->wb_pages); si->inmem_pages, si->wb_pages);
...@@ -297,6 +301,8 @@ static int stat_show(struct seq_file *s, void *v) ...@@ -297,6 +301,8 @@ static int stat_show(struct seq_file *s, void *v)
si->ndirty_node, si->node_pages); si->ndirty_node, si->node_pages);
seq_printf(s, " - dents: %4d in dirs:%4d\n", seq_printf(s, " - dents: %4d in dirs:%4d\n",
si->ndirty_dent, si->ndirty_dirs); si->ndirty_dent, si->ndirty_dirs);
seq_printf(s, " - datas: %4d in files:%4d\n",
si->ndirty_data, si->ndirty_files);
seq_printf(s, " - meta: %4d in %4d\n", seq_printf(s, " - meta: %4d in %4d\n",
si->ndirty_meta, si->meta_pages); si->ndirty_meta, si->meta_pages);
seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n", seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n",
...@@ -404,20 +410,23 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi) ...@@ -404,20 +410,23 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi)
kfree(si); kfree(si);
} }
void __init f2fs_create_root_stats(void) int __init f2fs_create_root_stats(void)
{ {
struct dentry *file; struct dentry *file;
f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL); f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL);
if (!f2fs_debugfs_root) if (!f2fs_debugfs_root)
return; return -ENOMEM;
file = debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root, file = debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root,
NULL, &stat_fops); NULL, &stat_fops);
if (!file) { if (!file) {
debugfs_remove(f2fs_debugfs_root); debugfs_remove(f2fs_debugfs_root);
f2fs_debugfs_root = NULL; f2fs_debugfs_root = NULL;
return -ENOMEM;
} }
return 0;
} }
void f2fs_destroy_root_stats(void) void f2fs_destroy_root_stats(void)
......
...@@ -172,8 +172,6 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, ...@@ -172,8 +172,6 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
namehash = f2fs_dentry_hash(&name); namehash = f2fs_dentry_hash(&name);
f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level); nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
nblock = bucket_blocks(level); nblock = bucket_blocks(level);
...@@ -238,6 +236,14 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, ...@@ -238,6 +236,14 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
goto out; goto out;
max_depth = F2FS_I(dir)->i_current_depth; max_depth = F2FS_I(dir)->i_current_depth;
if (unlikely(max_depth > MAX_DIR_HASH_DEPTH)) {
f2fs_msg(F2FS_I_SB(dir)->sb, KERN_WARNING,
"Corrupted max_depth of %lu: %u",
dir->i_ino, max_depth);
max_depth = MAX_DIR_HASH_DEPTH;
F2FS_I(dir)->i_current_depth = max_depth;
mark_inode_dirty(dir);
}
for (level = 0; level < max_depth; level++) { for (level = 0; level < max_depth; level++) {
de = find_in_level(dir, level, &fname, res_page); de = find_in_level(dir, level, &fname, res_page);
...@@ -444,7 +450,7 @@ error: ...@@ -444,7 +450,7 @@ error:
/* once the failed inode becomes a bad inode, i_mode is S_IFREG */ /* once the failed inode becomes a bad inode, i_mode is S_IFREG */
truncate_inode_pages(&inode->i_data, 0); truncate_inode_pages(&inode->i_data, 0);
truncate_blocks(inode, 0, false); truncate_blocks(inode, 0, false);
remove_dirty_dir_inode(inode); remove_dirty_inode(inode);
remove_inode_page(inode); remove_inode_page(inode);
return ERR_PTR(err); return ERR_PTR(err);
} }
...@@ -630,6 +636,7 @@ fail: ...@@ -630,6 +636,7 @@ fail:
f2fs_put_page(dentry_page, 1); f2fs_put_page(dentry_page, 1);
out: out:
f2fs_fname_free_filename(&fname); f2fs_fname_free_filename(&fname);
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
return err; return err;
} }
...@@ -651,6 +658,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir) ...@@ -651,6 +658,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
clear_inode_flag(F2FS_I(inode), FI_NEW_INODE); clear_inode_flag(F2FS_I(inode), FI_NEW_INODE);
fail: fail:
up_write(&F2FS_I(inode)->i_sem); up_write(&F2FS_I(inode)->i_sem);
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return err; return err;
} }
...@@ -695,6 +703,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, ...@@ -695,6 +703,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
int i; int i;
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
if (f2fs_has_inline_dentry(dir)) if (f2fs_has_inline_dentry(dir))
return f2fs_delete_inline_entry(dentry, page, dir, inode); return f2fs_delete_inline_entry(dentry, page, dir, inode);
...@@ -855,25 +865,27 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) ...@@ -855,25 +865,27 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
for (; n < npages; n++) { for (; n < npages; n++) {
dentry_page = get_lock_data_page(inode, n, false); dentry_page = get_lock_data_page(inode, n, false);
if (IS_ERR(dentry_page)) if (IS_ERR(dentry_page)) {
continue; err = PTR_ERR(dentry_page);
if (err == -ENOENT)
continue;
else
goto out;
}
dentry_blk = kmap(dentry_page); dentry_blk = kmap(dentry_page);
make_dentry_ptr(inode, &d, (void *)dentry_blk, 1); make_dentry_ptr(inode, &d, (void *)dentry_blk, 1);
if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) {
goto stop; kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
break;
}
ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK; ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK;
kunmap(dentry_page); kunmap(dentry_page);
f2fs_put_page(dentry_page, 1); f2fs_put_page(dentry_page, 1);
dentry_page = NULL;
}
stop:
if (dentry_page && !IS_ERR(dentry_page)) {
kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
} }
out: out:
f2fs_fname_crypto_free_buffer(&fstr); f2fs_fname_crypto_free_buffer(&fstr);
......
...@@ -36,7 +36,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi, ...@@ -36,7 +36,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
rb_link_node(&en->rb_node, parent, p); rb_link_node(&en->rb_node, parent, p);
rb_insert_color(&en->rb_node, &et->root); rb_insert_color(&en->rb_node, &et->root);
et->count++; atomic_inc(&et->node_cnt);
atomic_inc(&sbi->total_ext_node); atomic_inc(&sbi->total_ext_node);
return en; return en;
} }
...@@ -45,7 +45,7 @@ static void __detach_extent_node(struct f2fs_sb_info *sbi, ...@@ -45,7 +45,7 @@ static void __detach_extent_node(struct f2fs_sb_info *sbi,
struct extent_tree *et, struct extent_node *en) struct extent_tree *et, struct extent_node *en)
{ {
rb_erase(&en->rb_node, &et->root); rb_erase(&en->rb_node, &et->root);
et->count--; atomic_dec(&et->node_cnt);
atomic_dec(&sbi->total_ext_node); atomic_dec(&sbi->total_ext_node);
if (et->cached_en == en) if (et->cached_en == en)
...@@ -68,11 +68,13 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode) ...@@ -68,11 +68,13 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode)
et->root = RB_ROOT; et->root = RB_ROOT;
et->cached_en = NULL; et->cached_en = NULL;
rwlock_init(&et->lock); rwlock_init(&et->lock);
atomic_set(&et->refcount, 0); INIT_LIST_HEAD(&et->list);
et->count = 0; atomic_set(&et->node_cnt, 0);
sbi->total_ext_tree++; atomic_inc(&sbi->total_ext_tree);
} else {
atomic_dec(&sbi->total_zombie_tree);
list_del_init(&et->list);
} }
atomic_inc(&et->refcount);
up_write(&sbi->extent_tree_lock); up_write(&sbi->extent_tree_lock);
/* never died until evict_inode */ /* never died until evict_inode */
...@@ -131,7 +133,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, ...@@ -131,7 +133,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
{ {
struct rb_node *node, *next; struct rb_node *node, *next;
struct extent_node *en; struct extent_node *en;
unsigned int count = et->count; unsigned int count = atomic_read(&et->node_cnt);
node = rb_first(&et->root); node = rb_first(&et->root);
while (node) { while (node) {
...@@ -152,7 +154,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, ...@@ -152,7 +154,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
node = next; node = next;
} }
return count - et->count; return count - atomic_read(&et->node_cnt);
} }
static void __drop_largest_extent(struct inode *inode, static void __drop_largest_extent(struct inode *inode,
...@@ -164,34 +166,33 @@ static void __drop_largest_extent(struct inode *inode, ...@@ -164,34 +166,33 @@ static void __drop_largest_extent(struct inode *inode,
largest->len = 0; largest->len = 0;
} }
void f2fs_drop_largest_extent(struct inode *inode, pgoff_t fofs) /* return true, if inode page is changed */
{ bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
if (!f2fs_may_extent_tree(inode))
return;
__drop_largest_extent(inode, fofs, 1);
}
void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct extent_tree *et; struct extent_tree *et;
struct extent_node *en; struct extent_node *en;
struct extent_info ei; struct extent_info ei;
if (!f2fs_may_extent_tree(inode)) if (!f2fs_may_extent_tree(inode)) {
return; /* drop largest extent */
if (i_ext && i_ext->len) {
i_ext->len = 0;
return true;
}
return false;
}
et = __grab_extent_tree(inode); et = __grab_extent_tree(inode);
if (!i_ext || le32_to_cpu(i_ext->len) < F2FS_MIN_EXTENT_LEN) if (!i_ext || !i_ext->len)
return; return false;
set_extent_info(&ei, le32_to_cpu(i_ext->fofs), set_extent_info(&ei, le32_to_cpu(i_ext->fofs),
le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len)); le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len));
write_lock(&et->lock); write_lock(&et->lock);
if (et->count) if (atomic_read(&et->node_cnt))
goto out; goto out;
en = __init_extent_tree(sbi, et, &ei); en = __init_extent_tree(sbi, et, &ei);
...@@ -202,6 +203,7 @@ void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext) ...@@ -202,6 +203,7 @@ void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
} }
out: out:
write_unlock(&et->lock); write_unlock(&et->lock);
return false;
} }
static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
...@@ -549,45 +551,44 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode, ...@@ -549,45 +551,44 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
{ {
struct extent_tree *treevec[EXT_TREE_VEC_SIZE]; struct extent_tree *treevec[EXT_TREE_VEC_SIZE];
struct extent_tree *et, *next;
struct extent_node *en, *tmp; struct extent_node *en, *tmp;
unsigned long ino = F2FS_ROOT_INO(sbi); unsigned long ino = F2FS_ROOT_INO(sbi);
struct radix_tree_root *root = &sbi->extent_tree_root;
unsigned int found; unsigned int found;
unsigned int node_cnt = 0, tree_cnt = 0; unsigned int node_cnt = 0, tree_cnt = 0;
int remained; int remained;
bool do_free = false;
if (!test_opt(sbi, EXTENT_CACHE)) if (!test_opt(sbi, EXTENT_CACHE))
return 0; return 0;
if (!atomic_read(&sbi->total_zombie_tree))
goto free_node;
if (!down_write_trylock(&sbi->extent_tree_lock)) if (!down_write_trylock(&sbi->extent_tree_lock))
goto out; goto out;
/* 1. remove unreferenced extent tree */ /* 1. remove unreferenced extent tree */
while ((found = radix_tree_gang_lookup(root, list_for_each_entry_safe(et, next, &sbi->zombie_list, list) {
(void **)treevec, ino, EXT_TREE_VEC_SIZE))) { if (atomic_read(&et->node_cnt)) {
unsigned i; write_lock(&et->lock);
node_cnt += __free_extent_tree(sbi, et, true);
ino = treevec[found - 1]->ino + 1; write_unlock(&et->lock);
for (i = 0; i < found; i++) { }
struct extent_tree *et = treevec[i];
if (!atomic_read(&et->refcount)) {
write_lock(&et->lock);
node_cnt += __free_extent_tree(sbi, et, true);
write_unlock(&et->lock);
radix_tree_delete(root, et->ino); list_del_init(&et->list);
kmem_cache_free(extent_tree_slab, et); radix_tree_delete(&sbi->extent_tree_root, et->ino);
sbi->total_ext_tree--; kmem_cache_free(extent_tree_slab, et);
tree_cnt++; atomic_dec(&sbi->total_ext_tree);
atomic_dec(&sbi->total_zombie_tree);
tree_cnt++;
if (node_cnt + tree_cnt >= nr_shrink) if (node_cnt + tree_cnt >= nr_shrink)
goto unlock_out; goto unlock_out;
}
}
} }
up_write(&sbi->extent_tree_lock); up_write(&sbi->extent_tree_lock);
free_node:
/* 2. remove LRU extent entries */ /* 2. remove LRU extent entries */
if (!down_write_trylock(&sbi->extent_tree_lock)) if (!down_write_trylock(&sbi->extent_tree_lock))
goto out; goto out;
...@@ -599,15 +600,19 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) ...@@ -599,15 +600,19 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
if (!remained--) if (!remained--)
break; break;
list_del_init(&en->list); list_del_init(&en->list);
do_free = true;
} }
spin_unlock(&sbi->extent_lock); spin_unlock(&sbi->extent_lock);
if (do_free == false)
goto unlock_out;
/* /*
* reset ino for searching victims from beginning of global extent tree. * reset ino for searching victims from beginning of global extent tree.
*/ */
ino = F2FS_ROOT_INO(sbi); ino = F2FS_ROOT_INO(sbi);
while ((found = radix_tree_gang_lookup(root, while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root,
(void **)treevec, ino, EXT_TREE_VEC_SIZE))) { (void **)treevec, ino, EXT_TREE_VEC_SIZE))) {
unsigned i; unsigned i;
...@@ -615,9 +620,13 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) ...@@ -615,9 +620,13 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
for (i = 0; i < found; i++) { for (i = 0; i < found; i++) {
struct extent_tree *et = treevec[i]; struct extent_tree *et = treevec[i];
write_lock(&et->lock); if (!atomic_read(&et->node_cnt))
node_cnt += __free_extent_tree(sbi, et, false); continue;
write_unlock(&et->lock);
if (write_trylock(&et->lock)) {
node_cnt += __free_extent_tree(sbi, et, false);
write_unlock(&et->lock);
}
if (node_cnt + tree_cnt >= nr_shrink) if (node_cnt + tree_cnt >= nr_shrink)
goto unlock_out; goto unlock_out;
...@@ -637,7 +646,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode) ...@@ -637,7 +646,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode)
struct extent_tree *et = F2FS_I(inode)->extent_tree; struct extent_tree *et = F2FS_I(inode)->extent_tree;
unsigned int node_cnt = 0; unsigned int node_cnt = 0;
if (!et) if (!et || !atomic_read(&et->node_cnt))
return 0; return 0;
write_lock(&et->lock); write_lock(&et->lock);
...@@ -656,8 +665,12 @@ void f2fs_destroy_extent_tree(struct inode *inode) ...@@ -656,8 +665,12 @@ void f2fs_destroy_extent_tree(struct inode *inode)
if (!et) if (!et)
return; return;
if (inode->i_nlink && !is_bad_inode(inode) && et->count) { if (inode->i_nlink && !is_bad_inode(inode) &&
atomic_dec(&et->refcount); atomic_read(&et->node_cnt)) {
down_write(&sbi->extent_tree_lock);
list_add_tail(&et->list, &sbi->zombie_list);
atomic_inc(&sbi->total_zombie_tree);
up_write(&sbi->extent_tree_lock);
return; return;
} }
...@@ -666,11 +679,10 @@ void f2fs_destroy_extent_tree(struct inode *inode) ...@@ -666,11 +679,10 @@ void f2fs_destroy_extent_tree(struct inode *inode)
/* delete extent tree entry in radix tree */ /* delete extent tree entry in radix tree */
down_write(&sbi->extent_tree_lock); down_write(&sbi->extent_tree_lock);
atomic_dec(&et->refcount); f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
f2fs_bug_on(sbi, atomic_read(&et->refcount) || et->count);
radix_tree_delete(&sbi->extent_tree_root, inode->i_ino); radix_tree_delete(&sbi->extent_tree_root, inode->i_ino);
kmem_cache_free(extent_tree_slab, et); kmem_cache_free(extent_tree_slab, et);
sbi->total_ext_tree--; atomic_dec(&sbi->total_ext_tree);
up_write(&sbi->extent_tree_lock); up_write(&sbi->extent_tree_lock);
F2FS_I(inode)->extent_tree = NULL; F2FS_I(inode)->extent_tree = NULL;
...@@ -722,7 +734,9 @@ void init_extent_cache_info(struct f2fs_sb_info *sbi) ...@@ -722,7 +734,9 @@ void init_extent_cache_info(struct f2fs_sb_info *sbi)
init_rwsem(&sbi->extent_tree_lock); init_rwsem(&sbi->extent_tree_lock);
INIT_LIST_HEAD(&sbi->extent_list); INIT_LIST_HEAD(&sbi->extent_list);
spin_lock_init(&sbi->extent_lock); spin_lock_init(&sbi->extent_lock);
sbi->total_ext_tree = 0; atomic_set(&sbi->total_ext_tree, 0);
INIT_LIST_HEAD(&sbi->zombie_list);
atomic_set(&sbi->total_zombie_tree, 0);
atomic_set(&sbi->total_ext_node, 0); atomic_set(&sbi->total_ext_node, 0);
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/blkdev.h>
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
...@@ -173,9 +172,9 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi, ...@@ -173,9 +172,9 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi,
{ {
/* SSR allocates in a segment unit */ /* SSR allocates in a segment unit */
if (p->alloc_mode == SSR) if (p->alloc_mode == SSR)
return 1 << sbi->log_blocks_per_seg;