Skip to content

Commit f56ce41

Browse files
hnaztorvalds
authored andcommitted
mm: memcontrol: fix occasional OOMs due to proportional memory.low reclaim
We've noticed occasional OOM killing when memory.low settings are in effect for cgroups. This is unexpected and undesirable as memory.low is supposed to express non-OOMing memory priorities between cgroups. The reason for this is proportional memory.low reclaim. When cgroups are below their memory.low threshold, reclaim passes them over in the first round, and then retries if it couldn't find pages anywhere else. But when cgroups are slightly above their memory.low setting, page scan force is scaled down and diminished in proportion to the overage, to the point where it can cause reclaim to fail as well - only in that case we currently don't retry, and instead trigger OOM. To fix this, hook proportional reclaim into the same retry logic we have in place for when cgroups are skipped entirely. This way if reclaim fails and some cgroups were scanned with diminished pressure, we'll try another full-force cycle before giving up and OOMing. [[email protected]: coding-style fixes] Link: https://lkml.kernel.org/r/[email protected] Fixes: 9783aa9 ("mm, memcg: proportional memory.{low,min} reclaim") Signed-off-by: Johannes Weiner <[email protected]> Reported-by: Leon Yang <[email protected]> Reviewed-by: Rik van Riel <[email protected]> Reviewed-by: Shakeel Butt <[email protected]> Acked-by: Roman Gushchin <[email protected]> Acked-by: Chris Down <[email protected]> Acked-by: Michal Hocko <[email protected]> Cc: <[email protected]> [5.4+] Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 91ed3ed commit f56ce41

File tree

2 files changed

+34
-22
lines changed

2 files changed

+34
-22
lines changed

include/linux/memcontrol.h

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -612,12 +612,15 @@ static inline bool mem_cgroup_disabled(void)
612612
return !cgroup_subsys_enabled(memory_cgrp_subsys);
613613
}
614614

615-
static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root,
616-
struct mem_cgroup *memcg,
617-
bool in_low_reclaim)
615+
static inline void mem_cgroup_protection(struct mem_cgroup *root,
616+
struct mem_cgroup *memcg,
617+
unsigned long *min,
618+
unsigned long *low)
618619
{
620+
*min = *low = 0;
621+
619622
if (mem_cgroup_disabled())
620-
return 0;
623+
return;
621624

622625
/*
623626
* There is no reclaim protection applied to a targeted reclaim.
@@ -653,13 +656,10 @@ static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root,
653656
*
654657
*/
655658
if (root == memcg)
656-
return 0;
657-
658-
if (in_low_reclaim)
659-
return READ_ONCE(memcg->memory.emin);
659+
return;
660660

661-
return max(READ_ONCE(memcg->memory.emin),
662-
READ_ONCE(memcg->memory.elow));
661+
*min = READ_ONCE(memcg->memory.emin);
662+
*low = READ_ONCE(memcg->memory.elow);
663663
}
664664

665665
void mem_cgroup_calculate_protection(struct mem_cgroup *root,
@@ -1147,11 +1147,12 @@ static inline void memcg_memory_event_mm(struct mm_struct *mm,
11471147
{
11481148
}
11491149

1150-
static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root,
1151-
struct mem_cgroup *memcg,
1152-
bool in_low_reclaim)
1150+
static inline void mem_cgroup_protection(struct mem_cgroup *root,
1151+
struct mem_cgroup *memcg,
1152+
unsigned long *min,
1153+
unsigned long *low)
11531154
{
1154-
return 0;
1155+
*min = *low = 0;
11551156
}
11561157

11571158
static inline void mem_cgroup_calculate_protection(struct mem_cgroup *root,

mm/vmscan.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,12 @@ struct scan_control {
100100
unsigned int may_swap:1;
101101

102102
/*
103-
* Cgroups are not reclaimed below their configured memory.low,
104-
* unless we threaten to OOM. If any cgroups are skipped due to
105-
* memory.low and nothing was reclaimed, go back for memory.low.
103+
* Cgroup memory below memory.low is protected as long as we
104+
* don't threaten to OOM. If any cgroup is reclaimed at
105+
* reduced force or passed over entirely due to its memory.low
106+
* setting (memcg_low_skipped), and nothing is reclaimed as a
107+
* result, then go back for one more cycle that reclaims the protected
108+
* memory (memcg_low_reclaim) to avert OOM.
106109
*/
107110
unsigned int memcg_low_reclaim:1;
108111
unsigned int memcg_low_skipped:1;
@@ -2537,15 +2540,14 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
25372540
for_each_evictable_lru(lru) {
25382541
int file = is_file_lru(lru);
25392542
unsigned long lruvec_size;
2543+
unsigned long low, min;
25402544
unsigned long scan;
2541-
unsigned long protection;
25422545

25432546
lruvec_size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx);
2544-
protection = mem_cgroup_protection(sc->target_mem_cgroup,
2545-
memcg,
2546-
sc->memcg_low_reclaim);
2547+
mem_cgroup_protection(sc->target_mem_cgroup, memcg,
2548+
&min, &low);
25472549

2548-
if (protection) {
2550+
if (min || low) {
25492551
/*
25502552
* Scale a cgroup's reclaim pressure by proportioning
25512553
* its current usage to its memory.low or memory.min
@@ -2576,6 +2578,15 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
25762578
* hard protection.
25772579
*/
25782580
unsigned long cgroup_size = mem_cgroup_size(memcg);
2581+
unsigned long protection;
2582+
2583+
/* memory.low scaling, make sure we retry before OOM */
2584+
if (!sc->memcg_low_reclaim && low > min) {
2585+
protection = low;
2586+
sc->memcg_low_skipped = 1;
2587+
} else {
2588+
protection = min;
2589+
}
25792590

25802591
/* Avoid TOCTOU with earlier protection check */
25812592
cgroup_size = max(cgroup_size, protection);

0 commit comments

Comments
 (0)