[PATCH v2] mm, oom: task_will_free_mem(current) should ignore MMF_OOM_SKIP for once.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Tetsuo Handa wrote at http://lkml.kernel.org/r/201708102328.ACD34352.OHFOLJMQVSFOFt@xxxxxxxxxxxxxxxxxxx :
> Michal Hocko wrote:
> > On Thu 10-08-17 21:10:30, Tetsuo Handa wrote:
> > > Michal Hocko wrote:
> > > > On Tue 08-08-17 11:14:50, Tetsuo Handa wrote:
> > > > > Michal Hocko wrote:
> > > > > > On Sat 05-08-17 10:02:55, Tetsuo Handa wrote:
> > > > > > > Michal Hocko wrote:
> > > > > > > > On Wed 26-07-17 20:33:21, Tetsuo Handa wrote:
> > > > > > > > > My question is, how can users know it if somebody was OOM-killed needlessly
> > > > > > > > > by allowing MMF_OOM_SKIP to race.
> > > > > > > > 
> > > > > > > > Is it really important to know that the race is due to MMF_OOM_SKIP?
> > > > > > > 
> > > > > > > Yes, it is really important. Needlessly selecting even one OOM victim is
> > > > > > > a pain which is difficult to explain to and persuade some of customers.
> > > > > > 
> > > > > > How is this any different from a race with a task exiting an releasing
> > > > > > some memory after we have crossed the point of no return and will kill
> > > > > > something?
> > > > > 
> > > > > I'm not complaining about an exiting task releasing some memory after we have
> > > > > crossed the point of no return.
> > > > > 
> > > > > What I'm saying is that we can postpone "the point of no return" if we ignore
> > > > > MMF_OOM_SKIP for once (both this "oom_reaper: close race without using oom_lock"
> > > > > thread and "mm, oom: task_will_free_mem(current) should ignore MMF_OOM_SKIP for
> > > > > once." thread). These are race conditions we can avoid without crystal ball.
> > > > 
> > > > If those races are really that common than we can handle them even
> > > > without "try once more" tricks. Really this is just an ugly hack. If you
> > > > really care then make sure that we always try to allocate from memory
> > > > reserves before going down the oom path. In other words, try to find a
> > > > robust solution rather than tweaks around a problem.
> > > 
> > > Since your "mm, oom: allow oom reaper to race with exit_mmap" patch removes
> > > oom_lock serialization from the OOM reaper, possibility of calling out_of_memory()
> > > due to successful mutex_trylock(&oom_lock) would increase when the OOM reaper set
> > > MMF_OOM_SKIP quickly.
> > > 
> > > What if task_is_oom_victim(current) became true and MMF_OOM_SKIP was set
> > > on current->mm between after __gfp_pfmemalloc_flags() returned 0 and before
> > > out_of_memory() is called (due to successful mutex_trylock(&oom_lock)) ?
> > > 
> > > Excuse me? Are you suggesting to try memory reserves before
> > > task_is_oom_victim(current) becomes true?
> > 
> > No what I've tried to say is that if this really is a real problem,
> > which I am not sure about, then the proper way to handle that is to
> > attempt to allocate from memory reserves for an oom victim. I would be
> > even willing to take the oom_lock back into the oom reaper path if the
> > former turnes out to be awkward to implement. But all this assumes this
> > is a _real_ problem.
> 
> Aren't we back to square one? My question is, how can users know it if
> somebody was OOM-killed needlessly by allowing MMF_OOM_SKIP to race.
> 
> You don't want to call get_page_from_freelist() from out_of_memory(), do you?
> But without passing a flag "whether get_page_from_freelist() with memory reserves
> was already attempted if current thread is an OOM victim" to task_will_free_mem()
> in out_of_memory() and a flag "whether get_page_from_freelist() without memory
> reserves was already attempted if current thread is not an OOM victim" to
> test_bit(MMF_OOM_SKIP) in oom_evaluate_task(), we won't be able to know
> if somebody was OOM-killed needlessly by allowing MMF_OOM_SKIP to race.

Michal, I did not get your answer, and your "mm, oom: do not rely on
TIF_MEMDIE for memory reserves access" did not help solving this problem.
(I confirmed it by reverting your "mm, oom: allow oom reaper to race with
exit_mmap" and applying Andrea's "mm: oom: let oom_reap_task and exit_mmap
run concurrently" and this patch on top of linux-next-20170817.)

-----------
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sched.h>
#include <sys/mman.h>

#define NUMTHREADS 2
#define MMAPSIZE ((4096 * 1048576UL) / NUMTHREADS)
#define STACKSIZE 4096
static int pipe_fd[2] = { EOF, EOF };
static int memory_eater(void *unused)
{
	int fd = open("/dev/zero", O_RDONLY);
	char *buf = mmap(NULL, MMAPSIZE, PROT_WRITE | PROT_READ,
			 MAP_ANONYMOUS | MAP_SHARED, EOF, 0);
	read(pipe_fd[0], buf, 1);
	read(fd, buf, MMAPSIZE);
	pause();
	return 0;
}
int main(int argc, char *argv[])
{
	int i;
	char *stack;
	if (pipe(pipe_fd))
		return 1;
	stack = mmap(NULL, STACKSIZE * NUMTHREADS, PROT_WRITE | PROT_READ,
		     MAP_ANONYMOUS | MAP_SHARED, EOF, 0);
	for (i = 0; i < NUMTHREADS; i++)
		if (clone(memory_eater, stack + (i + 1) * STACKSIZE,
			  CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_FS |
			  CLONE_FILES, NULL) == -1)
			break;
	sleep(1);
	close(pipe_fd[1]);
	pause();
	return 0;
}
-----------

-----------
[  204.413605] Out of memory: Kill process 9286 (a.out) score 930 or sacrifice child
[  204.416241] Killed process 9286 (a.out) total-vm:4198476kB, anon-rss:72kB, file-rss:0kB, shmem-rss:3465520kB
[  204.419783] oom_reaper: reaped process 9286 (a.out), now anon-rss:0kB, file-rss:0kB, shmem-rss:3465720kB
[  204.455864] ------------[ cut here ]------------
[  204.457921] kernel BUG at mm/oom_kill.c:786!
[  204.459844] invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC
[  204.461877] Modules linked in: coretemp pcspkr sg vmw_vmci i2c_piix4 shpchp sd_mod ata_generic pata_acpi serio_raw mptspi scsi_transport_spi mptscsih vmwgfx drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops ahci ttm drm libahci ata_piix e1000 mptbase i2c_core libata ipv6
[  204.469328] CPU: 1 PID: 9287 Comm: a.out Not tainted 4.13.0-rc5-next-20170817+ #662
[  204.472117] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015
[  204.475265] task: ffff880135c88040 task.stack: ffff880137554000
[  204.477556] RIP: 0010:task_will_free_mem+0x1a7/0x240
[  204.479651] RSP: 0018:ffff880137557698 EFLAGS: 00010246
[  204.481750] RAX: 0000000000000000 RBX: ffff880135c88040 RCX: 00000000ffffffff
[  204.484344] RDX: ffff880135c88040 RSI: 0000000000000000 RDI: ffff880135c88040
[  204.487077] RBP: ffff8801375576b0 R08: 0000000000000000 R09: 0000000000000e6d
[  204.489565] R10: 0000000000000000 R11: 0000000000000e95 R12: ffff880133b48040
[  204.492019] R13: ffff88013f7fea20 R14: 0000000000000000 R15: 00000000014200ca
[  204.494467] FS:  00007fc4d067d740(0000) GS:ffff88013a000000(0000) knlGS:0000000000000000
[  204.497075] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  204.499231] CR2: 00007fc4b9ec2000 CR3: 0000000137b39004 CR4: 00000000001606e0
[  204.501907] Call Trace:
[  204.503426]  out_of_memory+0x54/0x560
[  204.505137]  __alloc_pages_nodemask+0xe91/0xf50
[  204.507002]  alloc_pages_vma+0x76/0x1a0
[  204.508694]  shmem_alloc_page+0x71/0xb0
[  204.510351]  ? native_sched_clock+0x36/0xa0
[  204.512059]  ? native_sched_clock+0x36/0xa0
[  204.513721]  ? find_get_entry+0x191/0x280
[  204.515327]  shmem_alloc_and_acct_page+0x83/0x230
[  204.517330]  shmem_getpage_gfp+0x1b6/0xe30
[  204.519005]  shmem_fault+0x97/0x200
[  204.520558]  ? __lock_acquire+0x4a7/0x1c20
[  204.522101]  ? __lock_acquire+0x4a7/0x1c20
[  204.523619]  __do_fault+0x19/0x120
[  204.524965]  __handle_mm_fault+0x8e3/0x1250
[  204.526484]  ? native_sched_clock+0x36/0xa0
[  204.527945]  handle_mm_fault+0x186/0x360
[  204.529355]  ? handle_mm_fault+0x47/0x360
[  204.530771]  __do_page_fault+0x1d2/0x510
[  204.532177]  do_page_fault+0x21/0x70
[  204.533563]  page_fault+0x22/0x30
[  204.534906] RIP: 0010:__clear_user+0x3d/0x70
[  204.536445] RSP: 0018:ffff880137557d58 EFLAGS: 00010206
[  204.538108] RAX: 0000000000000000 RBX: 0000000000000200 RCX: 0000000000000200
[  204.540173] RDX: 0000000000000000 RSI: 0000000000000008 RDI: 00007fc4b9ec2000
[  204.542215] RBP: ffff880137557d68 R08: 0000000000000001 R09: 0000000000000000
[  204.544263] R10: 0000000000000001 R11: 0000000000000001 R12: 00007fc4b9ec2000
[  204.546300] R13: ffff880137557e18 R14: 0000000069e16000 R15: 0000000000001000
[  204.548338]  ? __clear_user+0x1e/0x70
[  204.549679]  clear_user+0x34/0x50
[  204.551088]  iov_iter_zero+0x88/0x380
[  204.552403]  read_iter_zero+0x38/0xb0
[  204.553839]  new_sync_read+0xcc/0x110
[  204.555215]  __vfs_read+0x27/0x40
[  204.556605]  vfs_read+0xa0/0x160
[  204.557749]  SyS_read+0x53/0xc0
[  204.558907]  do_syscall_64+0x61/0x1d0
[  204.560197]  entry_SYSCALL64_slow_path+0x25/0x25
[  204.561628] RIP: 0033:0x7fc4d0194c30
[  204.562819] RSP: 002b:00007fc4d068afd8 EFLAGS: 00000246 ORIG_RAX: 0000000000000000
[  204.564793] RAX: ffffffffffffffda RBX: 00007fc4500ac000 RCX: 00007fc4d0194c30
[  204.566802] RDX: 0000000080000000 RSI: 00007fc4500ac000 RDI: 0000000000000005
[  204.568760] RBP: 0000000000000005 R08: ffffffffffffffff R09: 0000000000000000
[  204.570641] R10: 00007fc4d068ad60 R11: 0000000000000246 R12: 00000000004006d9
[  204.572522] R13: 00007ffc72529cc0 R14: 0000000000000000 R15: 0000000000000000
[  204.574393] Code: 83 c4 08 89 d8 5b 41 5c 5d c3 65 48 8b 14 25 00 c6 00 00 31 c0 48 39 d3 0f 85 90 fe ff ff f6 83 78 16 00 00 02 0f 85 83 fe ff ff <0f> 0b 80 3d b6 f3 b5 00 00 0f 85 f7 fe ff ff e8 05 ec f8 ff 84 
[  204.579049] RIP: task_will_free_mem+0x1a7/0x240 RSP: ffff880137557698
[  204.580840] ---[ end trace 2be364e2657b83fa ]---
-----------

Therefore, I propose this patch for inclusion.

----------------------------------------

>From cf6ef5a7b110d12e98bb2928e839abee16418188 Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
Date: Thu, 17 Aug 2017 14:45:31 +0900
Subject: [PATCH v2] mm, oom: task_will_free_mem(current) should ignore MMF_OOM_SKIP for once.

Manish Jaggi noticed that running LTP oom01/oom02 ltp tests with high core
count causes random kernel panics when an OOM victim which consumed memory
in a way the OOM reaper does not help was selected by the OOM killer [1].

----------
oom02       0  TINFO  :  start OOM testing for mlocked pages.
oom02       0  TINFO  :  expected victim is 4578.
oom02       0  TINFO  :  thread (ffff8b0e71f0), allocating 3221225472 bytes.
oom02       0  TINFO  :  thread (ffff8b8e71f0), allocating 3221225472 bytes.
(...snipped...)
oom02       0  TINFO  :  thread (ffff8a0e71f0), allocating 3221225472 bytes.
[  364.737486] oom02:4583 invoked oom-killer: gfp_mask=0x16080c0(GFP_KERNEL|__GFP_ZERO|__GFP_NOTRACK), nodemask=1,  order=0, oom_score_adj=0
(...snipped...)
[  365.036127] [ pid ]   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name
[  365.044691] [ 1905]     0  1905     3236     1714      10       4        0             0 systemd-journal
[  365.054172] [ 1908]     0  1908    20247      590       8       4        0             0 lvmetad
[  365.062959] [ 2421]     0  2421     3241      878       9       3        0         -1000 systemd-udevd
[  365.072266] [ 3125]     0  3125     3834      719       9       4        0         -1000 auditd
[  365.080963] [ 3145]     0  3145     1086      630       6       4        0             0 systemd-logind
[  365.090353] [ 3146]     0  3146     1208      596       7       3        0             0 irqbalance
[  365.099413] [ 3147]    81  3147     1118      625       5       4        0          -900 dbus-daemon
[  365.108548] [ 3149]   998  3149   116294     4180      26       5        0             0 polkitd
[  365.117333] [ 3164]   997  3164    19992      785       9       3        0             0 chronyd
[  365.126118] [ 3180]     0  3180    55605     7880      29       3        0             0 firewalld
[  365.135075] [ 3187]     0  3187    87842     3033      26       3        0             0 NetworkManager
[  365.144465] [ 3290]     0  3290    43037     1224      16       5        0             0 rsyslogd
[  365.153335] [ 3295]     0  3295   108279     6617      30       3        0             0 tuned
[  365.161944] [ 3308]     0  3308    27846      676      11       3        0             0 crond
[  365.170554] [ 3309]     0  3309     3332      616      10       3        0         -1000 sshd
[  365.179076] [ 3371]     0  3371    27307      364       6       3        0             0 agetty
[  365.187790] [ 3375]     0  3375    29397     1125      11       3        0             0 login
[  365.196402] [ 4178]     0  4178     4797     1119      14       4        0             0 master
[  365.205101] [ 4209]    89  4209     4823     1396      12       4        0             0 pickup
[  365.213798] [ 4211]    89  4211     4842     1485      12       3        0             0 qmgr
[  365.222325] [ 4491]     0  4491    27965     1022       8       3        0             0 bash
[  365.230849] [ 4513]     0  4513      670      365       5       3        0             0 oom02
[  365.239459] [ 4578]     0  4578 37776030 32890957   64257     138        0             0 oom02
[  365.248067] Out of memory: Kill process 4578 (oom02) score 952 or sacrifice child
[  365.255581] Killed process 4578 (oom02) total-vm:151104120kB, anon-rss:131562528kB, file-rss:1300kB, shmem-rss:0kB
[  365.266829] out_of_memory: Current (4583) has a pending SIGKILL
[  365.267347] oom_reaper: reaped process 4578 (oom02), now anon-rss:131559616kB, file-rss:0kB, shmem-rss:0kB
[  365.282658] oom_reaper: reaped process 4583 (oom02), now anon-rss:131561664kB, file-rss:0kB, shmem-rss:0kB
[  365.283361] oom02:4586 invoked oom-killer: gfp_mask=0x16040c0(GFP_KERNEL|__GFP_COMP|__GFP_NOTRACK), nodemask=1,  order=0, oom_score_adj=0
(...snipped...)
[  365.576164] oom02:4585 invoked oom-killer: gfp_mask=0x16080c0(GFP_KERNEL|__GFP_ZERO|__GFP_NOTRACK), nodemask=1,  order=0, oom_score_adj=0
(...snipped...)
[  365.576298] [ pid ]   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name
[  365.576338] [ 2421]     0  2421     3241      878       9       3        0         -1000 systemd-udevd
[  365.576342] [ 3125]     0  3125     3834      719       9       4        0         -1000 auditd
[  365.576347] [ 3309]     0  3309     3332      616      10       3        0         -1000 sshd
[  365.576356] [ 4580]     0  4578 37776030 32890417   64258     138        0             0 oom02
[  365.576361] Kernel panic - not syncing: Out of memory and no killable processes...
----------

Since commit 696453e66630ad45 ("mm, oom: task_will_free_mem should skip
oom_reaped tasks") changed task_will_free_mem(current) in out_of_memory()
to return false as soon as MMF_OOM_SKIP is set, many threads sharing the
victim's mm were not able to try allocation from memory reserves after the
OOM reaper gave up reclaiming memory.

Until Linux 4.7, we were using

  if (current->mm &&
      (fatal_signal_pending(current) || task_will_free_mem(current)))

as a condition to try allocation from memory reserves with the risk of OOM
lockup, but reports like [1] were impossible. Linux 4.8+ are regressed
compared to Linux 4.7 due to the risk of needlessly selecting more OOM
victims. We don't need to give up task_will_free_mem(current) before trying
allocation from memory reserves. We will need to select next OOM victim
only when allocation from memory reserves did not help.

There is no need that the OOM victim is such malicious that consumes all
memory. It is possible that a multithreaded but non memory hog process is
selected by the OOM killer, and the OOM reaper fails to reclaim memory due
to e.g. khugepaged [2], and the process fails to try allocation from memory
reserves.

Although "mm, oom: do not rely on TIF_MEMDIE for memory reserves access"
tried to reduce this race window by replacing TIF_MEMDIE with oom_mm, and
"mm: oom: let oom_reap_task and exit_mmap run concurrently" did not remove
oom_lock serialization, this race window is still easy to trigger. You can
confirm it by adding "BUG_ON(1);" at "task->oom_kill_free_check_raced = 1;"
of this patch.

Thus, this patch allows task_will_free_mem(current) to ignore MMF_OOM_SKIP
for once so that task_will_free_mem(current) will not start selecting next
OOM victim without trying allocation from memory reserves.

[1] http://lkml.kernel.org/r/e6c83a26-1d59-4afd-55cf-04e58bdde188@xxxxxxxxxxxxxxxxxx
[2] http://lkml.kernel.org/r/201708090835.ICI69305.VFFOLMHOStJOQF@xxxxxxxxxxxxxxxxxxx

Fixes: 696453e66630ad45 ("mm, oom: task_will_free_mem should skip oom_reaped tasks")
Reported-by: Manish Jaggi <mjaggi@xxxxxxxxxxxxxxxxxx>
Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
Cc: Michal Hocko <mhocko@xxxxxxxx>
Cc: Oleg Nesterov <oleg@xxxxxxxxxx>
Cc: Vladimir Davydov <vdavydov@xxxxxxxxxxxxx>
Cc: David Rientjes <rientjes@xxxxxxxxxx>
---
 include/linux/sched.h |  1 +
 mm/oom_kill.c         | 14 +++++++++++---
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/include/linux/sched.h b/include/linux/sched.h
index 6110471..11f8d54 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -652,6 +652,7 @@ struct task_struct {
 	/* disallow userland-initiated cgroup migration */
 	unsigned			no_cgroup_migration:1;
 #endif
+	unsigned			oom_kill_free_check_raced:1;
 
 	unsigned long			atomic_flags; /* Flags requiring atomic access. */
 
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index ab8348d..c5fb8a3 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -749,11 +749,19 @@ static bool task_will_free_mem(struct task_struct *task)
 		return false;
 
 	/*
-	 * This task has already been drained by the oom reaper so there are
-	 * only small chances it will free some more
+	 * The current thread might fail to try OOM_ALLOC allocation if the OOM
+	 * reaper set MMF_OOM_SKIP on this mm when the current thread was
+	 * between after __gfp_pfmemalloc_flags() and before out_of_memory().
+	 * Make sure that the current thread has tried OOM_ALLOC allocation
+	 * before starting to select the next OOM victims.
 	 */
-	if (test_bit(MMF_OOM_SKIP, &mm->flags))
+	if (test_bit(MMF_OOM_SKIP, &mm->flags)) {
+		if (task == current && !task->oom_kill_free_check_raced) {
+			task->oom_kill_free_check_raced = 1;
+			return true;
+		}
 		return false;
+	}
 
 	if (atomic_read(&mm->mm_users) <= 1)
 		return true;
-- 
2.9.5

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]
  Powered by Linux