diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 579cc16122d..ad053e0dcfd 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -46,8 +46,12 @@ #include #include #include +#include #include +#define CREATE_TRACE_POINTS +#include + #ifdef CONFIG_HIGHMEM #define _ZONE ZONE_HIGHMEM #else @@ -79,6 +83,96 @@ static unsigned long lowmem_deathpending_timeout; pr_info(x); \ } while (0) +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; @@ -273,7 +367,8 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) int rem = 0; int tasksize; int i; - int min_score_adj = OOM_SCORE_ADJ_MAX + 1; + int ret = 0; + short min_score_adj = OOM_SCORE_ADJ_MAX + 1; int minfree = 0; int selected_tasksize = 0; int selected_oom_score_adj; @@ -310,10 +405,13 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) break; } } - if (nr_to_scan > 0) - lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n", + if (nr_to_scan > 0) { + ret = adjust_minadj(&min_score_adj); + lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %hd\n", nr_to_scan, sc->gfp_mask, other_free, other_file, min_score_adj); + } + rem = global_page_state(NR_ACTIVE_ANON) + global_page_state(NR_ACTIVE_FILE) + global_page_state(NR_INACTIVE_ANON) + @@ -325,6 +423,10 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) if (nr_to_scan > 0) mutex_unlock(&scan_mutex); + if ((min_score_adj == OOM_SCORE_ADJ_MAX + 1) && + (nr_to_scan > 0)) + trace_almk_shrink(0, ret, other_free, other_file, 0); + return rem; } selected_oom_score_adj = min_score_adj; @@ -428,8 +530,12 @@ static int lowmem_shrink(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_shrink %lu, %x, return %d\n", nr_to_scan, sc->gfp_mask, rem); @@ -445,6 +551,7 @@ static struct shrinker lowmem_shrinker = { static int __init lowmem_init(void) { register_shrinker(&lowmem_shrinker); + vmpressure_notifier_register(&lmk_vmpr_nb); return 0; } diff --git a/include/trace/events/almk.h b/include/trace/events/almk.h new file mode 100644 index 00000000000..85d712d48f5 --- /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 +#include + +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 +