oom: avoid deferring oom killer if exiting task is being traced
authorDavid Rientjes <rientjes@google.com>
Tue, 22 Mar 2011 23:30:12 +0000 (16:30 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 Mar 2011 00:43:58 +0000 (17:43 -0700)
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: David Rientjes <rientjes@google.com>
Reported-by: Oleg 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>
Cc: <stable@kernel.org> [2.6.38.x]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/oom_kill.c

index d7f345e47e73a80dccfe1c6fb820ef3b2a7f1213..33b58615072cc57dd51dcbaf339498b5e99d7d1e 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/memcontrol.h>
 #include <linux/mempolicy.h>
 #include <linux/security.h>
+#include <linux/ptrace.h>
 
 int sysctl_panic_on_oom;
 int sysctl_oom_kill_allocating_task;
@@ -316,22 +317,29 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
                if (test_tsk_thread_flag(p, TIF_MEMDIE))
                        return ERR_PTR(-1UL);
 
-               /*
-                * This is in the process of releasing memory so wait for it
-                * to finish before killing some other task by mistake.
-                *
-                * However, if p is the current task, we allow the 'kill' to
-                * go ahead if it is exiting: this will simply set TIF_MEMDIE,
-                * 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)
-                               return ERR_PTR(-1UL);
-
-                       chosen = p;
-                       *ppoints = 1000;
+                       /*
+                        * If p is the current task and is in the process of
+                        * 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.
+                        *
+                        * The loop isn't broken here, however, in case other
+                        * threads are found to have already been oom killed.
+                        */
+                       if (p == current) {
+                               chosen = p;
+                               *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);
@@ -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) {
                        unsigned int child_points;
 
+                       if (child->mm == p->mm)
+                               continue;
                        /*
                         * oom_badness() returns 0 if the thread is unkillable
                         */