aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/oom.h
diff options
context:
space:
mode:
authorDavid Rientjes <rientjes@google.com>2012-07-31 19:43:44 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-31 21:42:44 -0400
commit9cbb78bb314360a860a8b23723971cb6fcb54176 (patch)
tree7983de03845b5914e0188ce119f9374711ffcce7 /include/linux/oom.h
parent462607ecc519b197f7b5cc6b024a1c26fa6fc0ac (diff)
mm, memcg: introduce own oom handler to iterate only over its own threads
The global oom killer is serialized by the per-zonelist try_set_zonelist_oom() which is used in the page allocator. Concurrent oom kills are thus a rare event and only occur in systems using mempolicies and with a large number of nodes. Memory controller oom kills, however, can frequently be concurrent since there is no serialization once the oom killer is called for oom conditions in several different memcgs in parallel. This creates a massive contention on tasklist_lock since the oom killer requires the readside for the tasklist iteration. If several memcgs are calling the oom killer, this lock can be held for a substantial amount of time, especially if threads continue to enter it as other threads are exiting. Since the exit path grabs the writeside of the lock with irqs disabled in a few different places, this can cause a soft lockup on cpus as a result of tasklist_lock starvation. The kernel lacks unfair writelocks, and successful calls to the oom killer usually result in at least one thread entering the exit path, so an alternative solution is needed. This patch introduces a seperate oom handler for memcgs so that they do not require tasklist_lock for as much time. Instead, it iterates only over the threads attached to the oom memcg and grabs a reference to the selected thread before calling oom_kill_process() to ensure it doesn't prematurely exit. This still requires tasklist_lock for the tasklist dump, iterating children of the selected process, and killing all other threads on the system sharing the same memory as the selected victim. So while this isn't a complete solution to tasklist_lock starvation, it significantly reduces the amount of time that it is held. Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Acked-by: Michal Hocko <mhocko@suse.cz> Signed-off-by: David Rientjes <rientjes@google.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Reviewed-by: Sha Zhengju <handai.szj@taobao.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include/linux/oom.h')
-rw-r--r--include/linux/oom.h16
1 files changed, 16 insertions, 0 deletions
diff --git a/include/linux/oom.h b/include/linux/oom.h
index eb9dc14362c5..5dc0e384ae9e 100644
--- a/include/linux/oom.h
+++ b/include/linux/oom.h
@@ -40,17 +40,33 @@ enum oom_constraint {
40 CONSTRAINT_MEMCG, 40 CONSTRAINT_MEMCG,
41}; 41};
42 42
43enum oom_scan_t {
44 OOM_SCAN_OK, /* scan thread and find its badness */
45 OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */
46 OOM_SCAN_ABORT, /* abort the iteration and return */
47 OOM_SCAN_SELECT, /* always select this thread first */
48};
49
43extern void compare_swap_oom_score_adj(int old_val, int new_val); 50extern void compare_swap_oom_score_adj(int old_val, int new_val);
44extern int test_set_oom_score_adj(int new_val); 51extern int test_set_oom_score_adj(int new_val);
45 52
46extern unsigned long oom_badness(struct task_struct *p, 53extern unsigned long oom_badness(struct task_struct *p,
47 struct mem_cgroup *memcg, const nodemask_t *nodemask, 54 struct mem_cgroup *memcg, const nodemask_t *nodemask,
48 unsigned long totalpages); 55 unsigned long totalpages);
56extern void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
57 unsigned int points, unsigned long totalpages,
58 struct mem_cgroup *memcg, nodemask_t *nodemask,
59 const char *message);
60
49extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); 61extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
50extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); 62extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
51 63
64extern enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
65 unsigned long totalpages, const nodemask_t *nodemask,
66 bool force_kill);
52extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, 67extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
53 int order); 68 int order);
69
54extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, 70extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
55 int order, nodemask_t *mask, bool force_kill); 71 int order, nodemask_t *mask, bool force_kill);
56extern int register_oom_notifier(struct notifier_block *nb); 72extern int register_oom_notifier(struct notifier_block *nb);