linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] oom, oom_reaper: do not enqueue same task twice
       [not found]                       ` <2b0c7d6c-c58a-da7d-6f0a-4900694ec2d3@gmail.com>
@ 2019-01-26 13:10                         ` Tetsuo Handa
  2019-01-26 13:10                           ` Tetsuo Handa
  2019-01-27  8:37                           ` Michal Hocko
  0 siblings, 2 replies; 13+ messages in thread
From: Tetsuo Handa @ 2019-01-26 13:10 UTC (permalink / raw)
  To: Arkadiusz Miśkiewicz, Andrew Morton
  Cc: Tejun Heo, cgroups, Aleksa Sarai, Jay Kamat, Roman Gushchin,
	Michal Hocko, Johannes Weiner, linux-kernel, Linus Torvalds,
	linux-mm

On 2019/01/26 20:29, Arkadiusz Miśkiewicz wrote:
> On 26/01/2019 12:09, Tetsuo Handa wrote:
>> Arkadiusz, will you try this patch?
> 
> 
> Works. Several tries and always getting 0 pids.current after ~1s.
> 

Thank you for testing.

I updated this patch to use tsk->signal->oom_mm (a snapshot of
tsk->mm saved by mark_oom_victim(tsk)) rather than raw tsk->mm
so that we don't need to worry about possibility of changing
tsk->mm across multiple wake_oom_reaper(tsk) calls.



>From 9c9e935fc038342c48461aabca666f1b544e32b1 Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Sat, 26 Jan 2019 21:57:25 +0900
Subject: [PATCH v2] oom, oom_reaper: do not enqueue same task twice

Arkadiusz reported that enabling memcg's group oom killing causes
strange memcg statistics where there is no task in a memcg despite
the number of tasks in that memcg is not 0. It turned out that there
is a bug in wake_oom_reaper() which allows enqueuing same task twice
which makes impossible to decrease the number of tasks in that memcg
due to a refcount leak.

This bug existed since the OOM reaper became invokable from
task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
but memcg's group oom killing made it easier to trigger this bug by
calling wake_oom_reaper() on the same task from one out_of_memory()
request.

Fix this bug using an approach used by commit 855b018325737f76
("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
As a side effect of this patch, this patch also avoids enqueuing
multiple threads sharing memory via task_will_free_mem(current) path.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reported-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Tested-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Fixes: af8e15cc85a25315 ("oom, oom_reaper: do not enqueue task if it is on the oom_reaper_list head")
---
 mm/oom_kill.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index f0e8cd9..057bfee 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -505,14 +505,6 @@ bool __oom_reap_task_mm(struct mm_struct *mm)
 	struct vm_area_struct *vma;
 	bool ret = true;
 
-	/*
-	 * Tell all users of get_user/copy_from_user etc... that the content
-	 * is no longer stable. No barriers really needed because unmapping
-	 * should imply barriers already and the reader would hit a page fault
-	 * if it stumbled over a reaped memory.
-	 */
-	set_bit(MMF_UNSTABLE, &mm->flags);
-
 	for (vma = mm->mmap ; vma; vma = vma->vm_next) {
 		if (!can_madv_dontneed_vma(vma))
 			continue;
@@ -647,8 +639,13 @@ static int oom_reaper(void *unused)
 
 static void wake_oom_reaper(struct task_struct *tsk)
 {
-	/* tsk is already queued? */
-	if (tsk == oom_reaper_list || tsk->oom_reaper_list)
+	/*
+	 * Tell all users of get_user/copy_from_user etc... that the content
+	 * is no longer stable. No barriers really needed because unmapping
+	 * should imply barriers already and the reader would hit a page fault
+	 * if it stumbled over a reaped memory.
+	 */
+	if (test_and_set_bit(MMF_UNSTABLE, &tsk->signal->oom_mm->flags))
 		return;
 
 	get_task_struct(tsk);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2] oom, oom_reaper: do not enqueue same task twice
  2019-01-26 13:10                         ` [PATCH v2] oom, oom_reaper: do not enqueue same task twice Tetsuo Handa
@ 2019-01-26 13:10                           ` Tetsuo Handa
  2019-01-27  8:37                           ` Michal Hocko
  1 sibling, 0 replies; 13+ messages in thread
From: Tetsuo Handa @ 2019-01-26 13:10 UTC (permalink / raw)
  To: Arkadiusz Miśkiewicz, Andrew Morton
  Cc: Tejun Heo, cgroups, Aleksa Sarai, Jay Kamat, Roman Gushchin,
	Michal Hocko, Johannes Weiner, linux-kernel, Linus Torvalds,
	linux-mm

On 2019/01/26 20:29, Arkadiusz Miśkiewicz wrote:
> On 26/01/2019 12:09, Tetsuo Handa wrote:
>> Arkadiusz, will you try this patch?
> 
> 
> Works. Several tries and always getting 0 pids.current after ~1s.
> 

Thank you for testing.

I updated this patch to use tsk->signal->oom_mm (a snapshot of
tsk->mm saved by mark_oom_victim(tsk)) rather than raw tsk->mm
so that we don't need to worry about possibility of changing
tsk->mm across multiple wake_oom_reaper(tsk) calls.



From 9c9e935fc038342c48461aabca666f1b544e32b1 Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Sat, 26 Jan 2019 21:57:25 +0900
Subject: [PATCH v2] oom, oom_reaper: do not enqueue same task twice

Arkadiusz reported that enabling memcg's group oom killing causes
strange memcg statistics where there is no task in a memcg despite
the number of tasks in that memcg is not 0. It turned out that there
is a bug in wake_oom_reaper() which allows enqueuing same task twice
which makes impossible to decrease the number of tasks in that memcg
due to a refcount leak.

This bug existed since the OOM reaper became invokable from
task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
but memcg's group oom killing made it easier to trigger this bug by
calling wake_oom_reaper() on the same task from one out_of_memory()
request.

Fix this bug using an approach used by commit 855b018325737f76
("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
As a side effect of this patch, this patch also avoids enqueuing
multiple threads sharing memory via task_will_free_mem(current) path.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reported-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Tested-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Fixes: af8e15cc85a25315 ("oom, oom_reaper: do not enqueue task if it is on the oom_reaper_list head")
---
 mm/oom_kill.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index f0e8cd9..057bfee 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -505,14 +505,6 @@ bool __oom_reap_task_mm(struct mm_struct *mm)
 	struct vm_area_struct *vma;
 	bool ret = true;
 
-	/*
-	 * Tell all users of get_user/copy_from_user etc... that the content
-	 * is no longer stable. No barriers really needed because unmapping
-	 * should imply barriers already and the reader would hit a page fault
-	 * if it stumbled over a reaped memory.
-	 */
-	set_bit(MMF_UNSTABLE, &mm->flags);
-
 	for (vma = mm->mmap ; vma; vma = vma->vm_next) {
 		if (!can_madv_dontneed_vma(vma))
 			continue;
@@ -647,8 +639,13 @@ static int oom_reaper(void *unused)
 
 static void wake_oom_reaper(struct task_struct *tsk)
 {
-	/* tsk is already queued? */
-	if (tsk == oom_reaper_list || tsk->oom_reaper_list)
+	/*
+	 * Tell all users of get_user/copy_from_user etc... that the content
+	 * is no longer stable. No barriers really needed because unmapping
+	 * should imply barriers already and the reader would hit a page fault
+	 * if it stumbled over a reaped memory.
+	 */
+	if (test_and_set_bit(MMF_UNSTABLE, &tsk->signal->oom_mm->flags))
 		return;
 
 	get_task_struct(tsk);
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v2] oom, oom_reaper: do not enqueue same task twice
  2019-01-26 13:10                         ` [PATCH v2] oom, oom_reaper: do not enqueue same task twice Tetsuo Handa
  2019-01-26 13:10                           ` Tetsuo Handa
@ 2019-01-27  8:37                           ` Michal Hocko
  2019-01-27 10:56                             ` Tetsuo Handa
  1 sibling, 1 reply; 13+ messages in thread
From: Michal Hocko @ 2019-01-27  8:37 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Arkadiusz Miśkiewicz, Andrew Morton, Tejun Heo, cgroups,
	Aleksa Sarai, Jay Kamat, Roman Gushchin, Johannes Weiner,
	linux-kernel, Linus Torvalds, linux-mm

On Sat 26-01-19 22:10:52, Tetsuo Handa wrote:
[...]
> >From 9c9e935fc038342c48461aabca666f1b544e32b1 Mon Sep 17 00:00:00 2001
> From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Date: Sat, 26 Jan 2019 21:57:25 +0900
> Subject: [PATCH v2] oom, oom_reaper: do not enqueue same task twice
> 
> Arkadiusz reported that enabling memcg's group oom killing causes
> strange memcg statistics where there is no task in a memcg despite
> the number of tasks in that memcg is not 0. It turned out that there
> is a bug in wake_oom_reaper() which allows enqueuing same task twice
> which makes impossible to decrease the number of tasks in that memcg
> due to a refcount leak.
> 
> This bug existed since the OOM reaper became invokable from
> task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
> but memcg's group oom killing made it easier to trigger this bug by
> calling wake_oom_reaper() on the same task from one out_of_memory()
> request.
> 
> Fix this bug using an approach used by commit 855b018325737f76
> ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
> As a side effect of this patch, this patch also avoids enqueuing
> multiple threads sharing memory via task_will_free_mem(current) path.

Thanks for the analysis and the patch. This should work, I believe but
I am not really thrilled to overload the meaning of the MMF_UNSTABLE.
The flag is meant to signal accessing address space is not stable and it
is not aimed to synchronize oom reaper with the oom path.

Can we make use mark_oom_victim directly? I didn't get to think that
through right now so I might be missing something but this should
prevent repeating queueing as well.

diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index f0e8cd9edb1a..dac4f2197e53 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -690,7 +690,7 @@ static void mark_oom_victim(struct task_struct *tsk)
 	WARN_ON(oom_killer_disabled);
 	/* OOM killer might race with memcg OOM */
 	if (test_and_set_tsk_thread_flag(tsk, TIF_MEMDIE))
-		return;
+		return false;
 
 	/* oom_mm is bound to the signal struct life time. */
 	if (!cmpxchg(&tsk->signal->oom_mm, NULL, mm)) {
@@ -707,6 +707,8 @@ static void mark_oom_victim(struct task_struct *tsk)
 	__thaw_task(tsk);
 	atomic_inc(&oom_victims);
 	trace_mark_victim(tsk->pid);
+
+	return true;
 }
 
 /**
@@ -873,7 +875,7 @@ static void __oom_kill_process(struct task_struct *victim)
 	 * reserves from the user space under its control.
 	 */
 	do_send_sig_info(SIGKILL, SEND_SIG_PRIV, victim, PIDTYPE_TGID);
-	mark_oom_victim(victim);
+	can_oom_reap = mark_oom_victim(victim);
 	pr_err("Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB\n",
 		task_pid_nr(victim), victim->comm, K(victim->mm->total_vm),
 		K(get_mm_counter(victim->mm, MM_ANONPAGES)),
@@ -954,8 +956,8 @@ static void oom_kill_process(struct oom_control *oc, const char *message)
 	 */
 	task_lock(p);
 	if (task_will_free_mem(p)) {
-		mark_oom_victim(p);
-		wake_oom_reaper(p);
+		if (mark_oom_victim(p)
+			wake_oom_reaper(p);
 		task_unlock(p);
 		put_task_struct(p);
 		return;
@@ -1084,8 +1086,8 @@ bool out_of_memory(struct oom_control *oc)
 	 * quickly exit and free its memory.
 	 */
 	if (task_will_free_mem(current)) {
-		mark_oom_victim(current);
-		wake_oom_reaper(current);
+		if (mark_oom_victim(current))
+			wake_oom_reaper(current);
 		return true;
 	}
 
-- 
Michal Hocko
SUSE Labs

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v2] oom, oom_reaper: do not enqueue same task twice
  2019-01-27  8:37                           ` Michal Hocko
@ 2019-01-27 10:56                             ` Tetsuo Handa
  2019-01-27 11:40                               ` Michal Hocko
  0 siblings, 1 reply; 13+ messages in thread
From: Tetsuo Handa @ 2019-01-27 10:56 UTC (permalink / raw)
  To: Michal Hocko
  Cc: Arkadiusz Miśkiewicz, Andrew Morton, Tejun Heo, cgroups,
	Aleksa Sarai, Jay Kamat, Roman Gushchin, Johannes Weiner,
	linux-kernel, Linus Torvalds, linux-mm

On 2019/01/27 17:37, Michal Hocko wrote:
> Thanks for the analysis and the patch. This should work, I believe but
> I am not really thrilled to overload the meaning of the MMF_UNSTABLE.
> The flag is meant to signal accessing address space is not stable and it
> is not aimed to synchronize oom reaper with the oom path.
> 
> Can we make use mark_oom_victim directly? I didn't get to think that
> through right now so I might be missing something but this should
> prevent repeating queueing as well.

Yes, TIF_MEMDIE would work. But you are planning to remove TIF_MEMDIE. Also,
TIF_MEMDIE can't avoid enqueuing many threads sharing mm_struct to the OOM
reaper. There is no need to enqueue many threads sharing mm_struct because
the OOM reaper acts on mm_struct rather than task_struct. Thus, enqueuing
based on per mm_struct flag sounds better, but MMF_OOM_VICTIM cannot be
set from wake_oom_reaper(victim) because victim's mm might be already inside
exit_mmap() when wake_oom_reaper(victim) is called after task_unlock(victim).

We could reintroduce MMF_OOM_KILLED in commit 855b018325737f76
("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task")
if you don't like overloading the meaning of the MMF_UNSTABLE. But since
MMF_UNSTABLE is available in Linux 4.9+ kernels (which covers all LTS stable
versions with the OOM reaper support), we can temporarily use MMF_UNSTABLE
for ease of backporting.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2] oom, oom_reaper: do not enqueue same task twice
  2019-01-27 10:56                             ` Tetsuo Handa
@ 2019-01-27 11:40                               ` Michal Hocko
  2019-01-27 14:57                                 ` [PATCH v3] " Tetsuo Handa
  0 siblings, 1 reply; 13+ messages in thread
From: Michal Hocko @ 2019-01-27 11:40 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Arkadiusz Miśkiewicz, Andrew Morton, Tejun Heo, cgroups,
	Aleksa Sarai, Jay Kamat, Roman Gushchin, Johannes Weiner,
	linux-kernel, Linus Torvalds, linux-mm

On Sun 27-01-19 19:56:06, Tetsuo Handa wrote:
> On 2019/01/27 17:37, Michal Hocko wrote:
> > Thanks for the analysis and the patch. This should work, I believe but
> > I am not really thrilled to overload the meaning of the MMF_UNSTABLE.
> > The flag is meant to signal accessing address space is not stable and it
> > is not aimed to synchronize oom reaper with the oom path.
> > 
> > Can we make use mark_oom_victim directly? I didn't get to think that
> > through right now so I might be missing something but this should
> > prevent repeating queueing as well.
> 
> Yes, TIF_MEMDIE would work. But you are planning to remove TIF_MEMDIE. Also,
> TIF_MEMDIE can't avoid enqueuing many threads sharing mm_struct to the OOM
> reaper. There is no need to enqueue many threads sharing mm_struct because
> the OOM reaper acts on mm_struct rather than task_struct. Thus, enqueuing
> based on per mm_struct flag sounds better, but MMF_OOM_VICTIM cannot be
> set from wake_oom_reaper(victim) because victim's mm might be already inside
> exit_mmap() when wake_oom_reaper(victim) is called after task_unlock(victim).
>
> We could reintroduce MMF_OOM_KILLED in commit 855b018325737f76
> ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task")
> if you don't like overloading the meaning of the MMF_UNSTABLE. But since
> MMF_UNSTABLE is available in Linux 4.9+ kernels (which covers all LTS stable
> versions with the OOM reaper support), we can temporarily use MMF_UNSTABLE
> for ease of backporting.

I agree that a per-mm state is more optimal but I would rather fix the
issue in a clear way first and only then think about an optimization on
top. Queueing based on mark_oom_victim (whatever that uses to guarantee
the victim is marked atomically and only once) makes sense from the
conceptual point of view and it makes a lot of sense to start from
there. MMF_UNSTABLE has a completely different purpose. So unless you
see a correctness issue with that then I would rather go that way.
-- 
Michal Hocko
SUSE Labs

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3] oom, oom_reaper: do not enqueue same task twice
  2019-01-27 11:40                               ` Michal Hocko
@ 2019-01-27 14:57                                 ` Tetsuo Handa
  2019-01-27 14:57                                   ` Tetsuo Handa
                                                     ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Tetsuo Handa @ 2019-01-27 14:57 UTC (permalink / raw)
  To: Michal Hocko
  Cc: Arkadiusz Miśkiewicz, Andrew Morton, Tejun Heo, cgroups,
	Aleksa Sarai, Jay Kamat, Roman Gushchin, Johannes Weiner,
	linux-kernel, Linus Torvalds, linux-mm

On 2019/01/27 20:40, Michal Hocko wrote:
> On Sun 27-01-19 19:56:06, Tetsuo Handa wrote:
>> On 2019/01/27 17:37, Michal Hocko wrote:
>>> Thanks for the analysis and the patch. This should work, I believe but
>>> I am not really thrilled to overload the meaning of the MMF_UNSTABLE.
>>> The flag is meant to signal accessing address space is not stable and it
>>> is not aimed to synchronize oom reaper with the oom path.
>>>
>>> Can we make use mark_oom_victim directly? I didn't get to think that
>>> through right now so I might be missing something but this should
>>> prevent repeating queueing as well.
>>
>> Yes, TIF_MEMDIE would work. But you are planning to remove TIF_MEMDIE. Also,
>> TIF_MEMDIE can't avoid enqueuing many threads sharing mm_struct to the OOM
>> reaper. There is no need to enqueue many threads sharing mm_struct because
>> the OOM reaper acts on mm_struct rather than task_struct. Thus, enqueuing
>> based on per mm_struct flag sounds better, but MMF_OOM_VICTIM cannot be
>> set from wake_oom_reaper(victim) because victim's mm might be already inside
>> exit_mmap() when wake_oom_reaper(victim) is called after task_unlock(victim).
>>
>> We could reintroduce MMF_OOM_KILLED in commit 855b018325737f76
>> ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task")
>> if you don't like overloading the meaning of the MMF_UNSTABLE. But since
>> MMF_UNSTABLE is available in Linux 4.9+ kernels (which covers all LTS stable
>> versions with the OOM reaper support), we can temporarily use MMF_UNSTABLE
>> for ease of backporting.
> 
> I agree that a per-mm state is more optimal but I would rather fix the
> issue in a clear way first and only then think about an optimization on
> top. Queueing based on mark_oom_victim (whatever that uses to guarantee
> the victim is marked atomically and only once) makes sense from the
> conceptual point of view and it makes a lot of sense to start from
> there. MMF_UNSTABLE has a completely different purpose. So unless you
> see a correctness issue with that then I would rather go that way.
> 

Then, adding a per mm_struct flag is better. I don't see the difference
between reusing MMF_UNSTABLE as a flag for whether wake_oom_reaper() for
that victim's memory was already called (what you think as an overload)
and reusing TIF_MEMDIE as a flag for whether wake_oom_reaper() for that
victim thread can be called (what I think as an overload). We want to
remove TIF_MEMDIE, and we can actually remove TIF_MEMDIE if you stop
whack-a-mole "can you observe it in real workload/program?" game.
I don't see a correctness issue with TIF_MEMDIE but I don't want to go
TIF_MEMDIE way.



>From 9c9e935fc038342c48461aabca666f1b544e32b1 Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Sun, 27 Jan 2019 23:51:37 +0900
Subject: [PATCH v3] oom, oom_reaper: do not enqueue same task twice

Arkadiusz reported that enabling memcg's group oom killing causes
strange memcg statistics where there is no task in a memcg despite
the number of tasks in that memcg is not 0. It turned out that there
is a bug in wake_oom_reaper() which allows enqueuing same task twice
which makes impossible to decrease the number of tasks in that memcg
due to a refcount leak.

This bug existed since the OOM reaper became invokable from
task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
but memcg's group oom killing made it easier to trigger this bug by
calling wake_oom_reaper() on the same task from one out_of_memory()
request.

Fix this bug using an approach used by commit 855b018325737f76
("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
As a side effect of this patch, this patch also avoids enqueuing
multiple threads sharing memory via task_will_free_mem(current) path.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reported-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Tested-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Fixes: af8e15cc85a25315 ("oom, oom_reaper: do not enqueue task if it is on the oom_reaper_list head")
---
 include/linux/sched/coredump.h | 1 +
 mm/oom_kill.c                  | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h
index ec912d0..ecdc654 100644
--- a/include/linux/sched/coredump.h
+++ b/include/linux/sched/coredump.h
@@ -71,6 +71,7 @@ static inline int get_dumpable(struct mm_struct *mm)
 #define MMF_HUGE_ZERO_PAGE	23      /* mm has ever used the global huge zero page */
 #define MMF_DISABLE_THP		24	/* disable THP for all VMAs */
 #define MMF_OOM_VICTIM		25	/* mm is the oom victim */
+#define MMF_OOM_REAP_QUEUED	26	/* mm was queued for oom_reaper */
 #define MMF_DISABLE_THP_MASK	(1 << MMF_DISABLE_THP)
 
 #define MMF_INIT_MASK		(MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index f0e8cd9..059e617 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -647,8 +647,8 @@ static int oom_reaper(void *unused)
 
 static void wake_oom_reaper(struct task_struct *tsk)
 {
-	/* tsk is already queued? */
-	if (tsk == oom_reaper_list || tsk->oom_reaper_list)
+	/* mm is already queued? */
+	if (test_and_set_bit(MMF_OOM_REAP_QUEUED, &tsk->signal->oom_mm->flags))
 		return;
 
 	get_task_struct(tsk);
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3] oom, oom_reaper: do not enqueue same task twice
  2019-01-27 14:57                                 ` [PATCH v3] " Tetsuo Handa
@ 2019-01-27 14:57                                   ` Tetsuo Handa
  2019-01-27 16:58                                   ` Michal Hocko
                                                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 13+ messages in thread
From: Tetsuo Handa @ 2019-01-27 14:57 UTC (permalink / raw)
  To: Michal Hocko
  Cc: Arkadiusz Miśkiewicz, Andrew Morton, Tejun Heo, cgroups,
	Aleksa Sarai, Jay Kamat, Roman Gushchin, Johannes Weiner,
	linux-kernel, Linus Torvalds, linux-mm

On 2019/01/27 20:40, Michal Hocko wrote:
> On Sun 27-01-19 19:56:06, Tetsuo Handa wrote:
>> On 2019/01/27 17:37, Michal Hocko wrote:
>>> Thanks for the analysis and the patch. This should work, I believe but
>>> I am not really thrilled to overload the meaning of the MMF_UNSTABLE.
>>> The flag is meant to signal accessing address space is not stable and it
>>> is not aimed to synchronize oom reaper with the oom path.
>>>
>>> Can we make use mark_oom_victim directly? I didn't get to think that
>>> through right now so I might be missing something but this should
>>> prevent repeating queueing as well.
>>
>> Yes, TIF_MEMDIE would work. But you are planning to remove TIF_MEMDIE. Also,
>> TIF_MEMDIE can't avoid enqueuing many threads sharing mm_struct to the OOM
>> reaper. There is no need to enqueue many threads sharing mm_struct because
>> the OOM reaper acts on mm_struct rather than task_struct. Thus, enqueuing
>> based on per mm_struct flag sounds better, but MMF_OOM_VICTIM cannot be
>> set from wake_oom_reaper(victim) because victim's mm might be already inside
>> exit_mmap() when wake_oom_reaper(victim) is called after task_unlock(victim).
>>
>> We could reintroduce MMF_OOM_KILLED in commit 855b018325737f76
>> ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task")
>> if you don't like overloading the meaning of the MMF_UNSTABLE. But since
>> MMF_UNSTABLE is available in Linux 4.9+ kernels (which covers all LTS stable
>> versions with the OOM reaper support), we can temporarily use MMF_UNSTABLE
>> for ease of backporting.
> 
> I agree that a per-mm state is more optimal but I would rather fix the
> issue in a clear way first and only then think about an optimization on
> top. Queueing based on mark_oom_victim (whatever that uses to guarantee
> the victim is marked atomically and only once) makes sense from the
> conceptual point of view and it makes a lot of sense to start from
> there. MMF_UNSTABLE has a completely different purpose. So unless you
> see a correctness issue with that then I would rather go that way.
> 

Then, adding a per mm_struct flag is better. I don't see the difference
between reusing MMF_UNSTABLE as a flag for whether wake_oom_reaper() for
that victim's memory was already called (what you think as an overload)
and reusing TIF_MEMDIE as a flag for whether wake_oom_reaper() for that
victim thread can be called (what I think as an overload). We want to
remove TIF_MEMDIE, and we can actually remove TIF_MEMDIE if you stop
whack-a-mole "can you observe it in real workload/program?" game.
I don't see a correctness issue with TIF_MEMDIE but I don't want to go
TIF_MEMDIE way.



From 9c9e935fc038342c48461aabca666f1b544e32b1 Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Sun, 27 Jan 2019 23:51:37 +0900
Subject: [PATCH v3] oom, oom_reaper: do not enqueue same task twice

Arkadiusz reported that enabling memcg's group oom killing causes
strange memcg statistics where there is no task in a memcg despite
the number of tasks in that memcg is not 0. It turned out that there
is a bug in wake_oom_reaper() which allows enqueuing same task twice
which makes impossible to decrease the number of tasks in that memcg
due to a refcount leak.

This bug existed since the OOM reaper became invokable from
task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
but memcg's group oom killing made it easier to trigger this bug by
calling wake_oom_reaper() on the same task from one out_of_memory()
request.

Fix this bug using an approach used by commit 855b018325737f76
("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
As a side effect of this patch, this patch also avoids enqueuing
multiple threads sharing memory via task_will_free_mem(current) path.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reported-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Tested-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Fixes: af8e15cc85a25315 ("oom, oom_reaper: do not enqueue task if it is on the oom_reaper_list head")
---
 include/linux/sched/coredump.h | 1 +
 mm/oom_kill.c                  | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h
index ec912d0..ecdc654 100644
--- a/include/linux/sched/coredump.h
+++ b/include/linux/sched/coredump.h
@@ -71,6 +71,7 @@ static inline int get_dumpable(struct mm_struct *mm)
 #define MMF_HUGE_ZERO_PAGE	23      /* mm has ever used the global huge zero page */
 #define MMF_DISABLE_THP		24	/* disable THP for all VMAs */
 #define MMF_OOM_VICTIM		25	/* mm is the oom victim */
+#define MMF_OOM_REAP_QUEUED	26	/* mm was queued for oom_reaper */
 #define MMF_DISABLE_THP_MASK	(1 << MMF_DISABLE_THP)
 
 #define MMF_INIT_MASK		(MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index f0e8cd9..059e617 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -647,8 +647,8 @@ static int oom_reaper(void *unused)
 
 static void wake_oom_reaper(struct task_struct *tsk)
 {
-	/* tsk is already queued? */
-	if (tsk == oom_reaper_list || tsk->oom_reaper_list)
+	/* mm is already queued? */
+	if (test_and_set_bit(MMF_OOM_REAP_QUEUED, &tsk->signal->oom_mm->flags))
 		return;
 
 	get_task_struct(tsk);
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] oom, oom_reaper: do not enqueue same task twice
  2019-01-27 14:57                                 ` [PATCH v3] " Tetsuo Handa
  2019-01-27 14:57                                   ` Tetsuo Handa
@ 2019-01-27 16:58                                   ` Michal Hocko
  2019-01-27 23:00                                   ` Roman Gushchin
                                                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 13+ messages in thread
From: Michal Hocko @ 2019-01-27 16:58 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Arkadiusz Miśkiewicz, Andrew Morton, Tejun Heo, cgroups,
	Aleksa Sarai, Jay Kamat, Roman Gushchin, Johannes Weiner,
	linux-kernel, Linus Torvalds, linux-mm

On Sun 27-01-19 23:57:38, Tetsuo Handa wrote:
[...]
> >From 9c9e935fc038342c48461aabca666f1b544e32b1 Mon Sep 17 00:00:00 2001
> From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Date: Sun, 27 Jan 2019 23:51:37 +0900
> Subject: [PATCH v3] oom, oom_reaper: do not enqueue same task twice
> 
> Arkadiusz reported that enabling memcg's group oom killing causes
> strange memcg statistics where there is no task in a memcg despite
> the number of tasks in that memcg is not 0. It turned out that there
> is a bug in wake_oom_reaper() which allows enqueuing same task twice
> which makes impossible to decrease the number of tasks in that memcg
> due to a refcount leak.
> 
> This bug existed since the OOM reaper became invokable from
> task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
> but memcg's group oom killing made it easier to trigger this bug by
> calling wake_oom_reaper() on the same task from one out_of_memory()
> request.
> 
> Fix this bug using an approach used by commit 855b018325737f76
> ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
> As a side effect of this patch, this patch also avoids enqueuing
> multiple threads sharing memory via task_will_free_mem(current) path.
> 
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Reported-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
> Tested-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
> Fixes: af8e15cc85a25315 ("oom, oom_reaper: do not enqueue task if it is on the oom_reaper_list head")

Acked-by: Michal Hocko <mhocko@suse.com>

> ---
>  include/linux/sched/coredump.h | 1 +
>  mm/oom_kill.c                  | 4 ++--
>  2 files changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h
> index ec912d0..ecdc654 100644
> --- a/include/linux/sched/coredump.h
> +++ b/include/linux/sched/coredump.h
> @@ -71,6 +71,7 @@ static inline int get_dumpable(struct mm_struct *mm)
>  #define MMF_HUGE_ZERO_PAGE	23      /* mm has ever used the global huge zero page */
>  #define MMF_DISABLE_THP		24	/* disable THP for all VMAs */
>  #define MMF_OOM_VICTIM		25	/* mm is the oom victim */
> +#define MMF_OOM_REAP_QUEUED	26	/* mm was queued for oom_reaper */
>  #define MMF_DISABLE_THP_MASK	(1 << MMF_DISABLE_THP)
>  
>  #define MMF_INIT_MASK		(MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\
> diff --git a/mm/oom_kill.c b/mm/oom_kill.c
> index f0e8cd9..059e617 100644
> --- a/mm/oom_kill.c
> +++ b/mm/oom_kill.c
> @@ -647,8 +647,8 @@ static int oom_reaper(void *unused)
>  
>  static void wake_oom_reaper(struct task_struct *tsk)
>  {
> -	/* tsk is already queued? */
> -	if (tsk == oom_reaper_list || tsk->oom_reaper_list)
> +	/* mm is already queued? */
> +	if (test_and_set_bit(MMF_OOM_REAP_QUEUED, &tsk->signal->oom_mm->flags))
>  		return;
>  
>  	get_task_struct(tsk);
> -- 
> 1.8.3.1

-- 
Michal Hocko
SUSE Labs

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] oom, oom_reaper: do not enqueue same task twice
  2019-01-27 14:57                                 ` [PATCH v3] " Tetsuo Handa
  2019-01-27 14:57                                   ` Tetsuo Handa
  2019-01-27 16:58                                   ` Michal Hocko
@ 2019-01-27 23:00                                   ` Roman Gushchin
  2019-01-28 18:15                                   ` Andrew Morton
  2019-01-28 21:53                                   ` Johannes Weiner
  4 siblings, 0 replies; 13+ messages in thread
From: Roman Gushchin @ 2019-01-27 23:00 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Michal Hocko, Arkadiusz Miśkiewicz, Andrew Morton,
	Tejun Heo, cgroups, Aleksa Sarai, Jay Kamat, Johannes Weiner,
	linux-kernel, Linus Torvalds, linux-mm

On Sun, Jan 27, 2019 at 11:57:38PM +0900, Tetsuo Handa wrote:
> On 2019/01/27 20:40, Michal Hocko wrote:
> > On Sun 27-01-19 19:56:06, Tetsuo Handa wrote:
> >> On 2019/01/27 17:37, Michal Hocko wrote:
> >>> Thanks for the analysis and the patch. This should work, I believe but
> >>> I am not really thrilled to overload the meaning of the MMF_UNSTABLE.
> >>> The flag is meant to signal accessing address space is not stable and it
> >>> is not aimed to synchronize oom reaper with the oom path.
> >>>
> >>> Can we make use mark_oom_victim directly? I didn't get to think that
> >>> through right now so I might be missing something but this should
> >>> prevent repeating queueing as well.
> >>
> >> Yes, TIF_MEMDIE would work. But you are planning to remove TIF_MEMDIE. Also,
> >> TIF_MEMDIE can't avoid enqueuing many threads sharing mm_struct to the OOM
> >> reaper. There is no need to enqueue many threads sharing mm_struct because
> >> the OOM reaper acts on mm_struct rather than task_struct. Thus, enqueuing
> >> based on per mm_struct flag sounds better, but MMF_OOM_VICTIM cannot be
> >> set from wake_oom_reaper(victim) because victim's mm might be already inside
> >> exit_mmap() when wake_oom_reaper(victim) is called after task_unlock(victim).
> >>
> >> We could reintroduce MMF_OOM_KILLED in commit 855b018325737f76
> >> ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task")
> >> if you don't like overloading the meaning of the MMF_UNSTABLE. But since
> >> MMF_UNSTABLE is available in Linux 4.9+ kernels (which covers all LTS stable
> >> versions with the OOM reaper support), we can temporarily use MMF_UNSTABLE
> >> for ease of backporting.
> > 
> > I agree that a per-mm state is more optimal but I would rather fix the
> > issue in a clear way first and only then think about an optimization on
> > top. Queueing based on mark_oom_victim (whatever that uses to guarantee
> > the victim is marked atomically and only once) makes sense from the
> > conceptual point of view and it makes a lot of sense to start from
> > there. MMF_UNSTABLE has a completely different purpose. So unless you
> > see a correctness issue with that then I would rather go that way.
> > 
> 
> Then, adding a per mm_struct flag is better. I don't see the difference
> between reusing MMF_UNSTABLE as a flag for whether wake_oom_reaper() for
> that victim's memory was already called (what you think as an overload)
> and reusing TIF_MEMDIE as a flag for whether wake_oom_reaper() for that
> victim thread can be called (what I think as an overload). We want to
> remove TIF_MEMDIE, and we can actually remove TIF_MEMDIE if you stop
> whack-a-mole "can you observe it in real workload/program?" game.
> I don't see a correctness issue with TIF_MEMDIE but I don't want to go
> TIF_MEMDIE way.
> 
> 
> 
> From 9c9e935fc038342c48461aabca666f1b544e32b1 Mon Sep 17 00:00:00 2001
> From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Date: Sun, 27 Jan 2019 23:51:37 +0900
> Subject: [PATCH v3] oom, oom_reaper: do not enqueue same task twice
> 
> Arkadiusz reported that enabling memcg's group oom killing causes
> strange memcg statistics where there is no task in a memcg despite
> the number of tasks in that memcg is not 0. It turned out that there
> is a bug in wake_oom_reaper() which allows enqueuing same task twice
> which makes impossible to decrease the number of tasks in that memcg
> due to a refcount leak.
> 
> This bug existed since the OOM reaper became invokable from
> task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
> but memcg's group oom killing made it easier to trigger this bug by
> calling wake_oom_reaper() on the same task from one out_of_memory()
> request.
> 
> Fix this bug using an approach used by commit 855b018325737f76
> ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
> As a side effect of this patch, this patch also avoids enqueuing
> multiple threads sharing memory via task_will_free_mem(current) path.
> 
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Reported-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
> Tested-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
> Fixes: af8e15cc85a25315 ("oom, oom_reaper: do not enqueue task if it is on the oom_reaper_list head")

Thank you, Tetsuo!

Acked-by: Roman Gushchin <guro@fb.com>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] oom, oom_reaper: do not enqueue same task twice
  2019-01-27 14:57                                 ` [PATCH v3] " Tetsuo Handa
                                                     ` (2 preceding siblings ...)
  2019-01-27 23:00                                   ` Roman Gushchin
@ 2019-01-28 18:15                                   ` Andrew Morton
  2019-01-28 18:42                                     ` Michal Hocko
  2019-01-28 21:53                                   ` Johannes Weiner
  4 siblings, 1 reply; 13+ messages in thread
From: Andrew Morton @ 2019-01-28 18:15 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Michal Hocko, Arkadiusz Miśkiewicz, Tejun Heo, cgroups,
	Aleksa Sarai, Jay Kamat, Roman Gushchin, Johannes Weiner,
	linux-kernel, Linus Torvalds, linux-mm

On Sun, 27 Jan 2019 23:57:38 +0900 Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> wrote:

> Arkadiusz reported that enabling memcg's group oom killing causes
> strange memcg statistics where there is no task in a memcg despite
> the number of tasks in that memcg is not 0. It turned out that there
> is a bug in wake_oom_reaper() which allows enqueuing same task twice
> which makes impossible to decrease the number of tasks in that memcg
> due to a refcount leak.
> 
> This bug existed since the OOM reaper became invokable from
> task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
> but memcg's group oom killing made it easier to trigger this bug by
> calling wake_oom_reaper() on the same task from one out_of_memory()
> request.
> 
> Fix this bug using an approach used by commit 855b018325737f76
> ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
> As a side effect of this patch, this patch also avoids enqueuing
> multiple threads sharing memory via task_will_free_mem(current) path.
> 

Do we think this is serious enough to warrant a -stable backport?


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] oom, oom_reaper: do not enqueue same task twice
  2019-01-28 18:15                                   ` Andrew Morton
@ 2019-01-28 18:42                                     ` Michal Hocko
  0 siblings, 0 replies; 13+ messages in thread
From: Michal Hocko @ 2019-01-28 18:42 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Tetsuo Handa, Arkadiusz Miśkiewicz, Tejun Heo, cgroups,
	Aleksa Sarai, Jay Kamat, Roman Gushchin, Johannes Weiner,
	linux-kernel, Linus Torvalds, linux-mm

On Mon 28-01-19 10:15:13, Andrew Morton wrote:
> On Sun, 27 Jan 2019 23:57:38 +0900 Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> wrote:
> 
> > Arkadiusz reported that enabling memcg's group oom killing causes
> > strange memcg statistics where there is no task in a memcg despite
> > the number of tasks in that memcg is not 0. It turned out that there
> > is a bug in wake_oom_reaper() which allows enqueuing same task twice
> > which makes impossible to decrease the number of tasks in that memcg
> > due to a refcount leak.
> > 
> > This bug existed since the OOM reaper became invokable from
> > task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
> > but memcg's group oom killing made it easier to trigger this bug by
> > calling wake_oom_reaper() on the same task from one out_of_memory()
> > request.
> > 
> > Fix this bug using an approach used by commit 855b018325737f76
> > ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
> > As a side effect of this patch, this patch also avoids enqueuing
> > multiple threads sharing memory via task_will_free_mem(current) path.
> > 
> 
> Do we think this is serious enough to warrant a -stable backport?

Yes, I would go with stable backport.

-- 
Michal Hocko
SUSE Labs


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] oom, oom_reaper: do not enqueue same task twice
  2019-01-27 14:57                                 ` [PATCH v3] " Tetsuo Handa
                                                     ` (3 preceding siblings ...)
  2019-01-28 18:15                                   ` Andrew Morton
@ 2019-01-28 21:53                                   ` Johannes Weiner
  2019-01-29 10:34                                     ` Tetsuo Handa
  4 siblings, 1 reply; 13+ messages in thread
From: Johannes Weiner @ 2019-01-28 21:53 UTC (permalink / raw)
  To: Tetsuo Handa
  Cc: Michal Hocko, Arkadiusz Miśkiewicz, Andrew Morton,
	Tejun Heo, cgroups, Aleksa Sarai, Jay Kamat, Roman Gushchin,
	linux-kernel, Linus Torvalds, linux-mm

Hi Tetsuo,

On Sun, Jan 27, 2019 at 11:57:38PM +0900, Tetsuo Handa wrote:
> From 9c9e935fc038342c48461aabca666f1b544e32b1 Mon Sep 17 00:00:00 2001
> From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Date: Sun, 27 Jan 2019 23:51:37 +0900
> Subject: [PATCH v3] oom, oom_reaper: do not enqueue same task twice
> 
> Arkadiusz reported that enabling memcg's group oom killing causes
> strange memcg statistics where there is no task in a memcg despite
> the number of tasks in that memcg is not 0. It turned out that there
> is a bug in wake_oom_reaper() which allows enqueuing same task twice
> which makes impossible to decrease the number of tasks in that memcg
> due to a refcount leak.
> 
> This bug existed since the OOM reaper became invokable from
> task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
> but memcg's group oom killing made it easier to trigger this bug by
> calling wake_oom_reaper() on the same task from one out_of_memory()
> request.

This changelog seems a little terse compared to how tricky this is.

Can you please include an explanation here *how* this bug is possible?
I.e. the race condition that causes the function te be entered twice
and the existing re-entrance check in there to fail.

> Fix this bug using an approach used by commit 855b018325737f76
> ("oom, oom_reaper: disable oom_reaper for oom_kill_allocating_task").
> As a side effect of this patch, this patch also avoids enqueuing
> multiple threads sharing memory via task_will_free_mem(current) path.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3] oom, oom_reaper: do not enqueue same task twice
  2019-01-28 21:53                                   ` Johannes Weiner
@ 2019-01-29 10:34                                     ` Tetsuo Handa
  0 siblings, 0 replies; 13+ messages in thread
From: Tetsuo Handa @ 2019-01-29 10:34 UTC (permalink / raw)
  To: Johannes Weiner
  Cc: Michal Hocko, Arkadiusz Miśkiewicz, Andrew Morton,
	Tejun Heo, cgroups, Aleksa Sarai, Jay Kamat, Roman Gushchin,
	linux-kernel, Linus Torvalds, linux-mm

Johannes Weiner wrote:
> On Sun, Jan 27, 2019 at 11:57:38PM +0900, Tetsuo Handa wrote:
> > This bug existed since the OOM reaper became invokable from
> > task_will_free_mem(current) path in out_of_memory() in Linux 4.7,
> > but memcg's group oom killing made it easier to trigger this bug by
> > calling wake_oom_reaper() on the same task from one out_of_memory()
> > request.
> 
> This changelog seems a little terse compared to how tricky this is.
> 
> Can you please include an explanation here *how* this bug is possible?
> I.e. the race condition that causes the function te be entered twice
> and the existing re-entrance check in there to fail.

OK. Here is an updated patch. Only changelog part has changed.
I hope this will provide enough information to stable kernel maintainers.
----------
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Subject: oom, oom_reaper: do not enqueue same task twice

Arkadiusz reported that enabling memcg's group oom killing causes strange
memcg statistics where there is no task in a memcg despite the number of
tasks in that memcg is not 0.  It turned out that there is a bug in
wake_oom_reaper() which allows enqueuing same task twice which makes
impossible to decrease the number of tasks in that memcg due to a refcount
leak.

This bug existed since the OOM reaper became invokable from
task_will_free_mem(current) path in out_of_memory() in Linux 4.7,

  T1@P1     |T2@P1     |T3@P1     |OOM reaper
  ----------+----------+----------+------------
                                   # Processing an OOM victim in a different memcg domain.
                        try_charge()
                          mem_cgroup_out_of_memory()
                            mutex_lock(&oom_lock)
             try_charge()
               mem_cgroup_out_of_memory()
                 mutex_lock(&oom_lock)
  try_charge()
    mem_cgroup_out_of_memory()
      mutex_lock(&oom_lock)
                            out_of_memory()
                              oom_kill_process(P1)
                                do_send_sig_info(SIGKILL, @P1)
                                mark_oom_victim(T1@P1)
                                wake_oom_reaper(T1@P1) # T1@P1 is enqueued.
                            mutex_unlock(&oom_lock)
                 out_of_memory()
                   mark_oom_victim(T2@P1)
                   wake_oom_reaper(T2@P1) # T2@P1 is enqueued.
                 mutex_unlock(&oom_lock)
      out_of_memory()
        mark_oom_victim(T1@P1)
        wake_oom_reaper(T1@P1) # T1@P1 is enqueued again due to oom_reaper_list == T2@P1 && T1@P1->oom_reaper_list == NULL.
      mutex_unlock(&oom_lock)
                                   # Completed processing an OOM victim in a different memcg domain.
                                   spin_lock(&oom_reaper_lock)
                                   # T1P1 is dequeued.
                                   spin_unlock(&oom_reaper_lock)

but memcg's group oom killing made it easier to trigger this bug by
calling wake_oom_reaper() on the same task from one out_of_memory()
request.

Fix this bug using an approach used by commit 855b018325737f76 ("oom,
oom_reaper: disable oom_reaper for oom_kill_allocating_task").  As a side
effect of this patch, this patch also avoids enqueuing multiple threads
sharing memory via task_will_free_mem(current) path.

Fixes: af8e15cc85a25315 ("oom, oom_reaper: do not enqueue task if it is on the oom_reaper_list head")
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reported-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Tested-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
Acked-by: Michal Hocko <mhocko@suse.com>
Acked-by: Roman Gushchin <guro@fb.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Aleksa Sarai <asarai@suse.de>
Cc: Jay Kamat <jgkamat@fb.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
---
 include/linux/sched/coredump.h | 1 +
 mm/oom_kill.c                  | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h
index ec912d0..ecdc654 100644
--- a/include/linux/sched/coredump.h
+++ b/include/linux/sched/coredump.h
@@ -71,6 +71,7 @@ static inline int get_dumpable(struct mm_struct *mm)
 #define MMF_HUGE_ZERO_PAGE	23      /* mm has ever used the global huge zero page */
 #define MMF_DISABLE_THP		24	/* disable THP for all VMAs */
 #define MMF_OOM_VICTIM		25	/* mm is the oom victim */
+#define MMF_OOM_REAP_QUEUED	26	/* mm was queued for oom_reaper */
 #define MMF_DISABLE_THP_MASK	(1 << MMF_DISABLE_THP)
 
 #define MMF_INIT_MASK		(MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index f0e8cd9..059e617 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -647,8 +647,8 @@ static int oom_reaper(void *unused)
 
 static void wake_oom_reaper(struct task_struct *tsk)
 {
-	/* tsk is already queued? */
-	if (tsk == oom_reaper_list || tsk->oom_reaper_list)
+	/* mm is already queued? */
+	if (test_and_set_bit(MMF_OOM_REAP_QUEUED, &tsk->signal->oom_mm->flags))
 		return;
 
 	get_task_struct(tsk);
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2019-01-29 10:34 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <df806a77-3327-9db5-8be2-976fde1c84e5@gmail.com>
     [not found] ` <20190117122535.njcbqhlmzozdkncw@mikami>
     [not found]   ` <1d36b181-cbaf-6694-1a31-2f7f55d15675@gmail.com>
     [not found]     ` <96ef6615-a5df-30af-b4dc-417a18ca63f1@gmail.com>
     [not found]       ` <1cdbef13-564d-61a6-95f4-579d2cad243d@gmail.com>
     [not found]         ` <20190125163731.GJ50184@devbig004.ftw2.facebook.com>
     [not found]           ` <a95d004a-4358-7efc-6d21-12aac4411b32@gmail.com>
     [not found]             ` <480296c4-ed7a-3265-e84a-298e42a0f1d5@I-love.SAKURA.ne.jp>
     [not found]               ` <6da6ca69-5a6e-a9f6-d091-f89a8488982a@gmail.com>
     [not found]                 ` <72aa8863-a534-b8df-6b9e-f69cf4dd5c4d@i-love.sakura.ne.jp>
     [not found]                   ` <33a07810-6dbc-36be-5bb6-a279773ccf69@i-love.sakura.ne.jp>
     [not found]                     ` <34e97b46-0792-cc66-e0f2-d72576cdec59@i-love.sakura.ne.jp>
     [not found]                       ` <2b0c7d6c-c58a-da7d-6f0a-4900694ec2d3@gmail.com>
2019-01-26 13:10                         ` [PATCH v2] oom, oom_reaper: do not enqueue same task twice Tetsuo Handa
2019-01-26 13:10                           ` Tetsuo Handa
2019-01-27  8:37                           ` Michal Hocko
2019-01-27 10:56                             ` Tetsuo Handa
2019-01-27 11:40                               ` Michal Hocko
2019-01-27 14:57                                 ` [PATCH v3] " Tetsuo Handa
2019-01-27 14:57                                   ` Tetsuo Handa
2019-01-27 16:58                                   ` Michal Hocko
2019-01-27 23:00                                   ` Roman Gushchin
2019-01-28 18:15                                   ` Andrew Morton
2019-01-28 18:42                                     ` Michal Hocko
2019-01-28 21:53                                   ` Johannes Weiner
2019-01-29 10:34                                     ` Tetsuo Handa

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).