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 11/12] net: bridge: fdb: cache VLAN destinations in configured entries
Date: Thu, 1 Oct 2026 10:36:00 +0300	[thread overview]
Message-ID: <9a9701d4-c304-4565-9544-a8c4df96e4a8@blackwall.org> (raw)
In-Reply-To: <20260930071411.2786201-12-razor@blackwall.org>

On 30/09/2026 10:14, Nikolay Aleksandrov wrote:
> Use the known port-VLAN destination for user-configured fdb entries and
> resolve switchdev vids with an fdb helper. VLAN 0, bridge entries and
> unconfigured switchdev vids retain raw port destinations. Keep the vid
> alongside the destination because it remains part of the fdb key.
> Use conditional replacement for same port destination upgrades so they
> don't overwrite a concurrent packet-learned roam. Port changing updates
> remain direct and report a forwarding change.
> 
> Reviewed-by: Ido Schimmel <idosch@nvidia.com>
> Signed-off-by: Nikolay Aleksandrov <razor@blackwall.org>
> ---
>   net/bridge/br_fdb.c | 61 +++++++++++++++++++++++++++++++++++++--------
>   1 file changed, 50 insertions(+), 11 deletions(-)
> 
> diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
> index 5664f3d649fa..730b178cf6c4 100644
> --- a/net/bridge/br_fdb.c
> +++ b/net/bridge/br_fdb.c
> @@ -1192,10 +1192,11 @@ static bool fdb_handle_notify(struct net_bridge_fdb_entry *fdb, u8 notify)
>   }
>   
>   /* Update (create or replace) forwarding database entry */
> -static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
> +static int fdb_add_entry(struct net_bridge *br, struct net_bridge_dst dst,
>   			 const u8 *addr, struct ndmsg *ndm, u16 flags, u16 vid,
>   			 struct nlattr *nfea_tb[])
>   {
> +	struct net_bridge_port *source = br_dst_port(dst);
>   	bool is_sticky = !!(ndm->ndm_flags & NTF_STICKY);
>   	bool refresh = !nfea_tb[NFEA_DONT_REFRESH];
>   	struct net_bridge_fdb_entry *fdb;
> @@ -1230,19 +1231,26 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
>   		if (!(flags & NLM_F_CREATE))
>   			return -ENOENT;
>   
> -		fdb = fdb_create(br, br_port_to_dst(source), addr, vid,
> +		fdb = fdb_create(br, dst, addr, vid,
>   				 BIT(BR_FDB_ADDED_BY_USER));
>   		if (!fdb)
>   			return -ENOMEM;
>   
>   		modified = true;
>   	} else {
> +		struct net_bridge_dst old_dst;
> +
>   		if (flags & NLM_F_EXCL)
>   			return -EEXIST;
>   
> -		if (br_fdb_dst_port(fdb) != source) {
> -			br_fdb_dst_write(fdb, br_port_to_dst(source));
> -			modified = true;
> +		old_dst = br_fdb_dst_read(fdb);
> +		if (!br_dst_equal(old_dst, dst)) {
> +			if (br_dst_port(old_dst) != source) {
> +				modified = true;
> +				br_fdb_dst_write(fdb, dst);
> +			} else {
> +				br_fdb_dst_replace(fdb, old_dst, dst);
> +			}
>   		}
>   
>   		set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);

Sashiko says:
  If a concurrent packet learning roam occurs via __fdb_update() updating the
  destination locklessly, could br_fdb_dst_replace() fail here?
  If it fails, is it safe to ignore the failure and proceed to set
  BR_FDB_ADDED_BY_USER and potentially other authoritative flags like
  BR_FDB_STATIC below? It appears this might apply the flags to the concurrently
  roamed destination rather than the requested one, potentially corrupting
  hardware offload state and locking traffic to the wrong port.

  - Yes, it is safe and intentional behaviour, it must fail if a roam to a different port
    happened. The flags races are well-known and pre-existing behaviour.

> @@ -1296,6 +1304,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
>   			bool *notified, struct netlink_ext_ack *extack)
>   {
>   	u16 vid = vlan ? vlan->vid : 0;
> +	struct net_bridge_dst dst;
>   	int err = 0;
>   
>   	if (ndm->ndm_flags & NTF_USE) {
> @@ -1320,8 +1329,10 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
>   		}
>   		err = br_fdb_external_learn_add(br, p, addr, vid, false, true);
>   	} else {
> +		dst = p && vlan ? br_vlan_to_dst(vlan) : br_port_to_dst(p);
>   		spin_lock_bh(&br->hash_lock);
> -		err = fdb_add_entry(br, p, addr, ndm, nlh_flags, vid, nfea_tb);
> +		err = fdb_add_entry(br, dst, addr, ndm, nlh_flags, vid,
> +				    nfea_tb);
>   		spin_unlock_bh(&br->hash_lock);
>   	}
>   
> @@ -1553,10 +1564,31 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
>   	rcu_read_unlock();
>   }
>   
> +static struct net_bridge_dst br_fdb_resolve_dst(const struct net_bridge_port *p,
> +						u16 vid)
> +{
> +	struct net_bridge_dst dst = br_port_to_dst(p);
> +	struct net_bridge_vlan *v;
> +
> +	lockdep_assert(rcu_read_lock_held() || lockdep_rtnl_is_held());
> +
> +	if (!p || !vid)
> +		return dst;
> +
> +	rcu_read_lock();
> +	v = br_vlan_find(nbp_vlan_group_rcu(p), vid);
> +	if (v)
> +		dst = br_vlan_to_dst(v);
> +	rcu_read_unlock();
> +
> +	return dst;
> +}
> +
>   int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>   			      const unsigned char *addr, u16 vid, bool locked,
>   			      bool swdev_notify)
>   {
> +	struct net_bridge_dst dst = br_fdb_resolve_dst(p, vid);
>   	struct net_bridge_fdb_entry *fdb;
>   	bool modified = false;
>   	int err = 0;
> @@ -1581,25 +1613,32 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
>   		if (locked)
>   			flags |= BIT(BR_FDB_LOCKED);
>   
> -		fdb = fdb_create(br, br_port_to_dst(p), addr, vid, flags);
> +		fdb = fdb_create(br, dst, addr, vid, flags);
>   		if (!fdb) {
>   			err = -ENOMEM;
>   			goto err_unlock;
>   		}
>   		fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
>   	} else {
> +		struct net_bridge_dst old_dst;
> +
> +		old_dst = br_fdb_dst_read(fdb);
>   		if (locked &&
>   		    (!test_bit(BR_FDB_LOCKED, &fdb->flags) ||
> -		     br_fdb_dst_port(fdb) != p)) {
> +		     br_dst_port(old_dst) != p)) {
>   			err = -EINVAL;
>   			goto err_unlock;
>   		}
>   
>   		WRITE_ONCE(fdb->updated, jiffies);
>   
> -		if (br_fdb_dst_port(fdb) != p) {
> -			br_fdb_dst_write(fdb, br_port_to_dst(p));
> -			modified = true;
> +		if (!br_dst_equal(old_dst, dst)) {
> +			if (br_dst_port(old_dst) != p) {
> +				modified = true;
> +				br_fdb_dst_write(fdb, dst);
> +			} else {
> +				br_fdb_dst_replace(fdb, old_dst, dst);
> +			}
>   		}
>   
>   		if (test_and_set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags)) {

Sashiko says:
  Similarly, if br_fdb_dst_replace() fails here due to a concurrent destination
  update in __fdb_update(), could hardware learning flags like
  BR_FDB_ADDED_BY_EXT_LEARN be misapplied to the new roamed port?

  - Yes, again pre-existing and well-known behaviour. See my reply above.



  reply	other threads:[~2026-10-01  7:36 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
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 [this message]
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=9a9701d4-c304-4565-9544-a8c4df96e4a8@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