Commit 462607ec authored by David Rientjes's avatar David Rientjes Committed by Linus Torvalds

mm, oom: introduce helper function to process threads during scan

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: default avatarKAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: default avatarKOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Reviewed-by: default avatarMichal Hocko <mhocko@suse.cz>
Signed-off-by: default avatarDavid Rientjes <rientjes@google.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: default avatarSha Zhengju <handai.szj@taobao.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 62ce1c70
...@@ -288,6 +288,59 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist, ...@@ -288,6 +288,59 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist,
} }
#endif #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 * Simple selection loop. We chose the process with the highest
* number of 'points'. We expect the caller will lock the tasklist. * 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, ...@@ -305,53 +358,19 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
do_each_thread(g, p) { do_each_thread(g, p) {
unsigned int points; unsigned int points;
if (p->exit_state) switch (oom_scan_process_thread(p, memcg, totalpages, nodemask,
continue; force_kill)) {
if (oom_unkillable_task(p, memcg, nodemask)) case OOM_SCAN_SELECT:
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)
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 = p;
chosen_points = ULONG_MAX; chosen_points = ULONG_MAX;
} else if (!force_kill) { /* fall through */
/* case OOM_SCAN_CONTINUE:
* If this task is not being ptraced on exit, continue;
* then wait for it to finish before killing case OOM_SCAN_ABORT:
* some other task unnecessarily.
*/
if (!(p->group_leader->ptrace & PT_TRACE_EXIT))
return ERR_PTR(-1UL); return ERR_PTR(-1UL);
} case OOM_SCAN_OK:
} break;
};
points = oom_badness(p, memcg, nodemask, totalpages); points = oom_badness(p, memcg, nodemask, totalpages);
if (points > chosen_points) { if (points > chosen_points) {
chosen = p; chosen = p;
......
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