* [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline
@ 2026-10-17 5:09 Siddharth Chintamaneni
2026-10-17 5:09 ` [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec() Siddharth Chintamaneni
` (3 more replies)
0 siblings, 4 replies; 16+ messages in thread
From: Siddharth Chintamaneni @ 2026-10-17 5:09 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,
stable, Hardik Garg
When CONFIG_MEMCG=y, lruvec_pgdat() returns lruvec->pgdat, which for the
per-node pgdat->__lruvec is left NULL by lruvec_init() and only filled in
lazily by mem_cgroup_lruvec(). Normally that happens early, e.g. on the
first folio_add_lru().
With cgroup_disable=memory, lru_gen_shrink_node() passes &pgdat->__lruvec
directly to shrink_one(). If direct reclaim runs before anything has
called mem_cgroup_lruvec() for the node, which can happen during early
boot under memory pressure (e.g. a kdump kernel), get_swappiness() ends
up dereferencing a NULL pgdat:
Unable to handle kernel NULL pointer dereference at virtual address 0000000000005040
pc : get_swappiness+0x68/0xb0
lr : try_to_shrink_lruvec+0x54/0x2c0
Call trace:
get_swappiness+0x68/0xb0 (P)
try_to_shrink_lruvec+0x54/0x2c0
shrink_one+0x8c/0x240
shrink_node+0xc6c/0xe08
do_try_to_free_pages+0xdc/0x570
try_to_free_pages+0x124/0x278
__alloc_frozen_pages_noprof+0x6e8/0xf78
alloc_pages_mpol+0xc4/0x1c0
alloc_frozen_pages_noprof+0x54/0xe8
alloc_pages_noprof+0x1c/0x40
atomic_pool_expand+0x98/0x300
__dma_atomic_pool_init+0x58/0xd8
dma_atomic_pool_init+0x154/0x170
do_one_initcall+0x64/0x350
kernel_init_freeable+0x214/0x430
kernel_init+0x2c/0x208
ret_from_fork+0x10/0x20
The classic LRU path and MGLRU before per-node lru_gen_folio lists
obtained the lruvec through mem_cgroup_lruvec(), which sets
lruvec->pgdat.
The above was hit on an arm64 kdump kernel (6.18-based) booted with
cgroup_disable=memory nr_cpus=4. It also reproduces on mainline on an
arm64 VM with CONFIG_ARM64_64K_PAGES=y, CONFIG_MEMCG=y,
CONFIG_LRU_GEN_ENABLED=y and:
mem=160M nr_cpus=4 coherent_pool=512M cgroup_disable=memory
These options only serve to force direct reclaim early in boot; with
160M the boot eventually ends in OOM regardless. On mainline the first
LRU reclaim happens while unpacking the initramfs, and faults the same
way:
Unable to handle kernel NULL pointer dereference at virtual address 0000000000005040
pc : get_swappiness+0xd4/0x150
Call trace:
get_swappiness+0xd4/0x150 (P)
try_to_shrink_lruvec+0x44/0x2b8
shrink_one+0x80/0x1a0
shrink_node+0xd98/0xea8
do_try_to_free_pages+0xf4/0x4b0
try_to_free_pages+0x110/0x360
__alloc_frozen_pages_noprof+0x8b0/0x1718
...
__filemap_get_folio_mpol+0x2ac/0x548
simple_write_begin+0x44/0xf8
page_symlink+0x178/0x198
...
do_populate_rootfs+0x13c/0x200
Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
Cc: stable@vger.kernel.org # 6.3+
Assisted-by: LLM
Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
---
mm/vmscan.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index f11491ee9ed5..0c57e4f1eb15 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -5253,7 +5253,7 @@ static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *
sc->nr_reclaimed = 0;
if (mem_cgroup_disabled())
- shrink_one(&pgdat->__lruvec, sc);
+ shrink_one(mem_cgroup_lruvec(NULL, pgdat), sc);
else
shrink_many(pgdat, sc);
--
2.45.4
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-17 5:09 [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline Siddharth Chintamaneni
@ 2026-10-17 5:09 ` Siddharth Chintamaneni
2026-10-18 21:24 ` Barry Song
2026-10-17 20:18 ` [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline Andrew Morton
` (2 subsequent siblings)
3 siblings, 1 reply; 16+ messages in thread
From: Siddharth Chintamaneni @ 2026-10-17 5:09 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,
stable, Hardik Garg
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.
Fixes: ec1c86b25f4b ("mm: multi-gen LRU: groundwork")
Cc: stable@vger.kernel.org # 6.2+
Assisted-by: LLM
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 related [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline
2026-10-17 5:09 [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline Siddharth Chintamaneni
2026-10-17 5:09 ` [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec() Siddharth Chintamaneni
@ 2026-10-17 20:18 ` Andrew Morton
2026-10-18 21:04 ` Barry Song
2026-10-21 6:15 ` Baolin Wang
3 siblings, 0 replies; 16+ messages in thread
From: Andrew Morton @ 2026-10-17 20:18 UTC (permalink / raw)
To: Siddharth Chintamaneni
Cc: hannes, david, mhocko, qi.zheng, shakeel.butt, ljs, kasong,
baohua, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
yuzhao, linux-mm, linux-kernel, apais, rlmenge, stable,
Hardik Garg
On Sat, 17 Oct 2026 05:09:27 +0000 Siddharth Chintamaneni <sidchintamaneni@gmail.com> wrote:
> When CONFIG_MEMCG=y, lruvec_pgdat() returns lruvec->pgdat, which for the
> per-node pgdat->__lruvec is left NULL by lruvec_init() and only filled in
> lazily by mem_cgroup_lruvec(). Normally that happens early, e.g. on the
> first folio_add_lru().
>
> With cgroup_disable=memory, lru_gen_shrink_node() passes &pgdat->__lruvec
> directly to shrink_one(). If direct reclaim runs before anything has
> called mem_cgroup_lruvec() for the node, which can happen during early
> boot under memory pressure (e.g. a kdump kernel), get_swappiness() ends
> up dereferencing a NULL pgdat:
Thanks, v2 lgtm.
Maintainers: please review and let us know whether you agree with the
cc:stable on both.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline
2026-10-17 5:09 [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline Siddharth Chintamaneni
2026-10-17 5:09 ` [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec() Siddharth Chintamaneni
2026-10-17 20:18 ` [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline Andrew Morton
@ 2026-10-18 21:04 ` Barry Song
2026-10-19 20:37 ` Siddharth Chintamaneni
2026-10-21 6:15 ` Baolin Wang
3 siblings, 1 reply; 16+ messages in thread
From: Barry Song @ 2026-10-18 21:04 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, stable, Hardik Garg
On Sat, Oct 17, 2026 at 1:09 PM Siddharth Chintamaneni
<sidchintamaneni@gmail.com> wrote:
>
> When CONFIG_MEMCG=y, lruvec_pgdat() returns lruvec->pgdat, which for the
> per-node pgdat->__lruvec is left NULL by lruvec_init() and only filled in
> lazily by mem_cgroup_lruvec(). Normally that happens early, e.g. on the
> first folio_add_lru().
>
> With cgroup_disable=memory, lru_gen_shrink_node() passes &pgdat->__lruvec
> directly to shrink_one(). If direct reclaim runs before anything has
> called mem_cgroup_lruvec() for the node, which can happen during early
> boot under memory pressure (e.g. a kdump kernel), get_swappiness() ends
> up dereferencing a NULL pgdat:
>
> Unable to handle kernel NULL pointer dereference at virtual address 0000000000005040
> pc : get_swappiness+0x68/0xb0
> lr : try_to_shrink_lruvec+0x54/0x2c0
> Call trace:
> get_swappiness+0x68/0xb0 (P)
> try_to_shrink_lruvec+0x54/0x2c0
> shrink_one+0x8c/0x240
> shrink_node+0xc6c/0xe08
> do_try_to_free_pages+0xdc/0x570
> try_to_free_pages+0x124/0x278
> __alloc_frozen_pages_noprof+0x6e8/0xf78
> alloc_pages_mpol+0xc4/0x1c0
> alloc_frozen_pages_noprof+0x54/0xe8
> alloc_pages_noprof+0x1c/0x40
> atomic_pool_expand+0x98/0x300
> __dma_atomic_pool_init+0x58/0xd8
> dma_atomic_pool_init+0x154/0x170
> do_one_initcall+0x64/0x350
> kernel_init_freeable+0x214/0x430
> kernel_init+0x2c/0x208
> ret_from_fork+0x10/0x20
>
> The classic LRU path and MGLRU before per-node lru_gen_folio lists
> obtained the lruvec through mem_cgroup_lruvec(), which sets
> lruvec->pgdat.
>
> The above was hit on an arm64 kdump kernel (6.18-based) booted with
> cgroup_disable=memory nr_cpus=4. It also reproduces on mainline on an
> arm64 VM with CONFIG_ARM64_64K_PAGES=y, CONFIG_MEMCG=y,
> CONFIG_LRU_GEN_ENABLED=y and:
>
> mem=160M nr_cpus=4 coherent_pool=512M cgroup_disable=memory
>
> These options only serve to force direct reclaim early in boot; with
> 160M the boot eventually ends in OOM regardless. On mainline the first
> LRU reclaim happens while unpacking the initramfs, and faults the same
> way:
>
> Unable to handle kernel NULL pointer dereference at virtual address 0000000000005040
> pc : get_swappiness+0xd4/0x150
> Call trace:
> get_swappiness+0xd4/0x150 (P)
> try_to_shrink_lruvec+0x44/0x2b8
> shrink_one+0x80/0x1a0
> shrink_node+0xd98/0xea8
> do_try_to_free_pages+0xf4/0x4b0
> try_to_free_pages+0x110/0x360
> __alloc_frozen_pages_noprof+0x8b0/0x1718
> ...
> __filemap_get_folio_mpol+0x2ac/0x548
> simple_write_begin+0x44/0xf8
> page_symlink+0x178/0x198
> ...
> do_populate_rootfs+0x13c/0x200
>
So this happens before any folios are added to the LRU and before
the userspace init process starts?
> Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
> Cc: stable@vger.kernel.org # 6.3+
> Assisted-by: LLM
> Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
> ---
> mm/vmscan.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/mm/vmscan.c b/mm/vmscan.c
> index f11491ee9ed5..0c57e4f1eb15 100644
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -5253,7 +5253,7 @@ static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *
> sc->nr_reclaimed = 0;
>
> if (mem_cgroup_disabled())
> - shrink_one(&pgdat->__lruvec, sc);
> + shrink_one(mem_cgroup_lruvec(NULL, pgdat), sc);
What's a bit weird is that even if we fix this here, with an empty
lruvec, we still won't be able to reclaim a single page. But it's
hard to see where we could break out early, right?
> else
> shrink_many(pgdat, sc);
Best Regards
Barry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-17 5:09 ` [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec() Siddharth Chintamaneni
@ 2026-10-18 21:24 ` Barry Song
2026-10-19 20:33 ` Siddharth Chintamaneni
0 siblings, 1 reply; 16+ messages in thread
From: Barry Song @ 2026-10-18 21:24 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, stable, Hardik Garg
On Sat, Oct 17, 2026 at 1:09 PM Siddharth Chintamaneni
<sidchintamaneni@gmail.com> wrote:
>
> 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.
So userspace must have booted, since otherwise we couldn't read
debugfs. The reachable case is therefore a memory node with no
LRU folios when debugfs is read? So this could happen on a multi-node
system where folios are landed on other nodes but not on this node?
>
> Fixes: ec1c86b25f4b ("mm: multi-gen LRU: groundwork")
> Cc: stable@vger.kernel.org # 6.2+
> Assisted-by: LLM
> 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);
> }
I've been struggling to understand why MGLRU needs its own
get_lruvec() when mem_cgroup_lruvec() already exists.
With your patch, it feels like we're one step closer to
getting rid of MGLRU's own variant in the future.
>
> static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc)
> --
> 2.45.4
Best Regards
Barry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-18 21:24 ` Barry Song
@ 2026-10-19 20:33 ` Siddharth Chintamaneni
2026-10-19 23:35 ` Barry Song
0 siblings, 1 reply; 16+ messages in thread
From: Siddharth Chintamaneni @ 2026-10-19 20:33 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, stable, Hardik Garg
On Sun, 18 Oct 2026 at 14:24, Barry Song <baohua@kernel.org> wrote:
>
> On Sat, Oct 17, 2026 at 1:09 PM Siddharth Chintamaneni
> <sidchintamaneni@gmail.com> wrote:
> >
> > 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.
>
> So userspace must have booted, since otherwise we couldn't read
> debugfs. The reachable case is therefore a memory node with no
> LRU folios when debugfs is read? So this could happen on a multi-node
> system where folios are landed on other nodes but not on this node?
>
That is my high level understanding. I'll let Andrew to comment if I
am missing something,
> >
> > Fixes: ec1c86b25f4b ("mm: multi-gen LRU: groundwork")
> > Cc: stable@vger.kernel.org # 6.2+
> > Assisted-by: LLM
> > 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);
> > }
>
> I've been struggling to understand why MGLRU needs its own
> get_lruvec() when mem_cgroup_lruvec() already exists.
> With your patch, it feels like we're one step closer to
> getting rid of MGLRU's own variant in the future.
>
I agree with that! I'm happy to send a follow-up cleanup patch to
remove get_lruvec if that makes sense. Not sure how it will affect
stable backports.
> >
> > static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc)
> > --
> > 2.45.4
>
> Best Regards
> Barry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline
2026-10-18 21:04 ` Barry Song
@ 2026-10-19 20:37 ` Siddharth Chintamaneni
2026-10-19 22:59 ` Barry Song
0 siblings, 1 reply; 16+ messages in thread
From: Siddharth Chintamaneni @ 2026-10-19 20:37 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, stable, Hardik Garg
On Sun, 18 Oct 2026 at 14:05, Barry Song <baohua@kernel.org> wrote:
>
> On Sat, Oct 17, 2026 at 1:09 PM Siddharth Chintamaneni
> <sidchintamaneni@gmail.com> wrote:
> >
> > When CONFIG_MEMCG=y, lruvec_pgdat() returns lruvec->pgdat, which for the
> > per-node pgdat->__lruvec is left NULL by lruvec_init() and only filled in
> > lazily by mem_cgroup_lruvec(). Normally that happens early, e.g. on the
> > first folio_add_lru().
> >
> > With cgroup_disable=memory, lru_gen_shrink_node() passes &pgdat->__lruvec
> > directly to shrink_one(). If direct reclaim runs before anything has
> > called mem_cgroup_lruvec() for the node, which can happen during early
> > boot under memory pressure (e.g. a kdump kernel), get_swappiness() ends
> > up dereferencing a NULL pgdat:
> >
> > Unable to handle kernel NULL pointer dereference at virtual address 0000000000005040
> > pc : get_swappiness+0x68/0xb0
> > lr : try_to_shrink_lruvec+0x54/0x2c0
> > Call trace:
> > get_swappiness+0x68/0xb0 (P)
> > try_to_shrink_lruvec+0x54/0x2c0
> > shrink_one+0x8c/0x240
> > shrink_node+0xc6c/0xe08
> > do_try_to_free_pages+0xdc/0x570
> > try_to_free_pages+0x124/0x278
> > __alloc_frozen_pages_noprof+0x6e8/0xf78
> > alloc_pages_mpol+0xc4/0x1c0
> > alloc_frozen_pages_noprof+0x54/0xe8
> > alloc_pages_noprof+0x1c/0x40
> > atomic_pool_expand+0x98/0x300
> > __dma_atomic_pool_init+0x58/0xd8
> > dma_atomic_pool_init+0x154/0x170
> > do_one_initcall+0x64/0x350
> > kernel_init_freeable+0x214/0x430
> > kernel_init+0x2c/0x208
> > ret_from_fork+0x10/0x20
> >
> > The classic LRU path and MGLRU before per-node lru_gen_folio lists
> > obtained the lruvec through mem_cgroup_lruvec(), which sets
> > lruvec->pgdat.
> >
> > The above was hit on an arm64 kdump kernel (6.18-based) booted with
> > cgroup_disable=memory nr_cpus=4. It also reproduces on mainline on an
> > arm64 VM with CONFIG_ARM64_64K_PAGES=y, CONFIG_MEMCG=y,
> > CONFIG_LRU_GEN_ENABLED=y and:
> >
> > mem=160M nr_cpus=4 coherent_pool=512M cgroup_disable=memory
> >
> > These options only serve to force direct reclaim early in boot; with
> > 160M the boot eventually ends in OOM regardless. On mainline the first
> > LRU reclaim happens while unpacking the initramfs, and faults the same
> > way:
> >
> > Unable to handle kernel NULL pointer dereference at virtual address 0000000000005040
> > pc : get_swappiness+0xd4/0x150
> > Call trace:
> > get_swappiness+0xd4/0x150 (P)
> > try_to_shrink_lruvec+0x44/0x2b8
> > shrink_one+0x80/0x1a0
> > shrink_node+0xd98/0xea8
> > do_try_to_free_pages+0xf4/0x4b0
> > try_to_free_pages+0x110/0x360
> > __alloc_frozen_pages_noprof+0x8b0/0x1718
> > ...
> > __filemap_get_folio_mpol+0x2ac/0x548
> > simple_write_begin+0x44/0xf8
> > page_symlink+0x178/0x198
> > ...
> > do_populate_rootfs+0x13c/0x200
> >
>
> So this happens before any folios are added to the LRU and before
> the userspace init process starts?
>
Yes!
> > Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
> > Cc: stable@vger.kernel.org # 6.3+
> > Assisted-by: LLM
> > Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
> > Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
> > ---
> > mm/vmscan.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/mm/vmscan.c b/mm/vmscan.c
> > index f11491ee9ed5..0c57e4f1eb15 100644
> > --- a/mm/vmscan.c
> > +++ b/mm/vmscan.c
> > @@ -5253,7 +5253,7 @@ static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *
> > sc->nr_reclaimed = 0;
> >
> > if (mem_cgroup_disabled())
> > - shrink_one(&pgdat->__lruvec, sc);
> > + shrink_one(mem_cgroup_lruvec(NULL, pgdat), sc);
>
> What's a bit weird is that even if we fix this here, with an empty
> lruvec, we still won't be able to reclaim a single page. But it's
> hard to see where we could break out early, right?
>
I am not really sure about simple bailout logic. Please let me know if
anyone have any thoughts.
> > else
> > shrink_many(pgdat, sc);
>
> Best Regards
> Barry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline
2026-10-19 20:37 ` Siddharth Chintamaneni
@ 2026-10-19 22:59 ` Barry Song
0 siblings, 0 replies; 16+ messages in thread
From: Barry Song @ 2026-10-19 22:59 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, stable, Hardik Garg
On Tue, Oct 20, 2026 at 4:37 AM Siddharth Chintamaneni
<sidchintamaneni@gmail.com> wrote:
[...]
> >
> > So this happens before any folios are added to the LRU and before
> > the userspace init process starts?
> >
>
> Yes!
>
Thanks for confirming.
> > > Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
> > > Cc: stable@vger.kernel.org # 6.3+
> > > Assisted-by: LLM
> > > Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
> > > Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
> > > ---
> > > mm/vmscan.c | 2 +-
> > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/mm/vmscan.c b/mm/vmscan.c
> > > index f11491ee9ed5..0c57e4f1eb15 100644
> > > --- a/mm/vmscan.c
> > > +++ b/mm/vmscan.c
> > > @@ -5253,7 +5253,7 @@ static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *
> > > sc->nr_reclaimed = 0;
> > >
> > > if (mem_cgroup_disabled())
> > > - shrink_one(&pgdat->__lruvec, sc);
> > > + shrink_one(mem_cgroup_lruvec(NULL, pgdat), sc);
> >
> > What's a bit weird is that even if we fix this here, with an empty
> > lruvec, we still won't be able to reclaim a single page. But it's
> > hard to see where we could break out early, right?
> >
>
> I am not really sure about simple bailout logic. Please let me know if
> anyone have any thoughts.
No idea. I also feel it might not be worth doing, as this
only affects very uncommon scenarios. Some unnecessary
reclaiming is unlikely to hurt real-world workloads, so
there's no need to escalate this into a fix.
For the patch,
Reviewed-by: Barry Song <baohua@kernel.org>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-19 20:33 ` Siddharth Chintamaneni
@ 2026-10-19 23:35 ` Barry Song
2026-10-20 0:16 ` Siddharth Chintamaneni
0 siblings, 1 reply; 16+ messages in thread
From: Barry Song @ 2026-10-19 23:35 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, stable, Hardik Garg
On Tue, Oct 20, 2026 at 4:33 AM Siddharth Chintamaneni
<sidchintamaneni@gmail.com> wrote:
>
> On Sun, 18 Oct 2026 at 14:24, Barry Song <baohua@kernel.org> wrote:
> >
> > On Sat, Oct 17, 2026 at 1:09 PM Siddharth Chintamaneni
> > <sidchintamaneni@gmail.com> wrote:
> > >
> > > 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.
> >
> > So userspace must have booted, since otherwise we couldn't read
> > debugfs. The reachable case is therefore a memory node with no
> > LRU folios when debugfs is read? So this could happen on a multi-node
> > system where folios are landed on other nodes but not on this node?
> >
>
> That is my high level understanding. I'll let Andrew to comment if I
> am missing something,
>
I made a silly toy setup by running two numa nodes with one of them has only 4KB
memory, the other one has 1G-4K:
qemu-system-aarch64 -M virt,gic-version=3,mte=off -cpu cortex-a53 -nographic \
-smp 4 \
-m 1G \
-object memory-backend-ram,id=ram0,size=1048572K \
-object memory-backend-ram,id=ram1,size=4K \
-numa node,nodeid=0,memdev=ram0,cpus=0-1 \
-numa node,nodeid=1,memdev=ram1,cpus=2-3 \
-kernel arch/arm64/boot/Image \
-nographic -append "cgroup_disable=memory
...
Then I got a machine where node1 is very difficult to have a page in lru.
then i ran the debugfs for mglru:
/ # cat /sys/kernel/debug/lru_gen
memcg 0
node 0
0 14972 0 953
1 14972 0 1
2 14972 18 3308
3 14972 0 0
node 1
0 14972 0 0
1 14972 0 0
2 14972 0 0
3 14972 0 0
now you see node 1 has no page in lru but yet i didn't have any crash.
i think i will need more time to figure out what is the problem and how to
reproduce it.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-19 23:35 ` Barry Song
@ 2026-10-20 0:16 ` Siddharth Chintamaneni
2026-10-20 0:43 ` Barry Song
0 siblings, 1 reply; 16+ messages in thread
From: Siddharth Chintamaneni @ 2026-10-20 0:16 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, stable, Hardik Garg
On Mon, 19 Oct 2026 at 16:36, Barry Song <baohua@kernel.org> wrote:
>
> On Tue, Oct 20, 2026 at 4:33 AM Siddharth Chintamaneni
> <sidchintamaneni@gmail.com> wrote:
> >
> > On Sun, 18 Oct 2026 at 14:24, Barry Song <baohua@kernel.org> wrote:
> > >
> > > On Sat, Oct 17, 2026 at 1:09 PM Siddharth Chintamaneni
> > > <sidchintamaneni@gmail.com> wrote:
> > > >
> > > > 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.
> > >
> > > So userspace must have booted, since otherwise we couldn't read
> > > debugfs. The reachable case is therefore a memory node with no
> > > LRU folios when debugfs is read? So this could happen on a multi-node
> > > system where folios are landed on other nodes but not on this node?
> > >
> >
> > That is my high level understanding. I'll let Andrew to comment if I
> > am missing something,
> >
>
> I made a silly toy setup by running two numa nodes with one of them has only 4KB
> memory, the other one has 1G-4K:
>
> qemu-system-aarch64 -M virt,gic-version=3,mte=off -cpu cortex-a53 -nographic \
> -smp 4 \
> -m 1G \
> -object memory-backend-ram,id=ram0,size=1048572K \
> -object memory-backend-ram,id=ram1,size=4K \
> -numa node,nodeid=0,memdev=ram0,cpus=0-1 \
> -numa node,nodeid=1,memdev=ram1,cpus=2-3 \
> -kernel arch/arm64/boot/Image \
> -nographic -append "cgroup_disable=memory
> ...
>
> Then I got a machine where node1 is very difficult to have a page in lru.
>
> then i ran the debugfs for mglru:
>
> / # cat /sys/kernel/debug/lru_gen
> memcg 0
> node 0
> 0 14972 0 953
> 1 14972 0 1
> 2 14972 18 3308
> 3 14972 0 0
> node 1
> 0 14972 0 0
> 1 14972 0 0
> 2 14972 0 0
> 3 14972 0 0
>
> now you see node 1 has no page in lru but yet i didn't have any crash.
>
> i think i will need more time to figure out what is the problem and how to
> reproduce it.
I've asked AI to repro the issue with the similar setup
qemu-system-aarch64 -M virt,gic-version=3,mte=off -cpu cortex-a53 -nographic \
-smp 4 -m 1G \
-object memory-backend-ram,id=ram0,size=1048512K \
-object memory-backend-ram,id=ram1,size=64K \
-numa node,nodeid=0,memdev=ram0,cpus=0-1 \
-numa node,nodeid=1,memdev=ram1,cpus=2-3 \
-kernel "$KERNEL" \
-initrd "$INITRD" \
-no-reboot \
-append "console=ttyAMA0 cgroup_disable=memory
initcall_blacklist=kswapd_init panic=-1"
and it was able to repo it. AI identified that kswapd is initializing
the pgdat during boot, so blacklisting kswap_init was able to repro
the crash when reading /sys/kernel/debug/lru_gen
[ 1.347868] Unable to handle kernel access to user memory outside
uaccess routines at virtual address 0000000000005040
[ 1.347920] Mem abort info:
[ 1.347929] ESR = 0x0000000096000005
[ 1.347950] EC = 0x25: DABT (current EL), IL = 32 bits
[ 1.347964] SET = 0, FnV = 0
[ 1.347974] EA = 0, S1PTW = 0
[ 1.347986] FSC = 0x05: level 1 translation fault
[ 1.348249] Data abort info:
[ 1.348277] ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000
[ 1.348291] CM = 0, WnR = 0, TnD = 0, TagAccess = 0
[ 1.348302] GCS = 0, Overlay = 0, DirtyBit = 0
[ 1.348345] user pgtable: 64k pages, 48-bit VAs, pgdp=00000000485f6600
[ 1.348369] [0000000000005040] pgd=0800000043640403,
p4d=0800000043640403, pud=0800000043640403, pmd=0800000043620403,
pte=0000000000000000
[ 1.349148] Internal error: Oops: 0000000096000005 [#1] SMP
[ 1.350414] Modules linked in:
[ 1.350896] CPU: 0 UID: 0 PID: 1 Comm: init Not tainted 7.3.0-rc7+
#9 PREEMPT(full)
[ 1.351111] Hardware name: linux,dummy-virt (DT)
[ 1.351411] pstate: 80400005 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 1.351581] pc : lru_gen_seq_show+0x5c/0x460
[ 1.351800] lr : lru_gen_seq_show+0x4c/0x460
[ 1.351898] sp : ffff80008034f9a0
[ 1.351975] x29: ffff80008034fa20 x28: ffff0000035e67e8 x27: 0000000000400cc0
[ 1.352297] x26: 000000007fff0000 x25: ffff0000035e67d8 x24: 0000000000000000
[ 1.352454] x23: ffff80008034fb00 x22: ffff80008034fb28 x21: 0000000000000000
[ 1.352626] x20: ffff0000035e67b0 x19: ffff00003ffc2f20 x18: ffff800080360060
[ 1.352775] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[ 1.352915] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000
[ 1.353058] x11: 0000000000000000 x10: 0000000000000000 x9 : ffffa3468a020c5c
[ 1.353226] x8 : ffff000008b07000 x7 : 0000000000000000 x6 : 000000000000003f
[ 1.353367] x5 : 00000000ffffffff x4 : 0000000000000003 x3 : 0000000000001000
[ 1.353511] x2 : ffff00003ffc2f20 x1 : 0000000000004000 x0 : 0000000000000000
[ 1.353732] Call trace:
[ 1.353925] lru_gen_seq_show+0x5c/0x460 (P)
[ 1.354110] seq_read_iter+0xec/0x4c0
[ 1.354213] seq_read+0xfc/0x148
[ 1.354290] full_proxy_read+0x78/0xc0
[ 1.354377] vfs_read+0xd0/0x338
[ 1.354453] ksys_read+0x7c/0x120
[ 1.354528] __arm64_sys_read+0x24/0x40
[ 1.354611] invoke_syscall+0xa8/0x120
[ 1.354695] el0_svc_common.constprop.0+0x4c/0x100
[ 1.354791] do_el0_svc+0x28/0x40
[ 1.354865] el0_svc+0x48/0x2b0
[ 1.354939] el0t_64_sync_handler+0xa8/0xf0
[ 1.355026] el0t_64_sync+0x1b8/0x1c0
[ 1.355250] Code: f9000fe0 f9432661 aa1303e2 91401021 (b9504033)
[ 1.355647] ---[ end trace 0000000000000000 ]---
[ 1.356555] Kernel panic - not syncing: Attempted to kill init!
exitcode=0x0000000b
[ 1.356888] SMP: stopping secondary CPUs
[ 1.357525] Kernel Offset: 0x234609c00000 from 0xffff800080000000
[ 1.357675] PHYS_OFFSET: 0x40000000
[ 1.357762] CPU features: 0x0,00000000,09000020,00100002,0800821b
[ 1.357901] Memory Limit: none
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-20 0:16 ` Siddharth Chintamaneni
@ 2026-10-20 0:43 ` Barry Song
2026-10-20 1:02 ` Siddharth Chintamaneni
0 siblings, 1 reply; 16+ messages in thread
From: Barry Song @ 2026-10-20 0:43 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, stable, Hardik Garg
On Tue, Oct 20, 2026 at 8:16 AM Siddharth Chintamaneni
<sidchintamaneni@gmail.com> wrote:
>
> On Mon, 19 Oct 2026 at 16:36, Barry Song <baohua@kernel.org> wrote:
> >
> > On Tue, Oct 20, 2026 at 4:33 AM Siddharth Chintamaneni
> > <sidchintamaneni@gmail.com> wrote:
> > >
> > > On Sun, 18 Oct 2026 at 14:24, Barry Song <baohua@kernel.org> wrote:
> > > >
> > > > On Sat, Oct 17, 2026 at 1:09 PM Siddharth Chintamaneni
> > > > <sidchintamaneni@gmail.com> wrote:
> > > > >
> > > > > 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.
> > > >
> > > > So userspace must have booted, since otherwise we couldn't read
> > > > debugfs. The reachable case is therefore a memory node with no
> > > > LRU folios when debugfs is read? So this could happen on a multi-node
> > > > system where folios are landed on other nodes but not on this node?
> > > >
> > >
> > > That is my high level understanding. I'll let Andrew to comment if I
> > > am missing something,
> > >
> >
> > I made a silly toy setup by running two numa nodes with one of them has only 4KB
> > memory, the other one has 1G-4K:
> >
> > qemu-system-aarch64 -M virt,gic-version=3,mte=off -cpu cortex-a53 -nographic \
> > -smp 4 \
> > -m 1G \
> > -object memory-backend-ram,id=ram0,size=1048572K \
> > -object memory-backend-ram,id=ram1,size=4K \
> > -numa node,nodeid=0,memdev=ram0,cpus=0-1 \
> > -numa node,nodeid=1,memdev=ram1,cpus=2-3 \
> > -kernel arch/arm64/boot/Image \
> > -nographic -append "cgroup_disable=memory
> > ...
> >
> > Then I got a machine where node1 is very difficult to have a page in lru.
> >
> > then i ran the debugfs for mglru:
> >
> > / # cat /sys/kernel/debug/lru_gen
> > memcg 0
> > node 0
> > 0 14972 0 953
> > 1 14972 0 1
> > 2 14972 18 3308
> > 3 14972 0 0
> > node 1
> > 0 14972 0 0
> > 1 14972 0 0
> > 2 14972 0 0
> > 3 14972 0 0
> >
> > now you see node 1 has no page in lru but yet i didn't have any crash.
> >
> > i think i will need more time to figure out what is the problem and how to
> > reproduce it.
>
> I've asked AI to repro the issue with the similar setup
>
> qemu-system-aarch64 -M virt,gic-version=3,mte=off -cpu cortex-a53 -nographic \
> -smp 4 -m 1G \
> -object memory-backend-ram,id=ram0,size=1048512K \
> -object memory-backend-ram,id=ram1,size=64K \
> -numa node,nodeid=0,memdev=ram0,cpus=0-1 \
> -numa node,nodeid=1,memdev=ram1,cpus=2-3 \
> -kernel "$KERNEL" \
> -initrd "$INITRD" \
> -no-reboot \
> -append "console=ttyAMA0 cgroup_disable=memory
> initcall_blacklist=kswapd_init panic=-1"
>
> and it was able to repo it. AI identified that kswapd is initializing
> the pgdat during boot, so blacklisting kswap_init was able to repro
> the crash when reading /sys/kernel/debug/lru_gen
Thanks!
I guess you mean the following:
-module_init(kswapd_init)
+//module_init(kswapd_init)
Yes, I can reproduce it with the above as well.
But kswapd_init() is definitely called before we enter userspace,
so this doesn't seem to be an issue here?
So I feel there is no bug here, but this cleanup is still welcome.
Am I missing any other scenarios?
Best Regards
Barry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-20 0:43 ` Barry Song
@ 2026-10-20 1:02 ` Siddharth Chintamaneni
2026-10-20 1:20 ` Barry Song
0 siblings, 1 reply; 16+ messages in thread
From: Siddharth Chintamaneni @ 2026-10-20 1:02 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, stable, Hardik Garg
On Mon, 19 Oct 2026 at 17:43, Barry Song <baohua@kernel.org> wrote:
>
> On Tue, Oct 20, 2026 at 8:16 AM Siddharth Chintamaneni
> <sidchintamaneni@gmail.com> wrote:
> >
> > On Mon, 19 Oct 2026 at 16:36, Barry Song <baohua@kernel.org> wrote:
> > >
> > > On Tue, Oct 20, 2026 at 4:33 AM Siddharth Chintamaneni
> > > <sidchintamaneni@gmail.com> wrote:
> > > >
> > > > On Sun, 18 Oct 2026 at 14:24, Barry Song <baohua@kernel.org> wrote:
> > > > >
> > > > > On Sat, Oct 17, 2026 at 1:09 PM Siddharth Chintamaneni
> > > > > <sidchintamaneni@gmail.com> wrote:
> > > > > >
> > > > > > 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.
> > > > >
> > > > > So userspace must have booted, since otherwise we couldn't read
> > > > > debugfs. The reachable case is therefore a memory node with no
> > > > > LRU folios when debugfs is read? So this could happen on a multi-node
> > > > > system where folios are landed on other nodes but not on this node?
> > > > >
> > > >
> > > > That is my high level understanding. I'll let Andrew to comment if I
> > > > am missing something,
> > > >
> > >
> > > I made a silly toy setup by running two numa nodes with one of them has only 4KB
> > > memory, the other one has 1G-4K:
> > >
> > > qemu-system-aarch64 -M virt,gic-version=3,mte=off -cpu cortex-a53 -nographic \
> > > -smp 4 \
> > > -m 1G \
> > > -object memory-backend-ram,id=ram0,size=1048572K \
> > > -object memory-backend-ram,id=ram1,size=4K \
> > > -numa node,nodeid=0,memdev=ram0,cpus=0-1 \
> > > -numa node,nodeid=1,memdev=ram1,cpus=2-3 \
> > > -kernel arch/arm64/boot/Image \
> > > -nographic -append "cgroup_disable=memory
> > > ...
> > >
> > > Then I got a machine where node1 is very difficult to have a page in lru.
> > >
> > > then i ran the debugfs for mglru:
> > >
> > > / # cat /sys/kernel/debug/lru_gen
> > > memcg 0
> > > node 0
> > > 0 14972 0 953
> > > 1 14972 0 1
> > > 2 14972 18 3308
> > > 3 14972 0 0
> > > node 1
> > > 0 14972 0 0
> > > 1 14972 0 0
> > > 2 14972 0 0
> > > 3 14972 0 0
> > >
> > > now you see node 1 has no page in lru but yet i didn't have any crash.
> > >
> > > i think i will need more time to figure out what is the problem and how to
> > > reproduce it.
> >
> > I've asked AI to repro the issue with the similar setup
> >
> > qemu-system-aarch64 -M virt,gic-version=3,mte=off -cpu cortex-a53 -nographic \
> > -smp 4 -m 1G \
> > -object memory-backend-ram,id=ram0,size=1048512K \
> > -object memory-backend-ram,id=ram1,size=64K \
> > -numa node,nodeid=0,memdev=ram0,cpus=0-1 \
> > -numa node,nodeid=1,memdev=ram1,cpus=2-3 \
> > -kernel "$KERNEL" \
> > -initrd "$INITRD" \
> > -no-reboot \
> > -append "console=ttyAMA0 cgroup_disable=memory
> > initcall_blacklist=kswapd_init panic=-1"
> >
> > and it was able to repo it. AI identified that kswapd is initializing
> > the pgdat during boot, so blacklisting kswap_init was able to repro
> > the crash when reading /sys/kernel/debug/lru_gen
>
> Thanks!
> I guess you mean the following:
>
> -module_init(kswapd_init)
> +//module_init(kswapd_init)
>
> Yes, I can reproduce it with the above as well.
>
> But kswapd_init() is definitely called before we enter userspace,
> so this doesn't seem to be an issue here?
>
Looking at the callee sites of get_lruvec, all of them happen after
boot. So it should be fine. Anyway code clean up will address the
issue.
> So I feel there is no bug here, but this cleanup is still welcome.
Are you ok with the patch as is, or would you recommend changing the
callsite of get_lruvec to mem_cgroup_lruvec?
> Am I missing any other scenarios?
>
> Best Regards
> Barry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-20 1:02 ` Siddharth Chintamaneni
@ 2026-10-20 1:20 ` Barry Song
2026-10-20 1:36 ` Siddharth Chintamaneni
0 siblings, 1 reply; 16+ messages in thread
From: Barry Song @ 2026-10-20 1: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, stable, Hardik Garg
On Tue, Oct 20, 2026 at 9:02 AM Siddharth Chintamaneni
<sidchintamaneni@gmail.com> wrote:
>
[...]
> >
> > Thanks!
> > I guess you mean the following:
> >
> > -module_init(kswapd_init)
> > +//module_init(kswapd_init)
> >
> > Yes, I can reproduce it with the above as well.
> >
> > But kswapd_init() is definitely called before we enter userspace,
> > so this doesn't seem to be an issue here?
> >
>
> Looking at the callee sites of get_lruvec, all of them happen after
> boot. So it should be fine. Anyway code clean up will address the
> issue.
>
> > So I feel there is no bug here, but this cleanup is still welcome.
>
> Are you ok with the patch as is, or would you recommend changing the
> callsite of get_lruvec to mem_cgroup_lruvec?
Overall, I'd be happy to see MGLRU-specific code being removed.
If possible, could we split this into a two-patch series? The
first patch would be this one, and the second would drop
get_lruvec().
In any case, if this isn't a bug fix, there's no need for a Fixes tag or
a stable backport.
BTW, I've been struggling to understand why MGLRU introduced
its own version in the first place. Maybe your follow-up
patchset will help clarify that :-)
Best Regards
Barry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-20 1:20 ` Barry Song
@ 2026-10-20 1:36 ` Siddharth Chintamaneni
2026-10-20 5:19 ` Barry Song
0 siblings, 1 reply; 16+ messages in thread
From: Siddharth Chintamaneni @ 2026-10-20 1:36 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, stable, Hardik Garg
On Mon, 19 Oct 2026 at 18:20, Barry Song <baohua@kernel.org> wrote:
>
> On Tue, Oct 20, 2026 at 9:02 AM Siddharth Chintamaneni
> <sidchintamaneni@gmail.com> wrote:
> >
> [...]
> > >
> > > Thanks!
> > > I guess you mean the following:
> > >
> > > -module_init(kswapd_init)
> > > +//module_init(kswapd_init)
> > >
> > > Yes, I can reproduce it with the above as well.
> > >
> > > But kswapd_init() is definitely called before we enter userspace,
> > > so this doesn't seem to be an issue here?
> > >
> >
> > Looking at the callee sites of get_lruvec, all of them happen after
> > boot. So it should be fine. Anyway code clean up will address the
> > issue.
> >
> > > So I feel there is no bug here, but this cleanup is still welcome.
> >
> > Are you ok with the patch as is, or would you recommend changing the
> > callsite of get_lruvec to mem_cgroup_lruvec?
>
> Overall, I'd be happy to see MGLRU-specific code being removed.
>
> If possible, could we split this into a two-patch series? The
> first patch would be this one, and the second would drop
> get_lruvec().
>
> In any case, if this isn't a bug fix, there's no need for a Fixes tag or
> a stable backport.
>
If that is the case, do we still need this patch?
> BTW, I've been struggling to understand why MGLRU introduced
> its own version in the first place. Maybe your follow-up
> patchset will help clarify that :-)
>
I have a theory -
when MGLRU was introduced lru_vec also returns NULL
https://elixir.bootlin.com/linux/v6.1.190/source/mm/vmscan.c#L3163
that is not the case anymore. I haven't looked at the refactoring that
happened later around it yet.
That might be the reason but I could be wrong.
> Best Regards
> Barry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec()
2026-10-20 1:36 ` Siddharth Chintamaneni
@ 2026-10-20 5:19 ` Barry Song
0 siblings, 0 replies; 16+ messages in thread
From: Barry Song @ 2026-10-20 5:19 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, stable, Hardik Garg
On Tue, Oct 20, 2026 at 9:37 AM Siddharth Chintamaneni
<sidchintamaneni@gmail.com> wrote:
[..]
>
> If that is the case, do we still need this patch?
>
yes. We need it as cleanup patches. not bug fixes.
Best Regards
Barry
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline
2026-10-17 5:09 [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline Siddharth Chintamaneni
` (2 preceding siblings ...)
2026-10-18 21:04 ` Barry Song
@ 2026-10-21 6:15 ` Baolin Wang
3 siblings, 0 replies; 16+ messages in thread
From: Baolin Wang @ 2026-10-21 6:15 UTC (permalink / raw)
To: Siddharth Chintamaneni, akpm, hannes
Cc: david, mhocko, qi.zheng, shakeel.butt, ljs, kasong, baohua,
axelrasmussen, yuanchu, weixugc, baoquan.he, yuzhao, linux-mm,
linux-kernel, apais, rlmenge, stable, Hardik Garg
On 10/17/26 1:09 PM, Siddharth Chintamaneni wrote:
> When CONFIG_MEMCG=y, lruvec_pgdat() returns lruvec->pgdat, which for the
> per-node pgdat->__lruvec is left NULL by lruvec_init() and only filled in
> lazily by mem_cgroup_lruvec(). Normally that happens early, e.g. on the
> first folio_add_lru().
>
> With cgroup_disable=memory, lru_gen_shrink_node() passes &pgdat->__lruvec
> directly to shrink_one(). If direct reclaim runs before anything has
> called mem_cgroup_lruvec() for the node, which can happen during early
> boot under memory pressure (e.g. a kdump kernel), get_swappiness() ends
> up dereferencing a NULL pgdat:
>
> Unable to handle kernel NULL pointer dereference at virtual address 0000000000005040
> pc : get_swappiness+0x68/0xb0
> lr : try_to_shrink_lruvec+0x54/0x2c0
> Call trace:
> get_swappiness+0x68/0xb0 (P)
> try_to_shrink_lruvec+0x54/0x2c0
> shrink_one+0x8c/0x240
> shrink_node+0xc6c/0xe08
> do_try_to_free_pages+0xdc/0x570
> try_to_free_pages+0x124/0x278
> __alloc_frozen_pages_noprof+0x6e8/0xf78
> alloc_pages_mpol+0xc4/0x1c0
> alloc_frozen_pages_noprof+0x54/0xe8
> alloc_pages_noprof+0x1c/0x40
> atomic_pool_expand+0x98/0x300
> __dma_atomic_pool_init+0x58/0xd8
> dma_atomic_pool_init+0x154/0x170
> do_one_initcall+0x64/0x350
> kernel_init_freeable+0x214/0x430
> kernel_init+0x2c/0x208
> ret_from_fork+0x10/0x20
>
> The classic LRU path and MGLRU before per-node lru_gen_folio lists
> obtained the lruvec through mem_cgroup_lruvec(), which sets
> lruvec->pgdat.
>
> The above was hit on an arm64 kdump kernel (6.18-based) booted with
> cgroup_disable=memory nr_cpus=4. It also reproduces on mainline on an
> arm64 VM with CONFIG_ARM64_64K_PAGES=y, CONFIG_MEMCG=y,
> CONFIG_LRU_GEN_ENABLED=y and:
>
> mem=160M nr_cpus=4 coherent_pool=512M cgroup_disable=memory
>
> These options only serve to force direct reclaim early in boot; with
> 160M the boot eventually ends in OOM regardless. On mainline the first
> LRU reclaim happens while unpacking the initramfs, and faults the same
> way:
>
> Unable to handle kernel NULL pointer dereference at virtual address 0000000000005040
> pc : get_swappiness+0xd4/0x150
> Call trace:
> get_swappiness+0xd4/0x150 (P)
> try_to_shrink_lruvec+0x44/0x2b8
> shrink_one+0x80/0x1a0
> shrink_node+0xd98/0xea8
> do_try_to_free_pages+0xf4/0x4b0
> try_to_free_pages+0x110/0x360
> __alloc_frozen_pages_noprof+0x8b0/0x1718
> ...
> __filemap_get_folio_mpol+0x2ac/0x548
> simple_write_begin+0x44/0xf8
> page_symlink+0x178/0x198
> ...
> do_populate_rootfs+0x13c/0x200
>
> Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
> Cc: stable@vger.kernel.org # 6.3+
> Assisted-by: LLM
> Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
> ---
Good catch. LGTM.
Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-10-21 6:16 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-17 5:09 [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline Siddharth Chintamaneni
2026-10-17 5:09 ` [PATCH v2 2/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref in get_lruvec() Siddharth Chintamaneni
2026-10-18 21:24 ` Barry Song
2026-10-19 20:33 ` Siddharth Chintamaneni
2026-10-19 23:35 ` Barry Song
2026-10-20 0:16 ` Siddharth Chintamaneni
2026-10-20 0:43 ` Barry Song
2026-10-20 1:02 ` Siddharth Chintamaneni
2026-10-20 1:20 ` Barry Song
2026-10-20 1:36 ` Siddharth Chintamaneni
2026-10-20 5:19 ` Barry Song
2026-10-17 20:18 ` [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline Andrew Morton
2026-10-18 21:04 ` Barry Song
2026-10-19 20:37 ` Siddharth Chintamaneni
2026-10-19 22:59 ` Barry Song
2026-10-21 6:15 ` Baolin Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox