[RFC PATCH v3 bpf-next 0/5] BPF-based per-cgroup NUMA balancing mode selection

From: Yafang Shao

Date: Sun Oct 25 2026 - 09:32:42 EST


Hello Tejun,

> On Mon, Oct 19, 2026 at 02:41:52PM +0800, Yafang Shao wrote:
>> An opt-out wins and binds the subtree: a DISABLED request anywhere in
>> the chain opts the cgroup's tasks out entirely. Otherwise the requested
>> bits OR together and the kernel reads them independently, the way
>> kernel.numa_balancing reads them, so a combined NORMAL|TIERING engages
>> both modes. A DISABLED request only speaks for the cgroup's tasks,
>> never for its memory, which follows the remaining requests of its
>> ancestors.
>
> Can you explain the reasoning behind the basic design decisions?

I missed your mail before. Sorry about the late response.

> - The folio paths resolve through folio_memcg() while the task paths use
> the task's cgroup. Why isn't the whole thing tied to memcg?

On the unified hierarchy the task's cgroup and its memcg are the same node
wherever the memory controller is enabled, so tying the whole thing to
memcg is behaviorally identical in the common case.

The only difference shows up on cgroups without the memory controller
enabled: today a request there still reaches the task paths (the field
lives on struct cgroup), while the folio paths could never see it, since
folios charge to the nearest memcg-enabled ancestor. That asymmetry is
exactly what your question points at.

I'm fine with anchoring both sides on memcg, and bpf_numab_set_mode()
will reject cgroups that don't have the memory controller enabled.
Deployments that don't enable the memory controller anywhere can
still make a system-wide request through the root memcg.

> - Why do the task and folio paths combine the chain differently? DISABLED
> binds the subtree for tasks but is skipped for folios.

DISABLED is an opt-out of work, not a freeze of memory: it speaks for
the cgroup's tasks and never for its memory. A folio charged to an
opted-out cgroup can still be hot for tasks that opted in; freezing it
would let one cgroup's opt-out block the promotion of the memory they
share

> - Some of the folio paths already have the faulting task.
> should_numa_migrate_memory() takes @p, and the do_numa_page() and
> migrate_misplaced_folio() checks run in the faulting task's context.
> Why not use the task there?

It comes down to the common migration backend, which runs for user
tasks and for kswapd alike: folio_migrate_flags().

Fault path (user task):

do_numa_page
+ mpol_misplaced
+ should_numa_migrate_memory
+ folio_numab_in_tiering_mode
+ migrate_misplaced_folio
+ migrate_pages
+ folio_migrate_flags
+ folio_numab_in_tiering_mode

Kswapd demotion (no task):

kswapd
+ migrate_pages
+ folio_migrate_flags
+ folio_numab_in_tiering_mode

folio_migrate_flags() must key on the folio: kswapd has no task to
ask. And one migration asks the same question twice: once to decide
whether the folio moves, once to reset its access time. The two
answers must agree. If one check keyed on the task and the other on
the folio, a folio whose toucher and owner disagree would move by
one rule and reset by another.

So the rule is simple: if a folio is available, key on the folio;
otherwise, key on the task. This also makes the keying robust to
future refactoring: if one of these functions is ever called by a
kernel thread, the answer does not change.

> Stepping back, the basic design decisions don't seem to have been
> thought through, and the descriptions restate them at length without
> explaining them. I'm not the best person to judge how useful the
> feature is in practice, but getting this version into acceptable shape
> is likely a lot more work than having someone who knows how to do it
> just write it. I don't think it makes sense to drag this out and force
> it into shape over many review rounds.

You're right — the descriptions restate the design at length without
explaining it. I'm not a native speaker, so I had an LLM rewrite my
commit logs, and it restated them at length without explaining
anything — the exact symptom you called out. In the next version I
will write them myself and only use the LLM to fix grammar.