lkml.org 
[lkml]   [2011]   [Dec]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: Android low memory killer vs. memory pressure notifications
> Personally I'd start thinking about the new [lightweight] notification
> stuff, i.e. something without mem_cg's downsides. Though, I'm Cc'ing
> Android folks so maybe they could enlighten us why in-kernel "lowmemory
> manager" might be a better idea. Plus Cc'ing folks that I think might
> be interested in this discussion.
>
> Thanks!
>
> p.s.
>
> I'm inlining the android memory killer code down below, just for the
> reference. It is quite small (and useful... though, currently only for
> Android case).
>
> - - - -
> From: Arve Hjønnevåg<arve@android.com>
> Subject: Android low memory killer driver
>
> The lowmemorykiller driver lets user-space specify a set of memory thresholds
> where processes with a range of oom_adj values will get killed. Specify the
> minimum oom_adj values in /sys/module/lowmemorykiller/parameters/adj and the
> number of free pages in /sys/module/lowmemorykiller/parameters/minfree. Both
> files take a comma separated list of numbers in ascending order.
>
> For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and
> "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill processes
> with a oom_adj value of 8 or higher when the free memory drops below 4096 pages
> and kill processes with a oom_adj value of 0 or higher when the free memory
> drops below 1024 pages.
>
> The driver considers memory used for caches to be free, but if a large
> percentage of the cached memory is locked this can be very inaccurate
> and processes may not get killed until the normal oom killer is triggered.
>
> ---
> mm/Kconfig | 7 ++
> mm/Makefile | 1 +
> mm/lowmemorykiller.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 183 insertions(+), 0 deletions(-)
> create mode 100644 mm/lowmemorykiller.c
>
> diff --git a/mm/Kconfig b/mm/Kconfig
> index 011b110..a2e7959 100644
> --- a/mm/Kconfig
> +++ b/mm/Kconfig
> @@ -259,6 +259,12 @@ config DEFAULT_MMAP_MIN_ADDR
> This value can be changed after boot using the
> /proc/sys/vm/mmap_min_addr tunable.
>
> +config LOW_MEMORY_KILLER
> + bool "Low Memory Killer"
> + help
> + The lowmemorykiller driver lets user-space specify a set of memory
> + thresholds where processes will get killed.
> +
> config ARCH_SUPPORTS_MEMORY_FAILURE
> bool
>
> diff --git a/mm/Makefile b/mm/Makefile
> index 50ec00e..10fb4ff 100644
> --- a/mm/Makefile
> +++ b/mm/Makefile
> @@ -47,6 +47,7 @@ obj-$(CONFIG_QUICKLIST) += quicklist.o
> obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o
> obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o
> obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o
> +obj-$(CONFIG_LOW_MEMORY_KILLER) += lowmemorykiller.o
> obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
> obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
> obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
> diff --git a/mm/lowmemorykiller.c b/mm/lowmemorykiller.c
> new file mode 100644
> index 0000000..4e51936
> --- /dev/null
> +++ b/mm/lowmemorykiller.c
> @@ -0,0 +1,175 @@
> +/*
> + * The lowmemorykiller driver lets user-space specify a set of memory thresholds
> + * where processes with a range of oom_adj values will get killed. Specify the
> + * minimum oom_adj values in /sys/module/lowmemorykiller/parameters/adj and the
> + * number of free pages in /sys/module/lowmemorykiller/parameters/minfree. Both
> + * files take a comma separated list of numbers in ascending order.
> + *
> + * For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and
> + * "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill processes
> + * with a oom_adj value of 8 or higher when the free memory drops below 4096 pages
> + * and kill processes with a oom_adj value of 0 or higher when the free memory
> + * drops below 1024 pages.
> + *
> + * The driver considers memory used for caches to be free, but if a large
> + * percentage of the cached memory is locked this can be very inaccurate
> + * and processes may not get killed until the normal oom killer is triggered.
> + *
> + * Copyright (C) 2007-2008 Google, Inc.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#include<linux/module.h>
> +#include<linux/kernel.h>
> +#include<linux/mm.h>
> +#include<linux/oom.h>
> +#include<linux/sched.h>
> +#include<linux/notifier.h>
> +
> +static uint32_t lowmem_debug_level = 2;
> +static int lowmem_adj[6] = {
> + 0,
> + 1,
> + 6,
> + 12,
> +};
> +static int lowmem_adj_size = 4;
> +static size_t lowmem_minfree[6] = {
> + 3 * 512, /* 6MB */
> + 2 * 1024, /* 8MB */
> + 4 * 1024, /* 16MB */
> + 16 * 1024, /* 64MB */
> +};
> +static int lowmem_minfree_size = 4;
> +
> +#define lowmem_print(level, x...) \
> + do { \
> + if (lowmem_debug_level>= (level)) \
> + printk(x); \
> + } while (0)
> +
> +static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
> +{
> + struct task_struct *p;
> + struct task_struct *selected = NULL;
> + int rem = 0;
> + int tasksize;
> + int i;
> + int min_adj = OOM_ADJUST_MAX + 1;
> + int selected_tasksize = 0;
> + int selected_oom_adj;
> + int array_size = ARRAY_SIZE(lowmem_adj);
> + int other_free = global_page_state(NR_FREE_PAGES);
> + int other_file = global_page_state(NR_FILE_PAGES) -
> + global_page_state(NR_SHMEM);
> +
> + 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++) {
> + if (other_free< lowmem_minfree[i]&&
> + other_file< lowmem_minfree[i]) {
> + min_adj = lowmem_adj[i];
> + break;
> + }
> + }
> + if (sc->nr_to_scan> 0)
> + lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n",
> + sc->nr_to_scan, sc->gfp_mask, other_free, other_file,
> + min_adj);
> + rem = global_page_state(NR_ACTIVE_ANON) +
> + global_page_state(NR_ACTIVE_FILE) +
> + global_page_state(NR_INACTIVE_ANON) +
> + global_page_state(NR_INACTIVE_FILE);

Seems incorrect. process killing only free anon pages, but not file cache.


> + if (sc->nr_to_scan<= 0 || min_adj == OOM_ADJUST_MAX + 1) {
> + lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n",
> + sc->nr_to_scan, sc->gfp_mask, rem);
> + return rem;
> + }
> + selected_oom_adj = min_adj;
> +
> + read_lock(&tasklist_lock);

Crazy inefficient. mere slab shrinker shouldn't take tasklist_lock.
Imagine if tasks are much plenty...

Moreover, if system have plenty file cache, any process shouldn't killed
at all! That's fundamental downside of this patch.


> + for_each_process(p) {
> + struct mm_struct *mm;
> + struct signal_struct *sig;
> + int oom_adj;
> +
> + task_lock(p);
> + mm = p->mm;
> + sig = p->signal;
> + if (!mm || !sig) {
> + task_unlock(p);
> + continue;
> + }
> + oom_adj = sig->oom_adj;
> + if (oom_adj< min_adj) {
> + task_unlock(p);
> + continue;
> + }
> + tasksize = get_mm_rss(mm);
> + task_unlock(p);
> + if (tasksize<= 0)
> + continue;
> + if (selected) {
> + if (oom_adj< selected_oom_adj)
> + continue;
> + if (oom_adj == selected_oom_adj&&
> + tasksize<= selected_tasksize)
> + continue;
> + }
> + selected = p;
> + selected_tasksize = tasksize;
> + selected_oom_adj = oom_adj;
> + lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
> + p->pid, p->comm, oom_adj, tasksize);
> + }
> + if (selected) {
> + lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
> + selected->pid, selected->comm,
> + selected_oom_adj, selected_tasksize);
> + force_sig(SIGKILL, selected);

Scary naive assumption. To send SIGKILL doesn't have a guarantee to kill
a process immediately if the task is stuck in kernel.


> + rem -= selected_tasksize;
> + }
> + lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",
> + sc->nr_to_scan, sc->gfp_mask, rem);
> + read_unlock(&tasklist_lock);
> + return rem;
> +}
> +
> +static struct shrinker lowmem_shrinker = {
> + .shrink = lowmem_shrink,
> + .seeks = DEFAULT_SEEKS * 16
> +};
> +
> +static int __init lowmem_init(void)
> +{
> + register_shrinker(&lowmem_shrinker);
> + return 0;
> +}
> +
> +static void __exit lowmem_exit(void)
> +{
> + unregister_shrinker(&lowmem_shrinker);
> +}
> +
> +module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
> +module_param_array_named(adj, lowmem_adj, int,&lowmem_adj_size,
> + S_IRUGO | S_IWUSR);
> +module_param_array_named(minfree, lowmem_minfree, uint,&lowmem_minfree_size,
> + S_IRUGO | S_IWUSR);
> +module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
> +
> +module_init(lowmem_init);
> +module_exit(lowmem_exit);
> +
> +MODULE_LICENSE("GPL");

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2011-12-19 18:33    [W:0.101 / U:0.988 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site