Netdev List
 help / color / mirror / Atom feed
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);
>   }
>   


  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