[PATCH net v4] net/packet: account user-triggered allocations to memcg
flat view
From: Ahmad AG <hidden>
Date: 2026-11-01 09:50:17
Also in:
lkml
Subsystem:
networking [general], packet sockets, the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Willem de Bruijn, Linus Torvalds
A packet socket makes allocations whose size or count its user chooses without charging them to the user's memory cgroup: the blocks and block array of a PACKET_RX_RING or PACKET_TX_RING ring and the rx_owner_map of a TPACKET_V1/V2 RX ring, the PACKET_FANOUT group (about 1 MB with max_num_members = 65536), and one packet_mclist per PACKET_ADD_MEMBERSHIP entry. CAP_NET_RAW in a user namespace is enough to pin memory far beyond memory.max this way. Allocate them with GFP_KERNEL_ACCOUNT, as the AF_XDP rings do since commit b0057c68df71 ("xsk: account ring allocations to memcg"), and with __GFP_NOWARN where the size comes from user space, so that a ring or group that does not fit prints no allocation warning. Fixed-size per-socket state is left unaccounted. Reproducer, as uid 1000 in a memory cgroup with memory.max 16M, after unshare(CLONE_NEWUSER | CLONE_NEWNET): open 64 AF_PACKET sockets, give each a TPACKET_V2 RX ring of 8 blocks of 1 MB, mmap the rings and touch every block. MemFree drops by 526052 KB while memory.current grows by 1280 KB. 256 fanout groups with max_num_members = 65536 (265144 KB used, 512 KB charged) and 100 sockets with 2000 PACKET_MR_PROMISC entries each on lo (45220 KB used, 124 KB charged) behave the same. With this change all three are stopped by the cgroup's OOM killer. psock_fanout, psock_tpacket and psock_snd.sh pass before and after. Found by code inspection with the help of an LLM and reproduced on an unmodified kernel without error injection; tested in a QEMU guest with KASAN on net. The ring pages and rx_owner_map were never charged, the rest not since commit a9bb7e620efd ("memcg: only account kmem allocations marked as __GFP_ACCOUNT"). The Fixes commit let the user size the fanout array. Fixes: 9c661b0b8544 ("net/packet: make packet_fanout.arr size configurable up to 64K") Cc: <stable+noautosel@kernel.org> # changes memcg charging of existing packet rings; not stable material Signed-off-by: Ahmad AG <redacted> --- Tool use: an LLM coding assistant (Claude Code) found the missing charges by code inspection, drafted the patch, the test program and this message, and ran the tests in the QEMU guest; I reviewed every hunk, the results and this message before sending. v4: - trim the commit message - add __GFP_NOWARN to the fanout group allocation - use __vcalloc() for the vmalloc fallback and bitmap_zalloc() for rx_owner_map v3: https://lore.kernel.org/netdev/20261031084316.387533-1-ahmadag1001@gmail.com/ (local) - Fixes 9c661b0b8544; stable+noautosel; drop Assisted-by; leave fixed-size allocations unaccounted; __GFP_NOWARN on the vmalloc fallback v2: https://lore.kernel.org/netdev/20261030100504.23498-1-ahmadag1001@gmail.com/ (local) - cover all user-triggered allocations in one patch v1: https://lore.kernel.org/netdev/CAASqGyZ-9hqZGWNJShxnrSiXDO==51SVsQW_x_mk993y1pNO8A@mail.gmail.com/ (local) net/packet/af_packet.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index cf6429f0947..ec6ab96c4f8 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c@@ -1778,7 +1778,8 @@ static int fanout_add(struct sock *sk, struct fanout_args *args) /* legacy PACKET_FANOUT_MAX */ args->max_num_members = 256; err = -ENOMEM; - match = kvzalloc_flex(*match, arr, args->max_num_members); + match = kvzalloc_flex(*match, arr, args->max_num_members, + GFP_KERNEL_ACCOUNT | __GFP_NOWARN); if (!match) goto out; write_pnet(&match->net, sock_net(sk));
@@ -3773,7 +3774,7 @@ static int packet_mc_add(struct sock *sk, struct packet_mreq_max *mreq) goto done; err = -ENOBUFS; - i = kmalloc_obj(*i); + i = kmalloc_obj(*i, GFP_KERNEL_ACCOUNT); if (i == NULL) goto done;
@@ -4494,7 +4495,7 @@ static void packet_free_tx_ring(struct sock *sk, struct pgv *pg_vec, static char *alloc_one_pg_vec_page(unsigned long order) { char *buffer; - gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP | + gfp_t gfp_flags = GFP_KERNEL_ACCOUNT | __GFP_COMP | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY; buffer = (char *) __get_free_pages(gfp_flags, order);
@@ -4502,7 +4503,8 @@ static char *alloc_one_pg_vec_page(unsigned long order) return buffer; /* __get_free_pages failed, fall back to vmalloc */ - buffer = vzalloc(array_size((1 << order), PAGE_SIZE)); + buffer = __vcalloc(1 << order, PAGE_SIZE, + GFP_KERNEL_ACCOUNT | __GFP_NOWARN); if (buffer) return buffer;
@@ -4523,7 +4525,8 @@ static struct pgv *alloc_pg_vec(struct tpacket_req *req, int order, bool tx_ring struct pgv *pg_vec; int i; - vec = kzalloc_flex(*vec, pg_vec, block_nr, GFP_KERNEL | __GFP_NOWARN); + vec = kzalloc_flex(*vec, pg_vec, block_nr, + GFP_KERNEL_ACCOUNT | __GFP_NOWARN); if (unlikely(!vec)) return NULL; vec->order = order;
@@ -4647,8 +4650,10 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, break; default: if (!tx_ring) { - rx_owner_map = bitmap_alloc(req->tp_frame_nr, - GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO); + rx_owner_map = + bitmap_zalloc(req->tp_frame_nr, + GFP_KERNEL_ACCOUNT | + __GFP_NOWARN); if (!rx_owner_map) goto out_free_pg_vec; }
base-commit: cb284914e749388eef402938bdb3115fe1f6d337 -- 2.43.0