[PATCH net] nexthop: account nexthop allocations to memcg
From: Yizhou Zhao
Date: Tue Jul 28 2026 - 02:02:04 EST
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@xxxxxxxxxxxxxxx
Reported-by: Yizhou Zhao <zhaoyz24@xxxxxxxxxxxxxxxxxxxxx>
Reported-by: Yuxiang Yang <yangyx22@xxxxxxxxxxxxxxxxxxxxx>
Reported-by: Ao Wang <wangao@xxxxxxxxxx>
Reported-by: Xuewei Feng <fengxw06@xxxxxxx>
Reported-by: Qi Li <qli01@xxxxxxxxxxxxxxx>
Reported-by: Ke Xu <xuke@xxxxxxxxxxxxxxx>
Assisted-by: Claude-Code:GLM-5.2
Signed-off-by: Yizhou Zhao <zhaoyz24@xxxxxxxxxxxxxxxxxxxxx>
---
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