[PATCH net] nexthop: account nexthop allocations to memcg
From: Yizhou Zhao <hidden>
Date: 2026-07-28 06:01:40
Also in:
lkml, stable
Subsystem:
networking [general], networking [ipv4/ipv6], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds
The nexthop creation path allocates struct nexthop, struct nh_info,
group storage, resilient bucket tables, transient notifier tables and
per-group stats without memcg accounting. As a result, a task with
CAP_NET_ADMIN can create nexthops and resilient groups whose kernel
memory is not charged to the task's memory cgroup, so memory.max does
not constrain this part of the workload.
A QEMU reproduction with Docker confirmed the problem. A container
granted CAP_NET_ADMIN and limited to memory.max=128M could repeatedly
create resilient nexthops while memory.current stayed at 589824 and host
VmallocUsed rose from 67044 kB to 231860 kB.
Fix this by charging the nexthop creation-path allocations to the
caller's memcg with GFP_KERNEL_ACCOUNT / __GFP_ACCOUNT, including the
per-group stats allocation added later. With this change, the same
workload hits memcg OOM with GFP_KERNEL_ACCOUNT in the allocation path
and host VmallocUsed stays flat at 67044 kB, so memory.max constrains
the workload again.
Fixes: ab84be7e54fc ("net: Initial nexthop code")
Fixes: f4676ea74b85 ("net: nexthop: Add nexthop group entry stats")
Cc: stable@vger.kernel.org
Reported-by: Yizhou Zhao <redacted>
Reported-by: Yuxiang Yang <redacted>
Reported-by: Ao Wang <redacted>
Reported-by: Xuewei Feng <redacted>
Reported-by: Qi Li <redacted>
Reported-by: Ke Xu <redacted>
Assisted-by: Claude-Code:GLM-5.2
Signed-off-by: Yizhou Zhao <redacted>
---diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c
index 44fe75004cac..c8685c583579 100644
--- a/net/ipv4/nexthop.c
+++ b/net/ipv4/nexthop.c@@ -169,7 +169,7 @@ static int nh_notifier_res_table_info_init(struct nh_notifier_info *info, info->type = NH_NOTIFIER_INFO_TYPE_RES_TABLE; size = struct_size(info->nh_res_table, nhs, num_nh_buckets); info->nh_res_table = __vmalloc(size, GFP_KERNEL | __GFP_ZERO | - __GFP_NOWARN); + __GFP_NOWARN | __GFP_ACCOUNT); if (!info->nh_res_table) return -ENOMEM;
@@ -533,7 +533,7 @@ static struct nexthop *nexthop_alloc(void) { struct nexthop *nh; - nh = kzalloc_obj(struct nexthop); + nh = kzalloc_obj(struct nexthop, GFP_KERNEL_ACCOUNT); if (nh) { INIT_LIST_HEAD(&nh->fi_list); INIT_LIST_HEAD(&nh->f6i_list);
@@ -548,7 +548,7 @@ static struct nh_group *nexthop_grp_alloc(u16 num_nh) { struct nh_group *nhg; - nhg = kzalloc_flex(*nhg, nh_entries, num_nh); + nhg = kzalloc_flex(*nhg, nh_entries, num_nh, GFP_KERNEL_ACCOUNT); if (nhg) nhg->num_nh = num_nh;
@@ -565,7 +565,8 @@ nexthop_res_table_alloc(struct net *net, u32 nhg_id, struct nh_config *cfg) unsigned long size; size = struct_size(res_table, nh_buckets, num_nh_buckets); - res_table = __vmalloc(size, GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN); + res_table = __vmalloc(size, GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN | + __GFP_ACCOUNT); if (!res_table) return NULL;
@@ -2799,7 +2800,8 @@ static struct nexthop *nexthop_create_group(struct net *net, nhg->has_v4 = true; nhg->nh_entries[i].stats = - netdev_alloc_pcpu_stats(struct nh_grp_entry_stats); + __netdev_alloc_pcpu_stats(struct nh_grp_entry_stats, + GFP_KERNEL_ACCOUNT); if (!nhg->nh_entries[i].stats) { err = -ENOMEM; nexthop_put(nhe);
@@ -2943,7 +2945,7 @@ static struct nexthop *nexthop_create(struct net *net, struct nh_config *cfg, if (!nh) return ERR_PTR(-ENOMEM); - nhi = kzalloc_obj(*nhi); + nhi = kzalloc_obj(*nhi, GFP_KERNEL_ACCOUNT); if (!nhi) { kfree(nh); return ERR_PTR(-ENOMEM); --
2.47.3