Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 99555943 authored by derfelot's avatar derfelot
Browse files

staging: Add Android low memory killer TNG from Sony kernel

Taken from Sony 47.2.A.10.107 stock kernel
parent a745d774
Loading
Loading
Loading
Loading
+23 −1
Original line number Diff line number Diff line
@@ -38,15 +38,37 @@ 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_TNG
	bool "Android Low Memory Killer TNG"
	select OOM_SCORE_NOTIFIER
	---help---
	  The NEXT generation (TNG) lowmemorykiller.
	  Registers processes to be killed when low memory conditions, this is useful
	  as there is no particular swap space on android.

	  The registered process will kills according to the priorities in android init
	  scripts (/init.rc), and it defines priority values with minimum free memory size
	  for each priority.

config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES
	bool "Android Low Memory Killer: detect oom_adj values"
	depends on ANDROID_LOW_MEMORY_KILLER
	depends on ANDROID_LOW_MEMORY_KILLER || ANDROID_LOW_MEMORY_KILLER_TNG
	default y
	---help---
	  Detect oom_adj values written to
	  /sys/module/lowmemorykiller/parameters/adj and convert them
	  to oom_score_adj values.

config ANDROID_LOW_MEMORY_KILLER_STATS
	bool "Android Low Memory Killer: collect statistics"
	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 depending on lmk implementation used
	  in TNG lowmemorykiller.

config SYNC
	bool "Synchronization framework"
	default n
+3 −0
Original line number Diff line number Diff line
@@ -6,6 +6,9 @@ obj-$(CONFIG_ASHMEM) += ashmem.o
obj-$(CONFIG_ANDROID_TIMED_OUTPUT)	+= timed_output.o
obj-$(CONFIG_ANDROID_TIMED_GPIO)	+= timed_gpio.o
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER)	+= lowmemorykiller.o
lowmemorykiller_tng := lowmemorykiller_tng.o lowmemorykiller_tasks.o lowmemorykiller_oom.o lowmemorykiller_vmpressure.o
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG)	+= lowmemorykiller_tng
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER_STATS)	+= lowmemorykiller_stats.o
obj-$(CONFIG_SYNC)			+= sync.o sync_debug.o
obj-$(CONFIG_SW_SYNC)			+= sw_sync.o
obj-$(CONFIG_ONESHOT_SYNC)		+= oneshot_sync.o
+61 −57
Original line number Diff line number Diff line
@@ -29,6 +29,11 @@
 * GNU General Public License for more details.
 *
 */
/*
 * NOTE: This file has been modified by Sony Mobile Communications Inc.
 * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc,
 * and licensed under the license of the file.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

@@ -42,8 +47,6 @@
#include <linux/rcupdate.h>
#include <linux/profile.h>
#include <linux/notifier.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/swap.h>
#include <linux/fs.h>
#include <linux/cpuset.h>
@@ -56,6 +59,7 @@

#define CREATE_TRACE_POINTS
#include <trace/events/almk.h>
#include <trace/events/lmk.h>

#ifdef CONFIG_HIGHMEM
#define _ZONE ZONE_HIGHMEM
@@ -66,15 +70,20 @@
#define CREATE_TRACE_POINTS
#include "trace/lowmemorykiller.h"

static uint32_t lowmem_debug_level = 1;
static short lowmem_adj[6] = {
#include "lowmemorykiller_stats.h"
#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG
#include "lowmemorykiller_tng.h"
#endif

uint32_t lowmem_debug_level = 1;
short lowmem_adj[6] = {
	0,
	1,
	6,
	12,
};
static int lowmem_adj_size = 4;
static int lowmem_minfree[6] = {
int lowmem_minfree[6] = {
	3 * 512,	/* 6MB */
	2 * 1024,	/* 8MB */
	4 * 1024,	/* 16MB */
@@ -85,6 +94,17 @@ static int lmk_fast_run = 1;

static unsigned long lowmem_deathpending_timeout;

int lowmem_min_param_size(void)
{
	int array_size = ARRAY_SIZE(lowmem_adj);

	if (lowmem_adj_size < array_size)
		array_size = lowmem_adj_size;
	if (lowmem_minfree_size < array_size)
		array_size = lowmem_minfree_size;
	return array_size;
}

#define lowmem_print(level, x...)			\
	do {						\
		if (lowmem_debug_level >= (level))	\
@@ -228,6 +248,7 @@ static void lmk_event_init(void)
static unsigned long lowmem_count(struct shrinker *s,
				  struct shrink_control *sc)
{
	lmk_inc_stats(LMK_COUNT);
	return global_page_state(NR_ACTIVE_ANON) +
		global_page_state(NR_ACTIVE_FILE) +
		global_page_state(NR_INACTIVE_ANON) +
@@ -288,12 +309,17 @@ static int lmk_vmpressure_notifier(struct notifier_block *nb,
	unsigned long pressure = action;
	int array_size = ARRAY_SIZE(lowmem_adj);

	if (!enable_adaptive_lmk)
	if (!enable_adaptive_lmk) {
#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG
		balance_cache();
#endif
		return 0;
	}

	if (pressure >= 95) {
		other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() -
			global_page_state(NR_SHMEM) -
			global_page_state(NR_UNEVICTABLE) -
			total_swapcache_pages();
		other_free = global_page_state(NR_FREE_PAGES);

@@ -307,6 +333,7 @@ static int lmk_vmpressure_notifier(struct notifier_block *nb,

		other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() -
			global_page_state(NR_SHMEM) -
			global_page_state(NR_UNEVICTABLE) -
			total_swapcache_pages();

		other_free = global_page_state(NR_FREE_PAGES);
@@ -331,7 +358,9 @@ static int lmk_vmpressure_notifier(struct notifier_block *nb,
		trace_almk_vmpressure(pressure, other_free, other_file);
		atomic_set(&shift_adj, 0);
	}

#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG
	balance_cache();
#endif
	return 0;
}

@@ -355,24 +384,6 @@ static int test_task_flag(struct task_struct *p, int flag)
	return 0;
}

static int test_task_state(struct task_struct *p, int state)
{
	struct task_struct *t;

	for_each_thread(p, t) {
		task_lock(t);
		if (t->state & state) {
			task_unlock(t);
			return 1;
		}
		task_unlock(t);
	}

	return 0;
}

static DEFINE_MUTEX(scan_mutex);

int can_use_cma_pages(gfp_t gfp_mask)
{
	int can_use = 0;
@@ -555,25 +566,16 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
	int selected_tasksize = 0;
	short selected_oom_score_adj;
	int array_size = ARRAY_SIZE(lowmem_adj);
	int other_free;
	int other_file;

	if (!mutex_trylock(&scan_mutex))
		return 0;

	other_free = global_page_state(NR_FREE_PAGES);

	if (global_page_state(NR_SHMEM) + total_swapcache_pages() <
		global_page_state(NR_FILE_PAGES) + zcache_pages())
		other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() -
	int other_free = global_page_state(NR_FREE_PAGES);
	int other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() -
		global_page_state(NR_SHMEM) -
		global_page_state(NR_UNEVICTABLE) -
		total_swapcache_pages();
	else
		other_file = 0;
	other_file = (other_file < 0) ? 0 : other_file;

	tune_lmk_param(&other_free, &other_file, sc);

	lmk_inc_stats(LMK_SCAN);
	if (lowmem_adj_size < array_size)
		array_size = lowmem_adj_size;
	if (lowmem_minfree_size < array_size)
@@ -596,7 +598,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
		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);
		trace_lmk_remain_scan(0, sc->nr_to_scan, sc->gfp_mask);
		return 0;
	}

@@ -614,10 +616,16 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
		if (test_task_flag(tsk, TIF_MM_RELEASED))
			continue;

		/* Ignore task if coredump in progress */
		if (tsk->mm && tsk->mm->core_state)
			continue;

		if (time_before_eq(jiffies, lowmem_deathpending_timeout)) {
			if (test_task_flag(tsk, TIF_MEMDIE)) {
				rcu_read_unlock();
				mutex_unlock(&scan_mutex);
				trace_lmk_remain_scan(0, sc->nr_to_scan,
						      sc->gfp_mask);
				lmk_inc_stats(LMK_TIMEOUT);
				return 0;
			}
		}
@@ -650,17 +658,6 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
	}
	if (selected) {
		long cache_size, cache_limit, free;

		if (test_task_flag(selected, TIF_MEMDIE) &&
		    (test_task_state(selected, TASK_UNINTERRUPTIBLE))) {
			lowmem_print(2, "'%s' (%d) is already killed\n",
				     selected->comm,
				     selected->pid);
			rcu_read_unlock();
			mutex_unlock(&scan_mutex);
			return 0;
		}

		task_lock(selected);
		send_sig(SIGKILL, selected, 0);
		/*
@@ -709,21 +706,24 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)

		lowmem_deathpending_timeout = jiffies + HZ;
		rem += selected_tasksize;
		trace_lmk_sigkill(selected->pid, selected->comm,
				 selected_oom_score_adj, selected_tasksize,
				 sc->gfp_mask);
		rcu_read_unlock();
		get_task_struct(selected);
		/* give the system time to free up the memory */
		msleep_interruptible(20);
		trace_almk_shrink(selected_tasksize, ret,
				  other_free, other_file,
				  selected_oom_score_adj);
		lmk_inc_stats(LMK_KILL);
	} else {
		trace_almk_shrink(1, ret, other_free, other_file, 0);
		lmk_inc_stats(LMK_WASTE);
		rcu_read_unlock();
	}

	lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n",
		     sc->nr_to_scan, sc->gfp_mask, rem);
	mutex_unlock(&scan_mutex);
	trace_lmk_remain_scan(rem, sc->nr_to_scan, sc->gfp_mask);

	if (selected) {
		handle_lmk_event(selected, selected_tasksize, min_score_adj);
@@ -741,9 +741,13 @@ static struct shrinker lowmem_shrinker = {

static int __init lowmem_init(void)
{
#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_TNG
	lowmem_init_tng(&lowmem_shrinker);
#endif
	register_shrinker(&lowmem_shrinker);
	vmpressure_notifier_register(&lmk_vmpr_nb);
	lmk_event_init();
	init_procfs_lmk();
	return 0;
}
device_initcall(lowmem_init);
+76 −0
Original line number Diff line number Diff line
/*
 * 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_H
#define __LOWMEMORYKILLER_H

#include <linux/version.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)

int __init lowmemorykiller_register_oom_notifier(void);
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;
};

int kill_needed(int level, gfp_t mask,
		struct calculated_params *cp);
void print_obituary(struct task_struct *doomed,
		    struct calculated_params *cp,
		    gfp_t gfp_mask);

void balance_cache(void);
ssize_t get_task_rss(struct task_struct *tsk);

/* kernel does not have a task_trylock and
 * to make it more obvious what the code do
 * we create a help function for it.
 * see sched.h for task_lock and task_unlock
 */
static inline int task_trylock_lmk(struct task_struct *p)
{
	return spin_trylock(&p->alloc_lock);
}

/* maybe not exact version, we need something betwwen 3.18 and 4.4.
 * using LINUX_VERSION_CODE like this will give a warning.
 * it it not OK for mainline but for multiple kernel version patches
 * I think it is OK.
 */
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 0, 0)
#define LMK_TAG_TASK_DIE(x) set_tsk_thread_flag(x, TIF_MEMDIE)
#elif LINUX_VERSION_CODE <= KERNEL_VERSION(4, 5, 0)
#define LMK_TAG_TASK_DIE(x) mark_oom_victim(x)
#else
#define LMK_TAG_TASK_DIE(x)			\
	do {					\
		if (x->mm)			\
			task_set_lmk_waiting(x);\
			if (!test_bit(MMF_OOM_SKIP, &x->mm->flags) && \
			    oom_reaper) { \
				mark_lmk_victim(x); \
				wake_oom_reaper(x);\
			} \
	} while (0)
#endif

#endif
+97 −0
Original line number Diff line number Diff line
/*
 *  lowmemorykiller_oom
 *
 *  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.
 */
/*
 * 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.
 */

/* add fake print format with original module name */
#define pr_fmt(fmt) "lowmemorykiller: " fmt

#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/oom.h>

#include <trace/events/lmk.h>

#include "lowmemorykiller.h"
#include "lowmemorykiller_stats.h"
#include "lowmemorykiller_tasks.h"

/**
 * lowmemorykiller_oom_notify - OOM notifier
 * @self:	notifier block struct
 * @notused:	not used
 * @parm:	returned - number of pages freed
 *
 * Return value:
 *	NOTIFY_OK
 **/

static int lowmemorykiller_oom_notify(struct notifier_block *self,
				      unsigned long notused, void *param)
{
	struct lmk_rb_watch *lrw;
	unsigned long *nfreed = param;

	lowmem_print(2, "oom notify event\n");
	*nfreed = 0;
	lmk_inc_stats(LMK_OOM_COUNT);
	spin_lock_bh(&lmk_task_lock);
	lrw = __lmk_task_first();
	if (lrw) {
		struct task_struct *selected = lrw->tsk;
		struct lmk_death_pending_entry *ldpt;

		if (!task_trylock_lmk(selected)) {
			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_bh(&lmk_task_lock);
		*nfreed = get_task_rss(lrw->tsk);
		send_sig(SIGKILL, selected, 0);
		LMK_TAG_TASK_DIE(selected);
		trace_lmk_sigkill(selected->pid, selected->comm,
				  -1, *nfreed,
				  0);

		task_unlock(selected);
		lmk_inc_stats(LMK_OOM_KILL_COUNT);
		goto out;
	}
unlock_out:
	spin_unlock_bh(&lmk_task_lock);
out:
	return NOTIFY_OK;
}

static struct notifier_block lowmemorykiller_oom_nb = {
	.notifier_call = lowmemorykiller_oom_notify
};

int __init lowmemorykiller_register_oom_notifier(void)
{
	register_oom_notifier(&lowmemorykiller_oom_nb);
	return 0;
}
Loading