From: Ali Firas <alishmery18@gmail.com>
To: netdev@vger.kernel.org, idosch@nvidia.com
Cc: kuba@kernel.org, pabeni@redhat.com, davem@davemloft.net,
edumazet@google.com, andrew+netdev@lunn.ch, horms@kernel.org,
razor@blackwall.org, roopa@nvidia.com, shuah@kernel.org,
linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org,
Ali Firas <alishmery18@gmail.com>
Subject: [PATCH net-next v4 6/7] vxlan: vnifilter: account the default FDB entry to memcg
Date: Wed, 14 Oct 2026 15:38:13 +0300 [thread overview]
Message-ID: <20261014123814.388557-7-alishmery18@gmail.com> (raw)
In-Reply-To: <20261014123814.388557-1-alishmery18@gmail.com>
A VNI added through the vnifilter interface with a group or remote
installs a default FDB entry for that VNI: vxlan_vni_update_group()
calls vxlan_update_default_fdb_entry(), which calls vxlan_fdb_update()
and vxlan_fdb_create(). The entry allocates a struct vxlan_fdb and, per
remote, a struct vxlan_rdst plus its per-CPU dst cache, all with
GFP_ATOMIC and none charged to a memory cgroup.
The previous patch accounts the per-VNI node and its stats; these FDB
objects are the rest of what one vnifilter request allocates per VNI.
Charge them to the caller, as commit c948f51c1654 ("memcg: enable
accounting for net_device and Tx/Rx queues") and commit e2c8d550a973
("netfilter: ebtables: account ebt_table_info to kmemcg") do: the
lifetime of this memory is the net namespace, not the allocating task,
so the OOM killer cannot reclaim it and it must be charged to the cgroup
of the task whose request allocates it.
Thread a gfp argument through vxlan_fdb_update() to
vxlan_fdb_alloc()/vxlan_fdb_append() on both paths it can take -- the
create path (vxlan_fdb_update_create() -> vxlan_fdb_create()) and the
path that changes an existing entry's remote
(vxlan_fdb_update_existing()) -- and a second gfp through
vxlan_update_default_fdb_entry(), so each caller chooses it. Every
non-vnifilter caller of vxlan_fdb_update() -- vxlan_fdb_add(),
vxlan_snoop(), vxlan_dev_create(), vxlan_changelink() and the switchdev
vxlan_fdb_external_learn_add() -- keeps plain GFP_ATOMIC, so the data
path and unrelated callers are unchanged. dst_cache_init() already takes
a gfp argument, so no change outside the driver is needed.
A per-request add or change of a VNI's remote (RTM_NEWTUNNEL, via
vxlan_vni_update_group()) passes GFP_NOWAIT | __GFP_ACCOUNT. These
allocations run under the hash spinlock and cannot sleep, but a
configuration path does not need the atomic reserves GFP_ATOMIC grants,
and GFP_NOWAIT makes the memcg charge hard-bounded rather than
force-charged: a cgroup at its limit fails the add or change there with
-ENOMEM instead of pushing memory.current past memory.max. The failure
is clean -- the new entry is allocated before the old is freed, so an
add that fails leaves the existing entry intact and returns the error;
no established FDB state is destroyed and no false success is returned.
Creating a VNI is gated the same way by the previous patch's node and
stats (GFP_KERNEL_ACCOUNT, hard-bounded). Measured on production builds
at 2 and 8 vCPU in an 8 MiB cgroup: over-limit adds and changes return
-ENOMEM and memory.current does not exceed memory.max.
The device-wide default-remote change (RTM_NEWLINK, via
vxlan_vnilist_update_group()) keeps GFP_ATOMIC | __GFP_ACCOUNT. That
walk touches every VNI inheriting the device default in one message,
outside the per-request cap, and its existing error unwind re-installs
entries; hard-bounding it would turn a cgroup limit into a mid-walk
failure that the unwind cannot cleanly reverse. Bounding and unwinding
that pre-existing walk is left to a separate change; here it stays
force-charged, as before this series, and is charged to the task
issuing the RTM_NEWLINK.
This adds no new failure path on the data or learning paths: their
allocations already existed and keep GFP_ATOMIC; only the vnifilter
configuration path gains an -ENOMEM return, which its callers already
propagate.
Assisted-by: LLM
Signed-off-by: Ali Firas <alishmery18@gmail.com>
---
v4: new patch.
drivers/net/vxlan/vxlan_core.c | 47 ++++++++++++++++-------------
drivers/net/vxlan/vxlan_private.h | 7 +++--
drivers/net/vxlan/vxlan_vnifilter.c | 14 ++++++---
3 files changed, 39 insertions(+), 29 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 278d41f58d89..7991b544af5a 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -577,7 +577,7 @@ static int vxlan_fdb_replace(struct vxlan_fdb *f,
/* Add/update destinations for multicast */
static int vxlan_fdb_append(struct vxlan_fdb *f,
union vxlan_addr *ip, __be16 port, __be32 vni,
- __u32 ifindex, struct vxlan_rdst **rdp)
+ __u32 ifindex, struct vxlan_rdst **rdp, gfp_t gfp)
{
struct vxlan_rdst *rd;
@@ -585,14 +585,14 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
if (rd)
return 0;
- rd = kmalloc_obj(*rd, GFP_ATOMIC);
+ rd = kmalloc_obj(*rd, gfp);
if (rd == NULL)
return -ENOMEM;
/* The driver can work correctly without a dst cache, so do not treat
* dst cache initialization errors as fatal.
*/
- dst_cache_init(&rd->dst_cache, GFP_ATOMIC | __GFP_NOWARN);
+ dst_cache_init(&rd->dst_cache, gfp | __GFP_NOWARN);
rd->remote_ip = *ip;
rd->remote_port = port;
@@ -779,11 +779,11 @@ static int vxlan_gpe_gro_complete(struct sock *sk, struct sk_buff *skb, int nhof
static struct vxlan_fdb *vxlan_fdb_alloc(struct vxlan_dev *vxlan, const u8 *mac,
__u16 state, __be32 src_vni,
- __u16 ndm_flags)
+ __u16 ndm_flags, gfp_t gfp)
{
struct vxlan_fdb *f;
- f = kmalloc_obj(*f, GFP_ATOMIC);
+ f = kmalloc_obj(*f, gfp);
if (!f)
return NULL;
memset(&f->key, 0, sizeof(f->key));
@@ -869,7 +869,7 @@ int vxlan_fdb_create(struct vxlan_dev *vxlan,
__u16 state, __be16 port, __be32 src_vni,
__be32 vni, __u32 ifindex, __u16 ndm_flags,
u32 nhid, struct vxlan_fdb **fdb,
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack, gfp_t gfp)
{
const struct vxlan_config *cfg = rcu_dereference_rtnl(vxlan->cfg);
struct vxlan_rdst *rd = NULL;
@@ -881,14 +881,14 @@ int vxlan_fdb_create(struct vxlan_dev *vxlan,
return -ENOSPC;
netdev_dbg(vxlan->dev, "add %pM -> %pIS\n", mac, ip);
- f = vxlan_fdb_alloc(vxlan, mac, state, src_vni, ndm_flags);
+ f = vxlan_fdb_alloc(vxlan, mac, state, src_vni, ndm_flags, gfp);
if (!f)
return -ENOMEM;
if (nhid)
rc = vxlan_fdb_nh_update(vxlan, f, nhid, extack);
else
- rc = vxlan_fdb_append(f, ip, port, vni, ifindex, &rd);
+ rc = vxlan_fdb_append(f, ip, port, vni, ifindex, &rd, gfp);
if (rc < 0)
goto errout;
@@ -984,7 +984,7 @@ static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan,
__u32 ifindex, __u16 ndm_flags,
struct vxlan_fdb *f, u32 nhid,
bool swdev_notify,
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack, gfp_t gfp)
{
__u16 fdb_flags = (ndm_flags & ~NTF_USE);
struct vxlan_rdst *rd = NULL;
@@ -1047,7 +1047,8 @@ static int vxlan_fdb_update_existing(struct vxlan_dev *vxlan,
if ((flags & NLM_F_APPEND) &&
(is_multicast_ether_addr(f->key.eth_addr) ||
is_zero_ether_addr(f->key.eth_addr))) {
- rc = vxlan_fdb_append(f, ip, port, vni, ifindex, &rd);
+ rc = vxlan_fdb_append(f, ip, port, vni, ifindex, &rd,
+ gfp);
if (rc < 0)
return rc;
@@ -1088,7 +1089,7 @@ static int vxlan_fdb_update_create(struct vxlan_dev *vxlan,
__be16 port, __be32 src_vni, __be32 vni,
__u32 ifindex, __u16 ndm_flags, u32 nhid,
bool swdev_notify,
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack, gfp_t gfp)
{
__u16 fdb_flags = (ndm_flags & ~NTF_USE);
struct vxlan_fdb *f;
@@ -1101,7 +1102,7 @@ static int vxlan_fdb_update_create(struct vxlan_dev *vxlan,
netdev_dbg(vxlan->dev, "add %pM -> %pIS\n", mac, ip);
rc = vxlan_fdb_create(vxlan, mac, ip, state, port, src_vni,
- vni, ifindex, fdb_flags, nhid, &f, extack);
+ vni, ifindex, fdb_flags, nhid, &f, extack, gfp);
if (rc < 0)
return rc;
@@ -1124,7 +1125,7 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan,
__be16 port, __be32 src_vni, __be32 vni,
__u32 ifindex, __u16 ndm_flags, u32 nhid,
bool swdev_notify,
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack, gfp_t gfp)
{
struct vxlan_fdb *f;
@@ -1138,7 +1139,8 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan,
return vxlan_fdb_update_existing(vxlan, ip, state, flags, port,
vni, ifindex, ndm_flags, f,
- nhid, swdev_notify, extack);
+ nhid, swdev_notify, extack,
+ gfp);
} else {
if (!(flags & NLM_F_CREATE))
return -ENOENT;
@@ -1146,7 +1148,7 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan,
return vxlan_fdb_update_create(vxlan, mac, ip, state, flags,
port, src_vni, vni, ifindex,
ndm_flags, nhid, swdev_notify,
- extack);
+ extack, gfp);
}
}
@@ -1287,7 +1289,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
err = vxlan_fdb_update(vxlan, addr, &ip, ndm->ndm_state, flags,
port, src_vni, vni, ifindex,
ndm->ndm_flags | NTF_VXLAN_ADDED_BY_USER,
- nhid, true, extack);
+ nhid, true, extack, GFP_ATOMIC);
spin_unlock_bh(&vxlan->hash_lock);
if (!err)
@@ -1507,7 +1509,8 @@ static enum skb_drop_reason vxlan_snoop(struct net_device *dev,
cfg->dst_port,
vni,
cfg->vni,
- ifindex, NTF_SELF, 0, true, NULL);
+ ifindex, NTF_SELF, 0, true, NULL,
+ GFP_ATOMIC);
spin_unlock(&vxlan->hash_lock);
}
@@ -4154,7 +4157,7 @@ static int vxlan_dev_create(struct net *net, struct net_device *dev,
cfg->vni,
cfg->vni,
cfg->remote_ifindex,
- NTF_SELF, 0, true, extack);
+ NTF_SELF, 0, true, extack, GFP_ATOMIC);
spin_unlock_bh(&vxlan->hash_lock);
if (err)
goto unlink;
@@ -4617,7 +4620,8 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
cfg->dst_port,
conf.vni, conf.vni,
conf.remote_ifindex,
- NTF_SELF, 0, true, extack);
+ NTF_SELF, 0, true, extack,
+ GFP_ATOMIC);
if (err) {
spin_unlock_bh(&vxlan->hash_lock);
if (lowerdev_changed)
@@ -4652,7 +4656,8 @@ static int vxlan_changelink(struct net_device *dev, struct nlattr *tb[],
&cfg->remote_ip,
conf.remote_ifindex,
cfg->remote_ifindex,
- NULL);
+ NULL,
+ GFP_ATOMIC | __GFP_ACCOUNT);
if (lowerdev_changed)
netdev_adjacent_change_abort(vxlan->lowerdev,
lowerdev, dev);
@@ -4972,7 +4977,7 @@ vxlan_fdb_external_learn_add(struct net_device *dev,
fdb_info->remote_vni,
fdb_info->remote_ifindex,
NTF_USE | NTF_SELF | NTF_EXT_LEARNED,
- 0, false, extack);
+ 0, false, extack, GFP_ATOMIC);
spin_unlock_bh(&vxlan->hash_lock);
return err;
diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h
index f0ee85f83732..b5a80ed3eff2 100644
--- a/drivers/net/vxlan/vxlan_private.h
+++ b/drivers/net/vxlan/vxlan_private.h
@@ -192,7 +192,7 @@ int vxlan_fdb_create(struct vxlan_dev *vxlan,
__u16 state, __be16 port, __be32 src_vni,
__be32 vni, __u32 ifindex, __u16 ndm_flags,
u32 nhid, struct vxlan_fdb **fdb,
- struct netlink_ext_ack *extack);
+ struct netlink_ext_ack *extack, gfp_t gfp);
int __vxlan_fdb_delete(struct vxlan_dev *vxlan,
const unsigned char *addr, union vxlan_addr ip,
__be16 port, __be32 src_vni, __be32 vni,
@@ -202,7 +202,8 @@ int vxlan_fdb_update(struct vxlan_dev *vxlan,
__u16 state, __u16 flags,
__be16 port, __be32 src_vni, __be32 vni,
__u32 ifindex, __u16 ndm_flags, u32 nhid,
- bool swdev_notify, struct netlink_ext_ack *extack);
+ bool swdev_notify, struct netlink_ext_ack *extack,
+ gfp_t gfp);
void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
const struct vxlan_config *cfg,
__be32 default_vni, struct vxlan_rdst *rdst, bool did_rsc);
@@ -229,7 +230,7 @@ int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
const union vxlan_addr *old_remote_ip,
const union vxlan_addr *remote_ip,
u32 old_ifindex, u32 new_ifindex,
- struct netlink_ext_ack *extack);
+ struct netlink_ext_ack *extack, gfp_t gfp);
int vxlan_vnilist_update_group(struct vxlan_dev *vxlan,
const union vxlan_addr *old_remote_ip,
const union vxlan_addr *new_remote_ip,
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index 4a631fcdf23a..4b43674cceff 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -553,7 +553,7 @@ int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
const union vxlan_addr *old_remote_ip,
const union vxlan_addr *remote_ip,
u32 old_ifindex, u32 new_ifindex,
- struct netlink_ext_ack *extack)
+ struct netlink_ext_ack *extack, gfp_t gfp)
{
const struct vxlan_config *cfg = rtnl_dereference(vxlan->cfg);
int err = 0;
@@ -575,7 +575,8 @@ int vxlan_update_default_fdb_entry(struct vxlan_dev *vxlan, __be32 vni,
vni,
vni,
new_ifindex,
- NTF_SELF, 0, true, extack);
+ NTF_SELF, 0, true, extack,
+ gfp);
if (err && extack) {
spin_unlock_bh(&vxlan->hash_lock);
return err;
@@ -636,7 +637,8 @@ static int vxlan_vni_update_group(struct vxlan_dev *vxlan,
oldrip, newrip,
cfg->remote_ifindex,
cfg->remote_ifindex,
- extack);
+ extack,
+ GFP_NOWAIT | __GFP_ACCOUNT);
if (ret)
goto out;
@@ -707,7 +709,8 @@ int vxlan_vnilist_update_group(struct vxlan_dev *vxlan,
ret = vxlan_update_default_fdb_entry(vxlan, vent->vni,
oldrip, newrip,
old_ifindex, new_ifindex,
- extack);
+ extack,
+ GFP_ATOMIC | __GFP_ACCOUNT);
if (ret)
goto err_unwind;
}
@@ -729,7 +732,8 @@ int vxlan_vnilist_update_group(struct vxlan_dev *vxlan,
vxlan_update_default_fdb_entry(vxlan, vent->vni,
newrip, oldrip,
new_ifindex, old_ifindex,
- NULL);
+ NULL,
+ GFP_ATOMIC | __GFP_ACCOUNT);
}
return ret;
}
--
2.53.0
next prev parent reply other threads:[~2026-10-14 12:40 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 12:38 [PATCH net-next v4 0/7] vxlan: vnifilter: bound a single request and account per-VNI memory Ali Firas
2026-10-14 12:38 ` [PATCH net-next v4 1/7] vxlan: vnifilter: validate the VXLAN_VNIFILTER_ENTRY nest Ali Firas
2026-10-14 12:38 ` [PATCH net-next v4 2/7] vxlan: vnifilter: reject VNIs outside the 24-bit space Ali Firas
2026-10-14 12:38 ` [PATCH net-next v4 3/7] vxlan: vnifilter: clamp the dumped VNI range to the request limit Ali Firas
2026-10-14 12:38 ` [PATCH net-next v4 4/7] vxlan: vnifilter: bound the number of VNIs one request may touch Ali Firas
2026-10-14 12:38 ` [PATCH net-next v4 5/7] vxlan: vnifilter: account per-VNI memory to memcg Ali Firas
2026-10-14 12:38 ` Ali Firas [this message]
2026-10-14 12:38 ` [PATCH net-next v4 7/7] selftests: net: test the vxlan vnifilter request limit and dump replay Ali Firas
2026-10-14 12:45 ` [PATCH net-next v4 0/7] vxlan: vnifilter: bound a single request and account per-VNI memory netdev-bot+sinfo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261014123814.388557-7-alishmery18@gmail.com \
--to=alishmery18@gmail.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=razor@blackwall.org \
--cc=roopa@nvidia.com \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox