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);