Re: [PATCH v5 00/36] Private Memory NUMA Nodes

From: Gregory Price

Date: Tue Sep 22 2026 - 18:05:42 EST


On Tue, Sep 22, 2026 at 03:27:44PM +0530, Arun George/Arun George wrote:
> Hi Gregory,
>
> Thanks for sharing the working branch.
>
> Some of us at Samsung are testing the series (both v4 & v5) on a
> compression capable CXL expander and are observing encouraging results.
>

This is really cool, thank you so much for spending the cycles to test.

I'm working on getting a v6 out soon. Glad to see the progress!

> Good things first! We feel that the isolation using private nodes and
> the capability selection using NODE_PRIVATE_CAP_* work functionally well
> for the compressed memory use cases.
>
> On improvement points, we observed much higher 'page_faults',
> 'allocation_stalls' etc. which contributed to the higher tail latencies
> in some tests. I hope these are already part of the optimization plans.
> The higher latencies might have resulted from the write_protection
> applied to the private node, I believe.
>

Yes, this is expected, and I think there are questions about how it
should be optimized both in a provisioning sense and a software sense.

For example, exposing a full memory expander as 100% compressible may
not make sense - given that this could many a lot of memory. I have
some data that shows it may make more sense to split a memory expander
into two regions to better fit the compressible region to meet the
actual capacity the cold-tail is capable of consuming.

On the software side there may be some ability to adjust this at
runtime with hotplug instead of requiring a reboot, but I think that
needs to be experimented with.

> On the tools side, we used MLC and Taobench for the tests.
>

Great! I also have some TaoBench and fio results i'm looking to share,
glad to have some additional peer review using Tao.

> Note that our intention for this phase of experiments was to test only
> the 'private node' layer based isolation, and not the cram and below
> layers for the compressed memory. Therefore we did not enable/utilize
> the compression capability in the hardware for these set of experiments
> (enabling compression would require the cram level ballooning/memory
> shrinking and cxl level interfacing driver to the compression device).
> That would be different set of experiments where we would be testing the
> cram balloon shrinkers and our alternate algorithms (upstream targeted).
> So cram and below layers were used only for enumeration of private node
> regions for these tests and not for the run-time memory shrinkers.
>

Agreed, i'm looking forward to getting past the isolation bits and get
more focused on the compression implementation details.

> ======= Test Methodology ==============
>
> We explored these 3 cases:
>
> 1) (Baseline – existing CXL infra): DRAM 16GB + CXL 32GB. Here we
> used the existing CXL driver infra to enable the device. No private node
> is involved. And compression is disabled on device.
>
> 2) (private node in default write protected path): DRAM 16GB + CXL
> Private Node 32GB. Here private node infra is used to enable the device.
> And compression is disabled on device.
>
> 3) (private node with no write protection): DRAM 16GB + CXL Private
> Node 32GB. Here private node infra is used to enable the device without
> the write protection/fencing enabled. Compression is disabled on device.
> We could not complete these runs as they resulted in kernel panics.
> Guess the code path is not stable yet for this (We had hoped that case 1
> and case 3 results would be similar). We also observed some unmovable
> page warnings logs in 'dmesg' before crash (might be related to the
> panic). Adding the log snippets at the end.
>

Very similar to my test setup, seems like good signal we are of similar
mind on the use case. (For readers: there was no coordination here).

I personally did not test private-node without write protection,
although it makes sense to test the throughput of a demotion-only
node as a baseline. Smart.

My guess is there was probably a reclaim throughput issue, or there may
have been a bug in the v4/v5 code. I will look at adding some pressure
tests to my suite that test demotion-only explicitly.

> ========= Results summary ===================
>
> Private (CRAM) node performance is lower compared to a normal CXL memory
> allocation path. VM stats shows more page_faults and allocation_stalls
> on the private node case. Could it be the allocator waits for migration
> path to demote pages to private node? Or the actual hot pages in dram
> got demoted to private node to make space during overwrites? We will try
> further analysis on this.
>

- allocator waits for migration?

Yes. And worse, once the node is full, migration will fail and you'll
swap directly from the top tier and reduce your reclaim behavior on
the lower node. It becomes very important to push proactive reclaim
on a demotion-only tier to ensure there is sufficient headroom to
receive demotions in the future, since direct-reclaim basically never
targets a lower-tier on the first pass.

There's a big discussion about whether tiered systems need to invert
their reclaim behavior (reclaim from the lowest node first to find
progress there, then do demotion - rather than the other way around).

- Hot pages in dram

Also yes. The comparison to make isn't only against a raw CXL node,
but also against Zswap or Zram or Swap. How does the workload fair
on a system with 16GB RAM and 32GB Zswap/Swap?

This is important context.

> ========== MLC Experiments ====================
>
> CMD:
> $./mlc --loaded_latency -j0 -c0 -b1g -k1-15 -W5 -r
>
> Experiment Results:
>
> 1. (Baseline) DRAM 16GB + CXL 32GB
>
> Inject Latency Bandwidth
> Delay (ns) MB/sec
> ================
> 00000 686.18 40137.5
> 00002 686.69 40124.0
> 00008 709.74 40362.7
...
> 2. (private node in default write protected path)
> DRAM 16GB + CXL Private Node(Uncompressed) 32GB
>
> Inject Latency Bandwidth
> Delay (ns) MB/sec
> ================
> 00000 320.09 12186.2
> 00002 543.42 22726.1
> 00008 482.11 28385.2
...
> Takeaway: Our MLC tests show a trade-off between the two configurations.
> During low inject delay, the private Node is faster. But when inject
> delay goes high, the baseline shows better latency. Could it be the TLB
> cache effects?
>

These latency results suggest to me that the memory being tested was
always DRAM, though the initial memory fault would have been CXL.

i.e. even if the memory was faulted directly onto the node via
mempolicy, the very first write to it would have caused promotion.

If you dropped the write protection you'd probably see it equals the
baseline.

The bandwidth numbers support this. Higher stall, but faster latency
means the migration was more beneficial for latency than letting the
memory sit on CXL.

Neat. I'm not sure these results are particularly useful in describing
how a workload would react.

> --------------------------------------------------------------------
> Case 3 dmesg log snippet:
>
> [ 82.525741] page: refcount:1 mapcount:0 mapping:0000000000000000
> index:0x0 pfn:0x5f5800
> [ 82.525753] flags:
> 0x17ffffc0002000(reserved|node=0|zone=2|lastcpupid=0x1fffff)
> [ 82.525762] raw: 0017ffffc0002000 ffefc85717d60008 ffefc85717d60008
> 0000000000000000
> [ 82.525764] raw: 0000000000000000 0000000000000000 00000001ffffffff
> 0000000000000000
> [ 82.525766] page dumped because: unmovable page
> ..............
>

Was this memory hotplugged via cram.c or did you replicate the code and
hotplug it another way? If so, did you hotplug it as ZONE_NORMAL or
ZONE_MOVABLE?

For a compressed tier, it must always be hotplugged as ZONE_MOVABLE,
without exception, because otherwise it can allow a GUP pin or kernel
allocation that would eventually become permanently stuck there - and by
definition of a compression tier must be 100% movable memory.

~Gregory