* [PATCH v2 0/2] mm: multi-gen LRU: replace get_lruvec() with mem_cgroup_lruvec() @ 2026-10-26 20:23 Siddharth Chintamaneni 2026-10-26 20:23 ` [PATCH v2 1/2] mm: multi-gen LRU: use mem_cgroup_lruvec() in get_lruvec() Siddharth Chintamaneni 2026-10-26 20:23 ` [PATCH v2 2/2] mm: multi-gen LRU: remove get_lruvec() Siddharth Chintamaneni 0 siblings, 2 replies; 9+ messages in thread From: Siddharth Chintamaneni @ 2026-10-26 20:23 UTC (permalink / raw) To: akpm, hannes Cc: david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, baohua, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang, yuzhao, linux-mm, linux-kernel, apais, rlmenge, Siddharth Chintamaneni This series cleans up MGLRU's get_lruvec() helper in mm/vmscan.c. Patch 1 updates get_lruvec() to delegate to mem_cgroup_lruvec() rather than returning &pgdat->__lruvec directly when memcg is disabled on the kernel command line. Patch 2 removes get_lruvec() completely and replaces all 15 call sites with mem_cgroup_lruvec(). Siddharth Chintamaneni (2): mm: multi-gen LRU: use mem_cgroup_lruvec() in get_lruvec() mm: multi-gen LRU: remove get_lruvec() mm/vmscan.c | 50 +++++++++++++++----------------------------------- 1 file changed, 15 insertions(+), 35 deletions(-) -- 2.45.4 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/2] mm: multi-gen LRU: use mem_cgroup_lruvec() in get_lruvec() 2026-10-26 20:23 [PATCH v2 0/2] mm: multi-gen LRU: replace get_lruvec() with mem_cgroup_lruvec() Siddharth Chintamaneni @ 2026-10-26 20:23 ` Siddharth Chintamaneni 2026-11-02 22:51 ` Barry Song 2026-10-26 20:23 ` [PATCH v2 2/2] mm: multi-gen LRU: remove get_lruvec() Siddharth Chintamaneni 1 sibling, 1 reply; 9+ messages in thread From: Siddharth Chintamaneni @ 2026-10-26 20:23 UTC (permalink / raw) To: akpm, hannes Cc: david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, baohua, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang, yuzhao, linux-mm, linux-kernel, apais, rlmenge, Siddharth Chintamaneni, Hardik Garg Andrew Morton noted that get_lruvec() has the same pattern as lru_gen_shrink_node(): when memcg is disabled on the command line (cgroup_disable=memory), it returns &pgdat->__lruvec directly instead of going through mem_cgroup_lruvec(), which initializes lruvec->pgdat. In practice all callers of get_lruvec() run after kswapd_init() has already initialized pgdat->__lruvec.pgdat on every N_MEMORY node, so this is not a runtime bug. Have get_lruvec() delegate to mem_cgroup_lruvec() directly. Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com> --- mm/vmscan.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 0c57e4f1eb15..99e9f07fa2e1 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2745,20 +2745,9 @@ static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid) { struct pglist_data *pgdat = NODE_DATA(nid); -#ifdef CONFIG_MEMCG - if (memcg) { - struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec; - - /* see the comment in mem_cgroup_lruvec() */ - if (!lruvec->pgdat) - lruvec->pgdat = pgdat; - - return lruvec; - } -#endif - VM_WARN_ON_ONCE(!mem_cgroup_disabled()); + VM_WARN_ON_ONCE(!memcg && !mem_cgroup_disabled()); - return &pgdat->__lruvec; + return mem_cgroup_lruvec(memcg, pgdat); } static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc) -- 2.45.4 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] mm: multi-gen LRU: use mem_cgroup_lruvec() in get_lruvec() 2026-10-26 20:23 ` [PATCH v2 1/2] mm: multi-gen LRU: use mem_cgroup_lruvec() in get_lruvec() Siddharth Chintamaneni @ 2026-11-02 22:51 ` Barry Song 2026-11-02 22:56 ` Andrew Morton 0 siblings, 1 reply; 9+ messages in thread From: Barry Song @ 2026-11-02 22:51 UTC (permalink / raw) To: Siddharth Chintamaneni Cc: akpm, hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang, yuzhao, linux-mm, linux-kernel, apais, rlmenge, Hardik Garg On Tue, Oct 27, 2026 at 4:23 AM Siddharth Chintamaneni <sidchintamaneni@gmail.com> wrote: > > Andrew Morton noted that get_lruvec() has the same pattern as > lru_gen_shrink_node(): when memcg is disabled on the command line > (cgroup_disable=memory), it returns &pgdat->__lruvec directly instead of > going through mem_cgroup_lruvec(), which initializes lruvec->pgdat. > > In practice all callers of get_lruvec() run after kswapd_init() has > already initialized pgdat->__lruvec.pgdat on every N_MEMORY node, so > this is not a runtime bug. Have get_lruvec() delegate to > mem_cgroup_lruvec() directly. Somehow, I feel this changelog is a bit weird. The earlier description doesn't really justify the final sentence: "Have get_lruvec() delegate to mem_cgroup_lruvec() directly." Can we change it to the below? The patch was initially developed to address a possible NULL reference [1]. Andrew Morton reported that get_lruvec() has the same problem as lru_gen_shrink_node(): when memcg is disabled on the command line (cgroup_disable=memory), it returns &pgdat->__lruvec directly and so bypasses the initialization of lruvec->pgdat. The MGLRU debugfs interface calls get_lruvec(), then dereferences the pgdat pointer in lru_gen_seq_show() and other paths, so these can hit the same NULL dereference. In practice, we found that this cannot happen, as all callers of get_lruvec() run after kswapd_init() has initialized pgdat->__lruvec.pgdat on every N_MEMORY node. Therefore, this is not a runtime bug. However, it would still be good to clean up get_lruvec() by removing the MGLRU-specific code and having it delegate directly to mem_cgroup_lruvec(). [1] https://lore.kernel.org/linux-mm/20261017050928.128582-2-sidchintamaneni@gmail.com/ > > Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> > Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com> > Signed-off-by: Hardik Garg <hargar@linux.microsoft.com> > --- With the changelog updated as suggested, Reviewed-by: Barry Song <baohua@kernel.org> ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 1/2] mm: multi-gen LRU: use mem_cgroup_lruvec() in get_lruvec() 2026-11-02 22:51 ` Barry Song @ 2026-11-02 22:56 ` Andrew Morton 0 siblings, 0 replies; 9+ messages in thread From: Andrew Morton @ 2026-11-02 22:56 UTC (permalink / raw) To: Barry Song Cc: Siddharth Chintamaneni, hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang, yuzhao, linux-mm, linux-kernel, apais, rlmenge, Hardik Garg On Tue, 3 Nov 2026 06:51:19 +0800 Barry Song <baohua@kernel.org> wrote: > On Tue, Oct 27, 2026 at 4:23 AM Siddharth Chintamaneni > <sidchintamaneni@gmail.com> wrote: > > > > Andrew Morton noted that get_lruvec() has the same pattern as > > lru_gen_shrink_node(): when memcg is disabled on the command line > > (cgroup_disable=memory), it returns &pgdat->__lruvec directly instead of > > going through mem_cgroup_lruvec(), which initializes lruvec->pgdat. > > > > In practice all callers of get_lruvec() run after kswapd_init() has > > already initialized pgdat->__lruvec.pgdat on every N_MEMORY node, so > > this is not a runtime bug. Have get_lruvec() delegate to > > mem_cgroup_lruvec() directly. > > Somehow, I feel this changelog is a bit weird. The earlier > description doesn't really justify the final sentence: > "Have get_lruvec() delegate to mem_cgroup_lruvec() directly." > > Can we change it to the below? > > The patch was initially developed to address a possible NULL > reference [1]. > > Andrew Morton reported that get_lruvec() has the same problem as > lru_gen_shrink_node(): when memcg is disabled on the command line > (cgroup_disable=memory), it returns &pgdat->__lruvec directly and so > bypasses the initialization of lruvec->pgdat. The MGLRU debugfs > interface calls get_lruvec(), then dereferences the pgdat pointer in > lru_gen_seq_show() and other paths, so these can hit the same NULL > dereference. > > In practice, we found that this cannot happen, as all callers of > get_lruvec() run after kswapd_init() has initialized > pgdat->__lruvec.pgdat on every N_MEMORY node. Therefore, this > is not a runtime bug. > > However, it would still be good to clean up get_lruvec() by > removing the MGLRU-specific code and having it delegate > directly to mem_cgroup_lruvec(). > > [1] https://lore.kernel.org/linux-mm/20261017050928.128582-2-sidchintamaneni@gmail.com/ Thanks, I locally made that change. From: Siddharth Chintamaneni <sidchintamaneni@gmail.com> Subject: mm: multi-gen LRU: use mem_cgroup_lruvec() in get_lruvec() Date: Mon, 26 Oct 2026 20:23:12 +0000 Patch series "mm: multi-gen LRU: replace get_lruvec() with mem_cgroup_lruvec()", v2. This series cleans up MGLRU's get_lruvec() helper in mm/vmscan.c. Patch 1 updates get_lruvec() to delegate to mem_cgroup_lruvec() rather than returning &pgdat->__lruvec directly when memcg is disabled on the kernel command line. Patch 2 removes get_lruvec() completely and replaces all 15 call sites with mem_cgroup_lruvec(). This patch (of 2): The patch was initially developed to address a possible NULL reference [1]. Andrew Morton reported that get_lruvec() has the same problem as lru_gen_shrink_node(): when memcg is disabled on the command line (cgroup_disable=memory), it returns &pgdat->__lruvec directly and so bypasses the initialization of lruvec->pgdat. The MGLRU debugfs interface calls get_lruvec(), then dereferences the pgdat pointer in lru_gen_seq_show() and other paths, so these can hit the same NULL dereference. In practice, we found that this cannot happen, as all callers of get_lruvec() run after kswapd_init() has initialized pgdat->__lruvec.pgdat on every N_MEMORY node. Therefore, this is not a runtime bug. However, it would still be good to clean up get_lruvec() by removing the MGLRU-specific code and having it delegate directly to mem_cgroup_lruvec(). Link: https://lore.kernel.org/20261026202313.63122-1-sidchintamaneni@gmail.com Link: https://lore.kernel.org/20261026202313.63122-2-sidchintamaneni@gmail.com Link: https://lore.kernel.org/linux-mm/20261017050928.128582-2-sidchintamaneni@gmail.com/ [1] Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> Reviewed-by: Barry Song <baohua@kernel.org> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: David Hildenbrand <david@kernel.org> Cc: Michal Hocko <mhocko@kernel.org> Cc: Qi Zheng <qi.zheng@linux.dev> Cc: Shakeel Butt <shakeel.butt@linux.dev> Cc: Lorenzo Stoakes <ljs@kernel.org> Cc: Kairui Song <kasong@tencent.com> Cc: Axel Rasmussen <axelrasmussen@google.com> Cc: Yuanchu Xie <yuanchu@google.com> Cc: Wei Xu <weixugc@google.com> Cc: Baoquan He <baoquan.he@linux.dev> Cc: Yu Zhao <yuzhao@google.com> Cc: <apais@microsoft.com> Cc: <rlmenge@gmail.com> --- mm/vmscan.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) --- a/mm/vmscan.c~mm-multi-gen-lru-use-mem_cgroup_lruvec-in-get_lruvec +++ a/mm/vmscan.c @@ -2866,20 +2866,9 @@ static struct lruvec *get_lruvec(struct { struct pglist_data *pgdat = NODE_DATA(nid); -#ifdef CONFIG_MEMCG - if (memcg) { - struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec; + VM_WARN_ON_ONCE(!memcg && !mem_cgroup_disabled()); - /* see the comment in mem_cgroup_lruvec() */ - if (!lruvec->pgdat) - lruvec->pgdat = pgdat; - - return lruvec; - } -#endif - VM_WARN_ON_ONCE(!mem_cgroup_disabled()); - - return &pgdat->__lruvec; + return mem_cgroup_lruvec(memcg, pgdat); } static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc) _ ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 2/2] mm: multi-gen LRU: remove get_lruvec() 2026-10-26 20:23 [PATCH v2 0/2] mm: multi-gen LRU: replace get_lruvec() with mem_cgroup_lruvec() Siddharth Chintamaneni 2026-10-26 20:23 ` [PATCH v2 1/2] mm: multi-gen LRU: use mem_cgroup_lruvec() in get_lruvec() Siddharth Chintamaneni @ 2026-10-26 20:23 ` Siddharth Chintamaneni 2026-11-02 22:56 ` Barry Song 1 sibling, 1 reply; 9+ messages in thread From: Siddharth Chintamaneni @ 2026-10-26 20:23 UTC (permalink / raw) To: akpm, hannes Cc: david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, baohua, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang, yuzhao, linux-mm, linux-kernel, apais, rlmenge, Siddharth Chintamaneni, Hardik Garg get_lruvec() was originally introduced in commit ec1c86b25f4b ("mm: multi-gen LRU: groundwork") to guard against NULL NODE_DATA(nid) on uninitialized NUMA nodes (`return pgdat ? &pgdat->__lruvec : NULL;`). After commit 09f49dca570a ("mm: handle uninitialized numa nodes gracefully") and commit 931b6a8b36a2 ("mm: multi-gen LRU: remove NULL checks on NODE_DATA()"), that NULL check was removed, leaving get_lruvec() as a thin wrapper around mem_cgroup_lruvec(). Remove get_lruvec() and call mem_cgroup_lruvec() directly at all call sites. Suggested-by: Barry Song <baohua@kernel.org> Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com> --- mm/vmscan.c | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 99e9f07fa2e1..5767b94fd8e8 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2741,15 +2741,6 @@ static bool should_clear_pmd_young(void) #define get_memcg_gen(seq) ((seq) % MEMCG_NR_GENS) #define get_memcg_bin(bin) ((bin) % MEMCG_NR_BINS) -static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid) -{ - struct pglist_data *pgdat = NODE_DATA(nid); - - VM_WARN_ON_ONCE(!memcg && !mem_cgroup_disabled()); - - return mem_cgroup_lruvec(memcg, pgdat); -} - static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc) { struct mem_cgroup *memcg = lruvec_memcg(lruvec); @@ -2942,7 +2933,7 @@ void lru_gen_add_mm(struct mm_struct *mm) spin_lock(&mm_list->lock); for_each_node_state(nid, N_MEMORY) { - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); struct lru_gen_mm_state *mm_state = get_mm_state(lruvec); /* the first addition since the last iteration */ @@ -2972,7 +2963,7 @@ void lru_gen_del_mm(struct mm_struct *mm) spin_lock(&mm_list->lock); for_each_node(nid) { - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); struct lru_gen_mm_state *mm_state = get_mm_state(lruvec); /* where the current iteration continues after */ @@ -4424,7 +4415,7 @@ void lru_gen_online_memcg(struct mem_cgroup *memcg) for_each_node(nid) { struct pglist_data *pgdat = NODE_DATA(nid); - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); spin_lock_irq(&pgdat->memcg_lru.lock); @@ -4446,7 +4437,7 @@ void lru_gen_offline_memcg(struct mem_cgroup *memcg) int nid; for_each_node(nid) { - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); lru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD); } @@ -4459,7 +4450,7 @@ void lru_gen_release_memcg(struct mem_cgroup *memcg) for_each_node(nid) { struct pglist_data *pgdat = NODE_DATA(nid); - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); spin_lock_irq(&pgdat->memcg_lru.lock); @@ -4480,7 +4471,7 @@ void lru_gen_release_memcg(struct mem_cgroup *memcg) void lru_gen_soft_reclaim(struct mem_cgroup *memcg, int nid) { - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); /* see the comment on MEMCG_NR_GENS */ if (READ_ONCE(lruvec->lrugen.seg) != MEMCG_LRU_HEAD) @@ -4489,7 +4480,7 @@ void lru_gen_soft_reclaim(struct mem_cgroup *memcg, int nid) bool recheck_lru_gen_max_memcg(struct mem_cgroup *memcg, int nid) { - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); int type; for (type = 0; type < ANON_AND_FILE; type++) { @@ -4536,7 +4527,7 @@ static void try_to_inc_max_seq_nowalk(struct mem_cgroup *memcg, */ void max_lru_gen_memcg(struct mem_cgroup *memcg, int nid) { - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); int type; for (type = 0; type < ANON_AND_FILE; type++) { @@ -4613,8 +4604,8 @@ void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, struct zone *zone; enum lru_list lru; - child_lruvec = get_lruvec(memcg, nid); - parent_lruvec = get_lruvec(parent, nid); + child_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); + parent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid)); for_each_managed_zone_pgdat(zone, NODE_DATA(nid), zid, MAX_NR_ZONES - 1) for (type = 0; type < ANON_AND_FILE; type++) @@ -5370,7 +5361,7 @@ static void lru_gen_change_state(bool enabled) int nid; for_each_node(nid) { - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); lruvec_lock_irq(lruvec); @@ -5501,7 +5492,7 @@ static void *lru_gen_seq_start(struct seq_file *m, loff_t *pos) for_each_node_state(nid, N_MEMORY) { if (!nr_to_skip--) - return get_lruvec(memcg, nid); + return mem_cgroup_lruvec(memcg, NODE_DATA(nid)); } } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); @@ -5533,7 +5524,7 @@ static void *lru_gen_seq_next(struct seq_file *m, void *v, loff_t *pos) nid = first_memory_node; } - return get_lruvec(memcg, nid); + return mem_cgroup_lruvec(memcg, NODE_DATA(nid)); } static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, @@ -5715,7 +5706,7 @@ static int run_cmd(char cmd, u64 memcg_id, int nid, unsigned long seq, goto done; sc->target_mem_cgroup = memcg; - lruvec = get_lruvec(memcg, nid); + lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); if (swappiness < MIN_SWAPPINESS) swappiness = get_swappiness(lruvec, sc); @@ -5899,7 +5890,7 @@ void lru_gen_exit_memcg(struct mem_cgroup *memcg) VM_WARN_ON_ONCE(mm_list && !list_empty(&mm_list->fifo)); for_each_node(nid) { - struct lruvec *lruvec = get_lruvec(memcg, nid); + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid)); struct lru_gen_mm_state *mm_state = get_mm_state(lruvec); VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0, -- 2.45.4 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: remove get_lruvec() 2026-10-26 20:23 ` [PATCH v2 2/2] mm: multi-gen LRU: remove get_lruvec() Siddharth Chintamaneni @ 2026-11-02 22:56 ` Barry Song 2026-11-02 23:35 ` Siddharth Chintamaneni 0 siblings, 1 reply; 9+ messages in thread From: Barry Song @ 2026-11-02 22:56 UTC (permalink / raw) To: Siddharth Chintamaneni Cc: akpm, hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang, yuzhao, linux-mm, linux-kernel, apais, rlmenge, Hardik Garg On Tue, Oct 27, 2026 at 4:23 AM Siddharth Chintamaneni <sidchintamaneni@gmail.com> wrote: > > get_lruvec() was originally introduced in commit ec1c86b25f4b ("mm: > multi-gen LRU: groundwork") to guard against NULL NODE_DATA(nid) on > uninitialized NUMA nodes (`return pgdat ? &pgdat->__lruvec : NULL;`). > After commit 09f49dca570a ("mm: handle uninitialized numa nodes > gracefully") and commit 931b6a8b36a2 ("mm: multi-gen LRU: remove NULL > checks on NODE_DATA()"), that NULL check was removed, leaving > get_lruvec() as a thin wrapper around mem_cgroup_lruvec(). > > Remove get_lruvec() and call mem_cgroup_lruvec() directly at all call > sites. > > Suggested-by: Barry Song <baohua@kernel.org> > Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> > Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com> > Signed-off-by: Hardik Garg <hargar@linux.microsoft.com> > --- It's great to see the MGLRU-specific get_lruvec() gone. Thanks! Hopefully, we can get rid of more MGLRU-specific code, such as get_swappiness(). Reviewed-by: Barry Song <baohua@kernel.org> ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: remove get_lruvec() 2026-11-02 22:56 ` Barry Song @ 2026-11-02 23:35 ` Siddharth Chintamaneni 2026-11-03 0:20 ` Barry Song 0 siblings, 1 reply; 9+ messages in thread From: Siddharth Chintamaneni @ 2026-11-02 23:35 UTC (permalink / raw) To: Barry Song Cc: akpm, hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang, yuzhao, linux-mm, linux-kernel, apais, rlmenge, Hardik Garg On Mon, 2 Nov 2026 at 14:56, Barry Song <baohua@kernel.org> wrote: > > On Tue, Oct 27, 2026 at 4:23 AM Siddharth Chintamaneni > <sidchintamaneni@gmail.com> wrote: > > > > get_lruvec() was originally introduced in commit ec1c86b25f4b ("mm: > > multi-gen LRU: groundwork") to guard against NULL NODE_DATA(nid) on > > uninitialized NUMA nodes (`return pgdat ? &pgdat->__lruvec : NULL;`). > > After commit 09f49dca570a ("mm: handle uninitialized numa nodes > > gracefully") and commit 931b6a8b36a2 ("mm: multi-gen LRU: remove NULL > > checks on NODE_DATA()"), that NULL check was removed, leaving > > get_lruvec() as a thin wrapper around mem_cgroup_lruvec(). > > > > Remove get_lruvec() and call mem_cgroup_lruvec() directly at all call > > sites. > > > > Suggested-by: Barry Song <baohua@kernel.org> > > Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> > > Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com> > > Signed-off-by: Hardik Garg <hargar@linux.microsoft.com> > > --- > > It's great to see the MGLRU-specific get_lruvec() gone. Thanks! > Hopefully, we can get rid of more MGLRU-specific code, > such as get_swappiness(). > I just looked into removing get_swappiness() in favor of its LRU variant (sc_swappiness()). Do you mean moving the additional conditionals directly into the callsites? something like this - <AI Suggested> diff --git a/mm/vmscan.c b/mm/vmscan.c --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2761,25 +2761,6 @@ static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid) return &pgdat->__lruvec; } -static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc) -{ - struct mem_cgroup *memcg = lruvec_memcg(lruvec); - struct pglist_data *pgdat = lruvec_pgdat(lruvec); - int swappiness = sc_swappiness(sc, memcg); - - if (swappiness == SWAPPINESS_ANON_ONLY) - return swappiness; - - if (!sc->may_swap) - return 0; - - if (!can_demote(pgdat->node_id, sc, memcg) && - mem_cgroup_get_nr_swap_pages(memcg) < MIN_LRU_BATCH) - return 0; - - return swappiness; -} - static int get_nr_gens(struct lruvec *lruvec, int type) { return lruvec->lrugen.max_seq - lruvec->lrugen.min_seq[type] + 1; @@ -4173,8 +4154,13 @@ static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc) { unsigned long total; - int swappiness = get_swappiness(lruvec, sc); struct mem_cgroup *memcg = lruvec_memcg(lruvec); + struct pglist_data *pgdat = lruvec_pgdat(lruvec); + int swappiness = sc_swappiness(sc, memcg); + + if (swappiness != SWAPPINESS_ANON_ONLY && + (!sc->may_swap || !can_reclaim_anon_pages(memcg, pgdat->node_id, sc))) + swappiness = 0; > Reviewed-by: Barry Song <baohua@kernel.org> ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: remove get_lruvec() 2026-11-02 23:35 ` Siddharth Chintamaneni @ 2026-11-03 0:20 ` Barry Song 2026-11-03 1:18 ` Siddharth Chintamaneni 0 siblings, 1 reply; 9+ messages in thread From: Barry Song @ 2026-11-03 0:20 UTC (permalink / raw) To: Siddharth Chintamaneni Cc: akpm, hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang, yuzhao, linux-mm, linux-kernel, apais, rlmenge, Hardik Garg On Tue, Nov 3, 2026 at 7:35 AM Siddharth Chintamaneni <sidchintamaneni@gmail.com> wrote: > > On Mon, 2 Nov 2026 at 14:56, Barry Song <baohua@kernel.org> wrote: > > > > On Tue, Oct 27, 2026 at 4:23 AM Siddharth Chintamaneni > > <sidchintamaneni@gmail.com> wrote: > > > > > > get_lruvec() was originally introduced in commit ec1c86b25f4b ("mm: > > > multi-gen LRU: groundwork") to guard against NULL NODE_DATA(nid) on > > > uninitialized NUMA nodes (`return pgdat ? &pgdat->__lruvec : NULL;`). > > > After commit 09f49dca570a ("mm: handle uninitialized numa nodes > > > gracefully") and commit 931b6a8b36a2 ("mm: multi-gen LRU: remove NULL > > > checks on NODE_DATA()"), that NULL check was removed, leaving > > > get_lruvec() as a thin wrapper around mem_cgroup_lruvec(). > > > > > > Remove get_lruvec() and call mem_cgroup_lruvec() directly at all call > > > sites. > > > > > > Suggested-by: Barry Song <baohua@kernel.org> > > > Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> > > > Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com> > > > Signed-off-by: Hardik Garg <hargar@linux.microsoft.com> > > > --- > > > > It's great to see the MGLRU-specific get_lruvec() gone. Thanks! > > Hopefully, we can get rid of more MGLRU-specific code, > > such as get_swappiness(). > > > > I just looked into removing get_swappiness() in favor of its LRU variant > (sc_swappiness()). Do you mean moving the additional conditionals > directly into the callsites? Nop. In MGLRU, swappiness has values: 0, 1–200, and 201. With swappiness=0, MGLRU only reclaims file pages and doesn't care about the hotness of anon pages. With swappiness=201, MGLRU only reclaims anon pages and doesn't care about the hotness of file pages. In both cases, we allow cold/hot inversions in inc_min_seq(), which can help reduce CPU usage: static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness) { /* For file type, skip the check if swappiness is anon only */ if (type && (swappiness == SWAPPINESS_ANON_ONLY)) goto done; /* For anon type, skip the check if swappiness is zero (file only) */ if (!type && !swappiness) goto done; ... } Then we could have cases where swappiness is 1 ~ 200, but we still cannot reclaim anon due to: 1. Swap is full, or the memcg's swap quota is exhausted. 2. The allocation uses GFP_NOIO, but the swap cache is low [1]. So right now, we have to hack get_swappiness() to return 0 to ensure that reclaim only happens on file pages, or we at least need to hack reclamation path to use swappiness=0. This is quite bad, as it can cause cold/hot inversions for anon pages. I believe this is already a bug in the current get_swappiness() implementation when swap is full. Ideally, swappiness values from 1 to 200 and the reclaimable type should be orthogonal; they should not be coupled. In other words, we shouldn't need to force swappiness=0 to reclaim only file pages. If swappiness is set to 60, it should remain 60 across all MGLRU reclaim paths. The reclaimable type should be handled separately, without forcing swappiness to 0 and introducing confusion and bugs. [1] https://lore.kernel.org/linux-mm/179248830836.1744016.8424217811286616919.noio-mglru-rfc-v2@bytedance.com/ Best, Barry ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: remove get_lruvec() 2026-11-03 0:20 ` Barry Song @ 2026-11-03 1:18 ` Siddharth Chintamaneni 0 siblings, 0 replies; 9+ messages in thread From: Siddharth Chintamaneni @ 2026-11-03 1:18 UTC (permalink / raw) To: Barry Song Cc: akpm, hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang, yuzhao, linux-mm, linux-kernel, apais, rlmenge, Hardik Garg On Mon, 2 Nov 2026 at 16:20, Barry Song <baohua@kernel.org> wrote: > > On Tue, Nov 3, 2026 at 7:35 AM Siddharth Chintamaneni > <sidchintamaneni@gmail.com> wrote: > > > > On Mon, 2 Nov 2026 at 14:56, Barry Song <baohua@kernel.org> wrote: > > > > > > On Tue, Oct 27, 2026 at 4:23 AM Siddharth Chintamaneni > > > <sidchintamaneni@gmail.com> wrote: > > > > > > > > get_lruvec() was originally introduced in commit ec1c86b25f4b ("mm: > > > > multi-gen LRU: groundwork") to guard against NULL NODE_DATA(nid) on > > > > uninitialized NUMA nodes (`return pgdat ? &pgdat->__lruvec : NULL;`). > > > > After commit 09f49dca570a ("mm: handle uninitialized numa nodes > > > > gracefully") and commit 931b6a8b36a2 ("mm: multi-gen LRU: remove NULL > > > > checks on NODE_DATA()"), that NULL check was removed, leaving > > > > get_lruvec() as a thin wrapper around mem_cgroup_lruvec(). > > > > > > > > Remove get_lruvec() and call mem_cgroup_lruvec() directly at all call > > > > sites. > > > > > > > > Suggested-by: Barry Song <baohua@kernel.org> > > > > Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com> > > > > Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com> > > > > Signed-off-by: Hardik Garg <hargar@linux.microsoft.com> > > > > --- > > > > > > It's great to see the MGLRU-specific get_lruvec() gone. Thanks! > > > Hopefully, we can get rid of more MGLRU-specific code, > > > such as get_swappiness(). > > > > > > > I just looked into removing get_swappiness() in favor of its LRU variant > > (sc_swappiness()). Do you mean moving the additional conditionals > > directly into the callsites? > > Nop. In MGLRU, swappiness has values: 0, 1–200, > and 201. > > With swappiness=0, MGLRU only reclaims file pages and > doesn't care about the hotness of anon pages. > > With swappiness=201, MGLRU only reclaims anon pages and > doesn't care about the hotness of file pages. > > In both cases, we allow cold/hot inversions in > inc_min_seq(), which can help reduce CPU usage: > > static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness) > { > > /* For file type, skip the check if swappiness is anon only */ > if (type && (swappiness == SWAPPINESS_ANON_ONLY)) > goto done; > > /* For anon type, skip the check if swappiness is zero (file only) */ > if (!type && !swappiness) > goto done; > ... > } > Interesting! > Then we could have cases where swappiness is 1 ~ 200, but > we still cannot reclaim anon due to: > > 1. Swap is full, or the memcg's swap quota is exhausted. > > 2. The allocation uses GFP_NOIO, but the swap cache is low > [1]. > > So right now, we have to hack get_swappiness() to return 0 > to ensure that reclaim only happens on file pages, or we at > least need to hack reclamation path to use swappiness=0. > > This is quite bad, as it can cause cold/hot inversions for > anon pages. I believe this is already a bug in the current > get_swappiness() implementation when swap is full. > > Ideally, swappiness values from 1 to 200 and the reclaimable > type should be orthogonal; they should not be coupled. > > In other words, we shouldn't need to force swappiness=0 to > reclaim only file pages. If swappiness is set to 60, it > should remain 60 across all MGLRU reclaim paths. The > reclaimable type should be handled separately, without > forcing swappiness to 0 and introducing confusion and bugs. > This is more involved than I initially thought. Let me play around with it a bit and come back. If you have any suggestions with experiments that I could run to get a good understanding on this problem, let me know. > [1] https://lore.kernel.org/linux-mm/179248830836.1744016.8424217811286616919.noio-mglru-rfc-v2@bytedance.com/ > > Best, > Barry ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-11-03 1:18 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-26 20:23 [PATCH v2 0/2] mm: multi-gen LRU: replace get_lruvec() with mem_cgroup_lruvec() Siddharth Chintamaneni 2026-10-26 20:23 ` [PATCH v2 1/2] mm: multi-gen LRU: use mem_cgroup_lruvec() in get_lruvec() Siddharth Chintamaneni 2026-11-02 22:51 ` Barry Song 2026-11-02 22:56 ` Andrew Morton 2026-10-26 20:23 ` [PATCH v2 2/2] mm: multi-gen LRU: remove get_lruvec() Siddharth Chintamaneni 2026-11-02 22:56 ` Barry Song 2026-11-02 23:35 ` Siddharth Chintamaneni 2026-11-03 0:20 ` Barry Song 2026-11-03 1:18 ` Siddharth Chintamaneni
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®