forked from Minki/linux
f2fs: introduce gc_merge mount option
In this patch, we will add two new mount options: "gc_merge" and "nogc_merge", when background_gc is on, "gc_merge" option can be set to let background GC thread to handle foreground GC requests, it can eliminate the sluggish issue caused by slow foreground GC operation when GC is triggered from a process with limited I/O and CPU resources. Original idea is from Xiang. Signed-off-by: Gao Xiang <xiang@kernel.org> Signed-off-by: Chao Yu <yuchao0@huawei.com> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
823d13e12b
commit
5911d2d1d1
@ -110,6 +110,12 @@ background_gc=%s Turn on/off cleaning operations, namely garbage
|
||||
on synchronous garbage collection running in background.
|
||||
Default value for this option is on. So garbage
|
||||
collection is on by default.
|
||||
gc_merge When background_gc is on, this option can be enabled to
|
||||
let background GC thread to handle foreground GC requests,
|
||||
it can eliminate the sluggish issue caused by slow foreground
|
||||
GC operation when GC is triggered from a process with limited
|
||||
I/O and CPU resources.
|
||||
nogc_merge Disable GC merge feature.
|
||||
disable_roll_forward Disable the roll-forward recovery routine
|
||||
norecovery Disable the roll-forward recovery routine, mounted read-
|
||||
only (i.e., -o ro,disable_roll_forward)
|
||||
|
@ -97,6 +97,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
|
||||
#define F2FS_MOUNT_NORECOVERY 0x04000000
|
||||
#define F2FS_MOUNT_ATGC 0x08000000
|
||||
#define F2FS_MOUNT_MERGE_CHECKPOINT 0x10000000
|
||||
#define F2FS_MOUNT_GC_MERGE 0x20000000
|
||||
|
||||
#define F2FS_OPTION(sbi) ((sbi)->mount_opt)
|
||||
#define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
|
||||
|
26
fs/f2fs/gc.c
26
fs/f2fs/gc.c
@ -31,19 +31,24 @@ static int gc_thread_func(void *data)
|
||||
struct f2fs_sb_info *sbi = data;
|
||||
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
|
||||
wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head;
|
||||
wait_queue_head_t *fggc_wq = &sbi->gc_thread->fggc_wq;
|
||||
unsigned int wait_ms;
|
||||
|
||||
wait_ms = gc_th->min_sleep_time;
|
||||
|
||||
set_freezable();
|
||||
do {
|
||||
bool sync_mode;
|
||||
bool sync_mode, foreground = false;
|
||||
|
||||
wait_event_interruptible_timeout(*wq,
|
||||
kthread_should_stop() || freezing(current) ||
|
||||
waitqueue_active(fggc_wq) ||
|
||||
gc_th->gc_wake,
|
||||
msecs_to_jiffies(wait_ms));
|
||||
|
||||
if (test_opt(sbi, GC_MERGE) && waitqueue_active(fggc_wq))
|
||||
foreground = true;
|
||||
|
||||
/* give it a try one time */
|
||||
if (gc_th->gc_wake)
|
||||
gc_th->gc_wake = 0;
|
||||
@ -90,7 +95,10 @@ static int gc_thread_func(void *data)
|
||||
goto do_gc;
|
||||
}
|
||||
|
||||
if (!down_write_trylock(&sbi->gc_lock)) {
|
||||
if (foreground) {
|
||||
down_write(&sbi->gc_lock);
|
||||
goto do_gc;
|
||||
} else if (!down_write_trylock(&sbi->gc_lock)) {
|
||||
stat_other_skip_bggc_count(sbi);
|
||||
goto next;
|
||||
}
|
||||
@ -107,14 +115,22 @@ static int gc_thread_func(void *data)
|
||||
else
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
do_gc:
|
||||
stat_inc_bggc_count(sbi->stat_info);
|
||||
if (!foreground)
|
||||
stat_inc_bggc_count(sbi->stat_info);
|
||||
|
||||
sync_mode = F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_SYNC;
|
||||
|
||||
/* foreground GC was been triggered via f2fs_balance_fs() */
|
||||
if (foreground)
|
||||
sync_mode = false;
|
||||
|
||||
/* if return value is not zero, no victim was selected */
|
||||
if (f2fs_gc(sbi, sync_mode, true, false, NULL_SEGNO))
|
||||
if (f2fs_gc(sbi, sync_mode, !foreground, false, NULL_SEGNO))
|
||||
wait_ms = gc_th->no_gc_sleep_time;
|
||||
|
||||
if (foreground)
|
||||
wake_up_all(&gc_th->fggc_wq);
|
||||
|
||||
trace_f2fs_background_gc(sbi->sb, wait_ms,
|
||||
prefree_segments(sbi), free_segments(sbi));
|
||||
|
||||
@ -148,6 +164,7 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
|
||||
|
||||
sbi->gc_thread = gc_th;
|
||||
init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head);
|
||||
init_waitqueue_head(&sbi->gc_thread->fggc_wq);
|
||||
sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi,
|
||||
"f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev));
|
||||
if (IS_ERR(gc_th->f2fs_gc_task)) {
|
||||
@ -165,6 +182,7 @@ void f2fs_stop_gc_thread(struct f2fs_sb_info *sbi)
|
||||
if (!gc_th)
|
||||
return;
|
||||
kthread_stop(gc_th->f2fs_gc_task);
|
||||
wake_up_all(&gc_th->fggc_wq);
|
||||
kfree(gc_th);
|
||||
sbi->gc_thread = NULL;
|
||||
}
|
||||
|
@ -42,6 +42,12 @@ struct f2fs_gc_kthread {
|
||||
|
||||
/* for changing gc mode */
|
||||
unsigned int gc_wake;
|
||||
|
||||
/* for GC_MERGE mount option */
|
||||
wait_queue_head_t fggc_wq; /*
|
||||
* caller of f2fs_balance_fs()
|
||||
* will wait on this wait queue.
|
||||
*/
|
||||
};
|
||||
|
||||
struct gc_inode_list {
|
||||
|
@ -503,8 +503,19 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
|
||||
* dir/node pages without enough free segments.
|
||||
*/
|
||||
if (has_not_enough_free_secs(sbi, 0, 0)) {
|
||||
down_write(&sbi->gc_lock);
|
||||
f2fs_gc(sbi, false, false, false, NULL_SEGNO);
|
||||
if (test_opt(sbi, GC_MERGE) && sbi->gc_thread &&
|
||||
sbi->gc_thread->f2fs_gc_task) {
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(&sbi->gc_thread->fggc_wq, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
wake_up(&sbi->gc_thread->gc_wait_queue_head);
|
||||
io_schedule();
|
||||
finish_wait(&sbi->gc_thread->fggc_wq, &wait);
|
||||
} else {
|
||||
down_write(&sbi->gc_lock);
|
||||
f2fs_gc(sbi, false, false, false, NULL_SEGNO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,6 +151,8 @@ enum {
|
||||
Opt_compress_chksum,
|
||||
Opt_compress_mode,
|
||||
Opt_atgc,
|
||||
Opt_gc_merge,
|
||||
Opt_nogc_merge,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
@ -223,6 +225,8 @@ static match_table_t f2fs_tokens = {
|
||||
{Opt_compress_chksum, "compress_chksum"},
|
||||
{Opt_compress_mode, "compress_mode=%s"},
|
||||
{Opt_atgc, "atgc"},
|
||||
{Opt_gc_merge, "gc_merge"},
|
||||
{Opt_nogc_merge, "nogc_merge"},
|
||||
{Opt_err, NULL},
|
||||
};
|
||||
|
||||
@ -1073,6 +1077,12 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
||||
case Opt_atgc:
|
||||
set_opt(sbi, ATGC);
|
||||
break;
|
||||
case Opt_gc_merge:
|
||||
set_opt(sbi, GC_MERGE);
|
||||
break;
|
||||
case Opt_nogc_merge:
|
||||
clear_opt(sbi, GC_MERGE);
|
||||
break;
|
||||
default:
|
||||
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
|
||||
p);
|
||||
@ -1675,6 +1685,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
||||
else if (F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF)
|
||||
seq_printf(seq, ",background_gc=%s", "off");
|
||||
|
||||
if (test_opt(sbi, GC_MERGE))
|
||||
seq_puts(seq, ",gc_merge");
|
||||
|
||||
if (test_opt(sbi, DISABLE_ROLL_FORWARD))
|
||||
seq_puts(seq, ",disable_roll_forward");
|
||||
if (test_opt(sbi, NORECOVERY))
|
||||
@ -2038,7 +2051,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
||||
* option. Also sync the filesystem.
|
||||
*/
|
||||
if ((*flags & SB_RDONLY) ||
|
||||
F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF) {
|
||||
(F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF &&
|
||||
!test_opt(sbi, GC_MERGE))) {
|
||||
if (sbi->gc_thread) {
|
||||
f2fs_stop_gc_thread(sbi);
|
||||
need_restart_gc = true;
|
||||
@ -4012,7 +4026,8 @@ reset_checkpoint:
|
||||
* If filesystem is not mounted as read-only then
|
||||
* do start the gc_thread.
|
||||
*/
|
||||
if (F2FS_OPTION(sbi).bggc_mode != BGGC_MODE_OFF && !f2fs_readonly(sb)) {
|
||||
if ((F2FS_OPTION(sbi).bggc_mode != BGGC_MODE_OFF ||
|
||||
test_opt(sbi, GC_MERGE)) && !f2fs_readonly(sb)) {
|
||||
/* After POR, we can run background GC thread.*/
|
||||
err = f2fs_start_gc_thread(sbi);
|
||||
if (err)
|
||||
|
Loading…
Reference in New Issue
Block a user