Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a9c52d49 authored by Park Ju Hyung's avatar Park Ju Hyung Committed by Razziell
Browse files

f2fs: rapidly GC all invalid blocks when screen goes off & plugged in



Rapidly GC'ing all invalid blocks only takes a few minute,
even under extreme scenario.

Wait until the screen goes off and detect if the power source is plugged in.
If it is, GC thread runs repeatedly with 1ms interval.

Since the device can go to sleep even with power source plugged in,
catch an additional wakelock.

When no more GC victim block is found, GC thread sleeps for
gc_no_gc_sleep_time(5m).

With the previous ioprio patch, the impact to system responsiveness
(e.g. waking up the screen) is minimal, if not zero.

This patch is intended to optimize background GC behavior specific to Android.
So, disable background_gc by default as well.

Change-Id: I02444ee4bcafc41e5cfc6f4ae6897c7df74ea89f
Signed-off-by: default avatarPark Ju Hyung <qkrwngud825@gmail.com>
Signed-off-by: default avatarAlbert I <krascgq@outlook.co.id>
parent 8f23087c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1288,6 +1288,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
@@ -2874,6 +2875,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);
+138 −7
Original line number Diff line number Diff line
@@ -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,6 +82,7 @@ static int gc_thread_func(void *data)
			break;

		if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) {
			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;
+3 −0
Original line number Diff line number Diff line
@@ -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;
+4 −1
Original line number Diff line number Diff line
@@ -873,6 +873,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.
@@ -1178,7 +1179,6 @@ static void default_options(struct f2fs_sb_info *sbi)
	sbi->active_logs = NR_CURSEG_TYPE;
	sbi->inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS;

	set_opt(sbi, BG_GC);
	set_opt(sbi, INLINE_XATTR);
	set_opt(sbi, INLINE_DATA);
	set_opt(sbi, INLINE_DENTRY);
@@ -2674,6 +2674,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)) {
@@ -2808,6 +2810,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);