Re: [PATCH v2 1/2] mm: multi-gen LRU: fix NULL lruvec->pgdat deref with memcg disabled on cmdline

From: Barry Song

Date: Sun Oct 18 2026 - 17:05:13 EST


On Sat, Oct 17, 2026 at 1:09 PM Siddharth Chintamaneni
<sidchintamaneni@xxxxxxxxx> 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@xxxxxxxxxxxxxxx # 6.3+
> Assisted-by: LLM
> Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@xxxxxxxxx>
> Signed-off-by: Hardik Garg <hargar@xxxxxxxxxxxxxxxxxxx>
> ---
> 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