mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes
@ 2026-10-30 21:16 Florian Fainelli
  2026-10-30 21:16 ` [PATCH net v3 1/8] net: dsa: b53: initialize the jumbo frame size register offset Florian Fainelli
                   ` (8 more replies)
  0 siblings, 9 replies; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:16 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Vivien Didelot, open list

This patch series contains fixes to issues reported by Sashiko on the
b53 PCS bug fixes being submitted, and then some more that came from
code review and testing.

Except for issue #2, all of the other issues did trigger in testing and
needed to be corrected.

Changes in v3:

- patch 2: added Daniel's R-b tag
- patch 3: mention in the commit message that deleting an absent entry
  now returns -ENOENT, including when the lookup runs out of space
- patch 4: document that the patch also fixes the clear of the capture
  port field in b53_mirror_del()
- patch 6: reference count the mirror source bits per port and
  direction, ignoring deletions without a matching reference
- patch 7: new, harden mirror register programming:
  - reset the mirror capture and source control registers at setup for
    b53 and bcm_sf2, since not all switches are reset
  - check and propagate all mirror register read/write errors, only
    taking a source reference once hardware programming succeeded
  - use the source masks rather than MIRROR_EN to decide whether the
    destination is occupied, and write the destination before the source
- log failures in b53_mirror_del() since it cannot return an error
- patch 8: new, fix the mirror capture port encoding on BCM5325 and
  BCM5365, which use an 11-bit bitmap (CPU port at bit 8) instead of a
  port ID, and translate the CPU port in the ingress/egress source
  masks as well (verified against the BCM5325E, BCM5325M and BCM5365P
  datasheets)

Changes in v2:

- collected Jonas' R-b tags
- corrected logic in patch #3 to propagate -ENOENT, memset after the
  debug print, translate -ENOSPC to -ENOENT when deleting

Florian Fainelli (8):
  net: dsa: b53: initialize the jumbo frame size register offset
  net: dsa: b53: fix the SRAB grant timeout check
  net: dsa: b53: clear stale state when creating an ARL entry
  net: dsa: b53: reject conflicting mirror destinations
  net: dsa: b53: use the correct override register 2 Gbps bit
  net: dsa: b53: reference count shared mirror source bits
  net: dsa: b53: harden mirror register programming
  net: dsa: b53: fix mirror port encoding on BCM5325 and BCM5365

 drivers/net/dsa/b53/b53_common.c | 183 ++++++++++++++++++++++++-------
 drivers/net/dsa/b53/b53_priv.h   |   3 +
 drivers/net/dsa/b53/b53_regs.h   |   1 +
 drivers/net/dsa/b53/b53_srab.c   |   2 +-
 drivers/net/dsa/bcm_sf2.c        |   5 +
 5 files changed, 152 insertions(+), 42 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net v3 1/8] net: dsa: b53: initialize the jumbo frame size register offset
  2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
@ 2026-10-30 21:16 ` Florian Fainelli
  2026-10-30 21:16 ` [PATCH net v3 2/8] net: dsa: b53: fix the SRAB grant timeout check Florian Fainelli
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:16 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Vivien Didelot, open list

The chip data specifies both the jumbo port mask and frame size register
offsets, but b53_switch_init() only copies the former into the device.
Since the device is zero-initialized, b53_set_jumbo() writes the frame
size to offset zero instead of the chip-specific size register at 0x05
or 0x08.

Copy jumbo_size_reg from the chip data along with jumbo_pm_reg so frame
size updates reach the intended register.

Fixes: 967dd82ffc52 ("net: dsa: b53: Add support for Broadcom RoboSwitch")
Reviewed-by: Jonas Gorski <jonas.gorski@gmail.com>
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
 drivers/net/dsa/b53/b53_common.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 0880310c9ce3..559d4b360c82 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -3109,6 +3109,7 @@ static int b53_switch_init(struct b53_device *dev)
 			dev->vta_regs[1] = chip->vta_regs[1];
 			dev->vta_regs[2] = chip->vta_regs[2];
 			dev->jumbo_pm_reg = chip->jumbo_pm_reg;
+			dev->jumbo_size_reg = chip->jumbo_size_reg;
 			dev->imp_port = chip->imp_port;
 			dev->num_vlans = chip->vlans;
 			dev->num_arl_bins = chip->arl_bins;
-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net v3 2/8] net: dsa: b53: fix the SRAB grant timeout check
  2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
  2026-10-30 21:16 ` [PATCH net v3 1/8] net: dsa: b53: initialize the jumbo frame size register offset Florian Fainelli
@ 2026-10-30 21:16 ` Florian Fainelli
  2026-10-30 21:16 ` [PATCH net v3 3/8] net: dsa: b53: clear stale state when creating an ARL entry Florian Fainelli
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:16 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, Jonas Gorski, Daniel Machon, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Vivien Didelot, open list

b53_srab_request_grant() polls for a grant up to 20 times, but checks for
an iteration count of five to detect a timeout. This rejects a grant
received on the sixth poll and reports success when all 20 polls finish
without a grant. The latter allows a register access to proceed without
ownership of the switch resources.

Check for exhaustion of all 20 iterations instead.

Fixes: 967dd82ffc52 ("net: dsa: b53: Add support for Broadcom RoboSwitch")
Reviewed-by: Jonas Gorski <jonas.gorski@gmail.com>
Reviewed-by: Daniel Machon <daniel.machon@microchip.com>
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
 drivers/net/dsa/b53/b53_srab.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/dsa/b53/b53_srab.c b/drivers/net/dsa/b53/b53_srab.c
index 83a03fca142a..f4689078ccd5 100644
--- a/drivers/net/dsa/b53/b53_srab.c
+++ b/drivers/net/dsa/b53/b53_srab.c
@@ -105,7 +105,7 @@ static int b53_srab_request_grant(struct b53_device *dev)
 			break;
 		usleep_range(10, 100);
 	}
-	if (WARN_ON(i == 5))
+	if (WARN_ON(i == 20))
 		return -EIO;
 
 	return 0;
-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net v3 3/8] net: dsa: b53: clear stale state when creating an ARL entry
  2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
  2026-10-30 21:16 ` [PATCH net v3 1/8] net: dsa: b53: initialize the jumbo frame size register offset Florian Fainelli
  2026-10-30 21:16 ` [PATCH net v3 2/8] net: dsa: b53: fix the SRAB grant timeout check Florian Fainelli
@ 2026-10-30 21:16 ` Florian Fainelli
  2026-10-31 21:56   ` netdev-bot+sashiko
  2026-10-30 21:16 ` [PATCH net v3 4/8] net: dsa: b53: reject conflicting mirror destinations Florian Fainelli
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:16 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Vivien Didelot, open list

When an ARL lookup does not find the requested MAC and VID, b53_arl_read()
returns the first free bin index but leaves the entry populated with the
last bin examined. The multicast add path then ORs the requested port into
that unrelated entry's forwarding bitmap, potentially forwarding packets
to ports which are not members of the multicast group.

Clear the entry before creating a new one. Also return without writing
anything when deleting an entry which is not present, instead of making
a new entry from stale bin contents.

Deleting an entry which is not present now returns -ENOENT, including
when the lookup ran out of space (-ENOSPC), where it previously returned
0 in that case.

Fixes: 5d65b64a3d97 ("net: dsa: b53: Add support for MDB")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
 drivers/net/dsa/b53/b53_common.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 559d4b360c82..c38e8c5838bd 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1990,11 +1990,15 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
 	case -ENOSPC:
 		dev_dbg(dev->dev, "{%pM,%.4d} no space left in ARL\n",
 			addr, vid);
-		return is_valid ? ret : 0;
+		return is_valid ? ret : -ENOENT;
 	case -ENOENT:
+		if (!is_valid)
+			return ret;
+
 		/* We could not find a matching MAC, so reset to a new entry */
 		dev_dbg(dev->dev, "{%pM,%.4d} not found, using idx: %d\n",
 			addr, vid, idx);
+		memset(&ent, 0, sizeof(ent));
 		break;
 	default:
 		dev_dbg(dev->dev, "{%pM,%.4d} found, using idx: %d\n",
-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net v3 4/8] net: dsa: b53: reject conflicting mirror destinations
  2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
                   ` (2 preceding siblings ...)
  2026-10-30 21:16 ` [PATCH net v3 3/8] net: dsa: b53: clear stale state when creating an ARL entry Florian Fainelli
@ 2026-10-30 21:16 ` Florian Fainelli
  2026-10-31 21:56   ` netdev-bot+sashiko
  2026-10-30 21:16 ` [PATCH net v3 5/8] net: dsa: b53: use the correct override register 2 Gbps bit Florian Fainelli
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:16 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Vivien Didelot, open list

The mirror capture control register selects one destination for all
ingress and egress mirror sources. Adding a rule for a different capture
port currently overwrites that field and silently redirects all existing
mirror rules.

Read the capture control before changing any source masks and reject a
different destination while mirroring is enabled. Return an extack which
describes the hardware limitation, and propagate a failed capture-control
read rather than making the decision from uninitialized data.

Clear the capture-port field when the final source is removed so a later
rule can select a new destination. This also fixes b53_mirror_del(),
which tried to clear the destination with ~mirror->to_local_port. That
only cleared the bits set in the port number, so a stale capture port
could remain after the last source was removed.

Fixes: ed3af5fd08eb ("net: dsa: b53: Add support for port mirroring")
Reviewed-by: Jonas Gorski <jonas.gorski@gmail.com>
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
 drivers/net/dsa/b53/b53_common.c | 29 +++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index c38e8c5838bd..378f47e7250c 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -2543,7 +2543,19 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 		   struct netlink_ext_ack *extack)
 {
 	struct b53_device *dev = ds->priv;
-	u16 reg, loc;
+	u16 reg, loc, cap_ctl;
+	int ret;
+
+	ret = b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &cap_ctl);
+	if (ret)
+		return ret;
+
+	if ((cap_ctl & MIRROR_EN) &&
+	    (cap_ctl & CAP_PORT_MASK) != mirror->to_local_port) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "Only one mirroring destination is supported");
+		return -EOPNOTSUPP;
+	}
 
 	if (ingress)
 		loc = B53_IG_MIR_CTL;
@@ -2554,11 +2566,10 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 	reg |= BIT(port);
 	b53_write16(dev, B53_MGMT_PAGE, loc, reg);
 
-	b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
-	reg &= ~CAP_PORT_MASK;
-	reg |= mirror->to_local_port;
-	reg |= MIRROR_EN;
-	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
+	cap_ctl &= ~CAP_PORT_MASK;
+	cap_ctl |= mirror->to_local_port;
+	cap_ctl |= MIRROR_EN;
+	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, cap_ctl);
 
 	return 0;
 }
@@ -2595,10 +2606,8 @@ void b53_mirror_del(struct dsa_switch *ds, int port,
 
 	b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
 	/* Both no longer have ports, let's disable mirroring */
-	if (loc_disable && other_loc_disable) {
-		reg &= ~MIRROR_EN;
-		reg &= ~mirror->to_local_port;
-	}
+	if (loc_disable && other_loc_disable)
+		reg &= ~(MIRROR_EN | CAP_PORT_MASK);
 	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
 }
 EXPORT_SYMBOL(b53_mirror_del);
-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net v3 5/8] net: dsa: b53: use the correct override register 2 Gbps bit
  2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
                   ` (3 preceding siblings ...)
  2026-10-30 21:16 ` [PATCH net v3 4/8] net: dsa: b53: reject conflicting mirror destinations Florian Fainelli
@ 2026-10-30 21:16 ` Florian Fainelli
  2026-10-31 21:56   ` netdev-bot+sashiko
  2026-10-30 21:16 ` [PATCH net v3 6/8] net: dsa: b53: reference count shared mirror source bits Florian Fainelli
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:16 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Vivien Didelot, open list

The IMP and per-port GMII override registers swap the override-enable and 2
Gbps speed bits. Using the IMP speed bit on a GMII port clears its override
enable and leaves any previous 2 Gbps selection intact. Select the speed
bit alongside the register and enable bit so lower speeds clear the correct
bit and 2 Gbps sets it.

Fixes: b6a8a5477fe9 ("net: dsa: b53: fix resetting speed and pause on forced link")
Reviewed-by: Jonas Gorski <jonas.gorski@gmail.com>
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
 drivers/net/dsa/b53/b53_common.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 378f47e7250c..75033b28100e 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1370,17 +1370,19 @@ static void b53_force_port_config(struct b53_device *dev, int port,
 				  int speed, int duplex,
 				  bool tx_pause, bool rx_pause)
 {
-	u8 reg, val, off;
+	u8 reg, val, off, speed2000;
 
 	/* Override the port settings */
 	if (port == dev->imp_port) {
 		off = B53_PORT_OVERRIDE_CTRL;
 		val = PORT_OVERRIDE_EN;
+		speed2000 = PORT_OVERRIDE_SPEED_2000M;
 	} else if (is5325(dev)) {
 		return;
 	} else {
 		off = B53_GMII_PORT_OVERRIDE_CTRL(port);
 		val = GMII_PO_EN;
+		speed2000 = GMII_PO_SPEED_2000M;
 	}
 
 	b53_read8(dev, B53_CTRL_PAGE, off, &reg);
@@ -1392,11 +1394,11 @@ static void b53_force_port_config(struct b53_device *dev, int port,
 
 	reg &= ~(0x3 << GMII_PO_SPEED_S);
 	if (is5301x(dev) || is58xx(dev))
-		reg &= ~PORT_OVERRIDE_SPEED_2000M;
+		reg &= ~speed2000;
 
 	switch (speed) {
 	case 2000:
-		reg |= PORT_OVERRIDE_SPEED_2000M;
+		reg |= speed2000;
 		fallthrough;
 	case SPEED_1000:
 		reg |= PORT_OVERRIDE_SPEED_1000M;
-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net v3 6/8] net: dsa: b53: reference count shared mirror source bits
  2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
                   ` (4 preceding siblings ...)
  2026-10-30 21:16 ` [PATCH net v3 5/8] net: dsa: b53: use the correct override register 2 Gbps bit Florian Fainelli
@ 2026-10-30 21:16 ` Florian Fainelli
  2026-10-31 21:56   ` netdev-bot+sashiko
  2026-10-30 21:16 ` [PATCH net v3 7/8] net: dsa: b53: harden mirror register programming Florian Fainelli
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:16 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Vivien Didelot, open list

Multiple tc matchall filters can use the same source port, direction and
capture destination, but the hardware represents that source with one
mask bit. Removing any one filter currently clears the bit even when
another filter still needs it, and can also disable mirroring globally.

Track references to each port's ingress and egress source bits. Program
a bit for the first reference and retain it until the last reference is
removed. Keep the two directions independent and ignore a deletion with
no matching reference to avoid underflow. The tc callbacks serialize this
accounting under RTNL.

Fixes: ed3af5fd08eb ("net: dsa: b53: Add support for port mirroring")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
 drivers/net/dsa/b53/b53_common.c | 16 ++++++++++++++++
 drivers/net/dsa/b53/b53_priv.h   |  2 ++
 2 files changed, 18 insertions(+)

diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 75033b28100e..b16167c9510b 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -2545,6 +2545,7 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 		   struct netlink_ext_ack *extack)
 {
 	struct b53_device *dev = ds->priv;
+	unsigned int *mirror_count = &dev->ports[port].mirror_count[ingress];
 	u16 reg, loc, cap_ctl;
 	int ret;
 
@@ -2559,6 +2560,11 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 		return -EOPNOTSUPP;
 	}
 
+	if (*mirror_count) {
+		(*mirror_count)++;
+		return 0;
+	}
+
 	if (ingress)
 		loc = B53_IG_MIR_CTL;
 	else
@@ -2573,6 +2579,8 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 	cap_ctl |= MIRROR_EN;
 	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, cap_ctl);
 
+	(*mirror_count)++;
+
 	return 0;
 }
 EXPORT_SYMBOL(b53_mirror_add);
@@ -2581,9 +2589,17 @@ void b53_mirror_del(struct dsa_switch *ds, int port,
 		    struct dsa_mall_mirror_tc_entry *mirror)
 {
 	struct b53_device *dev = ds->priv;
+	unsigned int *mirror_count =
+		&dev->ports[port].mirror_count[mirror->ingress];
 	bool loc_disable = false, other_loc_disable = false;
 	u16 reg, loc;
 
+	if (!*mirror_count)
+		return;
+
+	if (--(*mirror_count))
+		return;
+
 	if (mirror->ingress)
 		loc = B53_IG_MIR_CTL;
 	else
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index 29cca7945df7..910c71618d0b 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -121,6 +121,8 @@ struct b53_pcs {
 struct b53_port {
 	u16		vlan_ctl_mask;
 	u16		pvid;
+	/* References to each egress/ingress mirror source mask bit. */
+	unsigned int	mirror_count[2];
 	struct ethtool_keee eee;
 };
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net v3 7/8] net: dsa: b53: harden mirror register programming
  2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
                   ` (5 preceding siblings ...)
  2026-10-30 21:16 ` [PATCH net v3 6/8] net: dsa: b53: reference count shared mirror source bits Florian Fainelli
@ 2026-10-30 21:16 ` Florian Fainelli
  2026-10-31 21:56   ` netdev-bot+sashiko
  2026-10-30 21:16 ` [PATCH net v3 8/8] net: dsa: b53: fix mirror port encoding on BCM5325 and BCM5365 Florian Fainelli
  2026-10-30 22:11 ` [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
  8 siblings, 1 reply; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:16 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Vivien Didelot, open list

The mirror destination check can reject a first rule when a bootloader or
previous kernel left mirroring enabled. Not all supported switches receive
a hardware reset during setup. Unchecked register accesses can also
corrupt mirror state or report an unsuccessful rule installation as
successful. Reference counting must not record a first source reference
until hardware programming has succeeded.

Clear capture control and both source-control registers during b53 and
bcm_sf2 setup, propagating initialization failures. Check all mirror
register reads and writes and only increment the source reference count
after successful programming. Log deletion failures because the DSA
callback cannot return an error.

Use the source masks to decide whether a destination is occupied. This
preserves the destination when a failed deletion leaves sources enabled,
and permits a new destination after the last source was removed even if
the final capture-control clear failed. Program the capture destination
before the new source so a failed destination write cannot leave a new
source enabled. Clearing the inactive capture-port field is cleanup.

Fixes: ed3af5fd08eb ("net: dsa: b53: Add support for port mirroring")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
 drivers/net/dsa/b53/b53_common.c | 109 +++++++++++++++++++++----------
 drivers/net/dsa/b53/b53_priv.h   |   1 +
 drivers/net/dsa/bcm_sf2.c        |   5 ++
 3 files changed, 81 insertions(+), 34 deletions(-)

diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index b16167c9510b..c2706c2e643f 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1307,6 +1307,10 @@ static int b53_setup(struct dsa_switch *ds)
 		return ret;
 	}
 
+	ret = b53_mirror_init(ds);
+	if (ret)
+		return ret;
+
 	/* setup default vlan for filtering mode */
 	pvid = b53_default_pvid(dev);
 	vl = &dev->vlans[pvid];
@@ -2540,20 +2544,49 @@ enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port,
 }
 EXPORT_SYMBOL(b53_get_tag_protocol);
 
+int b53_mirror_init(struct dsa_switch *ds)
+{
+	struct b53_device *dev = ds->priv;
+	int ret;
+
+	/* A switch reset may leave the previous mirror configuration intact. */
+	ret = b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, 0);
+	if (ret)
+		return ret;
+
+	ret = b53_write16(dev, B53_MGMT_PAGE, B53_IG_MIR_CTL, 0);
+	if (ret)
+		return ret;
+
+	return b53_write16(dev, B53_MGMT_PAGE, B53_EG_MIR_CTL, 0);
+}
+EXPORT_SYMBOL(b53_mirror_init);
+
 int b53_mirror_add(struct dsa_switch *ds, int port,
 		   struct dsa_mall_mirror_tc_entry *mirror, bool ingress,
 		   struct netlink_ext_ack *extack)
 {
 	struct b53_device *dev = ds->priv;
 	unsigned int *mirror_count = &dev->ports[port].mirror_count[ingress];
-	u16 reg, loc, cap_ctl;
+	u16 reg, other_reg, loc, other_loc, cap_ctl;
 	int ret;
 
+	loc = ingress ? B53_IG_MIR_CTL : B53_EG_MIR_CTL;
+	other_loc = ingress ? B53_EG_MIR_CTL : B53_IG_MIR_CTL;
+
+	ret = b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
+	if (ret)
+		return ret;
+
+	ret = b53_read16(dev, B53_MGMT_PAGE, other_loc, &other_reg);
+	if (ret)
+		return ret;
+
 	ret = b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &cap_ctl);
 	if (ret)
 		return ret;
 
-	if ((cap_ctl & MIRROR_EN) &&
+	if (((reg | other_reg) & MIRROR_MASK) &&
 	    (cap_ctl & CAP_PORT_MASK) != mirror->to_local_port) {
 		NL_SET_ERR_MSG_MOD(extack,
 				   "Only one mirroring destination is supported");
@@ -2565,19 +2598,17 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 		return 0;
 	}
 
-	if (ingress)
-		loc = B53_IG_MIR_CTL;
-	else
-		loc = B53_EG_MIR_CTL;
-
-	b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
-	reg |= BIT(port);
-	b53_write16(dev, B53_MGMT_PAGE, loc, reg);
-
+	/* Select the destination before enabling the new source. */
 	cap_ctl &= ~CAP_PORT_MASK;
 	cap_ctl |= mirror->to_local_port;
 	cap_ctl |= MIRROR_EN;
-	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, cap_ctl);
+	ret = b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, cap_ctl);
+	if (ret)
+		return ret;
+
+	ret = b53_write16(dev, B53_MGMT_PAGE, loc, reg | BIT(port));
+	if (ret)
+		return ret;
 
 	(*mirror_count)++;
 
@@ -2591,8 +2622,8 @@ void b53_mirror_del(struct dsa_switch *ds, int port,
 	struct b53_device *dev = ds->priv;
 	unsigned int *mirror_count =
 		&dev->ports[port].mirror_count[mirror->ingress];
-	bool loc_disable = false, other_loc_disable = false;
-	u16 reg, loc;
+	u16 reg, other_reg, loc, other_loc;
+	int ret;
 
 	if (!*mirror_count)
 		return;
@@ -2600,33 +2631,43 @@ void b53_mirror_del(struct dsa_switch *ds, int port,
 	if (--(*mirror_count))
 		return;
 
-	if (mirror->ingress)
-		loc = B53_IG_MIR_CTL;
-	else
-		loc = B53_EG_MIR_CTL;
+	loc = mirror->ingress ? B53_IG_MIR_CTL : B53_EG_MIR_CTL;
+	other_loc = mirror->ingress ? B53_EG_MIR_CTL : B53_IG_MIR_CTL;
 
 	/* Update the desired ingress/egress register */
-	b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
+	ret = b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
+	if (ret)
+		goto err;
+
 	reg &= ~BIT(port);
-	if (!(reg & MIRROR_MASK))
-		loc_disable = true;
-	b53_write16(dev, B53_MGMT_PAGE, loc, reg);
+	ret = b53_write16(dev, B53_MGMT_PAGE, loc, reg);
+	if (ret)
+		goto err;
 
 	/* Now look at the other one to know if we can disable mirroring
 	 * entirely
 	 */
-	if (mirror->ingress)
-		b53_read16(dev, B53_MGMT_PAGE, B53_EG_MIR_CTL, &reg);
-	else
-		b53_read16(dev, B53_MGMT_PAGE, B53_IG_MIR_CTL, &reg);
-	if (!(reg & MIRROR_MASK))
-		other_loc_disable = true;
-
-	b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
-	/* Both no longer have ports, let's disable mirroring */
-	if (loc_disable && other_loc_disable)
-		reg &= ~(MIRROR_EN | CAP_PORT_MASK);
-	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
+	ret = b53_read16(dev, B53_MGMT_PAGE, other_loc, &other_reg);
+	if (ret)
+		goto err;
+
+	if ((reg | other_reg) & MIRROR_MASK)
+		return;
+
+	ret = b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
+	if (ret)
+		goto err;
+
+	reg &= ~(MIRROR_EN | CAP_PORT_MASK);
+	ret = b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
+	if (ret)
+		goto err;
+
+	return;
+
+err:
+	dev_err(dev->dev, "Failed to delete mirror rule for port %d: %d\n",
+		port, ret);
 }
 EXPORT_SYMBOL(b53_mirror_del);
 
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index 910c71618d0b..b871a82cbb23 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -546,6 +546,7 @@ int b53_mdb_add(struct dsa_switch *ds, int port,
 int b53_mdb_del(struct dsa_switch *ds, int port,
 		const struct switchdev_obj_port_mdb *mdb,
 		struct dsa_db db);
+int b53_mirror_init(struct dsa_switch *ds);
 int b53_mirror_add(struct dsa_switch *ds, int port,
 		   struct dsa_mall_mirror_tc_entry *mirror, bool ingress,
 		   struct netlink_ext_ack *extack);
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 9e5713015187..9f54cd414c18 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -1059,6 +1059,11 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)
 {
 	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
 	unsigned int port;
+	int ret;
+
+	ret = b53_mirror_init(ds);
+	if (ret)
+		return ret;
 
 	/* Enable all valid ports and disable those unused */
 	for (port = 0; port < priv->hw_params.num_ports; port++) {
-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net v3 8/8] net: dsa: b53: fix mirror port encoding on BCM5325 and BCM5365
  2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
                   ` (6 preceding siblings ...)
  2026-10-30 21:16 ` [PATCH net v3 7/8] net: dsa: b53: harden mirror register programming Florian Fainelli
@ 2026-10-30 21:16 ` Florian Fainelli
  2026-10-30 22:11 ` [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
  8 siblings, 0 replies; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:16 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Vivien Didelot, open list

BCM5325 and BCM5365 use an 11-bit bitmap for the mirror capture port,
whereas newer switches use a four-bit port ID. Programming the port
number directly can select no destination or the wrong destination, and
can select multiple destinations. For example, port 2 requires BIT(2),
but writing 2 selects port 1 instead.

Use the bitmap encoding on BCM5325 and BCM5365, including translating
logical CPU port 5 to hardware bit 8. Apply the corresponding field mask
when checking for a conflicting destination, replacing the destination,
and clearing it after the last mirror source is removed. Keep the port ID
encoding for newer switches.

The ingress and egress mirror source masks use the same layout on these
switches, with the CPU port at bit 8 and bits 5-7 reserved, so apply the
same translation when setting and clearing a source port bit.

Fixes: ed3af5fd08eb ("net: dsa: b53: Add support for port mirroring")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
 drivers/net/dsa/b53/b53_common.c | 40 +++++++++++++++++++++++++++-----
 drivers/net/dsa/b53/b53_regs.h   |  1 +
 2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index c2706c2e643f..c320f53b41cd 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -2544,6 +2544,31 @@ enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port,
 }
 EXPORT_SYMBOL(b53_get_tag_protocol);
 
+static u16 b53_mirror_capture_port_mask(struct b53_device *dev)
+{
+	if (is5325(dev) || is5365(dev))
+		return CAP_PORT_MASK_25;
+
+	return CAP_PORT_MASK;
+}
+
+static u16 b53_mirror_port_mask(struct b53_device *dev, int port)
+{
+	/* These switches use a bitmap with the CPU port at bit 8. */
+	if ((is5325(dev) || is5365(dev)) && port == B53_CPU_PORT_25)
+		port = B53_CPU_PORT;
+
+	return BIT(port);
+}
+
+static u16 b53_mirror_capture_port(struct b53_device *dev, int port)
+{
+	if (is5325(dev) || is5365(dev))
+		return b53_mirror_port_mask(dev, port);
+
+	return port;
+}
+
 int b53_mirror_init(struct dsa_switch *ds)
 {
 	struct b53_device *dev = ds->priv;
@@ -2569,6 +2594,8 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 	struct b53_device *dev = ds->priv;
 	unsigned int *mirror_count = &dev->ports[port].mirror_count[ingress];
 	u16 reg, other_reg, loc, other_loc, cap_ctl;
+	u16 cap_mask = b53_mirror_capture_port_mask(dev);
+	u16 cap_port = b53_mirror_capture_port(dev, mirror->to_local_port);
 	int ret;
 
 	loc = ingress ? B53_IG_MIR_CTL : B53_EG_MIR_CTL;
@@ -2587,7 +2614,7 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 		return ret;
 
 	if (((reg | other_reg) & MIRROR_MASK) &&
-	    (cap_ctl & CAP_PORT_MASK) != mirror->to_local_port) {
+	    (cap_ctl & cap_mask) != cap_port) {
 		NL_SET_ERR_MSG_MOD(extack,
 				   "Only one mirroring destination is supported");
 		return -EOPNOTSUPP;
@@ -2599,14 +2626,15 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 	}
 
 	/* Select the destination before enabling the new source. */
-	cap_ctl &= ~CAP_PORT_MASK;
-	cap_ctl |= mirror->to_local_port;
+	cap_ctl &= ~cap_mask;
+	cap_ctl |= cap_port;
 	cap_ctl |= MIRROR_EN;
 	ret = b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, cap_ctl);
 	if (ret)
 		return ret;
 
-	ret = b53_write16(dev, B53_MGMT_PAGE, loc, reg | BIT(port));
+	ret = b53_write16(dev, B53_MGMT_PAGE, loc,
+			  reg | b53_mirror_port_mask(dev, port));
 	if (ret)
 		return ret;
 
@@ -2639,7 +2667,7 @@ void b53_mirror_del(struct dsa_switch *ds, int port,
 	if (ret)
 		goto err;
 
-	reg &= ~BIT(port);
+	reg &= ~b53_mirror_port_mask(dev, port);
 	ret = b53_write16(dev, B53_MGMT_PAGE, loc, reg);
 	if (ret)
 		goto err;
@@ -2658,7 +2686,7 @@ void b53_mirror_del(struct dsa_switch *ds, int port,
 	if (ret)
 		goto err;
 
-	reg &= ~(MIRROR_EN | CAP_PORT_MASK);
+	reg &= ~(MIRROR_EN | b53_mirror_capture_port_mask(dev));
 	ret = b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
 	if (ret)
 		goto err;
diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h
index 54a278db67c9..821ac74777f2 100644
--- a/drivers/net/dsa/b53/b53_regs.h
+++ b/drivers/net/dsa/b53/b53_regs.h
@@ -239,6 +239,7 @@
 /* Mirror capture control register (16 bit) */
 #define B53_MIR_CAP_CTL			0x10
 #define  CAP_PORT_MASK			0xf
+#define  CAP_PORT_MASK_25		0x7ff
 #define  BLK_NOT_MIR			BIT(14)
 #define  MIRROR_EN			BIT(15)
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes
  2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
                   ` (7 preceding siblings ...)
  2026-10-30 21:16 ` [PATCH net v3 8/8] net: dsa: b53: fix mirror port encoding on BCM5325 and BCM5365 Florian Fainelli
@ 2026-10-30 22:11 ` Florian Fainelli
  8 siblings, 0 replies; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 22:11 UTC (permalink / raw)
  To: netdev
  Cc: Jonas Gorski, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Vivien Didelot,
	open list

On 10/30/26 14:16, Florian Fainelli wrote:
> This patch series contains fixes to issues reported by Sashiko on the
> b53 PCS bug fixes being submitted, and then some more that came from
> code review and testing.
> 
> Except for issue #2, all of the other issues did trigger in testing and
> needed to be corrected.

Apologies for the double send, had a typo on netdev@vger.kernel.org.
-- 
Florian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net v3 3/8] net: dsa: b53: clear stale state when creating an ARL entry
  2026-10-30 21:16 ` [PATCH net v3 3/8] net: dsa: b53: clear stale state when creating an ARL entry Florian Fainelli
@ 2026-10-31 21:56   ` netdev-bot+sashiko
  0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-31 21:56 UTC (permalink / raw)
  To: florian.fainelli
  Cc: netdev, jonas.gorski, andrew, olteanv, davem, edumazet, kuba,
	pabeni, vivien.didelot, linux-kernel

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 2 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] In b53_arl_op() (drivers/net/dsa/b53/b53_common.c), deleting a
  {MAC,VID} that is missing from the ARL now returns -ENOENT.
- [Medium] Deleting an ARL entry that is not in hardware now fails with
  -ENOENT, including the -ENOSPC lookup result, which used to return 0…

--

net: dsa: b53: clear stale state when creating an ARL entry

When an ARL lookup misses, the patch now clears the b53_arl_entry before
building a new one. This stops a multicast add from ORing the port into an
unrelated bin's bitmap. A delete of an absent entry now returns -ENOENT
without writing to hardware, and that includes the -ENOSPC lookup result,
which used to return 0.

> diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
> index 559d4b360c828..c38e8c5838bd8 100644
> --- a/drivers/net/dsa/b53/b53_common.c
> +++ b/drivers/net/dsa/b53/b53_common.c
> @@ -1990,11 +1990,15 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
>  	case -ENOSPC:
>  		dev_dbg(dev->dev, "{%pM,%.4d} no space left in ARL\n",
>  			addr, vid);
> -		return is_valid ? ret : 0;
> +		return is_valid ? ret : -ENOENT;
>  	case -ENOENT:
> +		if (!is_valid)
> +			return ret;

[Severity: Medium]
Can returning -ENOENT on delete leave DSA's host address tracking out of
sync with the ARL?

b53_fdb_del() and b53_mdb_del() pass this value back to DSA. On CPU/DSA
ports, when the last reference drops, dsa_port_do_fdb_del() in
net/dsa/switch.c treats any driver error as "still in hardware":

	err = ds->ops->port_fdb_del(ds, port, addr, vid, db);
	trace_dsa_fdb_del_hw(dp, addr, vid, &db, err);
	if (err) {
		refcount_set(&a->refcount, 1);
		goto out;
	}

	list_del(&a->list);
	kfree(a);

dsa_port_do_mdb_del() handles errors the same way. The struct dsa_mac_addr
then stays on dp->fdbs or dp->mdbs with refcount 1.

A later add of the same {addr, vid, db} finds that record in
dsa_port_do_fdb_add() or dsa_port_do_mdb_add() and only calls
refcount_inc(). port_fdb_add or port_mdb_add is never called, so the
hardware entry is not reinstalled. The record is only freed by the leftover
sweep in dsa_port_teardown().

How can the hardware lose an entry that DSA still tracks? b53 doesn't set
ds->fdb_isolation, so dsa_port_bridge_host_fdb_add/del() force
db.bridge.num = 0, and all bridges share one ARL namespace.

A unicast ARL entry holds a single port. A user-port static FDB for the
same MAC/VID, for example in another bridge, overwrites the CPU port's host
entry. Deleting that user-port FDB invalidates the entry, and the later
host delete then misses.

There may be a second path, though I have not confirmed it.
bcm_sf2_sw_resume() calls bcm_sf2_sw_rst() and then ds->ops->setup(), and
does not replay FDB/MDB entries. If that reset clears the ARL, every later
host delete would miss.

Before this patch, the 0 return let DSA free its record, so the next add
reprogrammed the switch. Would returning 0 for a delete of an absent entry
fix the stale-bin problem without this side effect?

The commit message mentions the return value change, but not its effect on
the DSA core refcounting. With the Fixes: tag this will also go to stable.
Later patches in the series don't touch b53_arl_op(), b53_fdb_del() or
b53_mdb_del().

[Severity: Medium]
On BCM5325 and BCM5365, does this make every MDB delete fail?

b53_mdb_add() returns before it touches the ARL:

	if (is5325(priv) || is5365(priv))
		return -EOPNOTSUPP;

b53_mdb_del() has no matching check:

	ret = b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, false);
	mutex_unlock(&priv->arl_mutex);
	if (ret)
		dev_err(ds->dev, "failed to delete MDB entry\n");

The bridge calls switchdev_port_obj_del() on RTM_DELMDB whether or not the
add was offloaded:

br_switchdev_mdb_notify()
  switchdev_port_obj_del()
    dsa_user_port_obj_del()
      dsa_port_mdb_del()
        dsa_port_do_mdb_del()
          b53_mdb_del()
            b53_arl_op() -> -ENOENT

So every group leave or expiry on a bridged 5325/5365 port with snooping
enabled would log "failed to delete MDB entry". It would also log a second
message from switchdev_port_obj_del_deferred(), which only ignores
-EOPNOTSUPP:

	err = switchdev_port_obj_del_now(dev, obj);
	if (err && err != -EOPNOTSUPP)
		switchdev_obj_id_to_helpful_msg(dev, obj->id, err, false);

On other chips, when a user-port FDB or MDB add fails with -ENOSPC, the
bridge delete that follows now fails too. For FDBs,
dsa_user_switchdev_event_work() then logs "port %d failed to delete %pM
vid %d from fdb".

Should b53_mdb_del() get the same 5325/5365 guard as b53_mdb_add()? Or
should a missing entry on delete be treated as success? b53_arl_op() and
b53_mdb_del() are still the same at the end of the series.

> +
>  		/* We could not find a matching MAC, so reset to a new entry */

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261030211617.288562-1-florian.fainelli%40broadcom.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net v3 4/8] net: dsa: b53: reject conflicting mirror destinations
  2026-10-30 21:16 ` [PATCH net v3 4/8] net: dsa: b53: reject conflicting mirror destinations Florian Fainelli
@ 2026-10-31 21:56   ` netdev-bot+sashiko
  0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-31 21:56 UTC (permalink / raw)
  To: florian.fainelli
  Cc: netdev, jonas.gorski, andrew, olteanv, davem, edumazet, kuba,
	pabeni, vivien.didelot, linux-kernel

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 (1) · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] At this commit, the new conflict check in b53_mirror_add()
  (drivers/net/dsa/b53/b53_common.c:2553-2557) decides whether rules…
- [Low] The commit message says the patch propagates a failed capture-
  control read, but only the B53_MIR_CAP_CTL read in b53_mirror_add()…

Pre-existing issues:
- [Medium] Each per-port, per-direction mirror source bit is shared by
  every tc matchall mirror rule on that port and direction, but it is…

--

net: dsa: b53: reject conflicting mirror destinations

With this patch, b53_mirror_add() reads B53_MIR_CAP_CTL first. While
MIRROR_EN is set, it rejects a new mirror rule whose destination differs
from the one already programmed. b53_mirror_del() now clears the whole
CAP_PORT_MASK field along with MIRROR_EN when the last source is removed.

> diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
> index c38e8c5838bd8..378f47e7250cd 100644
> --- a/drivers/net/dsa/b53/b53_common.c
> +++ b/drivers/net/dsa/b53/b53_common.c
> @@ -2543,7 +2543,19 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
>  		   struct netlink_ext_ack *extack)
>  {
>  	struct b53_device *dev = ds->priv;
> -	u16 reg, loc;
> +	u16 reg, loc, cap_ctl;
> +	int ret;
> +
> +	ret = b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &cap_ctl);
> +	if (ret)
> +		return ret;
> +
> +	if ((cap_ctl & MIRROR_EN) &&
> +	    (cap_ctl & CAP_PORT_MASK) != mirror->to_local_port) {
> +		NL_SET_ERR_MSG_MOD(extack,
> +				   "Only one mirroring destination is supported");
> +		return -EOPNOTSUPP;
> +	}

[Severity: Medium]
Can leftover hardware state make this check reject a valid first rule?

The check only looks at MIRROR_EN and CAP_PORT_MASK as read back from
B53_MIR_CAP_CTL. It doesn't look at B53_IG_MIR_CTL or B53_EG_MIR_CTL.

At this commit, nothing in the setup path writes any of these three
registers. The only writers are b53_mirror_add() and b53_mirror_del().
Also, b53_switch_reset() doesn't soft-reset every chip.

Suppose a bootloader, a previous kernel (kexec or warm reboot) or an
earlier driver bind left MIRROR_EN set with capture port X. The first tc
mirror rule to any port other than X would then fail with -EOPNOTSUPP and
"Only one mirroring destination is supported", even though no software
rule exists. Before this patch, the add path just overwrote the stale
destination.

The reverse case also seems possible. Stale source bits with MIRROR_EN
clear pass this check, so those ports end up mirrored. The same stale bits
also stop b53_mirror_del() from ever clearing MIRROR_EN.

The later patch "net: dsa: b53: harden mirror register programming" in
this series seems to fix this. It adds b53_mirror_init(), which zeroes all
three registers from b53_setup() and bcm_sf2_sw_setup(). It also bases the
occupancy test on the source masks:

	if (((reg | other_reg) & MIRROR_MASK) &&
	    (cap_ctl & cap_mask) != cap_port) {

Could that be folded into this patch, so this commit doesn't regress on
its own?

>  
>  	if (ingress)
>  		loc = B53_IG_MIR_CTL;
> @@ -2554,11 +2566,10 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
>  	reg |= BIT(port);
>  	b53_write16(dev, B53_MGMT_PAGE, loc, reg);
>  
> -	b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
> -	reg &= ~CAP_PORT_MASK;
> -	reg |= mirror->to_local_port;
> -	reg |= MIRROR_EN;
> -	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
> +	cap_ctl &= ~CAP_PORT_MASK;
> +	cap_ctl |= mirror->to_local_port;
> +	cap_ctl |= MIRROR_EN;
> +	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, cap_ctl);
>  
>  	return 0;
>  }

[Severity: Low]
The commit message says the patch will "propagate a failed
capture-control read rather than making the decision from uninitialized
data". Are the other register accesses here still unchecked?

Just above this hunk, the source mask read is not checked:

	b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
	reg |= BIT(port);
	b53_write16(dev, B53_MGMT_PAGE, loc, reg);

reg is an uninitialized stack variable. On failure, b53_mdio_read16() and
b53_spi_read16() return without writing *val. A failed read therefore
writes garbage back into B53_IG_MIR_CTL or B53_EG_MIR_CTL.

Both writes are unchecked too, and the function returns 0 either way. DSA
then keeps a mall entry that the hardware never accepted.

The source bit is enabled before B53_MIR_CAP_CTL is written. If the
capture-control write fails, MIRROR_EN can stay clear, and a later rule to
a different destination would pass the new guard.

b53_mirror_del() has the same pattern. If its B53_MIR_CAP_CTL read fails,
reg still holds the other-direction source mask, and that value is written
into the capture control register:

	b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
	...
	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);

The later patch "net: dsa: b53: harden mirror register programming" in
this series seems to fix all of these. Every read and write is checked,
B53_MIR_CAP_CTL is programmed before the source bit is enabled, and the
delete path jumps to an error label instead of writing back garbage.

> @@ -2595,10 +2606,8 @@ void b53_mirror_del(struct dsa_switch *ds, int port,
>  
>  	b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
>  	/* Both no longer have ports, let's disable mirroring */
> -	if (loc_disable && other_loc_disable) {
> -		reg &= ~MIRROR_EN;
> -		reg &= ~mirror->to_local_port;
> -	}
> +	if (loc_disable && other_loc_disable)
> +		reg &= ~(MIRROR_EN | CAP_PORT_MASK);

[Severity: Medium]
This isn't a bug introduced by this patch, but each per-port,
per-direction source bit is shared by every matchall mirror rule on that
port and direction, and it is not reference counted.

dsa_user_add_cls_matchall_mirred() in net/dsa/user.c allows several
identical rules, and each one is tracked as its own mall entry. Earlier in
b53_mirror_del(), deleting any one of them clears the shared bit:

	reg &= ~BIT(port);

With this patch, if that was the last bit, MIRROR_EN and CAP_PORT_MASK are
cleared as well. Does the remaining rule then stop mirroring in hardware
while it is still installed? And would the new guard in b53_mirror_add()
then accept a rule to a different destination?

The later patch "net: dsa: b53: reference count shared mirror source
bits" in this series seems to fix this. It adds mirror_count[2] to struct
b53_port, so the hardware is programmed only on the first reference and
cleared only when the last one goes away:

	if (--(*mirror_count))
		return;

>  	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
>  }
>  EXPORT_SYMBOL(b53_mirror_del);

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261030211617.288562-1-florian.fainelli%40broadcom.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net v3 5/8] net: dsa: b53: use the correct override register 2 Gbps bit
  2026-10-30 21:16 ` [PATCH net v3 5/8] net: dsa: b53: use the correct override register 2 Gbps bit Florian Fainelli
@ 2026-10-31 21:56   ` netdev-bot+sashiko
  0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-31 21:56 UTC (permalink / raw)
  To: florian.fainelli
  Cc: netdev, jonas.gorski, andrew, olteanv, davem, edumazet, kuba,
	pabeni, vivien.didelot, linux-kernel

Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.

Low priority / nit picks for your consideration:

- [Low] This patch makes b53_force_port_config()
  (drivers/net/dsa/b53/b53_common.c) write BIT(7) of the per-port GMII…

--

net: dsa: b53: use the correct override register 2 Gbps bit

The IMP and per-port GMII override registers use swapped bit positions
for override-enable and 2 Gbps speed. This patch adds a per-register
speed2000 bit to b53_force_port_config(). The function then uses that bit
both to clear the 2 Gbps selection on 5301x/58xx and to set it for speed
2000.

> diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
> index 378f47e7250cd..75033b28100ec 100644
> --- a/drivers/net/dsa/b53/b53_common.c
> +++ b/drivers/net/dsa/b53/b53_common.c
> @@ -1370,17 +1370,19 @@ static void b53_force_port_config(struct b53_device *dev, int port,
>  				  int speed, int duplex,
>  				  bool tx_pause, bool rx_pause)
>  {
> -	u8 reg, val, off;
> +	u8 reg, val, off, speed2000;
>  
>  	/* Override the port settings */
>  	if (port == dev->imp_port) {
>  		off = B53_PORT_OVERRIDE_CTRL;
>  		val = PORT_OVERRIDE_EN;
> +		speed2000 = PORT_OVERRIDE_SPEED_2000M;
>  	} else if (is5325(dev)) {
>  		return;
>  	} else {
>  		off = B53_GMII_PORT_OVERRIDE_CTRL(port);
>  		val = GMII_PO_EN;
> +		speed2000 = GMII_PO_SPEED_2000M;
>  	}
>  
>  	b53_read8(dev, B53_CTRL_PAGE, off, &reg);
> @@ -1392,11 +1394,11 @@ static void b53_force_port_config(struct b53_device *dev, int port,
>  
>  	reg &= ~(0x3 << GMII_PO_SPEED_S);
>  	if (is5301x(dev) || is58xx(dev))
> -		reg &= ~PORT_OVERRIDE_SPEED_2000M;
> +		reg &= ~speed2000;
>  
>  	switch (speed) {
>  	case 2000:
> -		reg |= PORT_OVERRIDE_SPEED_2000M;
> +		reg |= speed2000;

[Severity: Low]
Should the set in case 2000 use the same is5301x() / is58xx() check as
the clear above it?

On GMII ports, speed2000 is now GMII_PO_SPEED_2000M, and b53_regs.h
documents it as chip-specific:

    #define   GMII_PO_EN			BIT(6) /* Use the register contents */
    #define   GMII_PO_SPEED_2000M		BIT(7) /* BCM5301X only, requires setting 1000M */

Before this patch, reg |= PORT_OVERRIDE_SPEED_2000M on a GMII port set
BIT(6). That is GMII_PO_EN, and reg |= val had already set it. So on
chips other than 5301x/58xx, speed 2000 behaved the same as 1000.

With this patch, speed 2000 on a GMII port of any chip that is not
5301x, 58xx or 5325 (for example BCM53125, BCM5395, BCM5389 or BCM63xx)
writes BIT(7) of B53_GMII_PORT_OVERRIDE_CTRL(port). Since the clear is
gated on is5301x(dev) || is58xx(dev), nothing ever clears that bit again
on those chips.

Commit b6a8a5477fe9 gated the clear because this bit position "can have
different meanings on other chips, e.g. for BCM5389 this controls
scanning PHYs". The set path was never gated the same way.

The IMP-port side of this mismatch existed before this patch. This patch
extends it to all GMII ports.

One way to reach it is a DT fixed-link with speed = <2000> on a GMII port:
phylink_parse_fixedlink() only warns and keeps the speed, and phylink then
calls b53_phylink_mac_link_up()->b53_force_port_config() in MLO_AN_FIXED
mode. The 63xx RGMII path in b53_phylink_mac_link_up() reaches the same
function.

Would it be better to gate reg |= speed2000 on is5301x(dev) ||
is58xx(dev), or to reject speed 2000 on other chips?

>  		fallthrough;
>  	case SPEED_1000:
>  		reg |= PORT_OVERRIDE_SPEED_1000M;

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261030211617.288562-1-florian.fainelli%40broadcom.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net v3 6/8] net: dsa: b53: reference count shared mirror source bits
  2026-10-30 21:16 ` [PATCH net v3 6/8] net: dsa: b53: reference count shared mirror source bits Florian Fainelli
@ 2026-10-31 21:56   ` netdev-bot+sashiko
  0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-31 21:56 UTC (permalink / raw)
  To: florian.fainelli
  Cc: netdev, jonas.gorski, andrew, olteanv, davem, edumazet, kuba,
	pabeni, vivien.didelot, linux-kernel

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] This patch adds a per-port software counter of mirror source
  references (struct b53_port::mirror_count[2]).
- [Low] At this commit, the first-reference path in b53_mirror_add()
  (b53_common.c:2573-2582) ignores the return values of b53_read16(loc),…

--

net: dsa: b53: reference count shared mirror source bits

This commit adds a per-port, per-direction reference count for the b53
mirror source bits. The hardware bit is programmed on the first
reference and cleared only when the last reference is removed. A
delete with no matching reference is ignored.

> diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
> index 75033b28100ec..b16167c9510b8 100644
> --- a/drivers/net/dsa/b53/b53_common.c
> +++ b/drivers/net/dsa/b53/b53_common.c

[ ... ]

> @@ -2559,6 +2560,11 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
>  		return -EOPNOTSUPP;
>  	}
>  
> +	if (*mirror_count) {
> +		(*mirror_count)++;
> +		return 0;
> +	}
> +

[Severity: Medium]
Can mirror_count get out of sync with the hardware after a suspend/resume
on bcm_sf2?

On resume, the switch is reset and setup runs again:

drivers/net/dsa/bcm_sf2.c:bcm_sf2_sw_resume() {
    ...
	ret = bcm_sf2_sw_rst(priv);
    ...
	ret = bcm_sf2_cfp_resume(ds);
    ...
	ds->ops->setup(ds);
    ...
}

bcm_sf2_sw_rst() does a watchdog chip reset, or a reset_control
assert/deassert on BCM7278. That puts B53_MIR_CAP_CTL, B53_IG_MIR_CTL
and B53_EG_MIR_CTL back to their defaults. Later in the series,
bcm_sf2_sw_setup() also calls b53_mirror_init(), which writes 0 to all
three registers.

The only place dev->ports[] gets cleared is in b53_reset_switch():

	memset(priv->ports, 0, sizeof(*priv->ports) * priv->num_ports);

and only b53_setup() reaches that, not bcm_sf2_sw_setup(). Unlike CFP
with bcm_sf2_cfp_resume(), nothing replays the mirror configuration.
DSA also keeps the tc matchall filters installed across resume.

So after resume, a port can have mirror_count[dir] >= 1 while its source
bit and MIRROR_EN are both clear. If a new matchall mirror filter is
added on that port and direction, the destination check passes because
the registers read back as 0. It then reaches the early return above
and reports success without programming any register.

Wouldn't the filter show as offloaded while nothing is mirrored, until
every filter on that port and direction is removed?

Before this patch, every add did the full read-modify-write, so adding a
filter after resume reprogrammed the hardware. The later patches
"net: dsa: b53: harden mirror register programming" and "net: dsa: b53:
fix mirror port encoding on BCM5325 and BCM5365" keep this early return.

>  	if (ingress)
>  		loc = B53_IG_MIR_CTL;
>  	else
> @@ -2573,6 +2579,8 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
>  	cap_ctl |= MIRROR_EN;
>  	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, cap_ctl);
>  
> +	(*mirror_count)++;
> +

[Severity: Low]
At this commit, b53_mirror_add() ignores the return values of
b53_read16(loc), b53_write16(loc) and b53_write16(B53_MIR_CAP_CTL), and
then increments mirror_count anyway.

If one of those bus accesses fails, the count says the source is
programmed when it isn't. Every later add on that port and direction
then takes the if (*mirror_count) shortcut. Will that keep the source
unprogrammed?

In b53_mirror_del(), the count is also decremented before the unchecked
clear.

The later patch "net: dsa: b53: harden mirror register programming" in
this series fixes this. It checks every read and write and only
increments mirror_count after programming succeeds. On the delete side
it logs failures, since .port_mirror_del returns void. A failed clear
there leaves a stale bit with a count of 0. That can be recovered,
because the next add goes from 0 to 1 and reprograms the bit.

>  	return 0;
>  }
>  EXPORT_SYMBOL(b53_mirror_add);

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261030211617.288562-1-florian.fainelli%40broadcom.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH net v3 7/8] net: dsa: b53: harden mirror register programming
  2026-10-30 21:16 ` [PATCH net v3 7/8] net: dsa: b53: harden mirror register programming Florian Fainelli
@ 2026-10-31 21:56   ` netdev-bot+sashiko
  0 siblings, 0 replies; 16+ messages in thread
From: netdev-bot+sashiko @ 2026-10-31 21:56 UTC (permalink / raw)
  To: florian.fainelli
  Cc: netdev, jonas.gorski, andrew, olteanv, davem, edumazet, kuba,
	pabeni, vivien.didelot, linux-kernel

Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] This patch adds an unconditional b53_mirror_init() call to
  bcm_sf2_sw_setup() (drivers/net/dsa/bcm_sf2.c).

--

net: dsa: b53: harden mirror register programming

This commit clears the mirror capture control register and both
source-control registers during b53 and bcm_sf2 setup. It checks every
mirror register access and only increments the source reference count
once hardware programming has succeeded. It also decides whether the
destination is occupied from the source masks instead of MIRROR_EN.

> diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
> index 9e5713015187b..9f54cd414c18f 100644
> --- a/drivers/net/dsa/bcm_sf2.c
> +++ b/drivers/net/dsa/bcm_sf2.c
> @@ -1059,6 +1059,11 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)
>  {
>  	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
>  	unsigned int port;
> +	int ret;
> +
> +	ret = b53_mirror_init(ds);
> +	if (ret)
> +		return ret;
>  
>  	/* Enable all valid ports and disable those unused */
>  	for (port = 0; port < priv->hw_params.num_ports; port++) {

[Severity: Medium]
Can this lose the installed mirror rules across suspend/resume?

The resume path also calls bcm_sf2_sw_setup():

drivers/net/dsa/bcm_sf2.c:bcm_sf2_sw_resume() {
    ...
	ret = bcm_sf2_cfp_resume(ds);
    ...
	ds->ops->setup(ds);

	return 0;
}

So every resume now writes 0 to B53_MIR_CAP_CTL, B53_IG_MIR_CTL and
B53_EG_MIR_CTL. The tc matchall mirror rules stay installed.

dev->ports[].mirror_count[] also keeps its value. The only thing that
clears it is the memset in b53_reset_switch(), and the bcm_sf2 path never
calls that.

CFP gets restored by bcm_sf2_cfp_resume(), but nothing reprograms the
mirror registers after resume.

After resume, b53_mirror_add() would hit this:

	if (*mirror_count) {
		(*mirror_count)++;
		return 0;
	}

A new rule for a port and direction that already has a reference would
then return success without programming the hardware.

The new occupancy check would also read all-zero source masks:

	if (((reg | other_reg) & MIRROR_MASK) &&
	    (cap_ctl & CAP_PORT_MASK) != mirror->to_local_port) {

That would accept a rule with a different destination while software
still tracks rules for the old one.

Doesn't this break the rule that mirror_count[] follows the hardware
source mask bits? The commit message relies on that when it says
"Reference counting must not record a first source reference until
hardware programming has succeeded".

bcm_sf2_sw_rst() may already clear these registers through EN_CHIP_RST.
If so, the stale count came from the earlier patch in this series, "net:
dsa: b53: reference count shared mirror source bits", and this patch makes
the wipe unconditional.

If the reset does not clear them, which is the case the commit message
describes, this patch causes the loss on resume.

The problem is still there at the end of the series. "net: dsa: b53: fix
mirror port encoding on BCM5325 and BCM5365" only changes the port and
capture encoding.

A related question: bcm_sf2_sw_resume() ignores the return value of
ds->ops->setup(). If b53_mirror_init() fails during resume, the new early
return skips the port, VLAN and ACB setup below and no error is reported.
Is that intended?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261030211617.288562-1-florian.fainelli%40broadcom.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH net v3 7/8] net: dsa: b53: harden mirror register programming
  2026-10-30 21:13 Florian Fainelli
@ 2026-10-30 21:13 ` Florian Fainelli
  0 siblings, 0 replies; 16+ messages in thread
From: Florian Fainelli @ 2026-10-30 21:13 UTC (permalink / raw)
  To: netde
  Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Vivien Didelot,
	open list:BROADCOM B53/SF2 ETHERNET SWITCH DRIVER, open list

The mirror destination check can reject a first rule when a bootloader or
previous kernel left mirroring enabled. Not all supported switches receive
a hardware reset during setup. Unchecked register accesses can also
corrupt mirror state or report an unsuccessful rule installation as
successful. Reference counting must not record a first source reference
until hardware programming has succeeded.

Clear capture control and both source-control registers during b53 and
bcm_sf2 setup, propagating initialization failures. Check all mirror
register reads and writes and only increment the source reference count
after successful programming. Log deletion failures because the DSA
callback cannot return an error.

Use the source masks to decide whether a destination is occupied. This
preserves the destination when a failed deletion leaves sources enabled,
and permits a new destination after the last source was removed even if
the final capture-control clear failed. Program the capture destination
before the new source so a failed destination write cannot leave a new
source enabled. Clearing the inactive capture-port field is cleanup.

Fixes: ed3af5fd08eb ("net: dsa: b53: Add support for port mirroring")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
 drivers/net/dsa/b53/b53_common.c | 109 +++++++++++++++++++++----------
 drivers/net/dsa/b53/b53_priv.h   |   1 +
 drivers/net/dsa/bcm_sf2.c        |   5 ++
 3 files changed, 81 insertions(+), 34 deletions(-)

diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index b16167c9510b..c2706c2e643f 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1307,6 +1307,10 @@ static int b53_setup(struct dsa_switch *ds)
 		return ret;
 	}
 
+	ret = b53_mirror_init(ds);
+	if (ret)
+		return ret;
+
 	/* setup default vlan for filtering mode */
 	pvid = b53_default_pvid(dev);
 	vl = &dev->vlans[pvid];
@@ -2540,20 +2544,49 @@ enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port,
 }
 EXPORT_SYMBOL(b53_get_tag_protocol);
 
+int b53_mirror_init(struct dsa_switch *ds)
+{
+	struct b53_device *dev = ds->priv;
+	int ret;
+
+	/* A switch reset may leave the previous mirror configuration intact. */
+	ret = b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, 0);
+	if (ret)
+		return ret;
+
+	ret = b53_write16(dev, B53_MGMT_PAGE, B53_IG_MIR_CTL, 0);
+	if (ret)
+		return ret;
+
+	return b53_write16(dev, B53_MGMT_PAGE, B53_EG_MIR_CTL, 0);
+}
+EXPORT_SYMBOL(b53_mirror_init);
+
 int b53_mirror_add(struct dsa_switch *ds, int port,
 		   struct dsa_mall_mirror_tc_entry *mirror, bool ingress,
 		   struct netlink_ext_ack *extack)
 {
 	struct b53_device *dev = ds->priv;
 	unsigned int *mirror_count = &dev->ports[port].mirror_count[ingress];
-	u16 reg, loc, cap_ctl;
+	u16 reg, other_reg, loc, other_loc, cap_ctl;
 	int ret;
 
+	loc = ingress ? B53_IG_MIR_CTL : B53_EG_MIR_CTL;
+	other_loc = ingress ? B53_EG_MIR_CTL : B53_IG_MIR_CTL;
+
+	ret = b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
+	if (ret)
+		return ret;
+
+	ret = b53_read16(dev, B53_MGMT_PAGE, other_loc, &other_reg);
+	if (ret)
+		return ret;
+
 	ret = b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &cap_ctl);
 	if (ret)
 		return ret;
 
-	if ((cap_ctl & MIRROR_EN) &&
+	if (((reg | other_reg) & MIRROR_MASK) &&
 	    (cap_ctl & CAP_PORT_MASK) != mirror->to_local_port) {
 		NL_SET_ERR_MSG_MOD(extack,
 				   "Only one mirroring destination is supported");
@@ -2565,19 +2598,17 @@ int b53_mirror_add(struct dsa_switch *ds, int port,
 		return 0;
 	}
 
-	if (ingress)
-		loc = B53_IG_MIR_CTL;
-	else
-		loc = B53_EG_MIR_CTL;
-
-	b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
-	reg |= BIT(port);
-	b53_write16(dev, B53_MGMT_PAGE, loc, reg);
-
+	/* Select the destination before enabling the new source. */
 	cap_ctl &= ~CAP_PORT_MASK;
 	cap_ctl |= mirror->to_local_port;
 	cap_ctl |= MIRROR_EN;
-	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, cap_ctl);
+	ret = b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, cap_ctl);
+	if (ret)
+		return ret;
+
+	ret = b53_write16(dev, B53_MGMT_PAGE, loc, reg | BIT(port));
+	if (ret)
+		return ret;
 
 	(*mirror_count)++;
 
@@ -2591,8 +2622,8 @@ void b53_mirror_del(struct dsa_switch *ds, int port,
 	struct b53_device *dev = ds->priv;
 	unsigned int *mirror_count =
 		&dev->ports[port].mirror_count[mirror->ingress];
-	bool loc_disable = false, other_loc_disable = false;
-	u16 reg, loc;
+	u16 reg, other_reg, loc, other_loc;
+	int ret;
 
 	if (!*mirror_count)
 		return;
@@ -2600,33 +2631,43 @@ void b53_mirror_del(struct dsa_switch *ds, int port,
 	if (--(*mirror_count))
 		return;
 
-	if (mirror->ingress)
-		loc = B53_IG_MIR_CTL;
-	else
-		loc = B53_EG_MIR_CTL;
+	loc = mirror->ingress ? B53_IG_MIR_CTL : B53_EG_MIR_CTL;
+	other_loc = mirror->ingress ? B53_EG_MIR_CTL : B53_IG_MIR_CTL;
 
 	/* Update the desired ingress/egress register */
-	b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
+	ret = b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
+	if (ret)
+		goto err;
+
 	reg &= ~BIT(port);
-	if (!(reg & MIRROR_MASK))
-		loc_disable = true;
-	b53_write16(dev, B53_MGMT_PAGE, loc, reg);
+	ret = b53_write16(dev, B53_MGMT_PAGE, loc, reg);
+	if (ret)
+		goto err;
 
 	/* Now look at the other one to know if we can disable mirroring
 	 * entirely
 	 */
-	if (mirror->ingress)
-		b53_read16(dev, B53_MGMT_PAGE, B53_EG_MIR_CTL, &reg);
-	else
-		b53_read16(dev, B53_MGMT_PAGE, B53_IG_MIR_CTL, &reg);
-	if (!(reg & MIRROR_MASK))
-		other_loc_disable = true;
-
-	b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
-	/* Both no longer have ports, let's disable mirroring */
-	if (loc_disable && other_loc_disable)
-		reg &= ~(MIRROR_EN | CAP_PORT_MASK);
-	b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
+	ret = b53_read16(dev, B53_MGMT_PAGE, other_loc, &other_reg);
+	if (ret)
+		goto err;
+
+	if ((reg | other_reg) & MIRROR_MASK)
+		return;
+
+	ret = b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
+	if (ret)
+		goto err;
+
+	reg &= ~(MIRROR_EN | CAP_PORT_MASK);
+	ret = b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
+	if (ret)
+		goto err;
+
+	return;
+
+err:
+	dev_err(dev->dev, "Failed to delete mirror rule for port %d: %d\n",
+		port, ret);
 }
 EXPORT_SYMBOL(b53_mirror_del);
 
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index 910c71618d0b..b871a82cbb23 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -546,6 +546,7 @@ int b53_mdb_add(struct dsa_switch *ds, int port,
 int b53_mdb_del(struct dsa_switch *ds, int port,
 		const struct switchdev_obj_port_mdb *mdb,
 		struct dsa_db db);
+int b53_mirror_init(struct dsa_switch *ds);
 int b53_mirror_add(struct dsa_switch *ds, int port,
 		   struct dsa_mall_mirror_tc_entry *mirror, bool ingress,
 		   struct netlink_ext_ack *extack);
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 9e5713015187..9f54cd414c18 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -1059,6 +1059,11 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)
 {
 	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
 	unsigned int port;
+	int ret;
+
+	ret = b53_mirror_init(ds);
+	if (ret)
+		return ret;
 
 	/* Enable all valid ports and disable those unused */
 	for (port = 0; port < priv->hw_params.num_ports; port++) {
-- 
2.53.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-10-31 21:56 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-30 21:16 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
2026-10-30 21:16 ` [PATCH net v3 1/8] net: dsa: b53: initialize the jumbo frame size register offset Florian Fainelli
2026-10-30 21:16 ` [PATCH net v3 2/8] net: dsa: b53: fix the SRAB grant timeout check Florian Fainelli
2026-10-30 21:16 ` [PATCH net v3 3/8] net: dsa: b53: clear stale state when creating an ARL entry Florian Fainelli
2026-10-31 21:56   ` netdev-bot+sashiko
2026-10-30 21:16 ` [PATCH net v3 4/8] net: dsa: b53: reject conflicting mirror destinations Florian Fainelli
2026-10-31 21:56   ` netdev-bot+sashiko
2026-10-30 21:16 ` [PATCH net v3 5/8] net: dsa: b53: use the correct override register 2 Gbps bit Florian Fainelli
2026-10-31 21:56   ` netdev-bot+sashiko
2026-10-30 21:16 ` [PATCH net v3 6/8] net: dsa: b53: reference count shared mirror source bits Florian Fainelli
2026-10-31 21:56   ` netdev-bot+sashiko
2026-10-30 21:16 ` [PATCH net v3 7/8] net: dsa: b53: harden mirror register programming Florian Fainelli
2026-10-31 21:56   ` netdev-bot+sashiko
2026-10-30 21:16 ` [PATCH net v3 8/8] net: dsa: b53: fix mirror port encoding on BCM5325 and BCM5365 Florian Fainelli
2026-10-30 22:11 ` [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
  -- strict thread matches above, loose matches on Subject: below --
2026-10-30 21:13 Florian Fainelli
2026-10-30 21:13 ` [PATCH net v3 7/8] net: dsa: b53: harden mirror register programming Florian Fainelli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®