mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
mm: memcontrol: fix occasional OOMs due to proportional memory.low reclaim
[ Upstream commitf56ce412a5
] 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. [akpm@linux-foundation.org: coding-style fixes] Link: https://lkml.kernel.org/r/20210817180506.220056-1-hannes@cmpxchg.org Fixes:9783aa9917
("mm, memcg: proportional memory.{low,min} reclaim") Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> Reported-by: Leon Yang <lnyng@fb.com> Reviewed-by: Rik van Riel <riel@surriel.com> Reviewed-by: Shakeel Butt <shakeelb@google.com> Acked-by: Roman Gushchin <guro@fb.com> Acked-by: Chris Down <chris@chrisdown.name> Acked-by: Michal Hocko <mhocko@suse.com> Cc: <stable@vger.kernel.org> [5.4+] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
53e81668e1
commit
8132fc2bf4
2 changed files with 34 additions and 22 deletions
|
@ -360,12 +360,15 @@ static inline bool mem_cgroup_disabled(void)
|
|||
return !cgroup_subsys_enabled(memory_cgrp_subsys);
|
||||
}
|
||||
|
||||
static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root,
|
||||
struct mem_cgroup *memcg,
|
||||
bool in_low_reclaim)
|
||||
static inline void mem_cgroup_protection(struct mem_cgroup *root,
|
||||
struct mem_cgroup *memcg,
|
||||
unsigned long *min,
|
||||
unsigned long *low)
|
||||
{
|
||||
*min = *low = 0;
|
||||
|
||||
if (mem_cgroup_disabled())
|
||||
return 0;
|
||||
return;
|
||||
|
||||
/*
|
||||
* There is no reclaim protection applied to a targeted reclaim.
|
||||
|
@ -401,13 +404,10 @@ static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root,
|
|||
*
|
||||
*/
|
||||
if (root == memcg)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (in_low_reclaim)
|
||||
return READ_ONCE(memcg->memory.emin);
|
||||
|
||||
return max(READ_ONCE(memcg->memory.emin),
|
||||
READ_ONCE(memcg->memory.elow));
|
||||
*min = READ_ONCE(memcg->memory.emin);
|
||||
*low = READ_ONCE(memcg->memory.elow);
|
||||
}
|
||||
|
||||
void mem_cgroup_calculate_protection(struct mem_cgroup *root,
|
||||
|
@ -966,11 +966,12 @@ static inline void memcg_memory_event_mm(struct mm_struct *mm,
|
|||
{
|
||||
}
|
||||
|
||||
static inline unsigned long mem_cgroup_protection(struct mem_cgroup *root,
|
||||
struct mem_cgroup *memcg,
|
||||
bool in_low_reclaim)
|
||||
static inline void mem_cgroup_protection(struct mem_cgroup *root,
|
||||
struct mem_cgroup *memcg,
|
||||
unsigned long *min,
|
||||
unsigned long *low)
|
||||
{
|
||||
return 0;
|
||||
*min = *low = 0;
|
||||
}
|
||||
|
||||
static inline void mem_cgroup_calculate_protection(struct mem_cgroup *root,
|
||||
|
|
27
mm/vmscan.c
27
mm/vmscan.c
|
@ -102,9 +102,12 @@ struct scan_control {
|
|||
unsigned int may_swap:1;
|
||||
|
||||
/*
|
||||
* Cgroups are not reclaimed below their configured memory.low,
|
||||
* unless we threaten to OOM. If any cgroups are skipped due to
|
||||
* memory.low and nothing was reclaimed, go back for memory.low.
|
||||
* Cgroup memory below memory.low is protected as long as we
|
||||
* don't threaten to OOM. If any cgroup is reclaimed at
|
||||
* reduced force or passed over entirely due to its memory.low
|
||||
* setting (memcg_low_skipped), and nothing is reclaimed as a
|
||||
* result, then go back for one more cycle that reclaims the protected
|
||||
* memory (memcg_low_reclaim) to avert OOM.
|
||||
*/
|
||||
unsigned int memcg_low_reclaim:1;
|
||||
unsigned int memcg_low_skipped:1;
|
||||
|
@ -2323,15 +2326,14 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
|
|||
for_each_evictable_lru(lru) {
|
||||
int file = is_file_lru(lru);
|
||||
unsigned long lruvec_size;
|
||||
unsigned long low, min;
|
||||
unsigned long scan;
|
||||
unsigned long protection;
|
||||
|
||||
lruvec_size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx);
|
||||
protection = mem_cgroup_protection(sc->target_mem_cgroup,
|
||||
memcg,
|
||||
sc->memcg_low_reclaim);
|
||||
mem_cgroup_protection(sc->target_mem_cgroup, memcg,
|
||||
&min, &low);
|
||||
|
||||
if (protection) {
|
||||
if (min || low) {
|
||||
/*
|
||||
* Scale a cgroup's reclaim pressure by proportioning
|
||||
* its current usage to its memory.low or memory.min
|
||||
|
@ -2362,6 +2364,15 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
|
|||
* hard protection.
|
||||
*/
|
||||
unsigned long cgroup_size = mem_cgroup_size(memcg);
|
||||
unsigned long protection;
|
||||
|
||||
/* memory.low scaling, make sure we retry before OOM */
|
||||
if (!sc->memcg_low_reclaim && low > min) {
|
||||
protection = low;
|
||||
sc->memcg_low_skipped = 1;
|
||||
} else {
|
||||
protection = min;
|
||||
}
|
||||
|
||||
/* Avoid TOCTOU with earlier protection check */
|
||||
cgroup_size = max(cgroup_size, protection);
|
||||
|
|
Loading…
Reference in a new issue