summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/staging/android/lowmemorykiller.c105
-rw-r--r--include/trace/events/almk.h84
2 files changed, 188 insertions, 1 deletions
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 11535e3065ac..e60421299164 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -47,6 +47,10 @@
#include <linux/swap.h>
#include <linux/fs.h>
#include <linux/cpuset.h>
+#include <linux/vmpressure.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/almk.h>
#ifdef CONFIG_HIGHMEM
#define _ZONE ZONE_HIGHMEM
@@ -91,6 +95,95 @@ static unsigned long lowmem_count(struct shrinker *s,
global_page_state(NR_INACTIVE_FILE);
}
+static atomic_t shift_adj = ATOMIC_INIT(0);
+static short adj_max_shift = 353;
+
+/* User knob to enable/disable adaptive lmk feature */
+static int enable_adaptive_lmk;
+module_param_named(enable_adaptive_lmk, enable_adaptive_lmk, int,
+ S_IRUGO | S_IWUSR);
+
+/*
+ * This parameter controls the behaviour of LMK when vmpressure is in
+ * the range of 90-94. Adaptive lmk triggers based on number of file
+ * pages wrt vmpressure_file_min, when vmpressure is in the range of
+ * 90-94. Usually this is a pseudo minfree value, higher than the
+ * highest configured value in minfree array.
+ */
+static int vmpressure_file_min;
+module_param_named(vmpressure_file_min, vmpressure_file_min, int,
+ S_IRUGO | S_IWUSR);
+
+enum {
+ VMPRESSURE_NO_ADJUST = 0,
+ VMPRESSURE_ADJUST_ENCROACH,
+ VMPRESSURE_ADJUST_NORMAL,
+};
+
+int adjust_minadj(short *min_score_adj)
+{
+ int ret = VMPRESSURE_NO_ADJUST;
+
+ if (!enable_adaptive_lmk)
+ return 0;
+
+ if (atomic_read(&shift_adj) &&
+ (*min_score_adj > adj_max_shift)) {
+ if (*min_score_adj == OOM_SCORE_ADJ_MAX + 1)
+ ret = VMPRESSURE_ADJUST_ENCROACH;
+ else
+ ret = VMPRESSURE_ADJUST_NORMAL;
+ *min_score_adj = adj_max_shift;
+ }
+ atomic_set(&shift_adj, 0);
+
+ return ret;
+}
+
+static int lmk_vmpressure_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ int other_free, other_file;
+ unsigned long pressure = action;
+ int array_size = ARRAY_SIZE(lowmem_adj);
+
+ if (!enable_adaptive_lmk)
+ return 0;
+
+ if (pressure >= 95) {
+ other_file = global_page_state(NR_FILE_PAGES) -
+ global_page_state(NR_SHMEM) -
+ total_swapcache_pages();
+ other_free = global_page_state(NR_FREE_PAGES);
+
+ atomic_set(&shift_adj, 1);
+ trace_almk_vmpressure(pressure, other_free, other_file);
+ } else if (pressure >= 90) {
+ if (lowmem_adj_size < array_size)
+ array_size = lowmem_adj_size;
+ if (lowmem_minfree_size < array_size)
+ array_size = lowmem_minfree_size;
+
+ other_file = global_page_state(NR_FILE_PAGES) -
+ global_page_state(NR_SHMEM) -
+ total_swapcache_pages();
+
+ other_free = global_page_state(NR_FREE_PAGES);
+
+ if ((other_free < lowmem_minfree[array_size - 1]) &&
+ (other_file < vmpressure_file_min)) {
+ atomic_set(&shift_adj, 1);
+ trace_almk_vmpressure(pressure, other_free, other_file);
+ }
+ }
+
+ return 0;
+}
+
+static struct notifier_block lmk_vmpr_nb = {
+ .notifier_call = lmk_vmpressure_notifier,
+};
+
static int test_task_flag(struct task_struct *p, int flag)
{
struct task_struct *t = p;
@@ -285,6 +378,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
unsigned long rem = 0;
int tasksize;
int i;
+ int ret = 0;
short min_score_adj = OOM_SCORE_ADJ_MAX + 1;
int minfree = 0;
int selected_tasksize = 0;
@@ -320,11 +414,14 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
}
}
+ ret = adjust_minadj(&min_score_adj);
+
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);
if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
+ trace_almk_shrink(0, ret, other_free, other_file, 0);
lowmem_print(5, "lowmem_scan %lu, %x, return 0\n",
sc->nr_to_scan, sc->gfp_mask);
mutex_unlock(&scan_mutex);
@@ -432,8 +529,13 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
rcu_read_unlock();
/* give the system time to free up the memory */
msleep_interruptible(20);
- } else
+ trace_almk_shrink(selected_tasksize, ret,
+ other_free, other_file,
+ selected_oom_score_adj);
+ } else {
+ trace_almk_shrink(1, ret, other_free, other_file, 0);
rcu_read_unlock();
+ }
lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n",
sc->nr_to_scan, sc->gfp_mask, rem);
@@ -450,6 +552,7 @@ static struct shrinker lowmem_shrinker = {
static int __init lowmem_init(void)
{
register_shrinker(&lowmem_shrinker);
+ vmpressure_notifier_register(&lmk_vmpr_nb);
return 0;
}
device_initcall(lowmem_init);
diff --git a/include/trace/events/almk.h b/include/trace/events/almk.h
new file mode 100644
index 000000000000..85d712d48f50
--- /dev/null
+++ b/include/trace/events/almk.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM almk
+
+#if !defined(_TRACE_EVENT_ALMK_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_EVENT_ALMK_H
+
+#include <linux/tracepoint.h>
+#include <linux/types.h>
+
+TRACE_EVENT(almk_vmpressure,
+
+ TP_PROTO(unsigned long pressure,
+ int other_free,
+ int other_file),
+
+ TP_ARGS(pressure, other_free, other_file),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, pressure)
+ __field(int, other_free)
+ __field(int, other_file)
+ ),
+
+ TP_fast_assign(
+ __entry->pressure = pressure;
+ __entry->other_free = other_free;
+ __entry->other_file = other_file;
+ ),
+
+ TP_printk("%lu, %d, %d",
+ __entry->pressure, __entry->other_free,
+ __entry->other_file)
+);
+
+TRACE_EVENT(almk_shrink,
+
+ TP_PROTO(int tsize,
+ int vmp,
+ int other_free,
+ int other_file,
+ short adj),
+
+ TP_ARGS(tsize, vmp, other_free, other_file, adj),
+
+ TP_STRUCT__entry(
+ __field(int, tsize)
+ __field(int, vmp)
+ __field(int, other_free)
+ __field(int, other_file)
+ __field(short, adj)
+ ),
+
+ TP_fast_assign(
+ __entry->tsize = tsize;
+ __entry->vmp = vmp;
+ __entry->other_free = other_free;
+ __entry->other_file = other_file;
+ __entry->adj = adj;
+ ),
+
+ TP_printk("%d, %d, %d, %d, %d",
+ __entry->tsize,
+ __entry->vmp,
+ __entry->other_free,
+ __entry->other_file,
+ __entry->adj)
+);
+
+#endif
+
+#include <trace/define_trace.h>
+