[Devel] [PATCH rh7] mm/memcontrol: Avoid shrink_zone() endless looping on global reclaim

Konstantin Khorenko khorenko at virtuozzo.com
Wed Feb 17 14:02:57 MSK 2021


This version of the patch is for ReadyKernel patch only.

If the Node is highly loaded and we have a lot of mem cgroups to
scan/reclaim it's quite possible to race with some memcg death and
restart the reclaimer loop again and again causing kswapd to work
forever even if there are a lot of free memory already.

More precisely: kswapd may loop forever in shrink_zone() in
	do {} while ((memcg = mem_cgroup_iter(root, memcg, &reclaim)))

loop if mem_cgroup_iter() restarts from root memory cgroup again and
again.
And the latter could happen is memory cgroups are created/destroyed
faster than shrink_zone() reclaims all memory cgroups in a row.

Let's stop kswapd in case we see mem_cgroup_iter() has restarted several
times in shrink_zone(). We might prevent kswapd from making its work
good enough, but will definitely prevent the situation when permanent
kswapd work brings node to swapin/swapout while the node has a lot of
free RAM.

https://jira.sw.ru/browse/PSBM-123655

Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
---
 mm/vmscan.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index 81a115313582..7695bb1d4d72 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2657,6 +2657,7 @@ static void shrink_zone(struct zone *zone, struct scan_control *sc,
 	unsigned long nr_reclaimed, nr_scanned;
 	bool slab_only = sc->slab_only;
 	bool retry;
+	int root_memcg_counter = 0;
 
 	do {
 		struct mem_cgroup *root = sc->target_mem_cgroup;
@@ -2724,6 +2725,20 @@ static void shrink_zone(struct zone *zone, struct scan_control *sc,
 				mem_cgroup_iter_break(root, memcg);
 				break;
 			}
+
+			/*
+			 * We can loop in this cycle forever on global reclaim
+			 * if our Node had a lot of mem cgroups && disk is slow
+			 * && mem cgroups are created/destroyed often.
+			 * In this case mem_cgroup_iter() will restart the loop
+			 * from the root memcg again and again. Forever.
+			 */
+			if (global_reclaim(sc) &&
+			    (memcg == root_mem_cgroup) &&
+			    (root_memcg_counter++ > 5)) {
+				mem_cgroup_iter_break(root, memcg);
+				break;
+			}
 		} while ((memcg = mem_cgroup_iter(root, memcg, &reclaim)));
 
 		if ((!sc->has_inactive || !sc->nr_reclaimed)
-- 
2.24.3



More information about the Devel mailing list