From 0c1d7417aa2b6f6136ecfaf15d76d76c8d64d83d Mon Sep 17 00:00:00 2001 From: Liam Mark Date: Tue, 3 Mar 2020 20:32:17 -0800 Subject: [PATCH] mm: oom_kill: Support further prioritization of OOM kills When the OOM killer selects a task to kill it considers all tasks with an oom_score_adj value > OOM_SCORE_ADJ_MIN. This doesn't work well when the OS has 'high priority' tasks which should only be considered for killing after all other 'lower priority' tasks have first been killed. Add support for two new configs, PRIORITIZE_OOM_TASKS and OOM_TASK_PRIORITY_ADJ_LIMIT, which allows the system to be configured so that the OOM killer will first try to kill tasks which have an oom_score_adj greater than a certain value. Change-Id: Ic613138ae147427fd69b447ca5b674619ca625b2 Signed-off-by: Liam Mark --- include/linux/oom.h | 3 +++ mm/Kconfig | 27 ++++++++++++++++++++ mm/oom_kill.c | 61 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/include/linux/oom.h b/include/linux/oom.h index c880273c6199..c1fb18faf973 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -49,6 +49,9 @@ struct oom_control { unsigned long totalpages; struct task_struct *chosen; unsigned long chosen_points; +#ifdef CONFIG_PRIORITIZE_OOM_TASKS + short min_kill_adj; +#endif /* Used to print the constraint info. */ enum oom_constraint constraint; diff --git a/mm/Kconfig b/mm/Kconfig index 2c5a3693e769..707d77d83ac1 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -805,4 +805,31 @@ config ARCH_HAS_PTE_SPECIAL config ARCH_HAS_HUGEPD bool +config PRIORITIZE_OOM_TASKS + bool "Attempt to further prioritize OOM killer kills" + depends on QGKI + help + When the OOM killer selects a task to kill it considers all tasks + with a oom_score_adj value > OOM_SCORE_ADJ_MIN. This doesn't work + well when the OS has 'high priority' tasks which should only be + considered for killing after all other 'lower priority' tasks + have first been killed. + + Enable this option to impose a limit on the minimum task + oom_score_adj which should first be considered for killing + before allowing the OOM killer to select tasks without + limitation. + + If unsure, say N. + +config OOM_TASK_PRIORITY_ADJ_LIMIT + int "Min oom_score_adj when priotizing OOM killer kills" + depends on PRIORITIZE_OOM_TASKS + range -1000 1000 + default 0 + help + With this parameter you can limit the OOM killer to first kill + tasks with an oom_score_adj value above a certain threshold + before considering tasks with a lower oom_score_adj value. + endmenu diff --git a/mm/oom_kill.c b/mm/oom_kill.c index e7092bb1f300..87541dbe8d8b 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -317,6 +317,10 @@ static int oom_evaluate_task(struct task_struct *task, void *arg) { struct oom_control *oc = arg; unsigned long points; +#ifdef CONFIG_PRIORITIZE_OOM_TASKS + struct task_struct *p; + short adj; +#endif if (oom_unkillable_task(task)) goto next; @@ -337,6 +341,18 @@ static int oom_evaluate_task(struct task_struct *task, void *arg) goto abort; } +#ifdef CONFIG_PRIORITIZE_OOM_TASKS + p = find_lock_task_mm(task); + if (!p) + goto next; + + adj = p->signal->oom_score_adj; + task_unlock(p); + + if (adj < oc->min_kill_adj) + goto next; +#endif + /* * If task is allocating a lot of memory and has been marked to be * killed first if it triggers an oom, then select it. @@ -994,7 +1010,11 @@ static void oom_kill_process(struct oom_control *oc, const char *message) } task_unlock(victim); - if (__ratelimit(&oom_rs)) + if (__ratelimit(&oom_rs) +#ifdef CONFIG_PRIORITIZE_OOM_TASKS + && oc->min_kill_adj < CONFIG_OOM_TASK_PRIORITY_ADJ_LIMIT +#endif + ) dump_header(oc, victim); /* @@ -1070,13 +1090,9 @@ bool out_of_memory(struct oom_control *oc) if (oom_killer_disabled) return false; - - if (try_online_one_block(numa_node_id())) { - /* Got some memory back */ - WARN(1, "OOM killer had to online a memory block\n"); - return true; - } - +#ifdef CONFIG_PRIORITIZE_OOM_TASKS + oc->min_kill_adj = OOM_SCORE_ADJ_MIN; +#endif if (!is_memcg_oom(oc)) { blocking_notifier_call_chain(&oom_notify_list, 0, &freed); if (freed > 0) @@ -1112,19 +1128,44 @@ bool out_of_memory(struct oom_control *oc) oc->constraint = constrained_alloc(oc); if (oc->constraint != CONSTRAINT_MEMORY_POLICY) oc->nodemask = NULL; - check_panic_on_oom(oc); if (!is_memcg_oom(oc) && sysctl_oom_kill_allocating_task && current->mm && !oom_unkillable_task(current) && oom_cpuset_eligible(current, oc) && current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) { + check_panic_on_oom(oc); get_task_struct(current); oc->chosen = current; oom_kill_process(oc, "Out of memory (oom_kill_allocating_task)"); return true; } - select_bad_process(oc); +#ifdef CONFIG_PRIORITIZE_OOM_TASKS + if (oc->min_kill_adj < CONFIG_OOM_TASK_PRIORITY_ADJ_LIMIT) { + short prev_min_kill_adj = oc->min_kill_adj; + + oc->min_kill_adj = CONFIG_OOM_TASK_PRIORITY_ADJ_LIMIT; + select_bad_process(oc); + if (!oc->chosen) { + pr_warn_ratelimited("Could not find task with adj >= %d\n", + CONFIG_OOM_TASK_PRIORITY_ADJ_LIMIT); + oc->min_kill_adj = prev_min_kill_adj; + oc->chosen_points = 0; + } + + } +#endif + + if (!oc->chosen) { + if (try_online_one_block(numa_node_id())) { + /* Got some memory back */ + WARN(1, "OOM killer had to online a memory block\n"); + return true; + } + check_panic_on_oom(oc); + select_bad_process(oc); + } + /* Found nothing?!?! */ if (!oc->chosen) { dump_header(oc, NULL);