diff options
| -rw-r--r-- | fs/f2fs/f2fs.h | 6 | ||||
| -rw-r--r-- | fs/f2fs/gc.c | 145 | ||||
| -rw-r--r-- | fs/f2fs/gc.h | 3 | ||||
| -rw-r--r-- | fs/f2fs/super.c | 5 |
4 files changed, 151 insertions, 8 deletions
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a44a589ec..8d297b554 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1256,6 +1256,7 @@ struct f2fs_sb_info { char *s_qf_names[F2FS_MAXQUOTAS]; int s_jquota_fmt; /* Format of quota to use */ #endif + struct list_head list; }; #ifdef CONFIG_F2FS_FAULT_INJECTION @@ -2806,6 +2807,11 @@ int f2fs_migrate_page(struct address_space *mapping, struct page *newpage, */ int start_gc_thread(struct f2fs_sb_info *sbi); void stop_gc_thread(struct f2fs_sb_info *sbi); +void start_all_gc_threads(void); +void stop_all_gc_threads(void); +void f2fs_sbi_list_add(struct f2fs_sb_info *sbi); +void f2fs_sbi_list_del(struct f2fs_sb_info *sbi); + block_t start_bidx_of_node(unsigned int node_ofs, struct inode *inode); int f2fs_gc(struct f2fs_sb_info *sbi, bool sync, bool background, unsigned int segno); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 2ad858dd6..917bff037 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -16,6 +16,9 @@ #include <linux/kthread.h> #include <linux/delay.h> #include <linux/freezer.h> +#include <linux/fb.h> +#include <linux/power_supply.h> +#include <linux/wakelock.h> #include "f2fs.h" #include "node.h" @@ -23,14 +26,33 @@ #include "gc.h" #include <trace/events/f2fs.h> +#define TRIGGER_SOFF (!screen_on && power_supply_is_system_supplied()) +static bool screen_on = true; +// Use 1 instead of 0 to allow thread interrupts +static unsigned int soff_wait_ms = 1; + +static inline void gc_set_wakelock(struct f2fs_gc_kthread *gc_th, bool val) +{ + if (val) { + if (!wake_lock_active(&gc_th->gc_wakelock)) { + pr_info("f2fs: catching wakelock for GC\n"); + wake_lock(&gc_th->gc_wakelock); + } + } else { + if (wake_lock_active(&gc_th->gc_wakelock)) { + pr_info("f2fs: unlocking wakelock for GC\n"); + wake_unlock(&gc_th->gc_wakelock); + } + } +} + 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; - unsigned int wait_ms; - - wait_ms = gc_th->min_sleep_time; + unsigned int wait_ms = gc_th->min_sleep_time; + bool force_gc; set_freezable(); do { @@ -39,6 +61,17 @@ static int gc_thread_func(void *data) gc_th->gc_wake, msecs_to_jiffies(wait_ms)); + force_gc = TRIGGER_SOFF; + if (force_gc) { + gc_set_wakelock(gc_th, true); + wait_ms = soff_wait_ms; + gc_th->gc_urgent = 1; + } else { + gc_set_wakelock(gc_th, false); + wait_ms = gc_th->min_sleep_time; + gc_th->gc_urgent = 0; + } + /* give it a try one time */ if (gc_th->gc_wake) gc_th->gc_wake = 0; @@ -49,7 +82,8 @@ static int gc_thread_func(void *data) break; if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) { - increase_sleep_time(gc_th, &wait_ms); + if (!force_gc) + increase_sleep_time(gc_th, &wait_ms); continue; } @@ -79,6 +113,9 @@ static int gc_thread_func(void *data) if (!mutex_trylock(&sbi->gc_mutex)) goto next; + if (force_gc) + goto do_gc; + if (gc_th->gc_urgent) { wait_ms = gc_th->urgent_sleep_time; goto do_gc; @@ -98,8 +135,11 @@ do_gc: stat_inc_bggc_count(sbi); /* if return value is not zero, no victim was selected */ - if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC), true, NULL_SEGNO)) + if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC), true, NULL_SEGNO)) { wait_ms = gc_th->no_gc_sleep_time; + gc_set_wakelock(gc_th, false); + pr_info("f2fs: no more GC victim found, sleeping for %u ms\n", wait_ms); + } trace_f2fs_background_gc(sbi->sb, wait_ms, prefree_segments(sbi), free_segments(sbi)); @@ -118,6 +158,10 @@ int start_gc_thread(struct f2fs_sb_info *sbi) struct f2fs_gc_kthread *gc_th; dev_t dev = sbi->sb->s_bdev->bd_dev; int err = 0; + char buf[25]; + + if (sbi->gc_thread != NULL) + goto out; gc_th = f2fs_kmalloc(sbi, sizeof(struct f2fs_gc_kthread), GFP_KERNEL); if (!gc_th) { @@ -134,10 +178,13 @@ int start_gc_thread(struct f2fs_sb_info *sbi) gc_th->gc_urgent = 0; gc_th->gc_wake= 0; + snprintf(buf, sizeof(buf), "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); + + wake_lock_init(&gc_th->gc_wakelock, WAKE_LOCK_SUSPEND, buf); + 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, - "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); + sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, buf); if (IS_ERR(gc_th->f2fs_gc_task)) { err = PTR_ERR(gc_th->f2fs_gc_task); kfree(gc_th); @@ -155,10 +202,94 @@ void stop_gc_thread(struct f2fs_sb_info *sbi) if (!gc_th) return; kthread_stop(gc_th->f2fs_gc_task); + wake_lock_destroy(&gc_th->gc_wakelock); kfree(gc_th); sbi->gc_thread = NULL; } +static LIST_HEAD(f2fs_sbi_list); +static DEFINE_MUTEX(f2fs_sbi_mutex); + +void start_all_gc_threads(void) +{ + struct f2fs_sb_info *sbi; + + mutex_lock(&f2fs_sbi_mutex); + list_for_each_entry(sbi, &f2fs_sbi_list, list) { + start_gc_thread(sbi); + sbi->gc_thread->gc_wake = 1; + wake_up_interruptible_all(&sbi->gc_thread->gc_wait_queue_head); + } + mutex_unlock(&f2fs_sbi_mutex); +} + +void stop_all_gc_threads(void) +{ + struct f2fs_sb_info *sbi; + + mutex_lock(&f2fs_sbi_mutex); + list_for_each_entry(sbi, &f2fs_sbi_list, list) { + stop_gc_thread(sbi); + } + mutex_unlock(&f2fs_sbi_mutex); +} + +void f2fs_sbi_list_add(struct f2fs_sb_info *sbi) +{ + mutex_lock(&f2fs_sbi_mutex); + list_add_tail(&sbi->list, &f2fs_sbi_list); + mutex_unlock(&f2fs_sbi_mutex); +} + +void f2fs_sbi_list_del(struct f2fs_sb_info *sbi) +{ + mutex_lock(&f2fs_sbi_mutex); + list_del(&sbi->list); + mutex_unlock(&f2fs_sbi_mutex); +} + +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + + if ((event == FB_EVENT_BLANK) && evdata && evdata->data) { + blank = evdata->data; + + switch (*blank) { + case FB_BLANK_POWERDOWN: + screen_on = false; + /* + * Start all GC threads exclusively from here + * since the phone screen would turn on when + * a charger is connected + */ + if (TRIGGER_SOFF) + start_all_gc_threads(); + break; + case FB_BLANK_UNBLANK: + screen_on = true; + stop_all_gc_threads(); + break; + } + } + + return 0; +} + +static struct notifier_block fb_notifier_block = { + .notifier_call = fb_notifier_callback, +}; + +static int __init f2fs_gc_register_fb(void) +{ + fb_register_client(&fb_notifier_block); + + return 0; +} +late_initcall(f2fs_gc_register_fb); + static int select_gc_type(struct f2fs_gc_kthread *gc_th, int gc_type) { int gc_mode = (gc_type == BG_GC) ? GC_CB : GC_GREEDY; diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 9325191fa..adecfcd07 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -8,6 +8,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <linux/wakelock.h> + #define GC_THREAD_MIN_WB_PAGES 1 /* * a threshold to determine * whether IO subsystem is idle @@ -26,6 +28,7 @@ struct f2fs_gc_kthread { struct task_struct *f2fs_gc_task; wait_queue_head_t gc_wait_queue_head; + struct wake_lock gc_wakelock; /* for gc sleep time */ unsigned int urgent_sleep_time; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index c6c3161d4..a310c85e2 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -826,6 +826,7 @@ static void f2fs_put_super(struct super_block *sb) /* write_checkpoint can update stat informaion */ f2fs_destroy_stats(sbi); + f2fs_sbi_list_del(sbi); /* * normally superblock is clean, so we need to release this. @@ -1124,7 +1125,6 @@ static void default_options(struct f2fs_sb_info *sbi) /* init some FS parameters */ sbi->active_logs = NR_CURSEG_TYPE; - set_opt(sbi, BG_GC); set_opt(sbi, INLINE_XATTR); set_opt(sbi, INLINE_DATA); set_opt(sbi, INLINE_DENTRY); @@ -2482,6 +2482,8 @@ try_onemore: if (err) goto free_nm; + f2fs_sbi_list_add(sbi); + /* read root inode and dentry */ root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); if (IS_ERR(root)) { @@ -2596,6 +2598,7 @@ free_node_inode: f2fs_leave_shrinker(sbi); iput(sbi->node_inode); mutex_unlock(&sbi->umount_mutex); + f2fs_sbi_list_del(sbi); f2fs_destroy_stats(sbi); free_nm: destroy_node_manager(sbi); |
