diff options
-rw-r--r-- | include/linux/memcontrol.h | 9 | ||||
-rw-r--r-- | include/linux/oom.h | 16 | ||||
-rw-r--r-- | mm/memcontrol.c | 61 | ||||
-rw-r--r-- | mm/oom_kill.c | 48 |
4 files changed, 93 insertions, 41 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, | |||
180 | unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, | 180 | unsigned 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); |
183 | u64 mem_cgroup_get_limit(struct mem_cgroup *memcg); | 183 | extern void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, |
184 | int order); | ||
184 | 185 | ||
185 | void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx); | 186 | void 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 | ||
367 | static inline | ||
368 | u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) | ||
369 | { | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static inline void mem_cgroup_split_huge_fixup(struct page *head) | 368 | static 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 | ||
43 | enum 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 | |||
43 | extern void compare_swap_oom_score_adj(int old_val, int new_val); | 50 | extern void compare_swap_oom_score_adj(int old_val, int new_val); |
44 | extern int test_set_oom_score_adj(int new_val); | 51 | extern int test_set_oom_score_adj(int new_val); |
45 | 52 | ||
46 | extern unsigned long oom_badness(struct task_struct *p, | 53 | extern 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); |
56 | extern 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 | |||
49 | extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); | 61 | extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); |
50 | extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); | 62 | extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); |
51 | 63 | ||
64 | extern enum oom_scan_t oom_scan_process_thread(struct task_struct *task, | ||
65 | unsigned long totalpages, const nodemask_t *nodemask, | ||
66 | bool force_kill); | ||
52 | extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, | 67 | extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, |
53 | int order); | 68 | int order); |
69 | |||
54 | extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, | 70 | extern 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); |
56 | extern int register_oom_notifier(struct notifier_block *nb); | 72 | extern int register_oom_notifier(struct notifier_block *nb); |
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 4f73c823c59f..b78972e2f43f 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -1453,7 +1453,7 @@ static int mem_cgroup_count_children(struct mem_cgroup *memcg) | |||
1453 | /* | 1453 | /* |
1454 | * Return the memory (and swap, if configured) limit for a memcg. | 1454 | * Return the memory (and swap, if configured) limit for a memcg. |
1455 | */ | 1455 | */ |
1456 | u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) | 1456 | static u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) |
1457 | { | 1457 | { |
1458 | u64 limit; | 1458 | u64 limit; |
1459 | u64 memsw; | 1459 | u64 memsw; |
@@ -1469,6 +1469,65 @@ u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) | |||
1469 | return min(limit, memsw); | 1469 | return min(limit, memsw); |
1470 | } | 1470 | } |
1471 | 1471 | ||
1472 | void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, | ||
1473 | int order) | ||
1474 | { | ||
1475 | struct mem_cgroup *iter; | ||
1476 | unsigned long chosen_points = 0; | ||
1477 | unsigned long totalpages; | ||
1478 | unsigned int points = 0; | ||
1479 | struct task_struct *chosen = NULL; | ||
1480 | |||
1481 | totalpages = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1; | ||
1482 | for_each_mem_cgroup_tree(iter, memcg) { | ||
1483 | struct cgroup *cgroup = iter->css.cgroup; | ||
1484 | struct cgroup_iter it; | ||
1485 | struct task_struct *task; | ||
1486 | |||
1487 | cgroup_iter_start(cgroup, &it); | ||
1488 | while ((task = cgroup_iter_next(cgroup, &it))) { | ||
1489 | switch (oom_scan_process_thread(task, totalpages, NULL, | ||
1490 | false)) { | ||
1491 | case OOM_SCAN_SELECT: | ||
1492 | if (chosen) | ||
1493 | put_task_struct(chosen); | ||
1494 | chosen = task; | ||
1495 | chosen_points = ULONG_MAX; | ||
1496 | get_task_struct(chosen); | ||
1497 | /* fall through */ | ||
1498 | case OOM_SCAN_CONTINUE: | ||
1499 | continue; | ||
1500 | case OOM_SCAN_ABORT: | ||
1501 | cgroup_iter_end(cgroup, &it); | ||
1502 | mem_cgroup_iter_break(memcg, iter); | ||
1503 | if (chosen) | ||
1504 | put_task_struct(chosen); | ||
1505 | return; | ||
1506 | case OOM_SCAN_OK: | ||
1507 | break; | ||
1508 | }; | ||
1509 | points = oom_badness(task, memcg, NULL, totalpages); | ||
1510 | if (points > chosen_points) { | ||
1511 | if (chosen) | ||
1512 | put_task_struct(chosen); | ||
1513 | chosen = task; | ||
1514 | chosen_points = points; | ||
1515 | get_task_struct(chosen); | ||
1516 | } | ||
1517 | } | ||
1518 | cgroup_iter_end(cgroup, &it); | ||
1519 | } | ||
1520 | |||
1521 | if (!chosen) | ||
1522 | return; | ||
1523 | points = chosen_points * 1000 / totalpages; | ||
1524 | read_lock(&tasklist_lock); | ||
1525 | oom_kill_process(chosen, gfp_mask, order, points, totalpages, memcg, | ||
1526 | NULL, "Memory cgroup out of memory"); | ||
1527 | read_unlock(&tasklist_lock); | ||
1528 | put_task_struct(chosen); | ||
1529 | } | ||
1530 | |||
1472 | static unsigned long mem_cgroup_reclaim(struct mem_cgroup *memcg, | 1531 | static unsigned long mem_cgroup_reclaim(struct mem_cgroup *memcg, |
1473 | gfp_t gfp_mask, | 1532 | gfp_t gfp_mask, |
1474 | unsigned long flags) | 1533 | unsigned long flags) |
diff --git a/mm/oom_kill.c b/mm/oom_kill.c index f8eba9651c0c..c0c97aea837f 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c | |||
@@ -288,20 +288,13 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist, | |||
288 | } | 288 | } |
289 | #endif | 289 | #endif |
290 | 290 | ||
291 | enum oom_scan_t { | 291 | enum oom_scan_t oom_scan_process_thread(struct task_struct *task, |
292 | OOM_SCAN_OK, /* scan thread and find its badness */ | 292 | unsigned long totalpages, const nodemask_t *nodemask, |
293 | OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */ | 293 | bool force_kill) |
294 | OOM_SCAN_ABORT, /* abort the iteration and return */ | ||
295 | OOM_SCAN_SELECT, /* always select this thread first */ | ||
296 | }; | ||
297 | |||
298 | static enum oom_scan_t oom_scan_process_thread(struct task_struct *task, | ||
299 | struct mem_cgroup *memcg, unsigned long totalpages, | ||
300 | const nodemask_t *nodemask, bool force_kill) | ||
301 | { | 294 | { |
302 | if (task->exit_state) | 295 | if (task->exit_state) |
303 | return OOM_SCAN_CONTINUE; | 296 | return OOM_SCAN_CONTINUE; |
304 | if (oom_unkillable_task(task, memcg, nodemask)) | 297 | if (oom_unkillable_task(task, NULL, nodemask)) |
305 | return OOM_SCAN_CONTINUE; | 298 | return OOM_SCAN_CONTINUE; |
306 | 299 | ||
307 | /* | 300 | /* |
@@ -348,8 +341,8 @@ static enum oom_scan_t oom_scan_process_thread(struct task_struct *task, | |||
348 | * (not docbooked, we don't want this one cluttering up the manual) | 341 | * (not docbooked, we don't want this one cluttering up the manual) |
349 | */ | 342 | */ |
350 | static struct task_struct *select_bad_process(unsigned int *ppoints, | 343 | static struct task_struct *select_bad_process(unsigned int *ppoints, |
351 | unsigned long totalpages, struct mem_cgroup *memcg, | 344 | unsigned long totalpages, const nodemask_t *nodemask, |
352 | const nodemask_t *nodemask, bool force_kill) | 345 | bool force_kill) |
353 | { | 346 | { |
354 | struct task_struct *g, *p; | 347 | struct task_struct *g, *p; |
355 | struct task_struct *chosen = NULL; | 348 | struct task_struct *chosen = NULL; |
@@ -358,7 +351,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, | |||
358 | do_each_thread(g, p) { | 351 | do_each_thread(g, p) { |
359 | unsigned int points; | 352 | unsigned int points; |
360 | 353 | ||
361 | switch (oom_scan_process_thread(p, memcg, totalpages, nodemask, | 354 | switch (oom_scan_process_thread(p, totalpages, nodemask, |
362 | force_kill)) { | 355 | force_kill)) { |
363 | case OOM_SCAN_SELECT: | 356 | case OOM_SCAN_SELECT: |
364 | chosen = p; | 357 | chosen = p; |
@@ -371,7 +364,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, | |||
371 | case OOM_SCAN_OK: | 364 | case OOM_SCAN_OK: |
372 | break; | 365 | break; |
373 | }; | 366 | }; |
374 | points = oom_badness(p, memcg, nodemask, totalpages); | 367 | points = oom_badness(p, NULL, nodemask, totalpages); |
375 | if (points > chosen_points) { | 368 | if (points > chosen_points) { |
376 | chosen = p; | 369 | chosen = p; |
377 | chosen_points = points; | 370 | chosen_points = points; |
@@ -443,10 +436,10 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order, | |||
443 | } | 436 | } |
444 | 437 | ||
445 | #define K(x) ((x) << (PAGE_SHIFT-10)) | 438 | #define K(x) ((x) << (PAGE_SHIFT-10)) |
446 | static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, | 439 | void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, |
447 | unsigned int points, unsigned long totalpages, | 440 | unsigned int points, unsigned long totalpages, |
448 | struct mem_cgroup *memcg, nodemask_t *nodemask, | 441 | struct mem_cgroup *memcg, nodemask_t *nodemask, |
449 | const char *message) | 442 | const char *message) |
450 | { | 443 | { |
451 | struct task_struct *victim = p; | 444 | struct task_struct *victim = p; |
452 | struct task_struct *child; | 445 | struct task_struct *child; |
@@ -564,10 +557,6 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, | |||
564 | void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, | 557 | void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, |
565 | int order) | 558 | int order) |
566 | { | 559 | { |
567 | unsigned long limit; | ||
568 | unsigned int points = 0; | ||
569 | struct task_struct *p; | ||
570 | |||
571 | /* | 560 | /* |
572 | * If current has a pending SIGKILL, then automatically select it. The | 561 | * If current has a pending SIGKILL, then automatically select it. The |
573 | * goal is to allow it to allocate so that it may quickly exit and free | 562 | * goal is to allow it to allocate so that it may quickly exit and free |
@@ -579,13 +568,7 @@ void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, | |||
579 | } | 568 | } |
580 | 569 | ||
581 | check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL); | 570 | check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL); |
582 | limit = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1; | 571 | __mem_cgroup_out_of_memory(memcg, gfp_mask, order); |
583 | read_lock(&tasklist_lock); | ||
584 | p = select_bad_process(&points, limit, memcg, NULL, false); | ||
585 | if (p && PTR_ERR(p) != -1UL) | ||
586 | oom_kill_process(p, gfp_mask, order, points, limit, memcg, NULL, | ||
587 | "Memory cgroup out of memory"); | ||
588 | read_unlock(&tasklist_lock); | ||
589 | } | 572 | } |
590 | #endif | 573 | #endif |
591 | 574 | ||
@@ -710,7 +693,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, | |||
710 | struct task_struct *p; | 693 | struct task_struct *p; |
711 | unsigned long totalpages; | 694 | unsigned long totalpages; |
712 | unsigned long freed = 0; | 695 | unsigned long freed = 0; |
713 | unsigned int points; | 696 | unsigned int uninitialized_var(points); |
714 | enum oom_constraint constraint = CONSTRAINT_NONE; | 697 | enum oom_constraint constraint = CONSTRAINT_NONE; |
715 | int killed = 0; | 698 | int killed = 0; |
716 | 699 | ||
@@ -748,8 +731,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, | |||
748 | goto out; | 731 | goto out; |
749 | } | 732 | } |
750 | 733 | ||
751 | p = select_bad_process(&points, totalpages, NULL, mpol_mask, | 734 | p = select_bad_process(&points, totalpages, mpol_mask, force_kill); |
752 | force_kill); | ||
753 | /* Found nothing?!?! Either we hang forever, or we panic. */ | 735 | /* Found nothing?!?! Either we hang forever, or we panic. */ |
754 | if (!p) { | 736 | if (!p) { |
755 | dump_header(NULL, gfp_mask, order, NULL, mpol_mask); | 737 | dump_header(NULL, gfp_mask, order, NULL, mpol_mask); |