[PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk
From: Peter Enderborg Fundamental changes: 1 Does NOT take any RCU lock in shrinker functions. 2 It returns same result for scan and counts, so we dont need to do shinker will know when it is pointless to call scan. 3 It does not lock any other process than the one that is going to be killed. Background. The low memory killer scans for process that can be killed to free memory. This can be cpu consuming when there is a high demand for memory. This can be seen by analysing the kswapd0 task work. The stats function added in earler patch adds a counter for waste work. How it works. This patch create a structure within the lowmemory killer that caches the user spaces processes that it might kill. It is done with a sorted rbtree so we can very easy find the candidate to be killed, and knows its properies as memory usage and sorted by oom_score_adj to look up the task with highest oom_score_adj. To be able to achive this it uses oom_score_notify events. This patch also as a other effect, we are now free to do other lowmemorykiller configurations. Without the patch there is a need for a tradeoff between freed memory and task and rcu locks. This is no longer a concern for tuning lmk. This patch is not intended to do any calculation changes other than we do use the cache for calculate the count values and that makes kswapd0 to shrink other areas. Signed-off-by: Peter Enderborg --- drivers/staging/android/Kconfig | 1 + drivers/staging/android/Makefile| 1 + drivers/staging/android/lowmemorykiller.c | 294 +++- drivers/staging/android/lowmemorykiller.h | 15 ++ drivers/staging/android/lowmemorykiller_stats.c | 24 ++ drivers/staging/android/lowmemorykiller_stats.h | 14 +- drivers/staging/android/lowmemorykiller_tasks.c | 220 ++ drivers/staging/android/lowmemorykiller_tasks.h | 35 +++ 8 files changed, 498 insertions(+), 106 deletions(-) create mode 100644 drivers/staging/android/lowmemorykiller.h create mode 100644 drivers/staging/android/lowmemorykiller_tasks.c create mode 100644 drivers/staging/android/lowmemorykiller_tasks.h diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 96e86c7..899186c 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -16,6 +16,7 @@ config ASHMEM config ANDROID_LOW_MEMORY_KILLER bool "Android Low Memory Killer" + select OOM_SCORE_NOTIFIER ---help--- Registers processes to be killed when low memory conditions, this is useful as there is no particular swap space on android. diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index d710eb2..b7a8036 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -4,4 +4,5 @@ obj-y += ion/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER)+= lowmemorykiller.o +obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER)+= lowmemorykiller_tasks.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER_STATS) += lowmemorykiller_stats.o diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 15c1b38..1e275b1 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -41,10 +41,14 @@ #include #include #include +#include #include +#include +#include "lowmemorykiller.h" #include "lowmemorykiller_stats.h" +#include "lowmemorykiller_tasks.h" -static u32 lowmem_debug_level = 1; +u32 lowmem_debug_level = 1; static short lowmem_adj[6] = { 0, 1, @@ -62,135 +66,212 @@ static int lowmem_minfree[6] = { static int lowmem_minfree_size = 4; -static unsigned long lowmem_deathpending_timeout; - -#define lowmem_print(level, x...) \ - do {\ - if (lowmem_debug_level >= (level)) \ - pr_info(x); \ - } while (0) - -static unsigned long lowmem_count(struct shrinker *s, - struct shrink_control *sc) -{ - lmk_inc_stats(LMK_COUNT); - return global_node_page_state(NR_ACTIVE_ANON) + - global_node_page_state(NR_ACTIVE_FILE) + - global_node_page_state(NR_INACTIVE_ANON) + - global_node_page_state(NR_INACTIVE_FILE); -} +struct calculated_params { + long selected_tasksize; + long minfree; + int other_file; + int other_free; + int dynamic_max_queue_len; + short selected_oom_score_adj; + short min_score_adj; +}; -static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) +static int kill_needed(int level, struct shrink_control *sc, + struct calculated_params *cp) { - struct task_struct *tsk; - struct task_stru
[PATCH 2/3 staging-next] oom: Add notification for oom_score_adj
From: Peter Enderborg This adds subscribtion for changes in oom_score_adj, this value is important to android systems. For task that uses oom_score_adj they read the task list. This can be long and need rcu locks and has a impact on the system. Let the user track the changes based on oom_score_adj changes and keep them in their own context so they do their actions with minimal system impact. Signed-off-by: Peter Enderborg --- fs/proc/base.c | 13 +++ include/linux/oom_score_notifier.h | 47 kernel/Makefile| 1 + kernel/fork.c | 6 +++ kernel/oom_score_notifier.c| 75 ++ mm/Kconfig | 9 + 6 files changed, 151 insertions(+) create mode 100644 include/linux/oom_score_notifier.h create mode 100644 kernel/oom_score_notifier.c diff --git a/fs/proc/base.c b/fs/proc/base.c index 87c9a9a..60c2d9b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -87,6 +87,7 @@ #include #include #include +#include #ifdef CONFIG_HARDWALL #include #endif @@ -1057,6 +1058,7 @@ static int __set_oom_adj(struct file *file, int oom_adj, bool legacy) static DEFINE_MUTEX(oom_adj_mutex); struct mm_struct *mm = NULL; struct task_struct *task; + int old_oom_score_adj; int err = 0; task = get_proc_task(file_inode(file)); @@ -1102,9 +1104,20 @@ static int __set_oom_adj(struct file *file, int oom_adj, bool legacy) } } + old_oom_score_adj = task->signal->oom_score_adj; task->signal->oom_score_adj = oom_adj; if (!legacy && has_capability_noaudit(current, CAP_SYS_RESOURCE)) task->signal->oom_score_adj_min = (short)oom_adj; + +#ifdef CONFIG_OOM_SCORE_NOTIFIER + err = oom_score_notify_update(task, old_oom_score_adj); + if (err) { + /* rollback and error handle. */ + task->signal->oom_score_adj = old_oom_score_adj; + goto err_unlock; + } +#endif + trace_oom_score_adj_update(task); if (mm) { diff --git a/include/linux/oom_score_notifier.h b/include/linux/oom_score_notifier.h new file mode 100644 index 000..c5cea47 --- /dev/null +++ b/include/linux/oom_score_notifier.h @@ -0,0 +1,47 @@ +/* + * oom_score_notifier interface + * Copyright (C) 2017 Sony Mobile Communications Inc. + * + * Author: Peter Enderborg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_OOM_SCORE_NOTIFIER_H +#define _LINUX_OOM_SCORE_NOTIFIER_H + +#ifdef CONFIG_OOM_SCORE_NOTIFIER + +#include +#include +#include + +enum osn_msg_type { + OSN_NEW, + OSN_FREE, + OSN_UPDATE +}; + +extern struct atomic_notifier_head oom_score_notifier; +extern int oom_score_notifier_register(struct notifier_block *n); +extern int oom_score_notifier_unregister(struct notifier_block *n); +extern int oom_score_notify_free(struct task_struct *tsk); +extern int oom_score_notify_new(struct task_struct *tsk); +extern int oom_score_notify_update(struct task_struct *tsk, int old_score); + +struct oom_score_notifier_struct { + struct task_struct *tsk; + int old_score; +}; + +#else + +#define oom_score_notify_free(t) do {} while (0) +#define oom_score_notify_new(t) false +#define oom_score_notify_update(t, s) do {} while (0) + +#endif /* CONFIG_OOM_SCORE_NOTIFIER */ + +#endif /* _LINUX_OOM_SCORE_NOTIFIER_H */ diff --git a/kernel/Makefile b/kernel/Makefile index 12c679f..747c66c 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_SYSCTL) += utsname_sysctl.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o obj-$(CONFIG_TRACEPOINTS) += tracepoint.o +obj-$(CONFIG_OOM_SCORE_NOTIFIER) += oom_score_notifier.o obj-$(CONFIG_LATENCYTOP) += latencytop.o obj-$(CONFIG_ELFCORE) += elfcore.o obj-$(CONFIG_FUNCTION_TRACER) += trace/ diff --git a/kernel/fork.c b/kernel/fork.c index 11c5c8a..f8a1a89 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -391,6 +392,7 @@ void __put_task_struct(struct task_struct *tsk) exit_creds(tsk); delayacct_tsk_free(tsk); put_signal_struct(tsk->signal); + oom_score_notify_free(tsk); if (!profile_handoff_task(tsk)) free_task(tsk); @@ -1790,6 +1792,10 @@ static __latent_entropy struct task_struct *copy_process( init_task_pid(p, PIDTYPE_PID, pid); if (thread_group_leader(p)) { + retval = oom_score_notify_new(p); + if (retval) + goto bad_fork_cancel_cgroup; + init_task_pid(p,
[PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller
From: Peter Enderborg This collects stats for shrinker calls and how much waste work we do within the lowmemorykiller. Signed-off-by: Peter Enderborg --- drivers/staging/android/Kconfig | 11 drivers/staging/android/Makefile| 1 + drivers/staging/android/lowmemorykiller.c | 9 ++- drivers/staging/android/lowmemorykiller_stats.c | 85 + drivers/staging/android/lowmemorykiller_stats.h | 29 + 5 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/android/lowmemorykiller_stats.c create mode 100644 drivers/staging/android/lowmemorykiller_stats.h diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 6c00d6f..96e86c7 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -24,6 +24,17 @@ config ANDROID_LOW_MEMORY_KILLER scripts (/init.rc), and it defines priority values with minimum free memory size for each priority. +config ANDROID_LOW_MEMORY_KILLER_STATS + bool "Android Low Memory Killer: collect statistics" + depends on ANDROID_LOW_MEMORY_KILLER + default n + help + Create a file in /proc/lmkstats that includes + collected statistics about kills, scans and counts + and interaction with the shrinker. Its content + will be different depeding on lmk implementation used. + + source "drivers/staging/android/ion/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 7ed1be7..d710eb2 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -4,3 +4,4 @@ obj-y += ion/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER)+= lowmemorykiller.o +obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER_STATS) += lowmemorykiller_stats.o diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index ec3b665..15c1b38 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -42,6 +42,7 @@ #include #include #include +#include "lowmemorykiller_stats.h" static u32 lowmem_debug_level = 1; static short lowmem_adj[6] = { @@ -72,6 +73,7 @@ static unsigned long lowmem_deathpending_timeout; static unsigned long lowmem_count(struct shrinker *s, struct shrink_control *sc) { + lmk_inc_stats(LMK_COUNT); return global_node_page_state(NR_ACTIVE_ANON) + global_node_page_state(NR_ACTIVE_FILE) + global_node_page_state(NR_INACTIVE_ANON) + @@ -95,6 +97,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) global_node_page_state(NR_SHMEM) - total_swapcache_pages(); + lmk_inc_stats(LMK_SCAN); if (lowmem_adj_size < array_size) array_size = lowmem_adj_size; if (lowmem_minfree_size < array_size) @@ -134,6 +137,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (task_lmk_waiting(p) && time_before_eq(jiffies, lowmem_deathpending_timeout)) { task_unlock(p); + lmk_inc_stats(LMK_TIMEOUT); rcu_read_unlock(); return 0; } @@ -179,7 +183,9 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) other_free * (long)(PAGE_SIZE / 1024)); lowmem_deathpending_timeout = jiffies + HZ; rem += selected_tasksize; - } + lmk_inc_stats(LMK_KILL); + } else + lmk_inc_stats(LMK_WASTE); lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n", sc->nr_to_scan, sc->gfp_mask, rem); @@ -196,6 +202,7 @@ static struct shrinker lowmem_shrinker = { static int __init lowmem_init(void) { register_shrinker(&lowmem_shrinker); + init_procfs_lmk(); return 0; } device_initcall(lowmem_init); diff --git a/drivers/staging/android/lowmemorykiller_stats.c b/drivers/staging/android/lowmemorykiller_stats.c new file mode 100644 index 000..673691c --- /dev/null +++ b/drivers/staging/android/lowmemorykiller_stats.c @@ -0,0 +1,85 @@ +/* + * lowmemorykiller_stats + * + * Copyright (C) 2017 Sony Mobile Communications Inc. + * + * Author: Peter Enderborg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +/* This code is bookkeeping of statistical information + * from lowmemorykiller and provide a node in proc "/proc/lmkstats". + */ + +#include +#include +#include "