mm, oom: introduce helper function to process threads during scan
authorDavid Rientjes <rientjes@google.com>
Tue, 31 Jul 2012 23:43:40 +0000 (16:43 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 1 Aug 2012 01:42:44 +0000 (18:42 -0700)
This patch introduces a helper function to process each thread during the
iteration over the tasklist.  A new return type, enum oom_scan_t, is
defined to determine the future behavior of the iteration:

 - OOM_SCAN_OK: continue scanning the thread and find its badness,

 - OOM_SCAN_CONTINUE: do not consider this thread for oom kill, it's
   ineligible,

 - OOM_SCAN_ABORT: abort the iteration and return, or

 - OOM_SCAN_SELECT: always select this thread with the highest badness
   possible.

There is no functional change with this patch.  This new helper function
will be used in the next patch in the memory controller.

Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Signed-off-by: David Rientjes <rientjes@google.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Sha Zhengju <handai.szj@taobao.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/oom_kill.c

index e6c10640e56bba295023267d5922b613e11500cd..f8eba9651c0c28ee88452e2fd05bcadcbf629ef0 100644 (file)
@@ -288,6 +288,59 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist,
 }
 #endif
 
+enum oom_scan_t {
+       OOM_SCAN_OK,            /* scan thread and find its badness */
+       OOM_SCAN_CONTINUE,      /* do not consider thread for oom kill */
+       OOM_SCAN_ABORT,         /* abort the iteration and return */
+       OOM_SCAN_SELECT,        /* always select this thread first */
+};
+
+static enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
+               struct mem_cgroup *memcg, unsigned long totalpages,
+               const nodemask_t *nodemask, bool force_kill)
+{
+       if (task->exit_state)
+               return OOM_SCAN_CONTINUE;
+       if (oom_unkillable_task(task, memcg, nodemask))
+               return OOM_SCAN_CONTINUE;
+
+       /*
+        * This task already has access to memory reserves and is being killed.
+        * Don't allow any other task to have access to the reserves.
+        */
+       if (test_tsk_thread_flag(task, TIF_MEMDIE)) {
+               if (unlikely(frozen(task)))
+                       __thaw_task(task);
+               if (!force_kill)
+                       return OOM_SCAN_ABORT;
+       }
+       if (!task->mm)
+               return OOM_SCAN_CONTINUE;
+
+       if (task->flags & PF_EXITING) {
+               /*
+                * If task is current and is in the process of releasing memory,
+                * allow the "kill" to set TIF_MEMDIE, which will allow it to
+                * access memory reserves.  Otherwise, it may stall forever.
+                *
+                * The iteration isn't broken here, however, in case other
+                * threads are found to have already been oom killed.
+                */
+               if (task == current)
+                       return OOM_SCAN_SELECT;
+               else if (!force_kill) {
+                       /*
+                        * If this task is not being ptraced on exit, then wait
+                        * for it to finish before killing some other task
+                        * unnecessarily.
+                        */
+                       if (!(task->group_leader->ptrace & PT_TRACE_EXIT))
+                               return OOM_SCAN_ABORT;
+               }
+       }
+       return OOM_SCAN_OK;
+}
+
 /*
  * Simple selection loop. We chose the process with the highest
  * number of 'points'. We expect the caller will lock the tasklist.
@@ -305,53 +358,19 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
        do_each_thread(g, p) {
                unsigned int points;
 
-               if (p->exit_state)
-                       continue;
-               if (oom_unkillable_task(p, memcg, nodemask))
-                       continue;
-
-               /*
-                * This task already has access to memory reserves and is
-                * being killed. Don't allow any other task access to the
-                * memory reserve.
-                *
-                * Note: this may have a chance of deadlock if it gets
-                * blocked waiting for another task which itself is waiting
-                * for memory. Is there a better alternative?
-                */
-               if (test_tsk_thread_flag(p, TIF_MEMDIE)) {
-                       if (unlikely(frozen(p)))
-                               __thaw_task(p);
-                       if (!force_kill)
-                               return ERR_PTR(-1UL);
-               }
-               if (!p->mm)
+               switch (oom_scan_process_thread(p, memcg, totalpages, nodemask,
+                                               force_kill)) {
+               case OOM_SCAN_SELECT:
+                       chosen = p;
+                       chosen_points = ULONG_MAX;
+                       /* fall through */
+               case OOM_SCAN_CONTINUE:
                        continue;
-
-               if (p->flags & PF_EXITING) {
-                       /*
-                        * 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;
-                               chosen_points = ULONG_MAX;
-                       } else if (!force_kill) {
-                               /*
-                                * If this task is not being ptraced on exit,
-                                * then wait for it to finish before killing
-                                * some other task unnecessarily.
-                                */
-                               if (!(p->group_leader->ptrace & PT_TRACE_EXIT))
-                                       return ERR_PTR(-1UL);
-                       }
-               }
-
+               case OOM_SCAN_ABORT:
+                       return ERR_PTR(-1UL);
+               case OOM_SCAN_OK:
+                       break;
+               };
                points = oom_badness(p, memcg, nodemask, totalpages);
                if (points > chosen_points) {
                        chosen = p;