Re: [LSF/MM/BPF TOPIC][RFC PATCH v4 00/27] Private Memory Nodes (w/ Compressed RAM)
From: Gregory Price
Date: Wed Jun 10 2026 - 12:56:23 EST
On Wed, Jun 10, 2026 at 05:00:33PM +0200, David Hildenbrand (Arm) wrote:
> On 6/10/26 12:41, Gregory Price wrote:
> > On Wed, Jun 03, 2026 at 03:00:01PM +1000, Balbir Singh wrote:
> >
> > Notably: slub.c injects __GFP_THISNODE internally on behalf of kmalloc,
> > which causes spillage into private nodes because slub allows private
> > nodes in its mask. I think this is fixable.
> >
> > I have to inspect some other __GFP_THISNODE users (hugetlb, some arch
> > code, etc), but it seems like fully dropping the FALLBACK entries and
> > requiring __GFP_THISNODE might be sufficient.
>
> Sorry, I haven't been able to follow up so far, and not sure if that's what you
> are discussing here ...
>
> After the LSF/MM session, I was wondering, whether if we focus on allowing only
> folios allocations to end up on private memory nodes for now: could the
> __GFP_THISNODE approach work there?
>
> Essentially, disallow any allocations on non-folio paths, and allow folio
> allocation only with __GFP_THISNODE set.
>
> I have to find time to read the other mails in this thread, on my todo list.
>
> So sorry if that is precisely what is being discussed here.
>
So, I remember this being asked, and I didn't fully grok the request.
I'm still not sure I fully understand the question, so apologies if I'm
answer the wrong things here.
I understand this question in two ways:
1) Can we disallow PAGE allocation and limit this to FOLIO allocation
2) Can we disallow [Feature] (i.e. slab) allocation targeting the node.
1) Can we disallow page allocation and limit this to folios?
No, I don't think so.
Folio allocations are written in terms of page allocations, we would
have to rewrite folio allocation interfaces and introduce a bunch of
boilerplate for the sake of this.
struct page *__alloc_pages_noprof(gfp_t gfp, unsigned int order,
int preferred_nid, nodemask_t *nodemask)
{
struct page *page;
page = __alloc_frozen_pages_noprof(gfp, order, preferred_nid, nodemask);
if (page)
set_page_refcounted(page);
return page;
}
struct folio *__folio_alloc_noprof(gfp_t gfp, unsigned int order, int preferred_nid,
nodemask_t *nodemask)
{
struct page *page = __alloc_pages_noprof(gfp | __GFP_COMP, order,
preferred_nid, nodemask);
return page_rmappable_folio(page);
}
At the end of the day, this all reduces to `get_pages_from_freelist`,
and at that level we don't really care about folio vs page.
__GFP_COMP is insufficient to differentiate between a non-folio compound
page and a folio, and __GFP_COMP is passed into __alloc_pages_*
interfaces all over the kernel.
Trying to detach these paths things seems like a horrible rats nest /
not feasible / will create a lot of boilerplate for little value.
(I did not fully understand this request when it was asked, I do
not fully understand this request not, please let me know if I
have misunderstood what you were asking).
2) Can we disallow SLAB allocation.
Yeah, but I think a better question is whether there's a difference
between alloc_pages_node() and kmalloc_node() when it all just sinks
to the same fundamental code in mm/page_alloc.c
Maybe there's an argument for something like NP_OPT_KMALLOC (allow slab
allocations on the private node w/ __GFP_THISNODE)
On my current set, I don't implement any explicit filtering at all in
mm/page_alloc.c - the filtering is a function of the nodes not being
present in the FALLBACK list and only having a NOFALLBACK list.
What __GFP_THISNODE actually does under the hood is just switch
which zone list (FALLBACK vs NOFALLBACK) is used for the target node.
For isolation w/o __GFP_PRIVATE, we're removing N_MEMORY_PRIVATE nodes
from *their own FALLBACK* list and only adding them to their NOFALLBACK
list. That means to reach a private node you MUST use __GFP_THISNODE.
I realize this is confusing, but essentially we don't have to modify
mm/page_alloc.c to get the __GFP_THISNODE filtering, we get this from
the fallback/nofallback list construction.
Ok, so how does this flush out in practice - and why do I call this
filtering mechanism fragile?
consider kmalloc_node() and __slab_alloc():
kmalloc_node(...)
└─ ___slab_alloc() mm/slub.c:4406 pc.flags |= __GFP_THISNODE
└─ new_slab(s, pc.flags, node)
└─ allocate_slab(s, flags, node)
└─ alloc_slab_page(flags, node, oo, …)
└─ __alloc_frozen_pages(flags, order, node, NULL);
Slab silently upgrades the page allocator flags here to include
__GFP_THISNODE - even if the user didn't request that behavior.
This is exactly the kind of "spillage" I said was hard to police at LSF.
Without __GFP_PRIVATE, we have to keep an eye on what around the kernel
is using __GFP_THISNODE and how.
For mm/slub.c we can choose to do one of thwo things
1) 100% refuse slab allocations on private nodes, i.e.:
kmalloc_node(..., private_nid, __GFP_THISNODE)
And will fail (return NULL).
or
2) Do not upgrade private-node slab requests w/ __GFP_THISNODE
This allows kmalloc_node() to work the same as folio_alloc()
or alloc_pages() interfaces (__GFP_THISNODE is the key), with
the understanding that any __GFP_THISNODE user
We can opt these nodes into slab/kmalloc with a NP_OPT_SLAB
if the owner wants kmalloc_node(), with the understanding that any
caller using __GFP_THISNODE may get access.
That's the kind of fragility I was trying to avoid.
That said, in practice, I have found that basic kernel operations don't
generally target use kmalloc_node() w/ __GFP_THISNODE - there's just
nothing to prevent anyone from doing so.
So this seems promising...
And then theres arch/powerpc/platforms/powernv/memtrace.c
static u64 memtrace_alloc_node(u32 nid, u64 size)
{
... snip ...
page = alloc_contig_pages(nr_pages, GFP_KERNEL | __GFP_THISNODE |
__GFP_NOWARN | __GFP_ZERO, nid, NULL);
... snip ...
}
static int memtrace_init_regions_runtime(u64 size)
{
... snip ...
for_each_online_node(nid) {
m = memtrace_alloc_node(nid, size);
... snip ...
}
static int memtrace_enable_set(void *data, u64 val)
{
... snip ...
if (memtrace_init_regions_runtime(val))
goto out_unlock;
... snip ...
}
This is the *exact* pattern I said would be hard to police - and it
doesn't look like a bug, just not informed that private nodes exist.
This is why I'm concerned with trying to depend on __GFP_THISNODE as the
filtering function.
That said, the number of __GFP_THISNODE users is very limited
kernel-wide, so maybe that's an acceptable maintenance burden?
~Gregory