aboutsummaryrefslogtreecommitdiffstats
path: root/include
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
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')
-rw-r--r--include/linux/memcontrol.h9
-rw-r--r--include/linux/oom.h16
2 files changed, 18 insertions, 7 deletions
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index c0bff8976a69..2a80544aec99 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -180,7 +180,8 @@ static inline void mem_cgroup_dec_page_stat(struct page *page,
180unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, 180unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
181 gfp_t gfp_mask, 181 gfp_t gfp_mask,
182 unsigned long *total_scanned); 182 unsigned long *total_scanned);
183u64 mem_cgroup_get_limit(struct mem_cgroup *memcg); 183extern void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
184 int order);
184 185
185void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx); 186void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx);
186#ifdef CONFIG_TRANSPARENT_HUGEPAGE 187#ifdef CONFIG_TRANSPARENT_HUGEPAGE
@@ -364,12 +365,6 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
364 return 0; 365 return 0;
365} 366}
366 367
367static inline
368u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
369{
370 return 0;
371}
372
373static inline void mem_cgroup_split_huge_fixup(struct page *head) 368static inline void mem_cgroup_split_huge_fixup(struct page *head)
374{ 369{
375} 370}
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);