From: Nikolay Aleksandrov <razor@blackwall.org>
To: netdev@vger.kernel.org
Cc: idosch@nvidia.com, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, horms@kernel.org,
bridge@lists.linux.dev
Subject: Re: [PATCH net-next 08/12] net: bridge: vlan: quiesce readers before freeing port VLANs
Date: Thu, 1 Oct 2026 10:28:48 +0300 [thread overview]
Message-ID: <f1e507b7-94f5-4357-a437-aad7f8538146@blackwall.org> (raw)
In-Reply-To: <20260930071411.2786201-9-razor@blackwall.org>
On 30/09/2026 10:14, Nikolay Aleksandrov wrote:
> Later fdb entries will cache port-VLAN pointers so unpublish a VLAN, wait
> for a grace period (existing readers) and then purge or rewrite fdb
> references before releasing it. Cached destinations can continue forwarding
> until they are cleaned, that is acceptable so add a comment to document it.
> During port teardown unpublish the complete VLAN group first, clean the
> port fdbs and then release the VLANs. This lets all VLANs share one grace
> period.
>
> Reviewed-by: Ido Schimmel <idosch@nvidia.com>
> Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org>
> ---
> net/bridge/br_fdb.c | 20 ++++++++++++++++----
> net/bridge/br_if.c | 7 +++++--
> net/bridge/br_private.h | 12 ++++++++++--
> net/bridge/br_vlan.c | 21 +++++++++++++++------
> 4 files changed, 46 insertions(+), 14 deletions(-)
>
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index 7c68b540b358..307f9c12914e 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -883,7 +883,9 @@ void br_fdb_cleanup_by_dst(struct net_bridge *br,
>
> spin_lock_bh(&br->hash_lock);
> hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
> - if (br_fdb_dst_port(f) != p)
> + struct net_bridge_dst dst = br_fdb_dst_read(f);
> +
> + if (br_dst_port(dst) != p)
> continue;
>
> if (vlan && f->key.vlan_id == vlan->vid &&
> @@ -894,12 +896,22 @@ void br_fdb_cleanup_by_dst(struct net_bridge *br,
> continue;
> }
>
> - if (!do_all)
> + if (!do_all) {
> + if (vid && f->key.vlan_id != vid)
> + continue;
> +
> if (test_bit(BR_FDB_STATIC, &f->flags) ||
> (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags) &&
> - !test_bit(BR_FDB_OFFLOADED, &f->flags)) ||
> - (vid && f->key.vlan_id != vid))
> + !test_bit(BR_FDB_OFFLOADED, &f->flags))) {
> + /* The entry outlives the VLAN, so it must fall
> + * back to the raw port destination
> + */
> + if (vlan && br_dst_vlan(dst) == vlan)
> + br_fdb_dst_write(f,
> + br_port_to_dst(p));
> continue;
> + }
> + }
>
> if (test_bit(BR_FDB_LOCAL, &f->flags))
> fdb_delete_local(br, p, f);
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index d94558a5e3e9..2c05ebc1299d 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -333,8 +333,9 @@ static void update_headroom(struct net_bridge *br, int new_hr)
> */
> static void del_nbp(struct net_bridge_port *p)
> {
> - struct net_bridge *br = p->br;
> + struct net_bridge_vlan_group *vg;
> struct net_device *dev = p->dev;
> + struct net_bridge *br = p->br;
>
> sysfs_remove_link(br->ifobj, p->dev->name);
>
> @@ -354,8 +355,10 @@ static void del_nbp(struct net_bridge_port *p)
> update_headroom(br, get_max_headroom(br));
> netdev_reset_rx_headroom(dev);
>
> - nbp_vlan_flush(p);
> + vg = nbp_vlan_group(p);
> + nbp_vlan_group_unpublish(p);
> br_fdb_cleanup_by_dst(br, br_port_to_dst(p), 0, 1);
> + nbp_vlan_flush(p, vg);
> switchdev_deferred_process();
> nbp_backup_clear(p);
>
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index a790368b69e9..951b6ac5f484 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -1735,7 +1735,9 @@ int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
> int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
> bool *changed, struct netlink_ext_ack *extack);
> int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
> -void nbp_vlan_flush(struct net_bridge_port *port);
> +void nbp_vlan_group_unpublish(struct net_bridge_port *port);
> +void nbp_vlan_flush(struct net_bridge_port *port,
> + struct net_bridge_vlan_group *vg);
> int nbp_vlan_init(struct net_bridge_port *port, struct netlink_ext_ack *extack);
> int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask);
> void br_vlan_get_stats(const struct net_bridge_vlan *v,
> @@ -1894,7 +1896,13 @@ static inline int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
> return -EOPNOTSUPP;
> }
>
> -static inline void nbp_vlan_flush(struct net_bridge_port *port)
> +static inline void nbp_vlan_group_unpublish(struct net_bridge_port *port)
> +{
> +}
> +
> +static inline void
> +nbp_vlan_flush(struct net_bridge_port *port,
> + struct net_bridge_vlan_group *vg)
> {
> }
>
> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> index 8a914e249767..0675f74cf1b9 100644
> --- a/net/bridge/br_vlan.c
> +++ b/net/bridge/br_vlan.c
> @@ -1406,23 +1406,32 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
> v = br_vlan_find(vg, vid);
> if (!v)
> return -ENOENT;
> - br_fdb_cleanup_by_dst(port->br, br_vlan_to_dst(v), vid, 0);
> __vlan_unpublish(vg, v);
> + synchronize_net();
Sashiko says:
Does this synchronize_net() call introduce an RTNL stall and IPI storm when
processing a large VLAN range?
When a user issues an RTM_DELLINK command specifying a large range of VLANs
(e.g., vid 1-4094), br_process_vlan_info() iterates over the range sequentially:
net/bridge/br_netlink.c:br_process_vlan_info() {
...
for (v = (*vinfo_last)->vid; v <= vinfo_curr->vid; v++) {
bool curr_change = false;
tmp_vinfo.vid = v;
err = br_vlan_info(br, p, cmd, &tmp_vinfo, &curr_change,
extack);
...
}
This loop calls into br_vlan_info(), which then calls nbp_vlan_delete() for
each VLAN. Because the global RTNL lock is held during this process,
synchronize_net() translates to synchronize_rcu_expedited().
Calling this iteratively up to 4094 times blocks the RTNL lock for several
seconds and spams all CPUs with IPIs, causing a severe system-wide network
configuration Denial of Service. This is reachable by any user with
CAP_NET_ADMIN in the network namespace, which can often be obtained by
unprivileged local users via user namespaces.
Could these grace periods be batched or deferred when deleting multiple VLANs?
- Yes, they can be batched and I had them batched initially but when I ran my tests I didn't
notice any problems deleting a full VLAN range. It took 79ms on my host to delete 4k VLANs.
As I've stated in my cover letter - the complexity is not worth it, so I dropped that patch.
> + /* Traffic may still use v through cached fdb dsts until they are
> + * cleaned below. This is acceptable during vlan deletion. Above we
> + * drain the readers that could republish the dst before cleaning it
> + */
> + br_fdb_cleanup_by_dst(port->br, br_vlan_to_dst(v), vid, 0);
> __vlan_del(vg, v);
>
> return 0;
> }
>
> -void nbp_vlan_flush(struct net_bridge_port *port)
> +void nbp_vlan_group_unpublish(struct net_bridge_port *port)
> {
> - struct net_bridge_vlan_group *vg;
> -
> ASSERT_RTNL();
>
> - vg = nbp_vlan_group(port);
> - __vlan_flush(port->br, port, vg);
> RCU_INIT_POINTER(port->vlgrp, NULL);
> synchronize_net();
> +}
> +
> +void nbp_vlan_flush(struct net_bridge_port *port,
> + struct net_bridge_vlan_group *vg)
> +{
> + ASSERT_RTNL();
> +
> + __vlan_flush(port->br, port, vg);
> __vlan_group_free(vg);
> }
>
next prev parent reply other threads:[~2026-10-01 7:28 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-30 7:13 [PATCH net-next 00/12] net: bridge: vlan: optimize standard fdb fwding path Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 01/12] net: bridge: introduce a bridge destination type Nikolay Aleksandrov
2026-10-01 7:24 ` Nikolay Aleksandrov
2026-10-02 19:45 ` Jakub Kicinski
2026-10-02 20:09 ` Nikolay Aleksandrov
2026-10-02 20:18 ` Jakub Kicinski
2026-09-30 7:14 ` [PATCH net-next 02/12] net: bridge: use net_bridge_dst for fdb destinations Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 03/12] net: bridge: add VLAN support to bridge destinations Nikolay Aleksandrov
2026-10-01 11:59 ` netdev-bot+sashiko
2026-10-01 12:03 ` Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 04/12] net: bridge: vlan: return VLAN entries from ingress helpers Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 05/12] net: bridge: fdb: pass VLAN entries to learning updates Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 06/12] net: bridge: fdb: consolidate port-VLAN cleanup Nikolay Aleksandrov
2026-10-01 11:59 ` netdev-bot+sashiko
2026-10-01 12:05 ` Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 07/12] net: bridge: vlan: split unpublishing from deletion Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 08/12] net: bridge: vlan: quiesce readers before freeing port VLANs Nikolay Aleksandrov
2026-10-01 7:28 ` Nikolay Aleksandrov [this message]
2026-10-01 11:59 ` netdev-bot+sashiko
2026-10-01 12:07 ` Nikolay Aleksandrov
2026-10-02 19:50 ` Jakub Kicinski
2026-10-02 19:59 ` Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 09/12] net: bridge: fdb: factor out existing entry updates Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 10/12] net: bridge: fdb: cache port VLANs in learned entries Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 11/12] net: bridge: fdb: cache VLAN destinations in configured entries Nikolay Aleksandrov
2026-10-01 7:36 ` Nikolay Aleksandrov
2026-09-30 7:14 ` [PATCH net-next 12/12] net: bridge: fdb: avoid VLAN lookups in unicast forwarding Nikolay Aleksandrov
2026-10-02 20:00 ` [PATCH net-next 00/12] net: bridge: vlan: optimize standard fdb fwding path patchwork-bot+netdevbpf
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=f1e507b7-94f5-4357-a437-aad7f8538146@blackwall.org \
--to=razor@blackwall.org \
--cc=bridge@lists.linux.dev \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/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