[PATCH v2 0/5] memcg: clean up memory.high enforcement
From: Shakeel Butt
Date: Fri Oct 30 2026 - 16:56:40 EST
This series cleans up memory.high enforcement and uses a worker for
kernel threads and remote charges.
Kernel threads never return to userspace to run deferred reclaim.
Reclaiming and sleeping during a charge can also delay work they do for
other tasks.
Remote charges can go to a different cgroup from the caller's mm, for
example through filesystem notifications or faults in another process's
memory. The high handler uses current->mm, so it can check the caller's
limits instead of the limits of the charged cgroup.
Use high_work to reclaim from the charged hierarchy in these cases.
mm_match_cgroup() identifies charges outside the current mm's cgroup
and its ancestors. Local user charges keep their current handling.
Before that, force charges from PF_MEMALLOC tasks before trying the
limits, so they skip memory.high enforcement. Reclaim can charge memory
itself, for example when zswap stores a page, and the high_work worker
must not queue itself from its own reclaim. These charges still raise
MEMCG_MAX for the same cgroup as before.
This is best effort. A fast allocator can keep usage well above
memory.high; memory.max stays the hard limit, as it already is for
interrupts. The worker paths also skip memory.swap.high throttling and
its high-event reporting. memory.swap.high only throttles the charging
task, and a worker has no task to throttle. The cgroup's own tasks are
still throttled on their own charges, and memory.swap.max still caps
swap.
The first patch moves enforcement into memcg_enforce_high(). The second
forces charges from reclaim before the limit checks. The next two add
kernel thread and remote charge handling. The last simplifies the scan
and comments. The first and last patches have no intended behavior
change.
Based on mm-everything-2026-10-29-04-31. v1 was tested in an x86-64 VM
with loop writes, threaded NAPI, remote faults, notifications, and both
vhost worker modes. For v2, each patch was built with GCC and Clang
(W=1). In a VM test with incompressible zswap data and remote faults,
the high_work worker queued itself from its own reclaim in 118 of 284
runs without the PF_MEMALLOC patch, and never with the series. The runs
were counted with bpftrace on queue_work_on() inside high_work_func().
In another test, a parent cgroup was over memory.max while its child
was reclaimed. The child's zswap charges raised MEMCG_MAX on the parent
7133 times with the series and 7124 times without it.
Changes in v2:
- Rebased on mm-everything-2026-10-29-04-31.
- Collected Reviewed-by and Acked-by tags.
- Patch 1: add the missing Signed-off-by (Harry).
- Patch 2 (was 4): replaced with Michal's suggestion. Charges from
PF_MEMALLOC tasks are now forced before trying the limits, instead of
skipping enforcement in memcg_enforce_high(). MEMCG_MAX is still
raised for the first cgroup whose limit the charge goes over. Moved
before the kernel thread patch, so that no commit lets the worker
queue itself. (Michal)
- Patch 4: say more clearly which charges keep the local path.
- Patch 5: drop locals that are no longer needed, and update the
comments.
v1: https://lore.kernel.org/20261010210957.1350874-1-shakeel.butt@xxxxxxxxx
Shakeel Butt (5):
memcg: move memory.high enforcement out of try_charge_memcg()
memcg: force charges from reclaim before trying the limits
memcg: use high_work for kernel threads
memcg: use high_work for remote charges
memcg: simplify high limit handling after a charge
mm/memcontrol.c | 157 +++++++++++++++++++++++++++---------------------
1 file changed, 87 insertions(+), 70 deletions(-)
base-commit: 7e12256cff20c704fd5c0fbbd6a070fda8c341c9
--
2.53.0-Meta