From d6212a5f18c8f9f9cc884070a96e11907711217f Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Tue, 29 Jan 2013 18:30:07 +0900 Subject: f2fs: add un/freeze_fs into super_operations This patch supports ioctl FIFREEZE and FITHAW to snapshot filesystem. Before calling f2fs_freeze, all writers would be suspended and sync_fs would be completed. So no f2fs has to do something. Just background gc operation should be skipped due to generate dirty nodes and data until unfreeze. Signed-off-by: Changman Lee Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs/f2fs/gc.c') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c386910dacc5..9b5d0aad5daf 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -44,6 +44,11 @@ static int gc_thread_func(void *data) if (kthread_should_stop()) break; + if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) { + wait_ms = GC_THREAD_MAX_SLEEP_TIME; + continue; + } + f2fs_balance_fs(sbi); if (!test_opt(sbi, BG_GC)) -- cgit v1.2.3 From 3786dfdf4f00755ce5797c0e87eac8f898acb52c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jan 2013 22:47:02 +0900 Subject: f2fs: avoid redundant call to has_not_enough_free_secs in f2fs_gc After doing a write_checkpoint from garbage collection path if there is still need to do more garbage collection, gc_more label is used to jump and start the process again. And in that process, first step before getting victim is to check if there are not enough free sections, which is already done before doing a jump to gc_more. We can avoid the redundant call to check free sections, by checking the gc_type flag which will remain FG_GC(value 1) under this condition. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/f2fs/gc.c') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 9b5d0aad5daf..fb03be68cb20 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -672,7 +672,7 @@ gc_more: if (!(sbi->sb->s_flags & MS_ACTIVE)) goto stop; - if (has_not_enough_free_secs(sbi)) + if (gc_type == BG_GC && has_not_enough_free_secs(sbi)) gc_type = FG_GC; if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) -- cgit v1.2.3 From d4686d56ec912d55fd8a9d6d509de50de24e90ab Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Jan 2013 15:36:04 +0900 Subject: f2fs: avoid balanc_fs during evict_inode 1. Background Previously, if f2fs tries to move data blocks of an *evicting* inode during the cleaning process, it stops the process incompletely and then restarts the whole process, since it needs a locked inode to grab victim data pages in its address space. In order to get a locked inode, iget_locked() by f2fs_iget() is normally used, but, it waits if the inode is on freeing. So, here is a deadlock scenario. 1. f2fs_evict_inode() <- inode "A" 2. f2fs_balance_fs() 3. f2fs_gc() 4. gc_data_segment() 5. f2fs_iget() <- inode "A" too! If step #1 and #5 treat a same inode "A", step #5 would fall into deadlock since the inode "A" is on freeing. In order to resolve this, f2fs_iget_nowait() which skips __wait_on_freeing_inode() was introduced in step #5, and stops f2fs_gc() to complete f2fs_evict_inode(). 1. f2fs_evict_inode() <- inode "A" 2. f2fs_balance_fs() 3. f2fs_gc() 4. gc_data_segment() 5. f2fs_iget_nowait() <- inode "A", then stop f2fs_gc() w/ -ENOENT 2. Problem and Solution In the above scenario, however, f2fs cannot finish f2fs_evict_inode() only if: o there are not enough free sections, and o f2fs_gc() tries to move data blocks of the *evicting* inode repeatedly. So, the final solution is to use f2fs_iget() and remove f2fs_balance_fs() in f2fs_evict_inode(). The f2fs_evict_inode() actually truncates all the data and node blocks, which means that it doesn't produce any dirty node pages accordingly. So, we don't need to do f2fs_balance_fs() in practical. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 - fs/f2fs/file.c | 3 +-- fs/f2fs/gc.c | 2 +- fs/f2fs/inode.c | 33 --------------------------------- fs/f2fs/recovery.c | 2 +- 5 files changed, 3 insertions(+), 38 deletions(-) (limited to 'fs/f2fs/gc.c') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 975cb44851ab..5022a7d7f7ca 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -847,7 +847,6 @@ long f2fs_ioctl(struct file *, unsigned int, unsigned long); * inode.c */ void f2fs_set_inode_flags(struct inode *); -struct inode *f2fs_iget_nowait(struct super_block *, unsigned long); struct inode *f2fs_iget(struct super_block *, unsigned long); void update_inode(struct inode *, struct page *); int f2fs_write_inode(struct inode *, struct writeback_control *); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6cdab2c64fc6..3efa4739b00c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -298,8 +298,6 @@ void f2fs_truncate(struct inode *inode) inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); } - - f2fs_balance_fs(F2FS_SB(inode->i_sb)); } static int f2fs_getattr(struct vfsmount *mnt, @@ -356,6 +354,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) attr->ia_size != i_size_read(inode)) { truncate_setsize(inode, attr->ia_size); f2fs_truncate(inode); + f2fs_balance_fs(F2FS_SB(inode->i_sb)); } __setattr_copy(inode, attr); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index fb03be68cb20..375e69e2c6f1 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -579,7 +579,7 @@ next_step: ofs_in_node = le16_to_cpu(entry->ofs_in_node); if (phase == 2) { - inode = f2fs_iget_nowait(sb, dni.ino); + inode = f2fs_iget(sb, dni.ino); if (IS_ERR(inode)) continue; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 62433c63e2db..ddae412d30c8 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -16,11 +16,6 @@ #include "f2fs.h" #include "node.h" -struct f2fs_iget_args { - u64 ino; - int on_free; -}; - void f2fs_set_inode_flags(struct inode *inode) { unsigned int flags = F2FS_I(inode)->i_flags; @@ -40,34 +35,6 @@ void f2fs_set_inode_flags(struct inode *inode) inode->i_flags |= S_DIRSYNC; } -static int f2fs_iget_test(struct inode *inode, void *data) -{ - struct f2fs_iget_args *args = data; - - if (inode->i_ino != args->ino) - return 0; - if (inode->i_state & (I_FREEING | I_WILL_FREE)) { - args->on_free = 1; - return 0; - } - return 1; -} - -struct inode *f2fs_iget_nowait(struct super_block *sb, unsigned long ino) -{ - struct f2fs_iget_args args = { - .ino = ino, - .on_free = 0 - }; - struct inode *inode = ilookup5(sb, ino, f2fs_iget_test, &args); - - if (inode) - return inode; - if (!args.on_free) - return f2fs_iget(sb, ino); - return ERR_PTR(-ENOENT); -} - static int do_read_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index f42e4060b399..e2a3e1a8eae9 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -226,7 +226,7 @@ static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi, f2fs_put_page(node_page, 1); /* Deallocate previous index in the node page */ - inode = f2fs_iget_nowait(sbi->sb, ino); + inode = f2fs_iget(sbi->sb, ino); if (IS_ERR(inode)) return; -- cgit v1.2.3 From 48600e44c18e4eb0f7c02ec8633c4c56aef292f0 Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Mon, 4 Feb 2013 10:05:09 +0900 Subject: f2fs: remove unnecessary gc option check and balance_fs 1. If f2fs is mounted with background_gc_off option, checking BG_GC is not redundant. 2. f2fs_balance_fs is checked in f2fs_gc, so this is also redundant. Signed-off-by: Changman Lee Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'fs/f2fs/gc.c') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 375e69e2c6f1..8d293cb685ba 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -49,11 +49,6 @@ static int gc_thread_func(void *data) continue; } - f2fs_balance_fs(sbi); - - if (!test_opt(sbi, BG_GC)) - continue; - /* * [GC triggering condition] * 0. GC is not conducted currently. @@ -96,6 +91,8 @@ int start_gc_thread(struct f2fs_sb_info *sbi) { struct f2fs_gc_kthread *gc_th; + if (!test_opt(sbi, BG_GC)) + return 0; gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); if (!gc_th) return -ENOMEM; -- cgit v1.2.3 From ec7b1f2dd180afb1ce23aa44a7451e59bf2f3eb3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:52:28 +0900 Subject: f2fs: name gc task as per the block device Currently GC task is started for each f2fs formatted/mounted device. But, when we check the task list, using 'ps', there is no distinguishing factor between the tasks. So, name the task as per the block device just like the flusher threads. Also, remove the macro GC_THREAD_NAME and instead use the name: f2fs_gc to avoid name length truncation, as the command length is 16 -> TASK_COMM_LEN 16 and example name like: f2fs_gc_task:8:16 -> this exceeds name length Before Patch for 2 F2FS formatted partitions: root 28061 0.0 0.0 0 0 ? S 10:31 0:00 [f2fs_gc_task] root 28087 0.0 0.0 0 0 ? S 10:32 0:00 [f2fs_gc_task] After Patch: root 16756 0.0 0.0 0 0 ? S 14:57 0:00 [f2fs_gc-8:18] root 16765 0.0 0.0 0 0 ? S 14:57 0:00 [f2fs_gc-8:19] Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 3 ++- fs/f2fs/gc.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/f2fs/gc.c') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 8d293cb685ba..b1e498503e59 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -90,6 +90,7 @@ static int gc_thread_func(void *data) int start_gc_thread(struct f2fs_sb_info *sbi) { struct f2fs_gc_kthread *gc_th; + dev_t dev = sbi->sb->s_bdev->bd_dev; if (!test_opt(sbi, BG_GC)) return 0; @@ -100,7 +101,7 @@ int start_gc_thread(struct f2fs_sb_info *sbi) sbi->gc_thread = gc_th; init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, - GC_THREAD_NAME); + "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); if (IS_ERR(gc_th->f2fs_gc_task)) { kfree(gc_th); return -ENOMEM; diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index b026d9354ccd..3abdf83b5c16 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#define GC_THREAD_NAME "f2fs_gc_task" #define GC_THREAD_MIN_WB_PAGES 1 /* * a threshold to determine * whether IO subsystem is idle -- cgit v1.2.3 From 25718423ea6f7118f9c173adff0fac52414571b8 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:52:42 +0900 Subject: f2fs: mark gc_thread as NULL when thread creation is failed When gc thread creation is failed, mark gc_thread as NULL to avoid crash while trying to stop invalid thread in stop_gc_thread->kthread_stop. Instead make it return from: if (!gc_th) return; Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/f2fs/gc.c') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index b1e498503e59..16fdec355201 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -104,6 +104,7 @@ int start_gc_thread(struct f2fs_sb_info *sbi) "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); if (IS_ERR(gc_th->f2fs_gc_task)) { kfree(gc_th); + sbi->gc_thread = NULL; return -ENOMEM; } return 0; -- cgit v1.2.3 From 437275272f9e635673f065300e5d95226a25cb06 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 4 Feb 2013 15:11:17 +0900 Subject: f2fs: clarify and enhance the f2fs_gc flow This patch makes clearer the ambiguous f2fs_gc flow as follows. 1. Remove intermediate checkpoint condition during f2fs_gc (i.e., should_do_checkpoint() and GC_BLOCKED) 2. Remove unnecessary return values of f2fs_gc because of #1. (i.e., GC_NODE, GC_OK, etc) 3. Simplify write_checkpoint() because of #2. 4. Clarify the main f2fs_gc flow. o monitor how many freed sections during one iteration of do_garbage_collect(). o do GC more without checkpoints if we can't get enough free sections. o do checkpoint once we've got enough free sections through forground GCs. 5. Adopt thread-logging (Slack-Space-Recycle) scheme more aggressively on data log types. See. get_ssr_segement() Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 ++--- fs/f2fs/f2fs.h | 3 +- fs/f2fs/gc.c | 107 ++++++++++++++++++++------------------------------- fs/f2fs/gc.h | 16 -------- fs/f2fs/node.c | 2 +- fs/f2fs/recovery.c | 2 +- fs/f2fs/segment.c | 21 +++++++++- fs/f2fs/segment.h | 12 ++---- fs/f2fs/super.c | 4 +- 9 files changed, 72 insertions(+), 105 deletions(-) (limited to 'fs/f2fs/gc.c') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 2887c196b0a2..2b6fc131e2ce 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -539,7 +539,7 @@ retry: /* * Freeze all the FS-operations for checkpoint. */ -void block_operations(struct f2fs_sb_info *sbi) +static void block_operations(struct f2fs_sb_info *sbi) { int t; struct writeback_control wbc = { @@ -722,15 +722,13 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) /* * We guarantee that this checkpoint procedure should not fail. */ -void write_checkpoint(struct f2fs_sb_info *sbi, bool blocked, bool is_umount) +void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); unsigned long long ckpt_ver; - if (!blocked) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - } + mutex_lock(&sbi->cp_mutex); + block_operations(sbi); f2fs_submit_bio(sbi, DATA, true); f2fs_submit_bio(sbi, NODE, true); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 87840bccaf21..e7e7a29767d6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -969,8 +969,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *); void set_dirty_dir_page(struct inode *, struct page *); void remove_dirty_dir_inode(struct inode *); void sync_dirty_dir_inodes(struct f2fs_sb_info *); -void block_operations(struct f2fs_sb_info *); -void write_checkpoint(struct f2fs_sb_info *, bool, bool); +void write_checkpoint(struct f2fs_sb_info *, bool); void init_orphan_info(struct f2fs_sb_info *); int __init create_checkpoint_caches(void); void destroy_checkpoint_caches(void); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 16fdec355201..52d3a391b922 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -78,7 +78,8 @@ static int gc_thread_func(void *data) sbi->bg_gc++; - if (f2fs_gc(sbi) == GC_NONE) + /* if return value is not zero, no victim was selected */ + if (f2fs_gc(sbi)) wait_ms = GC_THREAD_NOGC_SLEEP_TIME; else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME) wait_ms = GC_THREAD_MAX_SLEEP_TIME; @@ -360,7 +361,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi, sentry = get_seg_entry(sbi, segno); ret = f2fs_test_bit(offset, sentry->cur_valid_map); mutex_unlock(&sit_i->sentry_lock); - return ret ? GC_OK : GC_NEXT; + return ret; } /* @@ -368,7 +369,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi, * On validity, copy that node with cold status, otherwise (invalid node) * ignore that. */ -static int gc_node_segment(struct f2fs_sb_info *sbi, +static void gc_node_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, unsigned int segno, int gc_type) { bool initial = true; @@ -380,21 +381,12 @@ next_step: for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { nid_t nid = le32_to_cpu(entry->nid); struct page *node_page; - int err; - /* - * It makes sure that free segments are able to write - * all the dirty node pages before CP after this CP. - * So let's check the space of dirty node pages. - */ - if (should_do_checkpoint(sbi)) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - return GC_BLOCKED; - } + /* stop BG_GC if there is not enough free sections. */ + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + return; - err = check_valid_map(sbi, segno, off); - if (err == GC_NEXT) + if (check_valid_map(sbi, segno, off) == 0) continue; if (initial) { @@ -424,7 +416,6 @@ next_step: }; sync_node_pages(sbi, 0, &wbc); } - return GC_DONE; } /* @@ -467,13 +458,13 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, node_page = get_node_page(sbi, nid); if (IS_ERR(node_page)) - return GC_NEXT; + return 0; get_node_info(sbi, nid, dni); if (sum->version != dni->version) { f2fs_put_page(node_page, 1); - return GC_NEXT; + return 0; } *nofs = ofs_of_node(node_page); @@ -481,8 +472,8 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, f2fs_put_page(node_page, 1); if (source_blkaddr != blkaddr) - return GC_NEXT; - return GC_OK; + return 0; + return 1; } static void move_data_page(struct inode *inode, struct page *page, int gc_type) @@ -523,13 +514,13 @@ out: * If the parent node is not valid or the data block address is different, * the victim data block is ignored. */ -static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, +static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, struct list_head *ilist, unsigned int segno, int gc_type) { struct super_block *sb = sbi->sb; struct f2fs_summary *entry; block_t start_addr; - int err, off; + int off; int phase = 0; start_addr = START_BLOCK(sbi, segno); @@ -543,20 +534,11 @@ next_step: unsigned int ofs_in_node, nofs; block_t start_bidx; - /* - * It makes sure that free segments are able to write - * all the dirty node pages before CP after this CP. - * So let's check the space of dirty node pages. - */ - if (should_do_checkpoint(sbi)) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - err = GC_BLOCKED; - goto stop; - } + /* stop BG_GC if there is not enough free sections. */ + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + return; - err = check_valid_map(sbi, segno, off); - if (err == GC_NEXT) + if (check_valid_map(sbi, segno, off) == 0) continue; if (phase == 0) { @@ -565,8 +547,7 @@ next_step: } /* Get an inode by ino with checking validity */ - err = check_dnode(sbi, entry, &dni, start_addr + off, &nofs); - if (err == GC_NEXT) + if (check_dnode(sbi, entry, &dni, start_addr + off, &nofs) == 0) continue; if (phase == 1) { @@ -606,11 +587,9 @@ next_iput: } if (++phase < 4) goto next_step; - err = GC_DONE; -stop: + if (gc_type == FG_GC) f2fs_submit_bio(sbi, DATA, true); - return err; } static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, @@ -624,17 +603,16 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, return ret; } -static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, +static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, struct list_head *ilist, int gc_type) { struct page *sum_page; struct f2fs_summary_block *sum; - int ret = GC_DONE; /* read segment summary of victim */ sum_page = get_sum_page(sbi, segno); if (IS_ERR(sum_page)) - return GC_ERROR; + return; /* * CP needs to lock sum_page. In this time, we don't need @@ -646,17 +624,16 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, switch (GET_SUM_TYPE((&sum->footer))) { case SUM_TYPE_NODE: - ret = gc_node_segment(sbi, sum->entries, segno, gc_type); + gc_node_segment(sbi, sum->entries, segno, gc_type); break; case SUM_TYPE_DATA: - ret = gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); + gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); break; } stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer))); stat_inc_call_count(sbi->stat_info); f2fs_put_page(sum_page, 0); - return ret; } int f2fs_gc(struct f2fs_sb_info *sbi) @@ -664,40 +641,38 @@ int f2fs_gc(struct f2fs_sb_info *sbi) struct list_head ilist; unsigned int segno, i; int gc_type = BG_GC; - int gc_status = GC_NONE; + int nfree = 0; + int ret = -1; INIT_LIST_HEAD(&ilist); gc_more: if (!(sbi->sb->s_flags & MS_ACTIVE)) goto stop; - if (gc_type == BG_GC && has_not_enough_free_secs(sbi)) + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) gc_type = FG_GC; if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) goto stop; + ret = 0; - for (i = 0; i < sbi->segs_per_sec; i++) { - /* - * do_garbage_collect will give us three gc_status: - * GC_ERROR, GC_DONE, and GC_BLOCKED. - * If GC is finished uncleanly, we have to return - * the victim to dirty segment list. - */ - gc_status = do_garbage_collect(sbi, segno + i, &ilist, gc_type); - if (gc_status != GC_DONE) - break; - } - if (has_not_enough_free_secs(sbi)) { - write_checkpoint(sbi, (gc_status == GC_BLOCKED), false); - if (has_not_enough_free_secs(sbi)) - goto gc_more; - } + for (i = 0; i < sbi->segs_per_sec; i++) + do_garbage_collect(sbi, segno + i, &ilist, gc_type); + + if (gc_type == FG_GC && + get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0) + nfree++; + + if (has_not_enough_free_secs(sbi, nfree)) + goto gc_more; + + if (gc_type == FG_GC) + write_checkpoint(sbi, false); stop: mutex_unlock(&sbi->gc_mutex); put_gc_inode(&ilist); - return gc_status; + return ret; } void build_gc_manager(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index c407a75a7daa..30b2db003acd 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -22,15 +22,6 @@ /* Search max. number of dirty segments to select a victim segment */ #define MAX_VICTIM_SEARCH 20 -enum { - GC_NONE = 0, - GC_ERROR, - GC_OK, - GC_NEXT, - GC_BLOCKED, - GC_DONE, -}; - struct f2fs_gc_kthread { struct task_struct *f2fs_gc_task; wait_queue_head_t gc_wait_queue_head; @@ -103,10 +94,3 @@ static inline int is_idle(struct f2fs_sb_info *sbi) struct request_list *rl = &q->root_rl; return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]); } - -static inline bool should_do_checkpoint(struct f2fs_sb_info *sbi) -{ - int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); - int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); - return free_sections(sbi) <= (node_secs + 2 * dent_secs + 2); -} diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 33fa6d506d94..43ce16422b75 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1135,7 +1135,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, /* First check balancing cached NAT entries */ if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) { - write_checkpoint(sbi, false, false); + write_checkpoint(sbi, false); return 0; } diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index e2a3e1a8eae9..01e1a03b54c8 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -373,5 +373,5 @@ void recover_fsync_data(struct f2fs_sb_info *sbi) out: destroy_fsync_dnodes(sbi, &inode_list); kmem_cache_destroy(fsync_entry_slab); - write_checkpoint(sbi, false, false); + write_checkpoint(sbi, false); } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7aa270f3538a..777f17e496e6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -29,7 +29,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi) * We should do GC or end up with checkpoint, if there are so many dirty * dir/node pages without enough free segments. */ - if (has_not_enough_free_secs(sbi)) { + if (has_not_enough_free_secs(sbi, 0)) { mutex_lock(&sbi->gc_mutex); f2fs_gc(sbi); } @@ -308,7 +308,7 @@ static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi, * If there is not enough reserved sections, * we should not reuse prefree segments. */ - if (has_not_enough_free_secs(sbi)) + if (has_not_enough_free_secs(sbi, 0)) return NULL_SEGNO; /* @@ -536,6 +536,23 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse) } } +static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; + + if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0)) + return v_ops->get_victim(sbi, + &(curseg)->next_segno, BG_GC, type, SSR); + + /* For data segments, let's do SSR more intensively */ + for (; type >= CURSEG_HOT_DATA; type--) + if (v_ops->get_victim(sbi, &(curseg)->next_segno, + BG_GC, type, SSR)) + return 1; + return 0; +} + /* * flush out current segment and replace it with new segment * This function should be returned with success, otherwise BUG diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 458bf5c726f7..552dadbb2327 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -450,21 +450,15 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi) return (free_sections(sbi) < overprovision_sections(sbi)); } -static inline int get_ssr_segment(struct f2fs_sb_info *sbi, int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - return DIRTY_I(sbi)->v_ops->get_victim(sbi, - &(curseg)->next_segno, BG_GC, type, SSR); -} - -static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi) +static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed) { int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + if (sbi->por_doing) return false; - return (free_sections(sbi) <= (node_secs + 2 * dent_secs + + return ((free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs + reserved_sections(sbi))); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ddb665f54d17..8c117649a035 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -112,7 +112,7 @@ static void f2fs_put_super(struct super_block *sb) f2fs_destroy_stats(sbi); stop_gc_thread(sbi); - write_checkpoint(sbi, false, true); + write_checkpoint(sbi, true); iput(sbi->node_inode); iput(sbi->meta_inode); @@ -136,7 +136,7 @@ int f2fs_sync_fs(struct super_block *sb, int sync) return 0; if (sync) - write_checkpoint(sbi, false, false); + write_checkpoint(sbi, false); else f2fs_balance_fs(sbi); -- cgit v1.2.3 From b7250d2d845822466356f7f22a650bf807090d7e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 5 Feb 2013 13:19:28 +0900 Subject: f2fs: fix calculation of max. gc cost in the SSR case In the SSR case, the max gc cost should be the number of pages in a segment. Otherwise, f2fs is able to fail getting dirty segments frequently for SSR. In get_victim_by_default() previously, while(1) { ... cost = get_gc_cost(); <- cost is between 0 ~ 512. ... if (cost == get_max_cost(sbi, &p)) <- max cost is UINT_MAX due to GC_CB type continue; if (nsearched++ >= MAX_VICTIM_SEARCH) break; } So, if there are a number of fully valid segments in series, f2fs cannot skip those segments by comparing the cost and max cost of each segment. Note that, the cost is the number of valid blocks at the time of the last checkpoint. Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs/f2fs/gc.c') diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 52d3a391b922..94b8a0c48453 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -146,6 +146,9 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type, static unsigned int get_max_cost(struct f2fs_sb_info *sbi, struct victim_sel_policy *p) { + /* SSR allocates in a segment unit */ + if (p->alloc_mode == SSR) + return 1 << sbi->log_blocks_per_seg; if (p->gc_mode == GC_GREEDY) return (1 << sbi->log_blocks_per_seg) * p->ofs_unit; else if (p->gc_mode == GC_CB) -- cgit v1.2.3