Commit cb5f27be authored by David Rientjes's avatar David Rientjes Committed by Greg Kroah-Hartman

oom: avoid deferring oom killer if exiting task is being traced

commit edd45544 upstream.

The oom killer naturally defers killing anything if it finds an eligible
task that is already exiting and has yet to detach its ->mm.  This avoids
unnecessarily killing tasks when one is already in the exit path and may
free enough memory that the oom killer is no longer needed.  This is
detected by PF_EXITING since threads that have already detached its ->mm
are no longer considered at all.

The problem with always deferring when a thread is PF_EXITING, however, is
that it may never actually exit when being traced, specifically if another
task is tracing it with PTRACE_O_TRACEEXIT.  The oom killer does not want
to defer in this case since there is no guarantee that thread will ever
exit without intervention.

This patch will now only defer the oom killer when a thread is PF_EXITING
and no ptracer has stopped its progress in the exit path.  It also ensures
that a child is sacrificed for the chosen parent only if it has a
different ->mm as the comment implies: this ensures that the thread group
leader is always targeted appropriately.
Signed-off-by: default avatarDavid Rientjes <rientjes@google.com>
Reported-by: default avatarOleg Nesterov <oleg@redhat.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Andrey Vagin <avagin@openvz.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent d00451ca
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/memcontrol.h> #include <linux/memcontrol.h>
#include <linux/mempolicy.h> #include <linux/mempolicy.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/ptrace.h>
int sysctl_panic_on_oom; int sysctl_panic_on_oom;
int sysctl_oom_kill_allocating_task; int sysctl_oom_kill_allocating_task;
...@@ -316,22 +317,29 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, ...@@ -316,22 +317,29 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
if (test_tsk_thread_flag(p, TIF_MEMDIE)) if (test_tsk_thread_flag(p, TIF_MEMDIE))
return ERR_PTR(-1UL); return ERR_PTR(-1UL);
if (p->flags & PF_EXITING) {
/* /*
* This is in the process of releasing memory so wait for it * If p is the current task and is in the process of
* to finish before killing some other task by mistake. * releasing memory, we allow the "kill" to set
* TIF_MEMDIE, which will allow it to gain access to
* memory reserves. Otherwise, it may stall forever.
* *
* However, if p is the current task, we allow the 'kill' to * The loop isn't broken here, however, in case other
* go ahead if it is exiting: this will simply set TIF_MEMDIE, * threads are found to have already been oom killed.
* which will allow it to gain access to memory reserves in
* the process of exiting and releasing its resources.
* Otherwise we could get an easy OOM deadlock.
*/ */
if (p->flags & PF_EXITING) { if (p == current) {
if (p != current)
return ERR_PTR(-1UL);
chosen = p; chosen = p;
*ppoints = 1000; *ppoints = 1000;
} else {
/*
* If this task is not being ptraced on exit,
* then wait for it to finish before killing
* some other task unnecessarily.
*/
if (!(task_ptrace(p->group_leader) &
PT_TRACE_EXIT))
return ERR_PTR(-1UL);
}
} }
points = oom_badness(p, mem, nodemask, totalpages); points = oom_badness(p, mem, nodemask, totalpages);
...@@ -493,6 +501,8 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, ...@@ -493,6 +501,8 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
list_for_each_entry(child, &t->children, sibling) { list_for_each_entry(child, &t->children, sibling) {
unsigned int child_points; unsigned int child_points;
if (child->mm == p->mm)
continue;
/* /*
* oom_badness() returns 0 if the thread is unkillable * oom_badness() returns 0 if the thread is unkillable
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment