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. Fixes: ed3af5fd08eb ("net: dsa: b53: Add support for port mirroring") Signed-off-by: Florian Fainelli --- 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 0871a10d8cbc..b8a7b380bac8 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 &= ~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, ®); /* 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