Concurrent reclaimers can request promotion after another reclaimer has already moved a memcg into the young generation. Requeueing it into a random bin again adds list mutations without advancing the old-generation scan.
Therefore, skip `MEMCG_LRU_YOUNG` when the memcg is already in the default segment of the current young generation. In my micro benchmark, this increases the request rate by 36% and reduces the average duration of each write request by 27%. Signed-off-by: Max Kellermann <[email protected]> --- mm/vmscan.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mm/vmscan.c b/mm/vmscan.c index 69976df5d537..922c59eb9629 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4445,6 +4445,14 @@ static void lru_gen_rotate_memcg(struct lruvec *lruvec, int op) else VM_WARN_ON_ONCE(true); + if (op == MEMCG_LRU_YOUNG && old == new && lruvec->lrugen.seg == seg) + /* + * This memcg is already in the young generation and + * reshuffling it again cannot help the scan of the + * old generation. + */ + goto unlock; + WRITE_ONCE(lruvec->lrugen.seg, seg); WRITE_ONCE(lruvec->lrugen.gen, new); @@ -4458,6 +4466,7 @@ static void lru_gen_rotate_memcg(struct lruvec *lruvec, int op) if (!pgdat->memcg_lru.nr_memcgs[old] && old == get_memcg_gen(pgdat->memcg_lru.seq)) WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1); +unlock: spin_unlock_irqrestore(&pgdat->memcg_lru.lock, flags); } -- 2.47.3

