public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
From: Vladimir Oltean <olteanv@gmail.com>
To: Daniel Golle <daniel@makrotopia.org>
Cc: Andrew Lunn <andrew@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>,
	Russell King <linux@armlinux.org.uk>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Frank Wunderlich <frankwu@gmx.de>, Chad Monroe <chad@monroe.io>,
	Cezary Wilmanski <cezary.wilmanski@adtran.com>,
	Liang Xu <lxu@maxlinear.com>,
	"Benny (Ying-Tsan) Weng" <yweng@maxlinear.com>,
	Jose Maria Verdu Munoz <jverdu@maxlinear.com>,
	Avinash Jayaraman <ajayaraman@maxlinear.com>,
	John Crispin <john@phrozen.org>
Subject: Re: [PATCH net-next 2/2] net: dsa: mxl862xx: implement bridge offloading
Date: Tue, 10 Mar 2026 01:27:00 +0200	[thread overview]
Message-ID: <20260309232700.vaawcbfj6gbjncs4@skbuf> (raw)
In-Reply-To: <8aebab4c4bffa7e756ffbad4e9c9c8160773dd1a.1772853341.git.daniel@makrotopia.org> <8aebab4c4bffa7e756ffbad4e9c9c8160773dd1a.1772853341.git.daniel@makrotopia.org>

On Sat, Mar 07, 2026 at 03:31:17AM +0000, Daniel Golle wrote:
> +static int mxl862xx_update_bridge(struct dsa_switch *ds,
> +				  struct mxl862xx_bridge *mxlbridge,
> +				  int port, bool join)

There's a lot of false sharing here. If you move the "if (join)" and
"if (!join)" blocks to their respective callers, you end up with a much
smaller truly common function.

> +{
> +	struct mxl862xx_priv *priv = ds->priv;
> +	struct dsa_port *dp;
> +	int member, ret;
> +
> +	if (join) {
> +		__set_bit(port, mxlbridge->portmap);
> +		priv->ports[port].bridge = mxlbridge;
> +	} else {
> +		__clear_bit(port, mxlbridge->portmap);
> +		priv->ports[port].bridge = NULL;
> +	}
> +
> +	/* Update all current bridge members' portmaps */
> +	for_each_set_bit(member, mxlbridge->portmap,
> +			 MXL862XX_MAX_BRIDGE_PORTS) {
> +		dp = dsa_to_port(ds, member);
> +
> +		/* Build portmap: CPU port + all bridge members except self */
> +		bitmap_copy(priv->ports[member].portmap, mxlbridge->portmap,
> +			    MXL862XX_MAX_BRIDGE_PORTS);
> +		__clear_bit(member, priv->ports[member].portmap);
> +		__set_bit(dp->cpu_dp->index, priv->ports[member].portmap);
> +
> +		priv->ports[member].learning = true;

Actually, dsa_port_inherit_brport_flags() / dsa_port_clear_brport_flags() ensures
the BR_LEARNING flag passed through mxl862xx_port_bridge_flags() will keep the
hardware in sync with the bridge. You don't need to set this to true or to false
at runtime. Just at initial probe time, before DSA has made any call.

> +		ret = mxl862xx_set_bridge_port(ds, member);
> +		if (ret)
> +			return ret;

What about going through all ports anyway, and return error at the end
if any bridge member portmap update failed? I know this patch set is
written assuming FW I/O failure is catastrophic, but here you may be
failing for a different port than the one which is leaving the bridge.
You can at least make an attempt to disable forwarding to/from that port.

> +	}
> +
> +	/* Revert leaving port to its single-port bridge */
> +	if (!join) {
> +		dp = dsa_to_port(ds, port);
> +
> +		bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
> +		__set_bit(dp->cpu_dp->index, priv->ports[port].portmap);
> +		priv->ports[port].flood_block = 0;
> +		priv->ports[port].learning = false;
> +		ret = mxl862xx_set_bridge_port(ds, port);
> +		if (ret)
> +			return ret;
> +
> +		mxl862xx_port_fast_age(ds, port);
> +	}
> +
> +	return 0;
> +}
> +
> +static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
> +				     struct dsa_bridge bridge,
> +				     bool *tx_fwd_offload,
> +				     struct netlink_ext_ack *extack)
> +{
> +	struct mxl862xx_bridge *mxlbridge;
>  
> -	return MXL862XX_API_WRITE(ds->priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg);
> +	mxlbridge = mxl862xx_find_bridge(ds, bridge);
> +	if (!mxlbridge) {
> +		mxlbridge = mxl862xx_allocate_bridge(ds, bridge.num);
> +		if (IS_ERR(mxlbridge))
> +			return PTR_ERR(mxlbridge);
> +	}
> +
> +	return mxl862xx_update_bridge(ds, mxlbridge, port, true);
> +}
> +
> +static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
> +				       struct dsa_bridge bridge)
> +{
> +	struct mxl862xx_bridge *mxlbridge;
> +
> +	mxlbridge = mxl862xx_find_bridge(ds, bridge);
> +	if (!mxlbridge)
> +		return;
> +
> +	mxl862xx_update_bridge(ds, mxlbridge, port, false);

Don't let this fail silently.

> +
> +	if (bitmap_empty(mxlbridge->portmap, MXL862XX_MAX_BRIDGE_PORTS))
> +		mxl862xx_free_bridge(ds, mxlbridge);
>  }
>  
>  static int mxl862xx_port_setup(struct dsa_switch *ds, int port)
> @@ -366,6 +614,262 @@ static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
>  		  config->supported_interfaces);
>  }
>  
> +static int mxl862xx_port_fdb_add(struct dsa_switch *ds, int port,
> +				 const unsigned char *addr, u16 vid, struct dsa_db db)
> +{
> +	struct mxl862xx_mac_table_add param = { };
> +	struct mxl862xx_priv *priv = ds->priv;
> +	struct mxl862xx_bridge *mxlbridge;
> +	u16 fid;
> +	int ret;
> +
> +	switch (db.type) {
> +	case DSA_DB_PORT:
> +		fid = priv->ports[db.dp->index].fid;
> +		break;
> +
> +	case DSA_DB_BRIDGE:
> +		mxlbridge = mxl862xx_find_bridge(ds, db.bridge);
> +		if (!mxlbridge)
> +			return -ENOENT;
> +		fid = mxlbridge->bridge_id;
> +		break;
> +
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	param.port_id = cpu_to_le32(port);
> +	param.fid = cpu_to_le16(fid);
> +	param.static_entry = true;
> +	param.tci = cpu_to_le16(vid & 0xFFF);
> +	memcpy(param.mac, addr, ETH_ALEN);

ether_addr_copy() - similar comment for port_fdb_del()

> +
> +	ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYADD, param);
> +	if (ret)
> +		dev_err(ds->dev, "failed to add FDB entry on port %d\n", port);
> +
> +	return ret;
> +}
> +
> +static int mxl862xx_port_fdb_del(struct dsa_switch *ds, int port,
> +				 const unsigned char *addr, u16 vid, struct dsa_db db)
> +{
> +	struct mxl862xx_mac_table_remove param = { };
> +	struct mxl862xx_priv *priv = ds->priv;
> +	struct mxl862xx_bridge *mxlbridge;
> +	u16 fid;
> +	int ret;
> +
> +	switch (db.type) {
> +	case DSA_DB_PORT:
> +		fid = priv->ports[db.dp->index].fid;
> +		break;
> +
> +	case DSA_DB_BRIDGE:
> +		/* Use multi-port bridge FID */
> +		mxlbridge = mxl862xx_find_bridge(ds, db.bridge);
> +		if (!mxlbridge)
> +			return -ENOENT;
> +		fid = mxlbridge->bridge_id;
> +		break;
> +
> +	default:
> +		return -EOPNOTSUPP;
> +	}

Please group this together with the FID selection from port_fdb_add()
into a single helper.

> +
> +	param.fid = cpu_to_le16(fid);
> +	param.tci = cpu_to_le16(vid & 0xFFF);
> +	memcpy(param.mac, addr, ETH_ALEN);
> +
> +	ret = MXL862XX_API_WRITE(priv, MXL862XX_MAC_TABLEENTRYREMOVE, param);
> +	if (ret)
> +		dev_err(ds->dev, "failed to remove FDB entry on port %d\n", port);
> +
> +	return ret;
> +}
> +
> +static int mxl862xx_port_fdb_dump(struct dsa_switch *ds, int port,
> +				  dsa_fdb_dump_cb_t *cb, void *data)
> +{
> +	struct mxl862xx_mac_table_read param = { };
> +	struct mxl862xx_priv *priv = ds->priv;
> +	u32 entry_port_id;
> +	int ret;
> +
> +	while (true) {
> +		ret = MXL862XX_API_READ(priv, MXL862XX_MAC_TABLEENTRYREAD, param);
> +		if (ret)
> +			return ret;
> +
> +		if (param.last)
> +			break;
> +
> +		entry_port_id = le32_to_cpu(param.port_id);
> +
> +		if (entry_port_id == port)
> +			cb(param.mac, param.tci & 0x0FFF,
> +			   param.static_entry, data);

Catch and propagate the error from the FDB dump callback. See commit
21b52fed928e ("net: dsa: sja1105: fix broken backpressure in
.port_fdb_dump").

> +
> +		memset(&param, 0, sizeof(param));
> +	}
> +
> +	return 0;
> +}
> +
> +static int mxl862xx_port_mdb_add(struct dsa_switch *ds, int port,
> +				 const struct switchdev_obj_port_mdb *mdb,
> +				 struct dsa_db db)
> +{
> +	return mxl862xx_port_fdb_add(ds, port, mdb->addr, mdb->vid, db);
> +}
> +
> +static int mxl862xx_port_mdb_del(struct dsa_switch *ds, int port,
> +				 const struct switchdev_obj_port_mdb *mdb,
> +				 struct dsa_db db)
> +{
> +	return mxl862xx_port_fdb_del(ds, port, mdb->addr, mdb->vid, db);
> +}
> +
> +static int mxl862xx_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
> +{
> +	struct mxl862xx_cfg param;
> +	int ret;
> +
> +	ret = MXL862XX_API_READ(ds->priv, MXL862XX_COMMON_CFGGET, param);
> +	if (ret) {
> +		dev_err(ds->dev, "failed to read switch config\n");
> +		return ret;
> +	}
> +
> +	param.mac_table_age_timer = cpu_to_le32(MXL862XX_AGETIMER_CUSTOM);
> +	param.age_timer = cpu_to_le32(msecs / 1000);
> +	ret = MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_CFGSET, param);
> +	if (ret)
> +		dev_err(ds->dev, "failed to set ageing\n");
> +
> +	return ret;
> +}
> +
> +static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port,
> +					u8 state)
> +{
> +	struct mxl862xx_stp_port_cfg param = {
> +		.port_id = cpu_to_le16(port),
> +	};
> +	struct mxl862xx_priv *priv = ds->priv;
> +	int ret;
> +
> +	switch (state) {
> +	case BR_STATE_DISABLED:
> +		param.port_state = MXL862XX_STP_PORT_STATE_DISABLE;
> +		break;
> +	case BR_STATE_BLOCKING:
> +	case BR_STATE_LISTENING:
> +		param.port_state = MXL862XX_STP_PORT_STATE_BLOCKING;
> +		break;
> +	case BR_STATE_LEARNING:
> +		param.port_state = MXL862XX_STP_PORT_STATE_LEARNING;
> +		break;
> +	case BR_STATE_FORWARDING:
> +		param.port_state = MXL862XX_STP_PORT_STATE_FORWARD;
> +		break;
> +	default:
> +		dev_err(ds->dev, "invalid STP state: %d\n", state);
> +		return;
> +	}
> +
> +	ret = MXL862XX_API_WRITE(priv, MXL862XX_STP_PORTCFGSET, param);
> +	if (ret) {
> +		dev_err(ds->dev, "failed to set STP state on port %d\n", port);
> +		return;
> +	}
> +
> +	/* The firmware may re-enable MAC learning as a side-effect of
> +	 * entering LEARNING or FORWARDING state (per 802.1D defaults).
> +	 * Re-apply the driver's intended learning and metering config
> +	 * so that standalone ports keep learning disabled.
> +	 */
> +	ret = mxl862xx_set_bridge_port(ds, port);
> +	if (ret)
> +		dev_err(ds->dev, "failed to reapply brport flags on port %d\n", port);

Do you need to also manually fast-age the port if p->learning == false? Packets
may be coming in as soon as you put the port in MXL862XX_STP_PORT_STATE_LEARNING
already, and they will end up in the FDB even if from Linux' perspective,
it had learning off all along. It is a race condition.

> +}

  parent reply	other threads:[~2026-03-09 23:27 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-07  3:29 [PATCH net-next 0/2] net: dsa: mxl862xx: add support for bridge offloading Daniel Golle
2026-03-07  3:30 ` [PATCH net-next 1/2] dsa: tag_mxl862xx: set dsa_default_offload_fwd_mark() Daniel Golle
2026-03-07  9:57   ` Jonas Gorski
2026-03-08 15:18     ` Daniel Golle
2026-03-08 20:15       ` Jonas Gorski
2026-03-09  0:02         ` Daniel Golle
2026-03-09  8:07           ` Jonas Gorski
2026-03-09 12:23             ` Daniel Golle
2026-03-09 23:33             ` Vladimir Oltean
2026-03-07  3:31 ` [PATCH net-next 2/2] net: dsa: mxl862xx: implement bridge offloading Daniel Golle
2026-03-09 23:22   ` Jakub Kicinski
2026-03-09 23:27   ` Vladimir Oltean [this message]
2026-03-07  3:39 ` [PATCH net-next 0/2] net: dsa: mxl862xx: add support for " Daniel Golle

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=20260309232700.vaawcbfj6gbjncs4@skbuf \
    --to=olteanv@gmail.com \
    --cc=ajayaraman@maxlinear.com \
    --cc=andrew@lunn.ch \
    --cc=cezary.wilmanski@adtran.com \
    --cc=chad@monroe.io \
    --cc=daniel@makrotopia.org \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=frankwu@gmx.de \
    --cc=horms@kernel.org \
    --cc=john@phrozen.org \
    --cc=jverdu@maxlinear.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=lxu@maxlinear.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=yweng@maxlinear.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