We may have scan budget remaining for anon or file but no folios in populatable generations to scan. In this case, `scan_folios()` may waste time scanning that type while the other type still has populatable folios. Run aging in this case to make more folios populatable. This also helps respect the scan budget distribution determined by swappiness. To avoid excessive aging, allow no-populatable aging only once per `try_to_shrink_lruvec()` invocation. With extreme swappiness values such as 1 and 200, reclaim initially focuses exclusively on the preferred type. In this case, defer aging the lagging type until `sc->priority` reaches `DEF_PRIORITY / 2`. This avoids unnecessary aging and has shown better performance in testing. Signed-off-by: Barry Song (Xiaomi) --- mm/vmscan.c | 49 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 59957fab915b..9071f8889b5c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -5109,7 +5109,8 @@ static bool lru_gen_imbalanced(struct lruvec *lruvec, struct scan_control *sc, } static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, - struct scan_control *sc, int swappiness) + struct scan_control *sc, int swappiness, + bool no_populatable) { DEFINE_MIN_SEQ(lruvec); @@ -5121,6 +5122,13 @@ static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, if (sc->priority == DEF_PRIORITY) return false; + /* + * Run aging if scan budget remains but no folios + * in populatable generations are available to scan. + */ + if (no_populatable) + return true; + /* better to run aging even though eviction is still possible */ if (evictable_min_seq(min_seq, swappiness) + MIN_NR_GENS == max_seq) return true; @@ -5208,6 +5216,13 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) bool need_rotate = false, should_age = false; int swappiness = get_swappiness(lruvec, sc); struct mem_cgroup *memcg = lruvec_memcg(lruvec); + /* + * Avoid aging the lagging type under extreme swappiness, + * as reclaim of the unpreferred type is disabled until + * DEF_PRIORITY / 2. + */ + bool allow_no_populatable_age = (sc->priority <= DEF_PRIORITY / 2) || + (swappiness > MIN_SWAPPINESS + 1 && swappiness < MAX_SWAPPINESS); /* * Proactive reclaim initiated by userspace for anonymous memory only. @@ -5225,22 +5240,19 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) lru_gen_prepare_scan(lruvec, sc, memcg, swappiness, nr); while (nr[LRU_GEN_ANON] > 0 || nr[LRU_GEN_FILE] > 0) { - DEFINE_MAX_SEQ(lruvec); + unsigned long orig_file, orig_anon; + unsigned long max_seq; + bool no_populatable; + long scanned; if (mem_cgroup_below_min(sc->target_mem_cgroup, memcg)) { need_rotate = true; break; } - if (should_run_aging(lruvec, max_seq, sc, swappiness)) { - trace_mm_mglru_aging(lruvec, sc, swappiness); - if (try_to_inc_max_seq(lruvec, max_seq, swappiness, false)) - need_rotate = true; - should_age = true; - } - - if (!evict_folio_lists(nr, lruvec, sc, swappiness, MIN_LRU_BATCH)) - break; + orig_anon = nr[LRU_GEN_ANON]; + orig_file = nr[LRU_GEN_FILE]; + scanned = evict_folio_lists(nr, lruvec, sc, swappiness, MIN_LRU_BATCH); if (should_abort_scan(lruvec, sc)) break; @@ -5252,6 +5264,21 @@ static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) if (root_reclaim(sc) && should_age) break; + /* Limit no_populatable aging to once per try_to_shrink_lruvec() */ + no_populatable = allow_no_populatable_age && + ((nr[LRU_GEN_ANON] && nr[LRU_GEN_ANON] == orig_anon) || + (nr[LRU_GEN_FILE] && nr[LRU_GEN_FILE] == orig_file)); + allow_no_populatable_age &= !no_populatable; + max_seq = READ_ONCE((lruvec)->lrugen.max_seq); + if (should_run_aging(lruvec, max_seq, sc, swappiness, no_populatable)) { + trace_mm_mglru_aging(lruvec, sc, swappiness); + if (try_to_inc_max_seq(lruvec, max_seq, swappiness, false)) + need_rotate = true; + should_age = true; + } + if (!scanned) + break; + cond_resched(); } -- 2.39.3 (Apple Git-146)