[PATCH RFC -next 0/5] net: charge socket memory budget to memcg upfront

From: Cai Xinchen

Date: Thu Sep 24 2026 - 03:41:42 EST


The memcg socket accounting currently charges pages to the memory
cgroup per grant (__sk_mem_schedule() publishing forward allocation)
and refunds them later from skb destructors. The refund side folds
per-skb "was this charged" snapshots back into the socket balance
under concurrent lockless RMW, and races there can drive the memcg
socket balance negative, ending with:

page_counter underflow
WARNING: ... mm/page_counter.c ... page_counter_cancel()

This series flips the model: a socket is charged its whole memory
budget (sk_sndbuf + sk_rcvbuf + sk_reserved_mem) to its memcg when the
budget is established or grows, and refunded when the budget shrinks
or the socket dies. Grants and per-skb charge/uncharge stop touching
the memcg entirely, so the racy refund pairing has no code left to go
wrong: refunds can never exceed charges and the balance cannot
underflow by construction.

Beyond removing the racy pairing, charging the budget is the more
accurate accounting: once the budget is established the memory is
effectively allocated to that socket - the protocol fills the buffers
on demand and no other socket in the cgroup can spend that capacity.
The "sock" counter therefore reports memory the sockets already own
rather than memory that happens to be in flight at the sampling
instant, the same model vm_committed_as uses for claimed address
space and hugetlb uses for reservations (charged at creation, not at
first touch). The visible consequence is that an idle socket holds
its full budget charged for its lifetime. Global protocol accounting
(memory_allocated, tcp_mem pressure) is untouched.

Patch overview:

1/5 net: charge socket memory budget to memcg upfront
Core tracker (sk_memcg_budget), lifecycle charge points, memcg
removal from the grant path, lazy shrink sync in sk_mem_reclaim().

2/5 tcp: sync memcg budget on protocol buffer updates
tcp_init_sock(), tcp_set_rcvlowat() and the four autotune/pressure
buffer writes in tcp_input.c.

3/5 mptcp: sync memcg budget and drop backlog page compensation
MPTCP budget writes, memcg inheritance transfer, and removal of
the now double-charging backlog_unaccounted compensation.

4/5 Docs/admin-guide/cgroup-v2: document upfront socket budget
charging

5/5 selftests/cgroup: compare socket memory deltas in test_memcg_sock
Make the test valid under both the old and the new model.

Tested: full arm64 build with 0 warnings; each intermediate state
compiles (bisectable); tools/testing/selftests/cgroup builds clean.
Runtime validation on the workload that used to trigger the underflow
is pending.

Cai Xinchen (5):
net: charge socket memory budget to memcg upfront
tcp: sync memcg budget on protocol buffer updates
mptcp: sync memcg budget and drop backlog page compensation
Docs/admin-guide/cgroup-v2: document upfront socket budget charging
selftests/cgroup: compare socket memory deltas in test_memcg_sock

Documentation/admin-guide/cgroup-v2.rst | 6 +-
include/net/sock.h | 21 ++
net/core/sock.c | 237 ++++++++++++++----
net/ipv4/tcp.c | 11 +-
net/ipv4/tcp_input.c | 10 +-
net/mptcp/protocol.c | 42 +---
net/mptcp/protocol.h | 2 +-
net/mptcp/sockopt.c | 3 +
net/mptcp/subflow.c | 5 +
.../selftests/cgroup/test_memcontrol.c | 18 +-
10 files changed, 261 insertions(+), 94 deletions(-)

--
2.18.0.huawei.25