Re: [PATCH v6] mm: assert exclusive nid/zonenum bits at the page/folio access sites
From: Andrew Morton
Date: Sun Jun 28 2026 - 00:30:42 EST
On Fri, 26 Jun 2026 10:06:29 +0800 Hui Zhu <hui.zhu@xxxxxxxxx> wrote:
> From: Hui Zhu <zhuhui@xxxxxxxxxx>
>
> KCSAN reports a data race between page_to_nid()/folio_pgdat() reading
> page->flags and folio_trylock()/folio_lock() concurrently doing
> test_and_set_bit_lock(PG_locked, ...) on the same word, e.g.:
>
> BUG: KCSAN: data-race in __lruvec_stat_mod_folio / shmem_get_folio_gfp
>
> The node id and zone id occupy fixed bit-ranges of page->flags that
> are set once at page init and never modified afterwards, so they can
> never overlap with the low PG_locked/PG_waiters bits touched by the
> folio lock path.
>
> ASSERT_EXCLUSIVE_BITS(mdf.f, ...) inside memdesc_nid()/memdesc_zonenum()
> used to check a by-value copy of the flags word, not the actual shared
> page->flags/folio->flags being modified concurrently, so it didn't
> reliably assert anything about the real race.
>
> For zonenum, move the assertion out of memdesc_zonenum() into
> page_zonenum() and folio_zonenum(), where flags is dereferenced
> directly from the page/folio.
>
> For nid, turn memdesc_nid() into a macro instead, so the mdf argument
> is expanded as the caller's own flags expression
> (PF_POISONED_CHECK(page)->flags or folio->flags) rather than copied
> into a function parameter, letting ASSERT_EXCLUSIVE_BITS() check the
> real page->flags/folio->flags directly.
>
> On CONFIG_NUMA=n, NODES_MASK is 0 and the old memdesc_nid() body
> folded to a constant, so page->flags/folio->flags was never actually
> read. ASSERT_EXCLUSIVE_BITS() is a real runtime check that can't be
> folded away, so doing it unconditionally would add a pointless read
> of page->flags/folio->flags and a check that can never fire. Keep
> page_to_nid()/folio_nid() as plain "return 0" static inline stubs
> under CONFIG_NUMA=n instead.
>
Thanks for persisting with this.
ASSERT_EXCLUSIVE_BITS is defined in kcsan-checks.h and I'm not
immediately seeing a reliable route by which mm.h and mmzone.h get to
include that file. I don't think kasan.h gives that to us,
surprisingly.
Unless I've missed it, we're presently getting kcsan-checks.h thanks to
good luck. Let's not rely upon good luck ;)
Also, Sashiko is saying stuff again. Does it look legitimate?
https://sashiko.dev/#/patchset/20260626020629.1042041-1-hui.zhu@xxxxxxxxx