* [PATCH net-next 0/3] net: dsa: make a shorter bridge ageing time take effect
@ 2026-11-04 11:48 Oleksij Rempel
2026-11-04 11:48 ` [PATCH net-next 1/3] net: dsa: microchip: ksz9477: apply a shorter ageing time at once Oleksij Rempel
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Oleksij Rempel @ 2026-11-04 11:48 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: netdev, linux-kernel, kernel, Jens Emil Schulz Østergaard,
Oleksij Rempel
With kernel STP, the bridge sets a shorter ageing time during a
topology change. When the topology change ends, the bridge restores
the configured ageing time. Without this series, two things go wrong:
- On a KSZ9893, a shorter ageing time does not take effect at once.
The new Age Period starts only after the running period ends.
Entries already in the table keep their old Age Count (patch 1).
- On any DSA switch with .set_ageing_time, the restore has no effect.
DSA writes the short value to the switch instead of the configured
value (patch 2).
A port can also leave the bridge during the topology change (patch 3):
- Before this series, DSA kept the short ageing time of that port. DSA
uses the smallest ageing time of all ports. While the port stayed out
of the bridge, later changes to a longer ageing time still wrote the
short value to the switch.
- With this series, DSA clears the ageing time of the port when the
port leaves. As before this series, DSA does not write the ageing
time to the switch at that moment. The switch keeps its current value
until the next write. The next write comes with the next ageing time
change, or when a port in the same DSA tree joins a bridge. Usually,
the next ageing time change is the restore at the end of the topology
change.
In a lab test on a KSZ9893 with a redundant path, I cut one path while
one station sent one-way traffic to another. I ran six setups. With the
series, the traffic stopped for 12 to 15 s in all six. That is about
the time STP needed to converge. Without the series, the traffic
stopped for 24 to 56 s in three setups. The other three failed before
the cut (see below).
Without the series, the switch kept the old FDB entry of the receiver.
The entry pointed to the port on which the traffic came in, so the
switch dropped the traffic. During the topology change, the switch still
ran its old, long Age Period. The entry therefore did not age out in
time (patch 1). The outage ends when the entry ages out or when the
receiver sends a frame. In one of the three measured setups, the sender
used normal ARP, so the receiver could answer ARP requests. There, the
outage was 24 to 46 s. Where the receiver sent no frame that the switch
could learn from, it was 51 to 56 s. Stations that receive much and send
little are affected most, for example receivers of a UDP stream or of
telemetry. I did not test traffic in both directions, such as TCP.
Without the series, the switch also stayed at the short ageing time
after a topology change ended (patch 2). In this test, the topology
change when the bridge came up already left the switch at 8 s instead
of 300 s, before the cut. The short value is twice the forward delay,
which was 4 s. Entries of stations that send little then
aged out after a few seconds. In three setups, the switch lost the
entry of the receiver this way before the cut.
Patches 2 and 3 change the DSA core. They affect every driver that
implements .set_ageing_time. For this reason, the series targets
net-next and has no Fixes tags.
To reproduce the restore case (patch 2):
ip link add br0 type bridge stp_state 0
ip link set lan1 master br0
ip link set lan2 master br0
ip link set br0 up
ip link set br0 type bridge ageing_time 1000
ip link set br0 type bridge ageing_time 30000
ip link takes centiseconds, so 1000 is 10 s and 30000 is 300 s. Then
read the ageing time from the switch, for example from the 32-bit
regmap in debugfs:
grep -E '^0*310:' /sys/kernel/debug/regmap/<device>-32/registers
The line shows the bytes of registers 0x0310 to 0x0313. Bits 5:3 of
the first byte are the Age Count. The last byte is the Age Period in
seconds. The ageing time is Age Count times Age Period. Without the
series, it is 1 x 10 s = 10 s. With the series, it is 2 x 150 s =
300 s, so the last byte reads 96.
To test a port that leaves the bridge (patch 3), run "ip link set lan2
nomaster" between the two ageing_time commands. Without the series,
and with only patches 1 and 2, the register readback shows 10 s. With
the whole series, it shows 300 s.
To test patch 1 on a fresh bridge, set the ageing time to 300 s and let
the switch learn an address on lan1. Then set the ageing time to 10 s
and watch the "self" entry of that address in "bridge fdb show dev
lan1". Without the series, the entry stayed in the table for 57 s to
more than 120 s in my tests on a KSZ9893. With patch 1, the switch
removes the dynamic entries at once. The switch learns the address
again from new traffic.
Tested on a KSZ9893. I did not run the forwarding selftests.
---
Oleksij Rempel (3):
net: dsa: microchip: ksz9477: apply a shorter ageing time at once
net: dsa: store the port ageing time before programming it
net: dsa: forget the port ageing time when the port leaves the bridge
drivers/net/dsa/microchip/ksz9477.c | 36 ++++++++++++++++++++++++++++++++----
net/dsa/port.c | 15 +++++++++++----
2 files changed, 43 insertions(+), 8 deletions(-)
---
base-commit: 4dd16e31135ea8437a720222df34e8745f385d6d
change-id: 20261103-ksz-stp-ageing-705aa69829e5
Best regards,
--
Oleksij Rempel <o.rempel@pengutronix.de>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next 1/3] net: dsa: microchip: ksz9477: apply a shorter ageing time at once
2026-11-04 11:48 [PATCH net-next 0/3] net: dsa: make a shorter bridge ageing time take effect Oleksij Rempel
@ 2026-11-04 11:48 ` Oleksij Rempel
2026-11-06 13:16 ` Andrew Lunn
2026-11-04 11:48 ` [PATCH net-next 2/3] net: dsa: store the port ageing time before programming it Oleksij Rempel
2026-11-04 11:48 ` [PATCH net-next 3/3] net: dsa: forget the port ageing time when the port leaves the bridge Oleksij Rempel
2 siblings, 1 reply; 7+ messages in thread
From: Oleksij Rempel @ 2026-11-04 11:48 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: netdev, linux-kernel, kernel, Jens Emil Schulz Østergaard,
Oleksij Rempel
On a KSZ9893 a new Age Period applies only after the running one has
ended, and an entry keeps the Age Count it was stored with. When the
bridge shortens the ageing time for an STP topology change, stale
entries outlive the topology change: 61 to 79 s were measured at
forward_delay 4 s, where the bridge asks for 8 s.
Pulse Fast Aging when the programmed ageing time gets shorter or the Age
Count smaller. On a KSZ9893 it removed the dynamic entries of the user
ports and started the new period at once; 2 ms was enough, the datasheet
gives no pulse length.
Tested on a KSZ9893 with kernel STP. Other chips using this function
were not tested.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
drivers/net/dsa/microchip/ksz9477.c | 36 ++++++++++++++++++++++++++++++++----
1 file changed, 32 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 72528a53b67d2d269d9a0505b0cc0c2348f06fad..b54f388b227f19c58c35ce3642935387c11e9356 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -1289,13 +1289,16 @@ static void ksz9477_phylink_get_caps(struct dsa_switch *ds, int port,
ksz_phylink_get_caps(ds, port, config);
}
+/* No pulse length in the datasheet; this one worked on a KSZ9893. */
+#define KSZ9477_FAST_AGE_PULSE_US (2 * USEC_PER_MSEC)
+
static int ksz9477_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
{
struct ksz_device *dev = ds->priv;
+ u8 data, mult, value, old_period;
+ u32 old_secs, new_secs, max_val;
u32 secs = msecs / 1000;
- u8 data, mult, value;
- u32 max_val;
- int ret;
+ int ret, err;
#define MAX_TIMER_VAL ((1 << 8) - 1)
@@ -1314,8 +1317,13 @@ static int ksz9477_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
if (ret < 0)
return ret;
+ ret = ksz_read8(dev, REG_SW_LUE_CTRL_3, &old_period);
+ if (ret < 0)
+ return ret;
+
/* Check whether there is need to update the multiplier. */
mult = FIELD_GET(SW_AGE_CNT_M, value);
+ old_secs = mult * old_period;
max_val = MAX_TIMER_VAL;
if (mult > 0) {
/* Try to use the same multiplier already in the register as
@@ -1337,7 +1345,27 @@ static int ksz9477_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
}
value = DIV_ROUND_UP(secs, data);
- return ksz_write8(dev, REG_SW_LUE_CTRL_3, value);
+ ret = ksz_write8(dev, REG_SW_LUE_CTRL_3, value);
+ if (ret < 0)
+ return ret;
+
+ /* On a KSZ9893 a new period applies only when the running one ends,
+ * and entries keep the Age Count they were stored with. If the total
+ * time or the Age Count got smaller, pulse Fast Aging; on a KSZ9893
+ * that removed the dynamic entries and started the new period.
+ */
+ new_secs = data * value;
+ if (new_secs >= old_secs && data >= mult)
+ return 0;
+
+ ret = ksz_rmw8(dev, REG_SW_LUE_CTRL_1, SW_FAST_AGING, SW_FAST_AGING);
+ if (!ret)
+ fsleep(KSZ9477_FAST_AGE_PULSE_US);
+
+ /* Clear the bit even after an error. */
+ err = ksz_rmw8(dev, REG_SW_LUE_CTRL_1, SW_FAST_AGING, 0);
+
+ return ret ?: err;
}
void ksz9477_port_queue_split(struct ksz_device *dev, int port)
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next 2/3] net: dsa: store the port ageing time before programming it
2026-11-04 11:48 [PATCH net-next 0/3] net: dsa: make a shorter bridge ageing time take effect Oleksij Rempel
2026-11-04 11:48 ` [PATCH net-next 1/3] net: dsa: microchip: ksz9477: apply a shorter ageing time at once Oleksij Rempel
@ 2026-11-04 11:48 ` Oleksij Rempel
2026-11-06 13:23 ` Andrew Lunn
2026-11-04 11:48 ` [PATCH net-next 3/3] net: dsa: forget the port ageing time when the port leaves the bridge Oleksij Rempel
2 siblings, 1 reply; 7+ messages in thread
From: Oleksij Rempel @ 2026-11-04 11:48 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: netdev, linux-kernel, kernel, Jens Emil Schulz Østergaard,
Oleksij Rempel
dsa_switch_fastest_ageing_time() takes the minimum of the cached
dp->ageing_time of all ports, but dsa_port_ageing_time() updates the
cached value only after the notifier. Raising the ageing time programs
the old, shorter value again, so after an STP topology change the switch
stays at 2 x forward_delay.
Store the new value before the notifier and restore it on error. This
affects all DSA drivers with .set_ageing_time: sja1105, which reloads
its static config on a change, will reload again when the bridge
restores the ageing time.
Reported in an automated review of the lan9645x bridge series (Link:
below).
Tested on a KSZ9893.
Link: https://lore.kernel.org/netdev/179180428624.434549.14425623884223795761@kernel.org/
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
net/dsa/port.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 1f5536c0dffc2311db24015697e0c8f5c014ac74..3195d8b7c69c5e0fbc4007421218c2ff1db0ce47 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -876,18 +876,22 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
{
unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
+ unsigned int old_ageing_time = dp->ageing_time;
struct dsa_notifier_ageing_time_info info;
int err;
info.ageing_time = ageing_time;
+ /* dsa_switch_fastest_ageing_time() takes the minimum over the cached
+ * values of all ports, so update this port's value first.
+ */
+ dp->ageing_time = ageing_time;
+
err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
if (err)
- return err;
+ dp->ageing_time = old_ageing_time;
- dp->ageing_time = ageing_time;
-
- return 0;
+ return err;
}
int dsa_port_mst_enable(struct dsa_port *dp, bool on,
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next 3/3] net: dsa: forget the port ageing time when the port leaves the bridge
2026-11-04 11:48 [PATCH net-next 0/3] net: dsa: make a shorter bridge ageing time take effect Oleksij Rempel
2026-11-04 11:48 ` [PATCH net-next 1/3] net: dsa: microchip: ksz9477: apply a shorter ageing time at once Oleksij Rempel
2026-11-04 11:48 ` [PATCH net-next 2/3] net: dsa: store the port ageing time before programming it Oleksij Rempel
@ 2026-11-04 11:48 ` Oleksij Rempel
2026-11-06 13:30 ` Andrew Lunn
2 siblings, 1 reply; 7+ messages in thread
From: Oleksij Rempel @ 2026-11-04 11:48 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: netdev, linux-kernel, kernel, Jens Emil Schulz Østergaard,
Oleksij Rempel
A port that leaves the bridge keeps its cached dp->ageing_time, and it
still counts for dsa_switch_fastest_ageing_time(). A port that leaves
during an STP topology change keeps the switch at the short value until
the port joins a bridge again.
Clear the cached value when the port leaves the bridge. The switch is
not reprogrammed on leave. If the leaving port held the shortest ageing
time, for example during a topology change, the switch keeps that value
until the next ageing time change or bridge join in the DSA tree.
Found by code inspection. On a KSZ9893 with lan1 and lan2 in a bridge,
setting the ageing time to 10 s, removing lan2 and setting 300 s left
the switch at 10 s without this patch and at 300 s with it.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
net/dsa/port.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 3195d8b7c69c5e0fbc4007421218c2ff1db0ce47..21b237332cc77b9f99520e71dae01c2c9b49d496 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -414,7 +414,10 @@ static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp,
/* Ageing time may be global to the switch chip, so don't change it
* here because we have no good reason (or value) to change it to.
+ * But forget the port's value, so that the shortest ageing time of
+ * the ports still in a bridge is the one programmed next time.
*/
+ dp->ageing_time = 0;
}
static int dsa_port_bridge_create(struct dsa_port *dp,
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next 1/3] net: dsa: microchip: ksz9477: apply a shorter ageing time at once
2026-11-04 11:48 ` [PATCH net-next 1/3] net: dsa: microchip: ksz9477: apply a shorter ageing time at once Oleksij Rempel
@ 2026-11-06 13:16 ` Andrew Lunn
0 siblings, 0 replies; 7+ messages in thread
From: Andrew Lunn @ 2026-11-06 13:16 UTC (permalink / raw)
To: Oleksij Rempel
Cc: Woojung Huh, UNGLinuxDriver, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, netdev,
linux-kernel, kernel, Jens Emil Schulz Østergaard
> + /* On a KSZ9893 a new period applies only when the running one ends,
> + * and entries keep the Age Count they were stored with. If the total
> + * time or the Age Count got smaller, pulse Fast Aging; on a KSZ9893
> + * that removed the dynamic entries and started the new period.
The tenses are wrong here.
On a KSZ9893 that removes the dynamic entries and starts the new
period.
Otherwise the logic seems sensible.
Andrew
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next 2/3] net: dsa: store the port ageing time before programming it
2026-11-04 11:48 ` [PATCH net-next 2/3] net: dsa: store the port ageing time before programming it Oleksij Rempel
@ 2026-11-06 13:23 ` Andrew Lunn
0 siblings, 0 replies; 7+ messages in thread
From: Andrew Lunn @ 2026-11-06 13:23 UTC (permalink / raw)
To: Oleksij Rempel
Cc: Woojung Huh, UNGLinuxDriver, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, netdev,
linux-kernel, kernel, Jens Emil Schulz Østergaard
On Wed, Nov 04, 2026 at 12:48:29PM +0100, Oleksij Rempel wrote:
> dsa_switch_fastest_ageing_time() takes the minimum of the cached
> dp->ageing_time of all ports, but dsa_port_ageing_time() updates the
> cached value only after the notifier. Raising the ageing time programs
> the old, shorter value again, so after an STP topology change the switch
> stays at 2 x forward_delay.
>
> Store the new value before the notifier and restore it on error. This
> affects all DSA drivers with .set_ageing_time: sja1105, which reloads
> its static config on a change, will reload again when the bridge
> restores the ageing time.
>
> Reported in an automated review of the lan9645x bridge series (Link:
> below).
>
> Tested on a KSZ9893.
>
> Link: https://lore.kernel.org/netdev/179180428624.434549.14425623884223795761@kernel.org/
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> ---
> net/dsa/port.c | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/net/dsa/port.c b/net/dsa/port.c
> index 1f5536c0dffc2311db24015697e0c8f5c014ac74..3195d8b7c69c5e0fbc4007421218c2ff1db0ce47 100644
> --- a/net/dsa/port.c
> +++ b/net/dsa/port.c
> @@ -876,18 +876,22 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
> {
> unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
> unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
> + unsigned int old_ageing_time = dp->ageing_time;
> struct dsa_notifier_ageing_time_info info;
> int err;
>
> info.ageing_time = ageing_time;
>
> + /* dsa_switch_fastest_ageing_time() takes the minimum over the cached
> + * values of all ports, so update this port's value first.
> + */
> + dp->ageing_time = ageing_time;
> +
> err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
This appears to make the info.ageing_time parameter pointless?
Either remove it, or pass the port it is to be applied to, so the
correct switch wide ageing time can be calculated?
Andrew
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next 3/3] net: dsa: forget the port ageing time when the port leaves the bridge
2026-11-04 11:48 ` [PATCH net-next 3/3] net: dsa: forget the port ageing time when the port leaves the bridge Oleksij Rempel
@ 2026-11-06 13:30 ` Andrew Lunn
0 siblings, 0 replies; 7+ messages in thread
From: Andrew Lunn @ 2026-11-06 13:30 UTC (permalink / raw)
To: Oleksij Rempel
Cc: Woojung Huh, UNGLinuxDriver, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, netdev,
linux-kernel, kernel, Jens Emil Schulz Østergaard
On Wed, Nov 04, 2026 at 12:48:30PM +0100, Oleksij Rempel wrote:
> A port that leaves the bridge keeps its cached dp->ageing_time, and it
> still counts for dsa_switch_fastest_ageing_time(). A port that leaves
> during an STP topology change keeps the switch at the short value until
> the port joins a bridge again.
>
> Clear the cached value when the port leaves the bridge. The switch is
> not reprogrammed on leave. If the leaving port held the shortest ageing
> time, for example during a topology change, the switch keeps that value
> until the next ageing time change or bridge join in the DSA tree.
Does the software bridge trigger an ageing time change soon
afterwards? Does a port leaving the software bridge trigger a topology
change?
I'm just trying to understand how long the fast ageing value will be
left in place.
Andrew
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-06 13:30 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-04 11:48 [PATCH net-next 0/3] net: dsa: make a shorter bridge ageing time take effect Oleksij Rempel
2026-11-04 11:48 ` [PATCH net-next 1/3] net: dsa: microchip: ksz9477: apply a shorter ageing time at once Oleksij Rempel
2026-11-06 13:16 ` Andrew Lunn
2026-11-04 11:48 ` [PATCH net-next 2/3] net: dsa: store the port ageing time before programming it Oleksij Rempel
2026-11-06 13:23 ` Andrew Lunn
2026-11-04 11:48 ` [PATCH net-next 3/3] net: dsa: forget the port ageing time when the port leaves the bridge Oleksij Rempel
2026-11-06 13:30 ` Andrew Lunn
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®