Skip to content

Commit 0304926

Browse files
Michal Hockotorvalds
authored andcommitted
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: Michal 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: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent bc448e8 commit 0304926

File tree

2 files changed

+22
-17
lines changed

2 files changed

+22
-17
lines changed

include/linux/sched.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1849,6 +1849,9 @@ struct task_struct {
18491849
unsigned long task_state_change;
18501850
#endif
18511851
int pagefault_disabled;
1852+
#ifdef CONFIG_MMU
1853+
struct list_head oom_reaper_list;
1854+
#endif
18521855
/* CPU-specific state of this task */
18531856
struct thread_struct thread;
18541857
/*

mm/oom_kill.c

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,10 @@ bool oom_killer_disabled __read_mostly;
418418
* victim (if that is possible) to help the OOM killer to move on.
419419
*/
420420
static struct task_struct *oom_reaper_th;
421-
static struct task_struct *task_to_reap;
422421
static DECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait);
422+
static LIST_HEAD(oom_reaper_list);
423+
static DEFINE_SPINLOCK(oom_reaper_lock);
424+
423425

424426
static bool __oom_reap_task(struct task_struct *tsk)
425427
{
@@ -524,36 +526,36 @@ static void oom_reap_task(struct task_struct *tsk)
524526
static int oom_reaper(void *unused)
525527
{
526528
while (true) {
527-
struct task_struct *tsk;
529+
struct task_struct *tsk = NULL;
528530

529531
wait_event_freezable(oom_reaper_wait,
530-
(tsk = READ_ONCE(task_to_reap)));
531-
oom_reap_task(tsk);
532-
WRITE_ONCE(task_to_reap, NULL);
532+
(!list_empty(&oom_reaper_list)));
533+
spin_lock(&oom_reaper_lock);
534+
if (!list_empty(&oom_reaper_list)) {
535+
tsk = list_first_entry(&oom_reaper_list,
536+
struct task_struct, oom_reaper_list);
537+
list_del(&tsk->oom_reaper_list);
538+
}
539+
spin_unlock(&oom_reaper_lock);
540+
541+
if (tsk)
542+
oom_reap_task(tsk);
533543
}
534544

535545
return 0;
536546
}
537547

538548
static void wake_oom_reaper(struct task_struct *tsk)
539549
{
540-
struct task_struct *old_tsk;
541-
542550
if (!oom_reaper_th)
543551
return;
544552

545553
get_task_struct(tsk);
546554

547-
/*
548-
* Make sure that only a single mm is ever queued for the reaper
549-
* because multiple are not necessary and the operation might be
550-
* disruptive so better reduce it to the bare minimum.
551-
*/
552-
old_tsk = cmpxchg(&task_to_reap, NULL, tsk);
553-
if (!old_tsk)
554-
wake_up(&oom_reaper_wait);
555-
else
556-
put_task_struct(tsk);
555+
spin_lock(&oom_reaper_lock);
556+
list_add(&tsk->oom_reaper_list, &oom_reaper_list);
557+
spin_unlock(&oom_reaper_lock);
558+
wake_up(&oom_reaper_wait);
557559
}
558560

559561
static int __init oom_init(void)

0 commit comments

Comments
 (0)