Re: [LSF/MM/BPF TOPIC][RFC PATCH v4 00/27] Private Memory Nodes (w/ Compressed RAM)
From: Balbir Singh <hidden>
Date: 2026-06-17 04:03:03
Also in:
cgroups, damon, linux-cxl, linux-mm, lkml
On Wed, Jun 10, 2026 at 12:37:34PM -0400, Gregory Price wrote:
On Wed, Jun 10, 2026 at 05:00:33PM +0200, David Hildenbrand (Arm) wrote:quoted
On 6/10/26 12:41, Gregory Price wrote:quoted
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).
I agree with this, any changes to folio only allocation could then be easily adapted for N_MEMORY_PRIVATE
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).Doesn't this iterate through N_MEMORY only? N_MEMORY_PRIVATE should not be in the regular for_each(...) loops
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?Balbir