linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller
@ 2017-02-14 16:09 peter.enderborg
  2017-02-14 16:09 ` [PATCH 2/3 staging-next] oom: Add notification for oom_score_adj peter.enderborg
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: peter.enderborg @ 2017-02-14 16:09 UTC (permalink / raw)
  To: devel, gregkh, linux-kernel, arve, riandrews, torvalds, linux-mm
  Cc: Peter Enderborg

From: Peter Enderborg <peter.enderborg@sonymobile.com>

This collects stats for shrinker calls and how much
waste work we do within the lowmemorykiller.

Signed-off-by: Peter Enderborg <peter.enderborg@sonymobile.com>
---
 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 <linux/rcupdate.h>
 #include <linux/profile.h>
 #include <linux/notifier.h>
+#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 0000000..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 <peter.enderborg@sonymobile.com>
+ *
+ *  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 <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include "lowmemorykiller_stats.h"
+
+struct lmk_stats {
+	atomic_long_t scans; /* counter as in shrinker scans */
+	atomic_long_t kills; /* the number of sigkills sent */
+	atomic_long_t waste; /* the numer of extensive calls that did
+			      * not lead to anything
+			      */
+	atomic_long_t timeout; /* counter for shrinker calls that needed
+				* to be cancelled due to pending kills
+				*/
+	atomic_long_t count; /* number of shrinker count calls */
+	atomic_long_t unknown; /* internal */
+} st;
+
+void lmk_inc_stats(int key)
+{
+	switch (key) {
+	case LMK_SCAN:
+		atomic_long_inc(&st.scans);
+		break;
+	case LMK_KILL:
+		atomic_long_inc(&st.kills);
+		break;
+	case LMK_WASTE:
+		atomic_long_inc(&st.waste);
+		break;
+	case LMK_TIMEOUT:
+		atomic_long_inc(&st.timeout);
+		break;
+	case LMK_COUNT:
+		atomic_long_inc(&st.count);
+		break;
+	default:
+		atomic_long_inc(&st.unknown);
+		break;
+	}
+}
+
+static int lmk_proc_show(struct seq_file *m, void *v)
+{
+	seq_printf(m, "kill: %ld\n", atomic_long_read(&st.kills));
+	seq_printf(m, "scan: %ld\n", atomic_long_read(&st.scans));
+	seq_printf(m, "waste: %ld\n", atomic_long_read(&st.waste));
+	seq_printf(m, "timeout: %ld\n", atomic_long_read(&st.timeout));
+	seq_printf(m, "count: %ld\n", atomic_long_read(&st.count));
+	seq_printf(m, "unknown: %ld (internal)\n",
+		   atomic_long_read(&st.unknown));
+
+	return 0;
+}
+
+static int lmk_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, lmk_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations lmk_proc_fops = {
+	.open		= lmk_proc_open,
+	.read		= seq_read,
+	.release	= single_release
+};
+
+int __init init_procfs_lmk(void)
+{
+	proc_create_data(LMK_PROCFS_NAME, 0444, NULL, &lmk_proc_fops, NULL);
+	return 0;
+}
diff --git a/drivers/staging/android/lowmemorykiller_stats.h b/drivers/staging/android/lowmemorykiller_stats.h
new file mode 100644
index 0000000..abeb6924
--- /dev/null
+++ b/drivers/staging/android/lowmemorykiller_stats.h
@@ -0,0 +1,29 @@
+/*
+ *  lowmemorykiller_stats interface
+ *
+ *  Copyright (C) 2017 Sony Mobile Communications Inc.
+ *
+ *  Author: Peter Enderborg <peter.enderborg@sonymobile.com>
+ *
+ *  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.
+ */
+
+enum  lmk_kill_stats {
+	LMK_SCAN = 1,
+	LMK_KILL = 2,
+	LMK_WASTE = 3,
+	LMK_TIMEOUT = 4,
+	LMK_COUNT = 5
+};
+
+#define LMK_PROCFS_NAME "lmkstats"
+
+#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_STATS
+void lmk_inc_stats(int key);
+int __init init_procfs_lmk(void);
+#else
+static inline void lmk_inc_stats(int key) { return; };
+static inline int __init init_procfs_lmk(void) { return 0; };
+#endif
-- 
2.4.2

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/3 staging-next] oom: Add notification for oom_score_adj
  2017-02-14 16:09 [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller peter.enderborg
@ 2017-02-14 16:09 ` peter.enderborg
  2017-02-14 16:09 ` [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk peter.enderborg
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 13+ messages in thread
From: peter.enderborg @ 2017-02-14 16:09 UTC (permalink / raw)
  To: devel, gregkh, linux-kernel, arve, riandrews, torvalds, linux-mm
  Cc: Peter Enderborg

From: Peter Enderborg <peter.enderborg@sonymobile.com>

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 <peter.enderborg@sonymobile.com>
---
 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 <linux/slab.h>
 #include <linux/flex_array.h>
 #include <linux/posix-timers.h>
+#include <linux/oom_score_notifier.h>
 #ifdef CONFIG_HARDWALL
 #include <asm/hardwall.h>
 #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 0000000..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 <peter.enderborg@sonymobile.com>
+ *
+ *  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 <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+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 <linux/signalfd.h>
 #include <linux/uprobes.h>
 #include <linux/aio.h>
+#include <linux/oom_score_notifier.h>
 #include <linux/compiler.h>
 #include <linux/sysctl.h>
 #include <linux/kcov.h>
@@ -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, PIDTYPE_PGID, task_pgrp(current));
 			init_task_pid(p, PIDTYPE_SID, task_session(current));
 
diff --git a/kernel/oom_score_notifier.c b/kernel/oom_score_notifier.c
new file mode 100644
index 0000000..6dd6d8e
--- /dev/null
+++ b/kernel/oom_score_notifier.c
@@ -0,0 +1,75 @@
+/*
+ *  oom_score_notifier interface
+ *  Copyright (C) 2017 Sony Mobile Communications Inc.
+ *
+ *  Author: Peter Enderborg <peter.enderborg@sonymobile.com>
+ *
+ *  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.
+ */
+
+
+#include <linux/notifier.h>
+#include <linux/oom_score_notifier.h>
+
+#ifdef CONFIG_OOM_SCORE_NOTIFIER
+ATOMIC_NOTIFIER_HEAD(oom_score_notifier);
+
+int oom_score_notifier_register(struct notifier_block *n)
+{
+	return atomic_notifier_chain_register(&oom_score_notifier, n);
+}
+EXPORT_SYMBOL_GPL(oom_score_notifier_register);
+
+int oom_score_notifier_unregister(struct notifier_block *n)
+{
+	return atomic_notifier_chain_unregister(&oom_score_notifier, n);
+}
+EXPORT_SYMBOL_GPL(oom_score_notifier_unregister);
+
+int oom_score_notify_free(struct task_struct *tsk)
+{
+	struct oom_score_notifier_struct osns;
+
+	osns.tsk = tsk;
+	return notifier_to_errno(atomic_notifier_call_chain(
+		&oom_score_notifier, OSN_FREE, &osns));
+}
+EXPORT_SYMBOL_GPL(oom_score_notify_free);
+
+int oom_score_notify_new(struct task_struct *tsk)
+{
+	struct oom_score_notifier_struct osns;
+
+	osns.tsk = tsk;
+	return notifier_to_errno(atomic_notifier_call_chain(
+		&oom_score_notifier, OSN_NEW, &osns));
+}
+EXPORT_SYMBOL_GPL(oom_score_notify_new);
+
+int oom_score_notify_update(struct task_struct *tsk, int old_score)
+{
+	struct oom_score_notifier_struct osns;
+
+	osns.tsk = tsk;
+	osns.old_score = old_score;
+	return notifier_to_errno(atomic_notifier_call_chain(&oom_score_notifier,
+							    OSN_UPDATE, &osns));
+}
+EXPORT_SYMBOL_GPL(oom_score_notify_update);
+
+#else
+inline int oom_score_notifier_register(struct notifier_block *n) { return 0; };
+inline int oom_score_notifier_unregister(struct notifier_block *n)
+{
+	return 0;
+};
+inline int oom_score_notify_free(struct task_struct *tsk) { return 0; };
+inline int oom_score_notify_new(struct task_struct *tsk) { return 0; };
+inline int oom_score_notify_update(struct task_struct *tsk, int old_score)
+{
+	return 0;
+};
+
+#endif
diff --git a/mm/Kconfig b/mm/Kconfig
index 9b8fccb..fb2a5d2 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -327,6 +327,15 @@ config MMU_NOTIFIER
 	bool
 	select SRCU
 
+config OOM_SCORE_NOTIFIER
+	bool "OOM score notifier"
+	default n
+	help
+	  This create a notifier for process oom_score_adj status.
+	  It create events for new, updated or freed tasks and
+	  are used to build a mirrored task list in
+	  lowmemmorykiller.
+
 config KSM
 	bool "Enable KSM for page merging"
 	depends on MMU
-- 
2.4.2

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk
  2017-02-14 16:09 [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller peter.enderborg
  2017-02-14 16:09 ` [PATCH 2/3 staging-next] oom: Add notification for oom_score_adj peter.enderborg
@ 2017-02-14 16:09 ` peter.enderborg
  2017-02-14 16:50 ` [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller Greg KH
  2017-02-14 16:51 ` Greg KH
  3 siblings, 0 replies; 13+ messages in thread
From: peter.enderborg @ 2017-02-14 16:09 UTC (permalink / raw)
  To: devel, gregkh, linux-kernel, arve, riandrews, torvalds, linux-mm
  Cc: Peter Enderborg

From: Peter Enderborg <peter.enderborg@sonymobile.com>

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 <peter.enderborg@sonymobile.com>
---
 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 <linux/swap.h>
 #include <linux/rcupdate.h>
 #include <linux/profile.h>
+#include <linux/slab.h>
 #include <linux/notifier.h>
+#include <linux/oom_score_notifier.h>
+#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_struct *selected = NULL;
-	unsigned long rem = 0;
-	int tasksize;
 	int i;
-	short min_score_adj = OOM_SCORE_ADJ_MAX + 1;
-	int minfree = 0;
-	int selected_tasksize = 0;
-	short selected_oom_score_adj;
 	int array_size = ARRAY_SIZE(lowmem_adj);
-	int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
-	int other_file = global_node_page_state(NR_FILE_PAGES) -
-				global_node_page_state(NR_SHMEM) -
-				total_swapcache_pages();
 
-	lmk_inc_stats(LMK_SCAN);
+	cp->other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
+	cp->other_file = global_page_state(NR_FILE_PAGES) -
+		global_page_state(NR_SHMEM) -
+		global_page_state(NR_UNEVICTABLE) -
+		total_swapcache_pages();
+
+	cp->minfree = 0;
+	cp->min_score_adj = OOM_SCORE_ADJ_MAX;
 	if (lowmem_adj_size < array_size)
 		array_size = lowmem_adj_size;
 	if (lowmem_minfree_size < array_size)
 		array_size = lowmem_minfree_size;
 	for (i = 0; i < array_size; i++) {
-		minfree = lowmem_minfree[i];
-		if (other_free < minfree && other_file < minfree) {
-			min_score_adj = lowmem_adj[i];
+		cp->minfree = lowmem_minfree[i];
+		if (cp->other_free < cp->minfree &&
+		    cp->other_file < cp->minfree) {
+			cp->min_score_adj = lowmem_adj[i];
 			break;
 		}
 	}
+	if (sc->nr_to_scan > 0)
+		lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %hd\n",
+			     sc->nr_to_scan, sc->gfp_mask, cp->other_free,
+			     cp->other_file, cp->min_score_adj);
+	cp->dynamic_max_queue_len = array_size - i + 1;
+	cp->selected_oom_score_adj = level;
+	if (level >= cp->min_score_adj)
+		return 1;
+
+	return 0;
+}
+
+static void print_obituary(struct task_struct *doomed,
+			   struct calculated_params *cp,
+			   struct shrink_control *sc) {
+	long cache_size = cp->other_file * (long)(PAGE_SIZE / 1024);
+	long cache_limit = cp->minfree * (long)(PAGE_SIZE / 1024);
+	long free = cp->other_free * (long)(PAGE_SIZE / 1024);
+
+	lowmem_print(1, "Killing '%s' (%d), adj %hd,\n"
+		     "   to free %ldkB on behalf of '%s' (%d) because\n"
+		     "   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n"
+		     "   Free memory is %ldkB above reserved.\n"
+		     "   Free CMA is %ldkB\n"
+		     "   Total reserve is %ldkB\n"
+		     "   Total free pages is %ldkB\n"
+		     "   Total file cache is %ldkB\n"
+		     "   Slab Reclaimable is %ldkB\n"
+		     "   Slab UnReclaimable is %ldkB\n"
+		     "   Total Slab is %ldkB\n"
+		     "   GFP mask is 0x%x\n"
+		     "   queue len is %d of max %d\n",
+		     doomed->comm, doomed->pid,
+		     cp->selected_oom_score_adj,
+		     cp->selected_tasksize * (long)(PAGE_SIZE / 1024),
+		     current->comm, current->pid,
+		     cache_size, cache_limit,
+		     cp->min_score_adj,
+		     free,
+		     global_page_state(NR_FREE_CMA_PAGES) *
+		     (long)(PAGE_SIZE / 1024),
+		     totalreserve_pages * (long)(PAGE_SIZE / 1024),
+		     global_page_state(NR_FREE_PAGES) *
+		     (long)(PAGE_SIZE / 1024),
+		     global_page_state(NR_FILE_PAGES) *
+		     (long)(PAGE_SIZE / 1024),
+		     global_page_state(NR_SLAB_RECLAIMABLE) *
+		     (long)(PAGE_SIZE / 1024),
+		     global_page_state(NR_SLAB_UNRECLAIMABLE) *
+		     (long)(PAGE_SIZE / 1024),
+		     global_page_state(NR_SLAB_RECLAIMABLE) *
+		     (long)(PAGE_SIZE / 1024) +
+		     global_page_state(NR_SLAB_UNRECLAIMABLE) *
+		     (long)(PAGE_SIZE / 1024),
+		     sc->gfp_mask,
+		     death_pending_len,
+		     cp->dynamic_max_queue_len);
+}
+
+static unsigned long lowmem_count(struct shrinker *s,
+				  struct shrink_control *sc)
+{
+	struct lmk_rb_watch *lrw;
+	struct calculated_params cp;
+	short score;
+
+	lmk_inc_stats(LMK_COUNT);
+	cp.selected_tasksize = 0;
+	spin_lock(&lmk_task_lock);
+	lrw = __lmk_first();
+	if (lrw && lrw->tsk->mm) {
+		int rss = get_mm_rss(lrw->tsk->mm);
 
-	lowmem_print(3, "lowmem_scan %lu, %x, ofree %d %d, ma %hd\n",
-		     sc->nr_to_scan, sc->gfp_mask, other_free,
-		     other_file, min_score_adj);
+		score = lrw->tsk->signal->oom_score_adj;
+		spin_unlock(&lmk_task_lock);
+		if (kill_needed(score, sc, &cp))
+			if (death_pending_len < cp.dynamic_max_queue_len)
+				cp.selected_tasksize = rss;
 
-	if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
-		lowmem_print(5, "lowmem_scan %lu, %x, return 0\n",
-			     sc->nr_to_scan, sc->gfp_mask);
-		return 0;
+	} else {
+		spin_unlock(&lmk_task_lock);
 	}
 
-	selected_oom_score_adj = min_score_adj;
+	return cp.selected_tasksize;
+}
+
 
-	rcu_read_lock();
-	for_each_process(tsk) {
-		struct task_struct *p;
-		short oom_score_adj;
+static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
+{
+	struct task_struct *selected = NULL;
+	unsigned long nr_to_scan = sc->nr_to_scan;
+	struct lmk_rb_watch *lrw;
+	int do_kill;
+	struct calculated_params cp;
 
-		if (tsk->flags & PF_KTHREAD)
-			continue;
+	lmk_inc_stats(LMK_SCAN);
 
-		p = find_lock_task_mm(tsk);
-		if (!p)
-			continue;
+	cp.selected_tasksize = 0;
+	spin_lock(&lmk_task_lock);
 
-		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;
+	lrw = __lmk_first();
+	if (lrw) {
+		if (lrw->tsk->mm) {
+			cp.selected_tasksize = get_mm_rss(lrw->tsk->mm);
+		} else {
+			lowmem_print(1, "pid:%d no mem\n", lrw->tsk->pid);
+			lmk_inc_stats(LMK_ERROR);
+			goto unlock_out;
 		}
-		oom_score_adj = p->signal->oom_score_adj;
-		if (oom_score_adj < min_score_adj) {
-			task_unlock(p);
-			continue;
+
+		do_kill = kill_needed(lrw->key, sc, &cp);
+
+		if (death_pending_len >= cp.dynamic_max_queue_len) {
+			lmk_inc_stats(LMK_BUSY);
+			goto unlock_out;
 		}
-		tasksize = get_mm_rss(p->mm);
-		task_unlock(p);
-		if (tasksize <= 0)
-			continue;
-		if (selected) {
-			if (oom_score_adj < selected_oom_score_adj)
-				continue;
-			if (oom_score_adj == selected_oom_score_adj &&
-			    tasksize <= selected_tasksize)
-				continue;
+
+		if (do_kill) {
+			struct lmk_death_pending_entry *ldpt;
+
+			selected = lrw->tsk;
+
+			/* there is a chance that task is locked,
+			 * and the case where it locked in oom_score_adj_write
+			 * we might have deadlock. There is no macro for it
+			 *  and this is the only place there is a try on
+			 * the task_lock.
+			 */
+			if (!spin_trylock(&selected->alloc_lock)) {
+				lmk_inc_stats(LMK_ERROR);
+				lowmem_print(1, "Failed to lock task.\n");
+				lmk_inc_stats(LMK_BUSY);
+				goto unlock_out;
+			}
+
+			/* move to kill pending set */
+			ldpt = kmem_cache_alloc(lmk_dp_cache, GFP_ATOMIC);
+			ldpt->tsk = selected;
+
+			__lmk_death_pending_add(ldpt);
+			if (!__lmk_task_remove(selected, lrw->key))
+				WARN_ON(1);
+
+			spin_unlock(&lmk_task_lock);
+
+			set_tsk_thread_flag(selected, TIF_MEMDIE);
+			send_sig(SIGKILL, selected, 0);
+			task_set_lmk_waiting(selected);
+
+			print_obituary(selected, &cp, sc);
+
+			task_unlock(selected);
+			lmk_inc_stats(LMK_KILL);
+			goto out;
+		} else {
+			lmk_inc_stats(LMK_WASTE);
 		}
-		selected = p;
-		selected_tasksize = tasksize;
-		selected_oom_score_adj = oom_score_adj;
-		lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n",
-			     p->comm, p->pid, oom_score_adj, tasksize);
+	} else {
+		lmk_inc_stats(LMK_NO_KILL);
 	}
-	if (selected) {
-		task_lock(selected);
-		send_sig(SIGKILL, selected, 0);
-		if (selected->mm)
-			task_set_lmk_waiting(selected);
-		task_unlock(selected);
-		lowmem_print(1, "Killing '%s' (%d), adj %hd,\n"
-				 "   to free %ldkB on behalf of '%s' (%d) because\n"
-				 "   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n"
-				 "   Free memory is %ldkB above reserved\n",
-			     selected->comm, selected->pid,
-			     selected_oom_score_adj,
-			     selected_tasksize * (long)(PAGE_SIZE / 1024),
-			     current->comm, current->pid,
-			     other_file * (long)(PAGE_SIZE / 1024),
-			     minfree * (long)(PAGE_SIZE / 1024),
-			     min_score_adj,
-			     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);
-	rcu_read_unlock();
-	return rem;
+unlock_out:
+	cp.selected_tasksize = SHRINK_STOP;
+	spin_unlock(&lmk_task_lock);
+out:
+	if (cp.selected_tasksize == 0)
+		lowmem_print(2, "list empty nothing to free\n");
+	lowmem_print(4, "lowmem_shrink %lu, %x, return %ld\n",
+		     nr_to_scan, sc->gfp_mask, cp.selected_tasksize);
+
+	return cp.selected_tasksize;
 }
 
 static struct shrinker lowmem_shrinker = {
@@ -201,6 +282,9 @@ static struct shrinker lowmem_shrinker = {
 
 static int __init lowmem_init(void)
 {
+	lmk_dp_cache = KMEM_CACHE(lmk_death_pending_entry, 0);
+	lmk_task_cache = KMEM_CACHE(lmk_rb_watch, 0);
+	oom_score_notifier_register(&lmk_oom_score_nb);
 	register_shrinker(&lowmem_shrinker);
 	init_procfs_lmk();
 	return 0;
diff --git a/drivers/staging/android/lowmemorykiller.h b/drivers/staging/android/lowmemorykiller.h
new file mode 100644
index 0000000..03c30f6
--- /dev/null
+++ b/drivers/staging/android/lowmemorykiller.h
@@ -0,0 +1,15 @@
+#ifndef __LOWMEMORYKILLER_H
+#define __LOWMEMORYKILLER_H
+
+/* The lowest score LMK is using */
+#define LMK_SCORE_THRESHOLD 0
+
+extern u32 lowmem_debug_level;
+
+#define lowmem_print(level, x...)			\
+	do {						\
+		if (lowmem_debug_level >= (level))	\
+			pr_info(x);			\
+	} while (0)
+
+#endif
diff --git a/drivers/staging/android/lowmemorykiller_stats.c b/drivers/staging/android/lowmemorykiller_stats.c
index 673691c..68dbcc0 100644
--- a/drivers/staging/android/lowmemorykiller_stats.c
+++ b/drivers/staging/android/lowmemorykiller_stats.c
@@ -15,7 +15,9 @@
 
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include "lowmemorykiller.h"
 #include "lowmemorykiller_stats.h"
+#include "lowmemorykiller_tasks.h"
 
 struct lmk_stats {
 	atomic_long_t scans; /* counter as in shrinker scans */
@@ -27,6 +29,10 @@ struct lmk_stats {
 				* to be cancelled due to pending kills
 				*/
 	atomic_long_t count; /* number of shrinker count calls */
+	atomic_long_t scan_busy; /* mutex held */
+	atomic_long_t no_kill; /* mutex held */
+	atomic_long_t busy;
+	atomic_long_t error;
 	atomic_long_t unknown; /* internal */
 } st;
 
@@ -48,6 +54,15 @@ void lmk_inc_stats(int key)
 	case LMK_COUNT:
 		atomic_long_inc(&st.count);
 		break;
+	case LMK_BUSY:
+		atomic_long_inc(&st.busy);
+		break;
+	case LMK_ERROR:
+		atomic_long_inc(&st.error);
+		break;
+	case LMK_NO_KILL:
+		atomic_long_inc(&st.no_kill);
+		break;
 	default:
 		atomic_long_inc(&st.unknown);
 		break;
@@ -61,6 +76,10 @@ static int lmk_proc_show(struct seq_file *m, void *v)
 	seq_printf(m, "waste: %ld\n", atomic_long_read(&st.waste));
 	seq_printf(m, "timeout: %ld\n", atomic_long_read(&st.timeout));
 	seq_printf(m, "count: %ld\n", atomic_long_read(&st.count));
+	seq_printf(m, "busy: %ld\n", atomic_long_read(&st.busy));
+	seq_printf(m, "error: %ld\n", atomic_long_read(&st.error));
+	seq_printf(m, "no kill: %ld\n", atomic_long_read(&st.no_kill));
+	seq_printf(m, "queue: %d\n", death_pending_len);
 	seq_printf(m, "unknown: %ld (internal)\n",
 		   atomic_long_read(&st.unknown));
 
@@ -83,3 +102,8 @@ int __init init_procfs_lmk(void)
 	proc_create_data(LMK_PROCFS_NAME, 0444, NULL, &lmk_proc_fops, NULL);
 	return 0;
 }
+
+void exit_procfs_lmk(void)
+{
+	remove_proc_entry(LMK_PROCFS_NAME, NULL);
+}
diff --git a/drivers/staging/android/lowmemorykiller_stats.h b/drivers/staging/android/lowmemorykiller_stats.h
index abeb6924..355fa53 100644
--- a/drivers/staging/android/lowmemorykiller_stats.h
+++ b/drivers/staging/android/lowmemorykiller_stats.h
@@ -10,12 +10,20 @@
  *  published by the Free Software Foundation.
  */
 
+#ifndef __LOWMEMORYKILLER_STATS_H
+#define __LOWMEMORYKILLER_STATS_H
+
 enum  lmk_kill_stats {
 	LMK_SCAN = 1,
 	LMK_KILL = 2,
 	LMK_WASTE = 3,
 	LMK_TIMEOUT = 4,
-	LMK_COUNT = 5
+	LMK_COUNT = 5,
+	LMK_SCAN_BUSY = 6,
+	LMK_NO_KILL = 7,
+	LMK_BUSY = 8,
+	LMK_ERROR = 9,
+
 };
 
 #define LMK_PROCFS_NAME "lmkstats"
@@ -23,7 +31,11 @@ enum  lmk_kill_stats {
 #ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_STATS
 void lmk_inc_stats(int key);
 int __init init_procfs_lmk(void);
+void exit_procfs_lmk(void);
 #else
 static inline void lmk_inc_stats(int key) { return; };
 static inline int __init init_procfs_lmk(void) { return 0; };
+static inline void exit_procfs_lmk(void) { return; };
+#endif
+
 #endif
diff --git a/drivers/staging/android/lowmemorykiller_tasks.c b/drivers/staging/android/lowmemorykiller_tasks.c
new file mode 100644
index 0000000..d895bf3
--- /dev/null
+++ b/drivers/staging/android/lowmemorykiller_tasks.c
@@ -0,0 +1,220 @@
+/*
+ *  lowmemorykiller_tasks
+ *
+ *  Copyright (C) 2017 Sony Mobile Communications Inc.
+ *
+ *  Author: Peter Enderborg <peter.enderborg@sonymobile.com>
+ *
+ *  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 files contains help functions for handling tasks within the
+ * lowmemorykiller. It track tasks that are in it's score range,
+ * and it track tasks that signaled to be killed
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/oom.h>
+#include <linux/slab.h>
+#include <linux/oom_score_notifier.h>
+
+#include "lowmemorykiller.h"
+#include "lowmemorykiller_tasks.h"
+
+static struct rb_root watch_tree = RB_ROOT;
+struct list_head lmk_death_pending;
+struct kmem_cache *lmk_dp_cache;
+struct kmem_cache *lmk_task_cache;
+
+/* We need a well defined order for our tree, score is the major order
+ * and we use pid to get a unique order.
+ * return -1 on smaller, 0 on equal and 1 on bigger
+ */
+
+enum {
+	LMK_OFR_LESS = -1,
+	LMK_OFR_EQUAL = 0,
+	LMK_OFR_GREATER = 1
+};
+
+/* to protect lmk task storage data structures */
+DEFINE_SPINLOCK(lmk_task_lock);
+LIST_HEAD(lmk_death_pending);
+
+int death_pending_len;
+
+static inline int lmk_task_orderfunc(int lkey, int lpid, int rkey, int rpid)
+{
+	if (lkey > rkey)
+		return LMK_OFR_GREATER;
+	if (lkey < rkey)
+		return LMK_OFR_LESS;
+	if (lpid > rpid)
+		return LMK_OFR_GREATER;
+	if (lpid < rpid)
+		return LMK_OFR_LESS;
+	return LMK_OFR_EQUAL;
+}
+
+static inline int __lmk_task_insert(struct rb_root *root,
+				    struct task_struct *tsk)
+{
+	struct rb_node **new = &root->rb_node, *parent = NULL;
+	struct lmk_rb_watch *t;
+
+	t = kmem_cache_alloc(lmk_task_cache, GFP_ATOMIC);
+	t->key = tsk->signal->oom_score_adj;
+	t->tsk = tsk;
+
+	/* Figure out where to put new node */
+	while (*new) {
+		struct lmk_rb_watch *this = rb_entry(*new,
+						     struct lmk_rb_watch,
+						     rb_node);
+		int result;
+
+		result = lmk_task_orderfunc(t->key, t->tsk->pid,
+					    this->key, this->tsk->pid);
+		if (result == LMK_OFR_EQUAL) {
+			lowmem_print(1, "Dupe key %d pid %d - key %d pid %d\n",
+				     t->key, t->tsk->pid,
+				     this->key, this->tsk->pid);
+			WARN_ON(1);
+			return 0;
+		}
+		parent = *new;
+		if (result > 0)
+			new = &((*new)->rb_left);
+		else
+			new = &((*new)->rb_right);
+	}
+
+	/* Add new node and rebalance tree. */
+	rb_link_node(&t->rb_node, parent, new);
+	rb_insert_color(&t->rb_node, root);
+
+	return 1;
+}
+
+static struct lmk_rb_watch *__lmk_task_search(struct rb_root *root,
+					      struct task_struct *tsk,
+					      int score)
+{
+	struct rb_node *node = root->rb_node;
+
+	while (node) {
+		struct lmk_rb_watch *data = rb_entry(node,
+						     struct lmk_rb_watch,
+						     rb_node);
+		int result;
+
+		result = lmk_task_orderfunc(data->key, data->tsk->pid,
+					    score, tsk->pid);
+
+		if (result < 0)
+			node = node->rb_left;
+		else if (result > 0)
+			node = node->rb_right;
+		else if (data->tsk == tsk)
+			return data;
+	}
+	return NULL;
+}
+
+int __lmk_task_remove(struct task_struct *tsk,
+		      int score)
+{
+	struct lmk_rb_watch *lrw;
+
+	lrw = __lmk_task_search(&watch_tree, tsk, score);
+	if (lrw) {
+		rb_erase(&lrw->rb_node, &watch_tree);
+		kmem_cache_free(lmk_task_cache, lrw);
+		return 1;
+	}
+
+	return 0;
+}
+
+static void lmk_task_watch(struct task_struct *tsk, int old_oom_score_adj)
+{
+	if (thread_group_leader(tsk) &&
+	    (tsk->signal->oom_score_adj >= LMK_SCORE_THRESHOLD ||
+	     old_oom_score_adj >= LMK_SCORE_THRESHOLD) &&
+	    !(tsk->flags & PF_KTHREAD)) {
+		spin_lock(&lmk_task_lock);
+		__lmk_task_remove(tsk, old_oom_score_adj);
+		if (tsk->signal->oom_score_adj >= LMK_SCORE_THRESHOLD)
+			if (!test_tsk_thread_flag(tsk, TIF_MEMDIE))
+				__lmk_task_insert(&watch_tree, tsk);
+		spin_unlock(&lmk_task_lock);
+	}
+}
+
+static void lmk_task_free(struct task_struct *tsk)
+{
+	if (thread_group_leader(tsk) &&
+	    !(tsk->flags & PF_KTHREAD)) {
+		struct lmk_death_pending_entry *dp_iterator;
+		int clear = 1;
+
+		spin_lock(&lmk_task_lock);
+		if (__lmk_task_remove(tsk, tsk->signal->oom_score_adj))
+			clear = 0;
+
+		/* check our kill queue */
+		list_for_each_entry(dp_iterator,
+				    &lmk_death_pending, lmk_dp_list) {
+			if (dp_iterator->tsk == tsk) {
+				list_del(&dp_iterator->lmk_dp_list);
+				kmem_cache_free(lmk_dp_cache, dp_iterator);
+				death_pending_len--;
+				clear = 0;
+				break;
+			}
+		}
+		spin_unlock(&lmk_task_lock);
+		if (clear) {
+			lowmem_print(2, "Pid not in list %d %d\n",
+				     tsk->pid, tsk->signal->oom_score_adj);
+		}
+	}
+}
+
+static int lmk_oom_score_notifier(struct notifier_block *nb,
+				  unsigned long action, void *data)
+{
+	struct oom_score_notifier_struct *osns = data;
+
+	switch (action) {
+	case OSN_NEW:
+		lmk_task_watch(osns->tsk, LMK_SCORE_THRESHOLD - 1);
+		break;
+	case OSN_FREE:
+		lmk_task_free(osns->tsk);
+		break;
+	case OSN_UPDATE:
+		lmk_task_watch(osns->tsk, osns->old_score);
+		break;
+	}
+	return 0;
+}
+
+int __lmk_death_pending_add(struct lmk_death_pending_entry *lwp)
+{
+	list_add(&lwp->lmk_dp_list, &lmk_death_pending);
+	death_pending_len++;
+	return 0;
+}
+
+struct lmk_rb_watch *__lmk_first(void)
+{
+	return rb_entry(rb_first(&watch_tree), struct lmk_rb_watch, rb_node);
+}
+
+struct notifier_block lmk_oom_score_nb = {
+	.notifier_call = lmk_oom_score_notifier,
+};
diff --git a/drivers/staging/android/lowmemorykiller_tasks.h b/drivers/staging/android/lowmemorykiller_tasks.h
new file mode 100644
index 0000000..b5e94d5
--- /dev/null
+++ b/drivers/staging/android/lowmemorykiller_tasks.h
@@ -0,0 +1,35 @@
+/*
+ *  lowmemorykiller_tasks interface
+ *
+ *  Copyright (C) 2017 Sony Mobile Communications Inc.
+ *
+ *  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 __LOWMEMORYKILLER_TASKS_H
+#define __LOWMEMORYKILLER_TASKS_H
+
+struct lmk_death_pending_entry {
+	struct list_head lmk_dp_list;
+	struct task_struct *tsk;
+};
+
+struct lmk_rb_watch {
+	struct rb_node rb_node;
+	struct task_struct *tsk;
+	int key;
+};
+
+extern int death_pending_len;
+extern struct kmem_cache *lmk_dp_cache;
+extern struct kmem_cache *lmk_task_cache;
+extern spinlock_t lmk_task_lock;
+extern struct notifier_block lmk_oom_score_nb;
+
+int __lmk_task_remove(struct task_struct *tsk, int score);
+int __lmk_death_pending_add(struct lmk_death_pending_entry *lwp);
+struct lmk_rb_watch *__lmk_first(void);
+
+#endif
-- 
2.4.2

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller
  2017-02-14 16:09 [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller peter.enderborg
  2017-02-14 16:09 ` [PATCH 2/3 staging-next] oom: Add notification for oom_score_adj peter.enderborg
  2017-02-14 16:09 ` [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk peter.enderborg
@ 2017-02-14 16:50 ` Greg KH
  2017-02-15  8:22   ` peter enderborg
  2017-02-14 16:51 ` Greg KH
  3 siblings, 1 reply; 13+ messages in thread
From: Greg KH @ 2017-02-14 16:50 UTC (permalink / raw)
  To: peter.enderborg; +Cc: devel, linux-kernel, arve, riandrews, torvalds, linux-mm

On Tue, Feb 14, 2017 at 05:09:30PM +0100, peter.enderborg@sonymobile.com wrote:
> From: Peter Enderborg <peter.enderborg@sonymobile.com>
> 
> This collects stats for shrinker calls and how much
> waste work we do within the lowmemorykiller.
> 
> Signed-off-by: Peter Enderborg <peter.enderborg@sonymobile.com>
> ---
>  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.

Ick, no new /proc files please, this isn't a "process" value.  What's
wrong with debugfs?

Also note the minor '  ' usage in your first sentence of the help text.

>  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 <linux/rcupdate.h>
>  #include <linux/profile.h>
>  #include <linux/notifier.h>
> +#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 0000000..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 <peter.enderborg@sonymobile.com>
> + *
> + *  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 <linux/proc_fs.h>
> +#include <linux/seq_file.h>
> +#include "lowmemorykiller_stats.h"
> +
> +struct lmk_stats {
> +	atomic_long_t scans; /* counter as in shrinker scans */
> +	atomic_long_t kills; /* the number of sigkills sent */
> +	atomic_long_t waste; /* the numer of extensive calls that did
> +			      * not lead to anything
> +			      */
> +	atomic_long_t timeout; /* counter for shrinker calls that needed
> +				* to be cancelled due to pending kills
> +				*/
> +	atomic_long_t count; /* number of shrinker count calls */
> +	atomic_long_t unknown; /* internal */

Why not document the structure in the proper kernel-doc format?

Also, do you really need atomic variables here?

> +} st;
> +
> +void lmk_inc_stats(int key)
> +{
> +	switch (key) {
> +	case LMK_SCAN:
> +		atomic_long_inc(&st.scans);
> +		break;
> +	case LMK_KILL:
> +		atomic_long_inc(&st.kills);
> +		break;
> +	case LMK_WASTE:
> +		atomic_long_inc(&st.waste);
> +		break;
> +	case LMK_TIMEOUT:
> +		atomic_long_inc(&st.timeout);
> +		break;
> +	case LMK_COUNT:
> +		atomic_long_inc(&st.count);
> +		break;
> +	default:
> +		atomic_long_inc(&st.unknown);
> +		break;
> +	}
> +}
> +
> +static int lmk_proc_show(struct seq_file *m, void *v)
> +{
> +	seq_printf(m, "kill: %ld\n", atomic_long_read(&st.kills));
> +	seq_printf(m, "scan: %ld\n", atomic_long_read(&st.scans));
> +	seq_printf(m, "waste: %ld\n", atomic_long_read(&st.waste));
> +	seq_printf(m, "timeout: %ld\n", atomic_long_read(&st.timeout));
> +	seq_printf(m, "count: %ld\n", atomic_long_read(&st.count));
> +	seq_printf(m, "unknown: %ld (internal)\n",
> +		   atomic_long_read(&st.unknown));
> +
> +	return 0;
> +}
> +
> +static int lmk_proc_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, lmk_proc_show, PDE_DATA(inode));
> +}
> +
> +static const struct file_operations lmk_proc_fops = {
> +	.open		= lmk_proc_open,
> +	.read		= seq_read,
> +	.release	= single_release
> +};
> +
> +int __init init_procfs_lmk(void)
> +{
> +	proc_create_data(LMK_PROCFS_NAME, 0444, NULL, &lmk_proc_fops, NULL);
> +	return 0;
> +}
> diff --git a/drivers/staging/android/lowmemorykiller_stats.h b/drivers/staging/android/lowmemorykiller_stats.h
> new file mode 100644
> index 0000000..abeb6924
> --- /dev/null
> +++ b/drivers/staging/android/lowmemorykiller_stats.h
> @@ -0,0 +1,29 @@
> +/*
> + *  lowmemorykiller_stats interface
> + *
> + *  Copyright (C) 2017 Sony Mobile Communications Inc.
> + *
> + *  Author: Peter Enderborg <peter.enderborg@sonymobile.com>
> + *
> + *  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.
> + */
> +
> +enum  lmk_kill_stats {

nit, two spaces?

> +	LMK_SCAN = 1,
> +	LMK_KILL = 2,
> +	LMK_WASTE = 3,
> +	LMK_TIMEOUT = 4,
> +	LMK_COUNT = 5
> +};
> +
> +#define LMK_PROCFS_NAME "lmkstats"

You only use it once, why create a #define?  Anyway, debugfs is better
for debugging stuff like this, that is what it was created for.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller
  2017-02-14 16:09 [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller peter.enderborg
                   ` (2 preceding siblings ...)
  2017-02-14 16:50 ` [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller Greg KH
@ 2017-02-14 16:51 ` Greg KH
  2017-02-15  8:21   ` peter enderborg
  3 siblings, 1 reply; 13+ messages in thread
From: Greg KH @ 2017-02-14 16:51 UTC (permalink / raw)
  To: peter.enderborg; +Cc: devel, linux-kernel, arve, riandrews, torvalds, linux-mm

On Tue, Feb 14, 2017 at 05:09:30PM +0100, peter.enderborg@sonymobile.com wrote:
> From: Peter Enderborg <peter.enderborg@sonymobile.com>
> 
> This collects stats for shrinker calls and how much
> waste work we do within the lowmemorykiller.
> 
> Signed-off-by: Peter Enderborg <peter.enderborg@sonymobile.com>

Wait, what changed from the previous versions of this patch?  Did you
take the review comments into consideration, or is this just a resend of
the original patches in a format that isn't corrupted?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller
  2017-02-14 16:51 ` Greg KH
@ 2017-02-15  8:21   ` peter enderborg
  2017-02-15 14:31     ` Greg KH
  0 siblings, 1 reply; 13+ messages in thread
From: peter enderborg @ 2017-02-15  8:21 UTC (permalink / raw)
  To: Greg KH; +Cc: devel, linux-kernel, arve, riandrews, torvalds, linux-mm

On 02/14/2017 05:51 PM, Greg KH wrote:
> On Tue, Feb 14, 2017 at 05:09:30PM +0100, peter.enderborg@sonymobile.com wrote:
>> From: Peter Enderborg <peter.enderborg@sonymobile.com>
>>
>> This collects stats for shrinker calls and how much
>> waste work we do within the lowmemorykiller.
>>
>> Signed-off-by: Peter Enderborg <peter.enderborg@sonymobile.com>
> Wait, what changed from the previous versions of this patch?  Did you
> take the review comments into consideration, or is this just a resend of
> the original patches in a format that isn't corrupted?
>
> thanks,
>
> greg k-h

This is just a send with git-send-email that seems to work better. Nothing
else than tab-spaces should be different. I would like to have some positive
feedback from google/android before I start to send updated patches to the list.
If google are ready for the userspace solution this patch set is pointless for
upstream kernel.

Michal Hocko is very negative to hole thing, but we have addressed at least some
issues he pointed out on the list in 2015. Is there any idea to continue?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller
  2017-02-14 16:50 ` [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller Greg KH
@ 2017-02-15  8:22   ` peter enderborg
  2017-02-15 14:29     ` Greg KH
  0 siblings, 1 reply; 13+ messages in thread
From: peter enderborg @ 2017-02-15  8:22 UTC (permalink / raw)
  To: Greg KH; +Cc: devel, linux-kernel, arve, riandrews, torvalds, linux-mm

On 02/14/2017 05:50 PM, Greg KH wrote:
> On Tue, Feb 14, 2017 at 05:09:30PM +0100, peter.enderborg@sonymobile.com wrote:
>> From: Peter Enderborg <peter.enderborg@sonymobile.com>
>>
>> This collects stats for shrinker calls and how much
>> waste work we do within the lowmemorykiller.
>>
>> Signed-off-by: Peter Enderborg <peter.enderborg@sonymobile.com>
>> ---
>>  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.
> Ick, no new /proc files please, this isn't a "process" value.  What's
> wrong with debugfs?
This is intended for android. Android users are very limited in their access
to linux part of the system on commercial models and lmk activity has a bad impact on the performance
of the device. Even the application developers has not much access so it seems to be fair to give
the users the information about why there is a problem.
> Also note the minor '  ' usage in your first sentence of the help text.
>
>>  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 <linux/rcupdate.h>
>>  #include <linux/profile.h>
>>  #include <linux/notifier.h>
>> +#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 0000000..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 <peter.enderborg@sonymobile.com>
>> + *
>> + *  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 <linux/proc_fs.h>
>> +#include <linux/seq_file.h>
>> +#include "lowmemorykiller_stats.h"
>> +
>> +struct lmk_stats {
>> +	atomic_long_t scans; /* counter as in shrinker scans */
>> +	atomic_long_t kills; /* the number of sigkills sent */
>> +	atomic_long_t waste; /* the numer of extensive calls that did
>> +			      * not lead to anything
>> +			      */
>> +	atomic_long_t timeout; /* counter for shrinker calls that needed
>> +				* to be cancelled due to pending kills
>> +				*/
>> +	atomic_long_t count; /* number of shrinker count calls */
>> +	atomic_long_t unknown; /* internal */
> Why not document the structure in the proper kernel-doc format?
>
> Also, do you really need atomic variables here?
I think so. The lmk are usually called from kswapd0,
but can also be called from other parts. At least for
arm the cost is low so better safe than sorry and
it is better to focus on have the spin_lock held as short
time as possible. 
>
>> +} st;
>> +
>> +void lmk_inc_stats(int key)
>> +{
>> +	switch (key) {
>> +	case LMK_SCAN:
>> +		atomic_long_inc(&st.scans);
>> +		break;
>> +	case LMK_KILL:
>> +		atomic_long_inc(&st.kills);
>> +		break;
>> +	case LMK_WASTE:
>> +		atomic_long_inc(&st.waste);
>> +		break;
>> +	case LMK_TIMEOUT:
>> +		atomic_long_inc(&st.timeout);
>> +		break;
>> +	case LMK_COUNT:
>> +		atomic_long_inc(&st.count);
>> +		break;
>> +	default:
>> +		atomic_long_inc(&st.unknown);
>> +		break;
>> +	}
>> +}
>> +
>> +static int lmk_proc_show(struct seq_file *m, void *v)
>> +{
>> +	seq_printf(m, "kill: %ld\n", atomic_long_read(&st.kills));
>> +	seq_printf(m, "scan: %ld\n", atomic_long_read(&st.scans));
>> +	seq_printf(m, "waste: %ld\n", atomic_long_read(&st.waste));
>> +	seq_printf(m, "timeout: %ld\n", atomic_long_read(&st.timeout));
>> +	seq_printf(m, "count: %ld\n", atomic_long_read(&st.count));
>> +	seq_printf(m, "unknown: %ld (internal)\n",
>> +		   atomic_long_read(&st.unknown));
>> +
>> +	return 0;
>> +}
>> +
>> +static int lmk_proc_open(struct inode *inode, struct file *file)
>> +{
>> +	return single_open(file, lmk_proc_show, PDE_DATA(inode));
>> +}
>> +
>> +static const struct file_operations lmk_proc_fops = {
>> +	.open		= lmk_proc_open,
>> +	.read		= seq_read,
>> +	.release	= single_release
>> +};
>> +
>> +int __init init_procfs_lmk(void)
>> +{
>> +	proc_create_data(LMK_PROCFS_NAME, 0444, NULL, &lmk_proc_fops, NULL);
>> +	return 0;
>> +}
>> diff --git a/drivers/staging/android/lowmemorykiller_stats.h b/drivers/staging/android/lowmemorykiller_stats.h
>> new file mode 100644
>> index 0000000..abeb6924
>> --- /dev/null
>> +++ b/drivers/staging/android/lowmemorykiller_stats.h
>> @@ -0,0 +1,29 @@
>> +/*
>> + *  lowmemorykiller_stats interface
>> + *
>> + *  Copyright (C) 2017 Sony Mobile Communications Inc.
>> + *
>> + *  Author: Peter Enderborg <peter.enderborg@sonymobile.com>
>> + *
>> + *  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.
>> + */
>> +
>> +enum  lmk_kill_stats {
> nit, two spaces?
>
>> +	LMK_SCAN = 1,
>> +	LMK_KILL = 2,
>> +	LMK_WASTE = 3,
>> +	LMK_TIMEOUT = 4,
>> +	LMK_COUNT = 5
>> +};
>> +
>> +#define LMK_PROCFS_NAME "lmkstats"
> You only use it once, why create a #define?  Anyway, debugfs is better
> for debugging stuff like this, that is what it was created for.
>From android selinux:
neverallow untrusted_app debugfs_type:file read;

And the define was created upon requests from internal reviews that prefer to
see this in headers.

I have debugfs patch ongoing, but it's more about the internal structure
for debugging than statistical data from the lmk.
> thanks,
>
> greg k-h

thanks

/Peter

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller
  2017-02-15  8:22   ` peter enderborg
@ 2017-02-15 14:29     ` Greg KH
  0 siblings, 0 replies; 13+ messages in thread
From: Greg KH @ 2017-02-15 14:29 UTC (permalink / raw)
  To: peter enderborg; +Cc: devel, riandrews, linux-kernel, linux-mm, arve, torvalds

On Wed, Feb 15, 2017 at 09:22:10AM +0100, peter enderborg wrote:
> On 02/14/2017 05:50 PM, Greg KH wrote:
> > On Tue, Feb 14, 2017 at 05:09:30PM +0100, peter.enderborg@sonymobile.com wrote:
> >> From: Peter Enderborg <peter.enderborg@sonymobile.com>
> >>
> >> This collects stats for shrinker calls and how much
> >> waste work we do within the lowmemorykiller.
> >>
> >> Signed-off-by: Peter Enderborg <peter.enderborg@sonymobile.com>
> >> ---
> >>  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.
> > Ick, no new /proc files please, this isn't a "process" value.  What's
> > wrong with debugfs?
> This is intended for android. Android users are very limited in their access
> to linux part of the system on commercial models and lmk activity has a bad impact on the performance
> of the device. Even the application developers has not much access so it seems to be fair to give
> the users the information about why there is a problem.

Why would you want to give "all users" this information at all?  This is
a debugging tool, your debugging userspace framework can use whatever
interface you create to access it (i.e. debugfs).

Again, do not add debugging stuff to /proc/ that's not ok, sorry.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller
  2017-02-15  8:21   ` peter enderborg
@ 2017-02-15 14:31     ` Greg KH
  0 siblings, 0 replies; 13+ messages in thread
From: Greg KH @ 2017-02-15 14:31 UTC (permalink / raw)
  To: peter enderborg; +Cc: devel, riandrews, linux-kernel, linux-mm, arve, torvalds

On Wed, Feb 15, 2017 at 09:21:56AM +0100, peter enderborg wrote:
> On 02/14/2017 05:51 PM, Greg KH wrote:
> > On Tue, Feb 14, 2017 at 05:09:30PM +0100, peter.enderborg@sonymobile.com wrote:
> >> From: Peter Enderborg <peter.enderborg@sonymobile.com>
> >>
> >> This collects stats for shrinker calls and how much
> >> waste work we do within the lowmemorykiller.
> >>
> >> Signed-off-by: Peter Enderborg <peter.enderborg@sonymobile.com>
> > Wait, what changed from the previous versions of this patch?  Did you
> > take the review comments into consideration, or is this just a resend of
> > the original patches in a format that isn't corrupted?
> >
> > thanks,
> >
> > greg k-h
> 
> This is just a send with git-send-email that seems to work better. Nothing
> else than tab-spaces should be different. I would like to have some positive
> feedback from google/android before I start to send updated patches to the list.
> If google are ready for the userspace solution this patch set is pointless for
> upstream kernel.
> 
> Michal Hocko is very negative to hole thing, but we have addressed at least some
> issues he pointed out on the list in 2015. Is there any idea to continue?

If Michal rejected this solution, then I wouldn't be spending much time
on it at all.  Instead, I strongly suggest you try to do what he pointed
out should be done instead.  If that requires userspace help, great, try
that and see what happens.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk
  2017-02-10  7:39   ` peter enderborg
@ 2017-02-10  7:57     ` Michal Hocko
  0 siblings, 0 replies; 13+ messages in thread
From: Michal Hocko @ 2017-02-10  7:57 UTC (permalink / raw)
  To: peter enderborg
  Cc: devel, Greg Kroah-Hartman, linux-kernel,
	Arve Hjønnevåg, Riley Andrews, Linus Torvalds,
	linux-mm

On Fri 10-02-17 08:39:11, peter enderborg wrote:
> On 02/09/2017 09:05 PM, Michal Hocko wrote:
> > On Thu 09-02-17 14:21:52, peter enderborg wrote:
> >> 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.
> > I have to admit I really do not understand big part of the above
> > paragraph as well as how this all is supposed to work. A quick glance
> > over the implementation. __lmk_task_insert seems to be only called from
> > the oom_score notifier context. If nobody updates the value then no task
> > will get into the tree. Or am I missing something really obvious here?
> > Moreover oom scores tend to be mostly same for tasks. That means that
> > your sorted tree will become sorted by pids in most cases. I do not see
> > any sorting based on the rss nor any updates that would reflect updates
> > of rss. How can this possibly work?
> 
> The task tree nodes are created,updated or removed from the notifier when
> there is a relevant oom_score_adj change. If no one create a task that
> is in the range for the lowmemorykiller the tree will be empty. This is
> an android feature so the score will be updated very often. It is
> part of activity manager to prioritise tasks.  Why should we do sort of
> rss?
 
Because the current lmk selects the tasks based on rss. And the patch
doesn't explain why this is no longer suitable and a different metric
shoult be used. If you also consider that the scale of oom_score_adj is
quite small, conllisions when you simply sort based on pids which is
more than questionable. I really fail to see how this can work
reasonably and why the change of the lmk semantic is even acceptable.

-- 
Michal Hocko
SUSE Labs

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk
  2017-02-09 20:05 ` Michal Hocko
@ 2017-02-10  7:39   ` peter enderborg
  2017-02-10  7:57     ` Michal Hocko
  0 siblings, 1 reply; 13+ messages in thread
From: peter enderborg @ 2017-02-10  7:39 UTC (permalink / raw)
  To: Michal Hocko
  Cc: devel, Greg Kroah-Hartman, linux-kernel,
	Arve Hjønnevåg, Riley Andrews, Linus Torvalds,
	linux-mm

On 02/09/2017 09:05 PM, Michal Hocko wrote:
> On Thu 09-02-17 14:21:52, peter enderborg wrote:
>> 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.
> I have to admit I really do not understand big part of the above
> paragraph as well as how this all is supposed to work. A quick glance
> over the implementation. __lmk_task_insert seems to be only called from
> the oom_score notifier context. If nobody updates the value then no task
> will get into the tree. Or am I missing something really obvious here?
> Moreover oom scores tend to be mostly same for tasks. That means that
> your sorted tree will become sorted by pids in most cases. I do not see
> any sorting based on the rss nor any updates that would reflect updates
> of rss. How can this possibly work?

The task tree nodes are created,updated or removed from the notifier when
there is a relevant oom_score_adj change. If no one create a task that
is in the range for the lowmemorykiller the tree will be empty. This is
an android feature so the score will be updated very often. It is
part of activity manager to prioritise tasks.  Why should we do sort of
rss?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk
  2017-02-09 13:21 [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk peter enderborg
@ 2017-02-09 20:05 ` Michal Hocko
  2017-02-10  7:39   ` peter enderborg
  0 siblings, 1 reply; 13+ messages in thread
From: Michal Hocko @ 2017-02-09 20:05 UTC (permalink / raw)
  To: peter enderborg
  Cc: devel, Greg Kroah-Hartman, linux-kernel,
	Arve Hjønnevåg, Riley Andrews, Linus Torvalds,
	linux-mm

On Thu 09-02-17 14:21:52, peter enderborg wrote:
> 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.

I have to admit I really do not understand big part of the above
paragraph as well as how this all is supposed to work. A quick glance
over the implementation. __lmk_task_insert seems to be only called from
the oom_score notifier context. If nobody updates the value then no task
will get into the tree. Or am I missing something really obvious here?
Moreover oom scores tend to be mostly same for tasks. That means that
your sorted tree will become sorted by pids in most cases. I do not see
any sorting based on the rss nor any updates that would reflect updates
of rss. How can this possibly work?
-- 
Michal Hocko
SUSE Labs

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk
@ 2017-02-09 13:21 peter enderborg
  2017-02-09 20:05 ` Michal Hocko
  0 siblings, 1 reply; 13+ messages in thread
From: peter enderborg @ 2017-02-09 13:21 UTC (permalink / raw)
  To: devel, Greg Kroah-Hartman, linux-kernel,
	Arve Hjønnevåg, Riley Andrews, Linus Torvalds,
	linux-mm

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 <peter.enderborg@sonymobile.com>
---
  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 <linux/swap.h>
  #include <linux/rcupdate.h>
  #include <linux/profile.h>
+#include <linux/slab.h>
  #include <linux/notifier.h>
+#include <linux/oom_score_notifier.h>
+#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_struct *selected = NULL;
-    unsigned long rem = 0;
-    int tasksize;
      int i;
-    short min_score_adj = OOM_SCORE_ADJ_MAX + 1;
-    int minfree = 0;
-    int selected_tasksize = 0;
-    short selected_oom_score_adj;
      int array_size = ARRAY_SIZE(lowmem_adj);
-    int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
-    int other_file = global_node_page_state(NR_FILE_PAGES) -
-                global_node_page_state(NR_SHMEM) -
-                total_swapcache_pages();

-    lmk_inc_stats(LMK_SCAN);
+    cp->other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
+    cp->other_file = global_page_state(NR_FILE_PAGES) -
+        global_page_state(NR_SHMEM) -
+        global_page_state(NR_UNEVICTABLE) -
+        total_swapcache_pages();
+
+    cp->minfree = 0;
+    cp->min_score_adj = OOM_SCORE_ADJ_MAX;
      if (lowmem_adj_size < array_size)
          array_size = lowmem_adj_size;
      if (lowmem_minfree_size < array_size)
          array_size = lowmem_minfree_size;
      for (i = 0; i < array_size; i++) {
-        minfree = lowmem_minfree[i];
-        if (other_free < minfree && other_file < minfree) {
-            min_score_adj = lowmem_adj[i];
+        cp->minfree = lowmem_minfree[i];
+        if (cp->other_free < cp->minfree &&
+            cp->other_file < cp->minfree) {
+            cp->min_score_adj = lowmem_adj[i];
              break;
          }
      }
+    if (sc->nr_to_scan > 0)
+        lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %hd\n",
+                 sc->nr_to_scan, sc->gfp_mask, cp->other_free,
+                 cp->other_file, cp->min_score_adj);
+    cp->dynamic_max_queue_len = array_size - i + 1;
+    cp->selected_oom_score_adj = level;
+    if (level >= cp->min_score_adj)
+        return 1;
+
+    return 0;
+}
+
+static void print_obituary(struct task_struct *doomed,
+               struct calculated_params *cp,
+               struct shrink_control *sc) {
+    long cache_size = cp->other_file * (long)(PAGE_SIZE / 1024);
+    long cache_limit = cp->minfree * (long)(PAGE_SIZE / 1024);
+    long free = cp->other_free * (long)(PAGE_SIZE / 1024);
+
+    lowmem_print(1, "Killing '%s' (%d), adj %hd,\n"
+             "   to free %ldkB on behalf of '%s' (%d) because\n"
+             "   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n"
+             "   Free memory is %ldkB above reserved.\n"
+             "   Free CMA is %ldkB\n"
+             "   Total reserve is %ldkB\n"
+             "   Total free pages is %ldkB\n"
+             "   Total file cache is %ldkB\n"
+             "   Slab Reclaimable is %ldkB\n"
+             "   Slab UnReclaimable is %ldkB\n"
+             "   Total Slab is %ldkB\n"
+             "   GFP mask is 0x%x\n"
+             "   queue len is %d of max %d\n",
+             doomed->comm, doomed->pid,
+             cp->selected_oom_score_adj,
+             cp->selected_tasksize * (long)(PAGE_SIZE / 1024),
+             current->comm, current->pid,
+             cache_size, cache_limit,
+             cp->min_score_adj,
+             free,
+             global_page_state(NR_FREE_CMA_PAGES) *
+             (long)(PAGE_SIZE / 1024),
+             totalreserve_pages * (long)(PAGE_SIZE / 1024),
+             global_page_state(NR_FREE_PAGES) *
+             (long)(PAGE_SIZE / 1024),
+             global_page_state(NR_FILE_PAGES) *
+             (long)(PAGE_SIZE / 1024),
+             global_page_state(NR_SLAB_RECLAIMABLE) *
+             (long)(PAGE_SIZE / 1024),
+             global_page_state(NR_SLAB_UNRECLAIMABLE) *
+             (long)(PAGE_SIZE / 1024),
+             global_page_state(NR_SLAB_RECLAIMABLE) *
+             (long)(PAGE_SIZE / 1024) +
+             global_page_state(NR_SLAB_UNRECLAIMABLE) *
+             (long)(PAGE_SIZE / 1024),
+             sc->gfp_mask,
+             death_pending_len,
+             cp->dynamic_max_queue_len);
+}
+
+static unsigned long lowmem_count(struct shrinker *s,
+                  struct shrink_control *sc)
+{
+    struct lmk_rb_watch *lrw;
+    struct calculated_params cp;
+    short score;
+
+    lmk_inc_stats(LMK_COUNT);
+    cp.selected_tasksize = 0;
+    spin_lock(&lmk_task_lock);
+    lrw = __lmk_first();
+    if (lrw && lrw->tsk->mm) {
+        int rss = get_mm_rss(lrw->tsk->mm);

-    lowmem_print(3, "lowmem_scan %lu, %x, ofree %d %d, ma %hd\n",
-             sc->nr_to_scan, sc->gfp_mask, other_free,
-             other_file, min_score_adj);
+        score = lrw->tsk->signal->oom_score_adj;
+        spin_unlock(&lmk_task_lock);
+        if (kill_needed(score, sc, &cp))
+            if (death_pending_len < cp.dynamic_max_queue_len)
+                cp.selected_tasksize = rss;

-    if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
-        lowmem_print(5, "lowmem_scan %lu, %x, return 0\n",
-                 sc->nr_to_scan, sc->gfp_mask);
-        return 0;
+    } else {
+        spin_unlock(&lmk_task_lock);
      }

-    selected_oom_score_adj = min_score_adj;
+    return cp.selected_tasksize;
+}
+

-    rcu_read_lock();
-    for_each_process(tsk) {
-        struct task_struct *p;
-        short oom_score_adj;
+static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
+{
+    struct task_struct *selected = NULL;
+    unsigned long nr_to_scan = sc->nr_to_scan;
+    struct lmk_rb_watch *lrw;
+    int do_kill;
+    struct calculated_params cp;

-        if (tsk->flags & PF_KTHREAD)
-            continue;
+    lmk_inc_stats(LMK_SCAN);

-        p = find_lock_task_mm(tsk);
-        if (!p)
-            continue;
+    cp.selected_tasksize = 0;
+    spin_lock(&lmk_task_lock);

-        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;
+    lrw = __lmk_first();
+    if (lrw) {
+        if (lrw->tsk->mm) {
+            cp.selected_tasksize = get_mm_rss(lrw->tsk->mm);
+        } else {
+            lowmem_print(1, "pid:%d no mem\n", lrw->tsk->pid);
+            lmk_inc_stats(LMK_ERROR);
+            goto unlock_out;
          }
-        oom_score_adj = p->signal->oom_score_adj;
-        if (oom_score_adj < min_score_adj) {
-            task_unlock(p);
-            continue;
+
+        do_kill = kill_needed(lrw->key, sc, &cp);
+
+        if (death_pending_len >= cp.dynamic_max_queue_len) {
+            lmk_inc_stats(LMK_BUSY);
+            goto unlock_out;
          }
-        tasksize = get_mm_rss(p->mm);
-        task_unlock(p);
-        if (tasksize <= 0)
-            continue;
-        if (selected) {
-            if (oom_score_adj < selected_oom_score_adj)
-                continue;
-            if (oom_score_adj == selected_oom_score_adj &&
-                tasksize <= selected_tasksize)
-                continue;
+
+        if (do_kill) {
+            struct lmk_death_pending_entry *ldpt;
+
+            selected = lrw->tsk;
+
+            /* there is a chance that task is locked,
+             * and the case where it locked in oom_score_adj_write
+             * we might have deadlock. There is no macro for it
+             *  and this is the only place there is a try on
+             * the task_lock.
+             */
+            if (!spin_trylock(&selected->alloc_lock)) {
+                lmk_inc_stats(LMK_ERROR);
+                lowmem_print(1, "Failed to lock task.\n");
+                lmk_inc_stats(LMK_BUSY);
+                goto unlock_out;
+            }
+
+            /* move to kill pending set */
+            ldpt = kmem_cache_alloc(lmk_dp_cache, GFP_ATOMIC);
+            ldpt->tsk = selected;
+
+            __lmk_death_pending_add(ldpt);
+            if (!__lmk_task_remove(selected, lrw->key))
+                WARN_ON(1);
+
+            spin_unlock(&lmk_task_lock);
+
+            set_tsk_thread_flag(selected, TIF_MEMDIE);
+            send_sig(SIGKILL, selected, 0);
+            task_set_lmk_waiting(selected);
+
+            print_obituary(selected, &cp, sc);
+
+            task_unlock(selected);
+            lmk_inc_stats(LMK_KILL);
+            goto out;
+        } else {
+            lmk_inc_stats(LMK_WASTE);
          }
-        selected = p;
-        selected_tasksize = tasksize;
-        selected_oom_score_adj = oom_score_adj;
-        lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n",
-                 p->comm, p->pid, oom_score_adj, tasksize);
+    } else {
+        lmk_inc_stats(LMK_NO_KILL);
      }
-    if (selected) {
-        task_lock(selected);
-        send_sig(SIGKILL, selected, 0);
-        if (selected->mm)
-            task_set_lmk_waiting(selected);
-        task_unlock(selected);
-        lowmem_print(1, "Killing '%s' (%d), adj %hd,\n"
-                 "   to free %ldkB on behalf of '%s' (%d) because\n"
-                 "   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n"
-                 "   Free memory is %ldkB above reserved\n",
-                 selected->comm, selected->pid,
-                 selected_oom_score_adj,
-                 selected_tasksize * (long)(PAGE_SIZE / 1024),
-                 current->comm, current->pid,
-                 other_file * (long)(PAGE_SIZE / 1024),
-                 minfree * (long)(PAGE_SIZE / 1024),
-                 min_score_adj,
-                 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);
-    rcu_read_unlock();
-    return rem;
+unlock_out:
+    cp.selected_tasksize = SHRINK_STOP;
+    spin_unlock(&lmk_task_lock);
+out:
+    if (cp.selected_tasksize == 0)
+        lowmem_print(2, "list empty nothing to free\n");
+    lowmem_print(4, "lowmem_shrink %lu, %x, return %ld\n",
+             nr_to_scan, sc->gfp_mask, cp.selected_tasksize);
+
+    return cp.selected_tasksize;
  }

  static struct shrinker lowmem_shrinker = {
@@ -201,6 +282,9 @@ static struct shrinker lowmem_shrinker = {

  static int __init lowmem_init(void)
  {
+    lmk_dp_cache = KMEM_CACHE(lmk_death_pending_entry, 0);
+    lmk_task_cache = KMEM_CACHE(lmk_rb_watch, 0);
+    oom_score_notifier_register(&lmk_oom_score_nb);
      register_shrinker(&lowmem_shrinker);
      init_procfs_lmk();
      return 0;
diff --git a/drivers/staging/android/lowmemorykiller.h b/drivers/staging/android/lowmemorykiller.h
new file mode 100644
index 0000000..03c30f6
--- /dev/null
+++ b/drivers/staging/android/lowmemorykiller.h
@@ -0,0 +1,15 @@
+#ifndef __LOWMEMORYKILLER_H
+#define __LOWMEMORYKILLER_H
+
+/* The lowest score LMK is using */
+#define LMK_SCORE_THRESHOLD 0
+
+extern u32 lowmem_debug_level;
+
+#define lowmem_print(level, x...)            \
+    do {                        \
+        if (lowmem_debug_level >= (level))    \
+            pr_info(x);            \
+    } while (0)
+
+#endif
diff --git a/drivers/staging/android/lowmemorykiller_stats.c b/drivers/staging/android/lowmemorykiller_stats.c
index 673691c..68dbcc0 100644
--- a/drivers/staging/android/lowmemorykiller_stats.c
+++ b/drivers/staging/android/lowmemorykiller_stats.c
@@ -15,7 +15,9 @@

  #include <linux/proc_fs.h>
  #include <linux/seq_file.h>
+#include "lowmemorykiller.h"
  #include "lowmemorykiller_stats.h"
+#include "lowmemorykiller_tasks.h"

  struct lmk_stats {
      atomic_long_t scans; /* counter as in shrinker scans */
@@ -27,6 +29,10 @@ struct lmk_stats {
                  * to be cancelled due to pending kills
                  */
      atomic_long_t count; /* number of shrinker count calls */
+    atomic_long_t scan_busy; /* mutex held */
+    atomic_long_t no_kill; /* mutex held */
+    atomic_long_t busy;
+    atomic_long_t error;
      atomic_long_t unknown; /* internal */
  } st;

@@ -48,6 +54,15 @@ void lmk_inc_stats(int key)
      case LMK_COUNT:
          atomic_long_inc(&st.count);
          break;
+    case LMK_BUSY:
+        atomic_long_inc(&st.busy);
+        break;
+    case LMK_ERROR:
+        atomic_long_inc(&st.error);
+        break;
+    case LMK_NO_KILL:
+        atomic_long_inc(&st.no_kill);
+        break;
      default:
          atomic_long_inc(&st.unknown);
          break;
@@ -61,6 +76,10 @@ static int lmk_proc_show(struct seq_file *m, void *v)
      seq_printf(m, "waste: %ld\n", atomic_long_read(&st.waste));
      seq_printf(m, "timeout: %ld\n", atomic_long_read(&st.timeout));
      seq_printf(m, "count: %ld\n", atomic_long_read(&st.count));
+    seq_printf(m, "busy: %ld\n", atomic_long_read(&st.busy));
+    seq_printf(m, "error: %ld\n", atomic_long_read(&st.error));
+    seq_printf(m, "no kill: %ld\n", atomic_long_read(&st.no_kill));
+    seq_printf(m, "queue: %d\n", death_pending_len);
      seq_printf(m, "unknown: %ld (internal)\n",
             atomic_long_read(&st.unknown));

@@ -83,3 +102,8 @@ int __init init_procfs_lmk(void)
      proc_create_data(LMK_PROCFS_NAME, 0444, NULL, &lmk_proc_fops, NULL);
      return 0;
  }
+
+void exit_procfs_lmk(void)
+{
+    remove_proc_entry(LMK_PROCFS_NAME, NULL);
+}
diff --git a/drivers/staging/android/lowmemorykiller_stats.h b/drivers/staging/android/lowmemorykiller_stats.h
index abeb6924..355fa53 100644
--- a/drivers/staging/android/lowmemorykiller_stats.h
+++ b/drivers/staging/android/lowmemorykiller_stats.h
@@ -10,12 +10,20 @@
   *  published by the Free Software Foundation.
   */

+#ifndef __LOWMEMORYKILLER_STATS_H
+#define __LOWMEMORYKILLER_STATS_H
+
  enum  lmk_kill_stats {
      LMK_SCAN = 1,
      LMK_KILL = 2,
      LMK_WASTE = 3,
      LMK_TIMEOUT = 4,
-    LMK_COUNT = 5
+    LMK_COUNT = 5,
+    LMK_SCAN_BUSY = 6,
+    LMK_NO_KILL = 7,
+    LMK_BUSY = 8,
+    LMK_ERROR = 9,
+
  };

  #define LMK_PROCFS_NAME "lmkstats"
@@ -23,7 +31,11 @@ enum  lmk_kill_stats {
  #ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_STATS
  void lmk_inc_stats(int key);
  int __init init_procfs_lmk(void);
+void exit_procfs_lmk(void);
  #else
  static inline void lmk_inc_stats(int key) { return; };
  static inline int __init init_procfs_lmk(void) { return 0; };
+static inline void exit_procfs_lmk(void) { return; };
+#endif
+
  #endif
diff --git a/drivers/staging/android/lowmemorykiller_tasks.c b/drivers/staging/android/lowmemorykiller_tasks.c
new file mode 100644
index 0000000..d895bf3
--- /dev/null
+++ b/drivers/staging/android/lowmemorykiller_tasks.c
@@ -0,0 +1,220 @@
+/*
+ *  lowmemorykiller_tasks
+ *
+ *  Copyright (C) 2017 Sony Mobile Communications Inc.
+ *
+ *  Author: Peter Enderborg <peter.enderborg@sonymobile.com>
+ *
+ *  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 files contains help functions for handling tasks within the
+ * lowmemorykiller. It track tasks that are in it's score range,
+ * and it track tasks that signaled to be killed
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/oom.h>
+#include <linux/slab.h>
+#include <linux/oom_score_notifier.h>
+
+#include "lowmemorykiller.h"
+#include "lowmemorykiller_tasks.h"
+
+static struct rb_root watch_tree = RB_ROOT;
+struct list_head lmk_death_pending;
+struct kmem_cache *lmk_dp_cache;
+struct kmem_cache *lmk_task_cache;
+
+/* We need a well defined order for our tree, score is the major order
+ * and we use pid to get a unique order.
+ * return -1 on smaller, 0 on equal and 1 on bigger
+ */
+
+enum {
+    LMK_OFR_LESS = -1,
+    LMK_OFR_EQUAL = 0,
+    LMK_OFR_GREATER = 1
+};
+
+/* to protect lmk task storage data structures */
+DEFINE_SPINLOCK(lmk_task_lock);
+LIST_HEAD(lmk_death_pending);
+
+int death_pending_len;
+
+static inline int lmk_task_orderfunc(int lkey, int lpid, int rkey, int rpid)
+{
+    if (lkey > rkey)
+        return LMK_OFR_GREATER;
+    if (lkey < rkey)
+        return LMK_OFR_LESS;
+    if (lpid > rpid)
+        return LMK_OFR_GREATER;
+    if (lpid < rpid)
+        return LMK_OFR_LESS;
+    return LMK_OFR_EQUAL;
+}
+
+static inline int __lmk_task_insert(struct rb_root *root,
+                    struct task_struct *tsk)
+{
+    struct rb_node **new = &root->rb_node, *parent = NULL;
+    struct lmk_rb_watch *t;
+
+    t = kmem_cache_alloc(lmk_task_cache, GFP_ATOMIC);
+    t->key = tsk->signal->oom_score_adj;
+    t->tsk = tsk;
+
+    /* Figure out where to put new node */
+    while (*new) {
+        struct lmk_rb_watch *this = rb_entry(*new,
+                             struct lmk_rb_watch,
+                             rb_node);
+        int result;
+
+        result = lmk_task_orderfunc(t->key, t->tsk->pid,
+                        this->key, this->tsk->pid);
+        if (result == LMK_OFR_EQUAL) {
+            lowmem_print(1, "Dupe key %d pid %d - key %d pid %d\n",
+                     t->key, t->tsk->pid,
+                     this->key, this->tsk->pid);
+            WARN_ON(1);
+            return 0;
+        }
+        parent = *new;
+        if (result > 0)
+            new = &((*new)->rb_left);
+        else
+            new = &((*new)->rb_right);
+    }
+
+    /* Add new node and rebalance tree. */
+    rb_link_node(&t->rb_node, parent, new);
+    rb_insert_color(&t->rb_node, root);
+
+    return 1;
+}
+
+static struct lmk_rb_watch *__lmk_task_search(struct rb_root *root,
+                          struct task_struct *tsk,
+                          int score)
+{
+    struct rb_node *node = root->rb_node;
+
+    while (node) {
+        struct lmk_rb_watch *data = rb_entry(node,
+                             struct lmk_rb_watch,
+                             rb_node);
+        int result;
+
+        result = lmk_task_orderfunc(data->key, data->tsk->pid,
+                        score, tsk->pid);
+
+        if (result < 0)
+            node = node->rb_left;
+        else if (result > 0)
+            node = node->rb_right;
+        else if (data->tsk == tsk)
+            return data;
+    }
+    return NULL;
+}
+
+int __lmk_task_remove(struct task_struct *tsk,
+              int score)
+{
+    struct lmk_rb_watch *lrw;
+
+    lrw = __lmk_task_search(&watch_tree, tsk, score);
+    if (lrw) {
+        rb_erase(&lrw->rb_node, &watch_tree);
+        kmem_cache_free(lmk_task_cache, lrw);
+        return 1;
+    }
+
+    return 0;
+}
+
+static void lmk_task_watch(struct task_struct *tsk, int old_oom_score_adj)
+{
+    if (thread_group_leader(tsk) &&
+        (tsk->signal->oom_score_adj >= LMK_SCORE_THRESHOLD ||
+         old_oom_score_adj >= LMK_SCORE_THRESHOLD) &&
+        !(tsk->flags & PF_KTHREAD)) {
+        spin_lock(&lmk_task_lock);
+        __lmk_task_remove(tsk, old_oom_score_adj);
+        if (tsk->signal->oom_score_adj >= LMK_SCORE_THRESHOLD)
+            if (!test_tsk_thread_flag(tsk, TIF_MEMDIE))
+                __lmk_task_insert(&watch_tree, tsk);
+        spin_unlock(&lmk_task_lock);
+    }
+}
+
+static void lmk_task_free(struct task_struct *tsk)
+{
+    if (thread_group_leader(tsk) &&
+        !(tsk->flags & PF_KTHREAD)) {
+        struct lmk_death_pending_entry *dp_iterator;
+        int clear = 1;
+
+        spin_lock(&lmk_task_lock);
+        if (__lmk_task_remove(tsk, tsk->signal->oom_score_adj))
+            clear = 0;
+
+        /* check our kill queue */
+        list_for_each_entry(dp_iterator,
+                    &lmk_death_pending, lmk_dp_list) {
+            if (dp_iterator->tsk == tsk) {
+                list_del(&dp_iterator->lmk_dp_list);
+                kmem_cache_free(lmk_dp_cache, dp_iterator);
+                death_pending_len--;
+                clear = 0;
+                break;
+            }
+        }
+        spin_unlock(&lmk_task_lock);
+        if (clear) {
+            lowmem_print(2, "Pid not in list %d %d\n",
+                     tsk->pid, tsk->signal->oom_score_adj);
+        }
+    }
+}
+
+static int lmk_oom_score_notifier(struct notifier_block *nb,
+                  unsigned long action, void *data)
+{
+    struct oom_score_notifier_struct *osns = data;
+
+    switch (action) {
+    case OSN_NEW:
+        lmk_task_watch(osns->tsk, LMK_SCORE_THRESHOLD - 1);
+        break;
+    case OSN_FREE:
+        lmk_task_free(osns->tsk);
+        break;
+    case OSN_UPDATE:
+        lmk_task_watch(osns->tsk, osns->old_score);
+        break;
+    }
+    return 0;
+}
+
+int __lmk_death_pending_add(struct lmk_death_pending_entry *lwp)
+{
+    list_add(&lwp->lmk_dp_list, &lmk_death_pending);
+    death_pending_len++;
+    return 0;
+}
+
+struct lmk_rb_watch *__lmk_first(void)
+{
+    return rb_entry(rb_first(&watch_tree), struct lmk_rb_watch, rb_node);
+}
+
+struct notifier_block lmk_oom_score_nb = {
+    .notifier_call = lmk_oom_score_notifier,
+};
diff --git a/drivers/staging/android/lowmemorykiller_tasks.h b/drivers/staging/android/lowmemorykiller_tasks.h
new file mode 100644
index 0000000..b5e94d5
--- /dev/null
+++ b/drivers/staging/android/lowmemorykiller_tasks.h
@@ -0,0 +1,35 @@
+/*
+ *  lowmemorykiller_tasks interface
+ *
+ *  Copyright (C) 2017 Sony Mobile Communications Inc.
+ *
+ *  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 __LOWMEMORYKILLER_TASKS_H
+#define __LOWMEMORYKILLER_TASKS_H
+
+struct lmk_death_pending_entry {
+    struct list_head lmk_dp_list;
+    struct task_struct *tsk;
+};
+
+struct lmk_rb_watch {
+    struct rb_node rb_node;
+    struct task_struct *tsk;
+    int key;
+};
+
+extern int death_pending_len;
+extern struct kmem_cache *lmk_dp_cache;
+extern struct kmem_cache *lmk_task_cache;
+extern spinlock_t lmk_task_lock;
+extern struct notifier_block lmk_oom_score_nb;
+
+int __lmk_task_remove(struct task_struct *tsk, int score);
+int __lmk_death_pending_add(struct lmk_death_pending_entry *lwp);
+struct lmk_rb_watch *__lmk_first(void);
+
+#endif
-- 
2.4.2

^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2017-02-15 14:31 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-14 16:09 [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller peter.enderborg
2017-02-14 16:09 ` [PATCH 2/3 staging-next] oom: Add notification for oom_score_adj peter.enderborg
2017-02-14 16:09 ` [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk peter.enderborg
2017-02-14 16:50 ` [PATCH 1/3 staging-next] android: Collect statistics from lowmemorykiller Greg KH
2017-02-15  8:22   ` peter enderborg
2017-02-15 14:29     ` Greg KH
2017-02-14 16:51 ` Greg KH
2017-02-15  8:21   ` peter enderborg
2017-02-15 14:31     ` Greg KH
  -- strict thread matches above, loose matches on Subject: below --
2017-02-09 13:21 [PATCH 3/3 staging-next] mm: Remove RCU and tasklocks from lmk peter enderborg
2017-02-09 20:05 ` Michal Hocko
2017-02-10  7:39   ` peter enderborg
2017-02-10  7:57     ` Michal Hocko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).