mirror of
https://github.com/S3NEO/android_kernel_samsung_msm8226.git
synced 2024-11-07 03:47:13 +00:00
lowmemorykiller: adapt to vmpressure
There were issues reported, where page cache thrashing was observed because of LMK not killing tasks when required, resulting in sluggishness and higher app launch latency. LMK does not kill a task for the following reasons. 1. The free and file pages are above the LMK thresholds 2. LMK tries to pick task with an adj level corresponding to current thresholds, but fails to do so because of the absence of tasks in that level. But sometimes it is better to kill a lower adj task, than thrashing. And there are cases where the number of file pages are huge, though we dont thrash, the reclaim process becomes time consuming, since LMK triggers will be delayed because of higher number of file pages. Even in such cases, when reclaim path finds it difficult to reclaim pages, it is better to trigger lmk to free up some memory faster. The basic idea here is to make LMK more aggressive dynamically when such a thrashing scenario is detected. To detect thrashing, this patch uses vmpressure events. The values of vmpressure upon which an action has to be taken, was derived empirically. This patch also adds tracepoints to validate this feature, almk_shrink and almk_vmpressure. Two knobs are available for the user to tune adaptive lmk behaviour. /sys/module/lowmemorykiller/parameters/adaptive_lmk - Write 1 to enable the feature, 0 to disable. By default disabled. /sys/module/lowmemorykiller/parameters/vmpressure_file_min - This parameter controls the behaviour of LMK when vmpressure is in the range of 90-94. Adaptive lmk triggers based on number 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. Change-Id: I1a08160c35d3e33bdfd1d2c789c288fc07d0f0d3 Signed-off-by: Vinayak Menon <vinmenon@codeaurora.org> Signed-off-by: Kevin F. Haggerty <haggertk@lineageos.org>
This commit is contained in:
parent
81a0a5e6c9
commit
effddb8b2e
2 changed files with 195 additions and 4 deletions
|
@ -46,8 +46,12 @@
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/cpuset.h>
|
#include <linux/cpuset.h>
|
||||||
#include <linux/show_mem_notifier.h>
|
#include <linux/show_mem_notifier.h>
|
||||||
|
#include <linux/vmpressure.h>
|
||||||
#include <linux/zcache.h>
|
#include <linux/zcache.h>
|
||||||
|
|
||||||
|
#define CREATE_TRACE_POINTS
|
||||||
|
#include <trace/events/almk.h>
|
||||||
|
|
||||||
#ifdef CONFIG_HIGHMEM
|
#ifdef CONFIG_HIGHMEM
|
||||||
#define _ZONE ZONE_HIGHMEM
|
#define _ZONE ZONE_HIGHMEM
|
||||||
#else
|
#else
|
||||||
|
@ -79,6 +83,96 @@ static unsigned long lowmem_deathpending_timeout;
|
||||||
pr_info(x); \
|
pr_info(x); \
|
||||||
} while (0)
|
} 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)
|
static int test_task_flag(struct task_struct *p, int flag)
|
||||||
{
|
{
|
||||||
struct task_struct *t;
|
struct task_struct *t;
|
||||||
|
@ -273,7 +367,8 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
|
||||||
int rem = 0;
|
int rem = 0;
|
||||||
int tasksize;
|
int tasksize;
|
||||||
int i;
|
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 minfree = 0;
|
||||||
int selected_tasksize = 0;
|
int selected_tasksize = 0;
|
||||||
int selected_oom_score_adj;
|
int selected_oom_score_adj;
|
||||||
|
@ -310,10 +405,13 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nr_to_scan > 0)
|
if (nr_to_scan > 0) {
|
||||||
lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n",
|
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,
|
nr_to_scan, sc->gfp_mask, other_free,
|
||||||
other_file, min_score_adj);
|
other_file, min_score_adj);
|
||||||
|
}
|
||||||
|
|
||||||
rem = global_page_state(NR_ACTIVE_ANON) +
|
rem = global_page_state(NR_ACTIVE_ANON) +
|
||||||
global_page_state(NR_ACTIVE_FILE) +
|
global_page_state(NR_ACTIVE_FILE) +
|
||||||
global_page_state(NR_INACTIVE_ANON) +
|
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)
|
if (nr_to_scan > 0)
|
||||||
mutex_unlock(&scan_mutex);
|
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;
|
return rem;
|
||||||
}
|
}
|
||||||
selected_oom_score_adj = min_score_adj;
|
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();
|
rcu_read_unlock();
|
||||||
/* give the system time to free up the memory */
|
/* give the system time to free up the memory */
|
||||||
msleep_interruptible(20);
|
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();
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",
|
lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",
|
||||||
nr_to_scan, sc->gfp_mask, rem);
|
nr_to_scan, sc->gfp_mask, rem);
|
||||||
|
@ -445,6 +551,7 @@ static struct shrinker lowmem_shrinker = {
|
||||||
static int __init lowmem_init(void)
|
static int __init lowmem_init(void)
|
||||||
{
|
{
|
||||||
register_shrinker(&lowmem_shrinker);
|
register_shrinker(&lowmem_shrinker);
|
||||||
|
vmpressure_notifier_register(&lmk_vmpr_nb);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
84
include/trace/events/almk.h
Normal file
84
include/trace/events/almk.h
Normal file
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue