From: Florian Fainelli <florian.fainelli@broadcom.com>
To: netde@vger.kernel.org
Cc: Florian Fainelli <florian.fainelli@broadcom.com>,
Jonas Gorski <jonas.gorski@gmail.com>,
Andrew Lunn <andrew@lunn.ch>, Vladimir Oltean <olteanv@gmail.com>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Vivien Didelot <vivien.didelot@gmail.com>,
netdev@vger.kernel.org (open list:BROADCOM B53/SF2 ETHERNET
SWITCH DRIVER), linux-kernel@vger.kernel.org (open list)
Subject: [PATCH net v3 7/8] net: dsa: b53: harden mirror register programming
Date: Fri, 30 Oct 2026 14:13:49 -0700 [thread overview]
Message-ID: <20261030211350.287566-8-florian.fainelli@broadcom.com> (raw)
In-Reply-To: <20261030211350.287566-1-florian.fainelli@broadcom.com>
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, ®);
+ 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 |= 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, ®);
+ ret = b53_read16(dev, B53_MGMT_PAGE, loc, ®);
+ 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, ®);
- else
- b53_read16(dev, B53_MGMT_PAGE, B53_IG_MIR_CTL, ®);
- if (!(reg & MIRROR_MASK))
- other_loc_disable = true;
-
- b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, ®);
- /* 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, ®);
+ 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
next prev parent reply other threads:[~2026-10-30 21:14 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 21:13 [PATCH net v3 0/8] net: dsa: b53: Preliminary fixes Florian Fainelli
2026-10-30 21:13 ` [PATCH net v3 1/8] net: dsa: b53: initialize the jumbo frame size register offset Florian Fainelli
2026-10-30 21:13 ` [PATCH net v3 2/8] net: dsa: b53: fix the SRAB grant timeout check Florian Fainelli
2026-10-30 21:13 ` [PATCH net v3 3/8] net: dsa: b53: clear stale state when creating an ARL entry Florian Fainelli
2026-10-30 21:13 ` [PATCH net v3 4/8] net: dsa: b53: reject conflicting mirror destinations Florian Fainelli
2026-10-30 21:13 ` [PATCH net v3 5/8] net: dsa: b53: use the correct override register 2 Gbps bit Florian Fainelli
2026-10-30 21:13 ` [PATCH net v3 6/8] net: dsa: b53: reference count shared mirror source bits Florian Fainelli
2026-10-30 21:13 ` Florian Fainelli [this message]
2026-10-30 21:13 ` [PATCH net v3 8/8] net: dsa: b53: fix mirror port encoding on BCM5325 and BCM5365 Florian Fainelli
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 7/8] net: dsa: b53: harden mirror register programming Florian Fainelli
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=20261030211350.287566-8-florian.fainelli@broadcom.com \
--to=florian.fainelli@broadcom.com \
--cc=andrew@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=jonas.gorski@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netde@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=olteanv@gmail.com \
--cc=pabeni@redhat.com \
--cc=vivien.didelot@gmail.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
all inboxes | Powered by JetHome®