Commit 03049269 authored by Michal Hocko's avatar Michal Hocko Committed by Linus Torvalds

mm, oom_reaper: implement OOM victims queuing

wake_oom_reaper has allowed only 1 oom victim to be queued.  The main
reason for that was the simplicity as other solutions would require some
way of queuing.  The current approach is racy and that was deemed
sufficient as the oom_reaper is considered a best effort approach to
help with oom handling when the OOM victim cannot terminate in a
reasonable time.  The race could lead to missing an oom victim which can
get stuck

out_of_memory
  wake_oom_reaper
    cmpxchg // OK
    			oom_reaper
			  oom_reap_task
			    __oom_reap_task
oom_victim terminates
			      atomic_inc_not_zero // fail
out_of_memory
  wake_oom_reaper
    cmpxchg // fails
			  task_to_reap = NULL

This race requires 2 OOM invocations in a short time period which is not
very likely but certainly not impossible.  E.g.  the original victim
might have not released a lot of memory for some reason.

The situation would improve considerably if wake_oom_reaper used a more
robust queuing.  This is what this patch implements.  This means adding
oom_reaper_list list_head into task_struct (eat a hole before embeded
thread_struct for that purpose) and a oom_reaper_lock spinlock for
queuing synchronization.  wake_oom_reaper will then add the task on the
queue and oom_reaper will dequeue it.
Signed-off-by: default avatarMichal Hocko <mhocko@suse.com>
Cc: Vladimir Davydov <vdavydov@virtuozzo.com>
Cc: Andrea Argangeli <andrea@kernel.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent bc448e89
...@@ -1849,6 +1849,9 @@ struct task_struct { ...@@ -1849,6 +1849,9 @@ struct task_struct {
unsigned long task_state_change; unsigned long task_state_change;
#endif #endif
int pagefault_disabled; int pagefault_disabled;
#ifdef CONFIG_MMU
struct list_head oom_reaper_list;
#endif
/* CPU-specific state of this task */ /* CPU-specific state of this task */
struct thread_struct thread; struct thread_struct thread;
/* /*
......
...@@ -418,8 +418,10 @@ bool oom_killer_disabled __read_mostly; ...@@ -418,8 +418,10 @@ bool oom_killer_disabled __read_mostly;
* victim (if that is possible) to help the OOM killer to move on. * victim (if that is possible) to help the OOM killer to move on.
*/ */
static struct task_struct *oom_reaper_th; static struct task_struct *oom_reaper_th;
static struct task_struct *task_to_reap;
static DECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait); static DECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait);
static LIST_HEAD(oom_reaper_list);
static DEFINE_SPINLOCK(oom_reaper_lock);
static bool __oom_reap_task(struct task_struct *tsk) static bool __oom_reap_task(struct task_struct *tsk)
{ {
...@@ -524,12 +526,20 @@ static void oom_reap_task(struct task_struct *tsk) ...@@ -524,12 +526,20 @@ static void oom_reap_task(struct task_struct *tsk)
static int oom_reaper(void *unused) static int oom_reaper(void *unused)
{ {
while (true) { while (true) {
struct task_struct *tsk; struct task_struct *tsk = NULL;
wait_event_freezable(oom_reaper_wait, wait_event_freezable(oom_reaper_wait,
(tsk = READ_ONCE(task_to_reap))); (!list_empty(&oom_reaper_list)));
spin_lock(&oom_reaper_lock);
if (!list_empty(&oom_reaper_list)) {
tsk = list_first_entry(&oom_reaper_list,
struct task_struct, oom_reaper_list);
list_del(&tsk->oom_reaper_list);
}
spin_unlock(&oom_reaper_lock);
if (tsk)
oom_reap_task(tsk); oom_reap_task(tsk);
WRITE_ONCE(task_to_reap, NULL);
} }
return 0; return 0;
...@@ -537,23 +547,15 @@ static int oom_reaper(void *unused) ...@@ -537,23 +547,15 @@ static int oom_reaper(void *unused)
static void wake_oom_reaper(struct task_struct *tsk) static void wake_oom_reaper(struct task_struct *tsk)
{ {
struct task_struct *old_tsk;
if (!oom_reaper_th) if (!oom_reaper_th)
return; return;
get_task_struct(tsk); get_task_struct(tsk);
/* spin_lock(&oom_reaper_lock);
* Make sure that only a single mm is ever queued for the reaper list_add(&tsk->oom_reaper_list, &oom_reaper_list);
* because multiple are not necessary and the operation might be spin_unlock(&oom_reaper_lock);
* disruptive so better reduce it to the bare minimum.
*/
old_tsk = cmpxchg(&task_to_reap, NULL, tsk);
if (!old_tsk)
wake_up(&oom_reaper_wait); wake_up(&oom_reaper_wait);
else
put_task_struct(tsk);
} }
static int __init oom_init(void) static int __init oom_init(void)
......
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