Currently, on memcg reclaim we only invoke oom if we fail to reclaim anything from the memory cgroup several times in a row. As a result, When a memcg is near its limit, processes might get stuck in __mem_cgroup_try_charge invoking reclaimer over and over again and consuming a lot of cpu time in the meantime instead of calling oom.
The global reclaimer has a heuristic to detect such situations: if we scan all reclaimable pages more than 6 times w/o reclaiming anything, we stop reclaiming and go oom. This patch implements a similar technique for memcg reclaim. I will try to submit something like this upstream. https://jira.sw.ru/browse/PSBM-35155 Signed-off-by: Vladimir Davydov <[email protected]> --- include/linux/mmzone.h | 1 + mm/swap.c | 1 + mm/vmscan.c | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index abe7110d8fbe..faf3c96abed2 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -211,6 +211,7 @@ struct zone_reclaim_stat { struct lruvec { struct list_head lists[NR_LRU_LISTS]; struct zone_reclaim_stat reclaim_stat; + unsigned long pages_scanned; #ifdef CONFIG_MEMCG struct zone *zone; #endif diff --git a/mm/swap.c b/mm/swap.c index 851fff9e1853..eb3add716907 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -60,6 +60,7 @@ static void __page_cache_release(struct page *page) VM_BUG_ON_PAGE(!PageLRU(page), page); __ClearPageLRU(page); del_page_from_lru_list(page, lruvec, page_off_lru(page)); + lruvec->pages_scanned = 0; spin_unlock_irqrestore(&zone->lru_lock, flags); } } diff --git a/mm/vmscan.c b/mm/vmscan.c index cdebfcd9f154..c5a31fa7c10e 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -178,6 +178,20 @@ static unsigned long get_lru_size(struct lruvec *lruvec, enum lru_list lru) return zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru); } +static bool lruvec_reclaimable(struct lruvec *lruvec, bool may_swap) +{ + unsigned long reclaimable; + + reclaimable = get_lru_size(lruvec, LRU_ACTIVE_FILE) + + get_lru_size(lruvec, LRU_INACTIVE_FILE); + + if (may_swap && get_nr_swap_pages() > 0) + reclaimable += get_lru_size(lruvec, LRU_ACTIVE_ANON) + + get_lru_size(lruvec, LRU_INACTIVE_ANON); + + return lruvec->pages_scanned < reclaimable * 6; +} + /* * Add a shrinker callback to be called from the vm. */ @@ -1533,6 +1547,7 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, __mod_zone_page_state(zone, NR_LRU_BASE + lru, -nr_taken); __mod_zone_page_state(zone, NR_ISOLATED_ANON + file, nr_taken); + lruvec->pages_scanned += nr_scanned; if (global_reclaim(sc)) { zone->pages_scanned += nr_scanned; if (current_is_kswapd()) @@ -1552,6 +1567,9 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, spin_lock_irq(&zone->lru_lock); + if (nr_reclaimed) + lruvec->pages_scanned = 0; + reclaim_stat->recent_scanned[file] += nr_taken; if (global_reclaim(sc)) { @@ -1725,6 +1743,7 @@ static void shrink_active_list(unsigned long nr_to_scan, nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &l_hold, &nr_scanned, sc, isolate_mode, lru); + lruvec->pages_scanned += nr_scanned; if (global_reclaim(sc)) zone->pages_scanned += nr_scanned; @@ -2360,6 +2379,10 @@ static void shrink_zone(struct zone *zone, struct scan_control *sc, continue; lruvec = mem_cgroup_zone_lruvec(zone, memcg); + if (!global_reclaim(sc) && + !lruvec_reclaimable(lruvec, sc->may_swap)) + continue; + scanned = sc->nr_scanned; shrink_lruvec(lruvec, sc, &lru_pages); -- 2.1.4 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
