Re: [PATCH] mm: mempolicy: fix automatic numa balancing for shmem

From: Gregory Price

Date: Wed Jul 01 2026 - 11:39:06 EST


On Wed, Jul 01, 2026 at 07:03:32PM +0800, Huang, Ying wrote:
> Gregory Price <gourry@xxxxxxxxxx> writes:
>
> > On Tue, Jun 30, 2026 at 07:20:50PM +0800, Huang, Ying wrote:
> >> Gregory Price <gourry@xxxxxxxxxx> writes:
> >>
> >> [snip]
> >>
> >> > Demotions don't care about mempolicy, so opting shmem out of NUMA
> >> > balancing and mbind'ing on a tiered system is just full sadness.
> >> >
> >> > This is all just more evidence that demotion needs to be completely
> >> > redone, it's creating a mess of undefined behavior for memory placement.
> >>
> >> It's hard to respect mempolicy during demotion in the current
> >> implementation. Do you have any ideas on how to improve this?
> >>
> >
> > I think it's feasible we could respect per-vma mempolicies, but not
> > per-task. That would at least make this particular interaction less
> > painful and mbind() would do what you'd expect. It is a bit racy,
> > but with MPOL_MF_MOVE_ALL the user can get what they actually want.
>
> Yes. Per-vma mempolicy support is possible.
>
> > I think task-wide mempolicy is problematic and generally a bad idea
> > on tiered systems, maybe it's ok if we simply document task policies
> > are not respected on tiered systems?
>
> Anyway, it's convenient to use numactl to manage mempolicy.
>

It can be, but there's also many footguns with task-wide policy.

Something i found while seeing if i could make ZONE_NORMAL nodes more
reliably hotpluggable:

diff --git a/lib/stackdepot.c b/lib/stackdepot.c
index dd2717ff94bf..9ceeb56574ef 100644
--- a/lib/stackdepot.c
+++ b/lib/stackdepot.c
@@ -682,7 +682,15 @@ depot_stack_handle_t stack_depot_save_flags(unsigned long *entries,
* we won't be able to do that under the lock.
*/
if (unlikely(can_alloc && !READ_ONCE(new_pool))) {
- page = alloc_pages(gfp_nested_mask(alloc_flags),
+ /*
+ * The stack depot pool is a global, never-freed allocation.
+ * Use alloc_pages_node() on the CPU-local node instead of
+ * alloc_pages() so the pool does not inherit a transient task's
+ * NUMA mempolicy (e.g. MPOL_BIND to a CPU-less/bound node), which
+ * would strand this long-lived page on that node forever.
+ */
+ page = alloc_pages_node(numa_node_id(),
+ gfp_nested_mask(alloc_flags),
DEPOT_POOL_ORDER);
if (page)
prealloc = page_address(page);

This is a global, permanently allocated, resource that inherits a task
mempolicy's placement because that task *happened* to be the first one
to touch it.

There are many alloc_pages() calls (155 instances kernel-wide) that
inherit a task mempolicy when that's probably not what we want.

alloc_pages() is called in: net/, lib/, kexec_core/, drivers/, arch/

you can imagine a task setting `set_mempolicy(INTERLEAVE, ALL)` and the
result is a bunch of random driver memory gets spread all over the place
along with the task's heap. Is that really what the caller wanted, or
did they just want userland data spread about?

But at this point it's a 20 year old interface, not much we can do about
it without making *someone* sad :[

I considered proposing MPOL_F_MOVABLE_ONLY to mean (roughly) "userland
memory only" - and then slowly trying to migrate numactl to make this
the default.

> Is it possible to enable NUMA_BALANCING_MEMORY_TIERING for non-default
> VMAs? If we don't enable NUMA_BALANCING_NORMAL, the overhead should be
> OK because the page table entries are changed to PROTN_ONE only for
> pages on the slow tier.
>

hmmm, will have to give this some thought.

> Additionally, we may need to consider cpusets.
>

Direct reclaim considers cpusets for the reclaiming task (added
recently), kswapd sits in its own cgroup.

Cross-cpuset checks - i'm not sure how tractable that is. We ignore it
for now, recognizing that if something is cross-cpuset it's some
definition of shared/global object (e.g. pagecache mappings).

~Gregory