stp: react to a port losing carrier

The state machine never looked at link state, so a port whose cable was
pulled stayed in forwarding: it kept being announced, kept its learned
entries, and the most ordinary topology change there is went unnoticed.
Observed on hardware - a non-edge port with the link administratively
down still reported forwarding and left the topology-change counter at
zero for the whole observation window.

Sample the carrier bitmap once per second, alongside the tx-budget refill
(the 50 Hz tick has no business doing register reads). On carrier loss put
the port back to blocking and run the normal topology-change path, which
flushes just that port's entries. On carrier return re-run the listen
period rather than forwarding immediately - the segment may have been
rewired while we were down - and clear the operational edge flag so a port
that was auto-edged has to earn it again.

stp_setup() seeds the bitmap from the hardware so enabling STP does not
report every already-down port as a fresh topology change.
This commit is contained in:
d00f
2026-08-18 23:28:34 +02:00
committed by d00f
parent 7a790b32b1
commit f3c1b7f3ac
+40
View File
@@ -91,6 +91,8 @@ __xdata uint16_t port_hello[10]; /* hello TX countdown */
__xdata uint16_t stp_bpdu_age[10]; /* ticks since last BPDU seen on port (saturating) */ __xdata uint16_t stp_bpdu_age[10]; /* ticks since last BPDU seen on port (saturating) */
__xdata uint8_t stp_tx_budget[10]; /* tx hold: BPDUs left in the current second */ __xdata uint8_t stp_tx_budget[10]; /* tx hold: BPDUs left in the current second */
__xdata uint16_t stp_sec_tick; /* 1 s window for the tx budget */ __xdata uint16_t stp_sec_tick; /* 1 s window for the tx budget */
__xdata uint16_t stp_link_prev; /* carrier bitmap as of the last check */
__xdata uint16_t stp_link_now;
/* Scratch (8051: locals would overflow the internal-RAM overlay area) */ /* Scratch (8051: locals would overflow the internal-RAM overlay area) */
__xdata uint8_t stp_scratch; __xdata uint8_t stp_scratch;
@@ -450,6 +452,39 @@ void stp_timers(void) __banked
stp_failsafe_tripped = 1; stp_failsafe_tripped = 1;
return; return;
} }
/* Link supervision. Without this the state machine never learns
* that a port lost carrier: it keeps the port in forwarding, keeps
* announcing on it, and never flushes what was learned behind it -
* yet losing a link is the most ordinary topology change there is.
* Once per second is soon enough, and it keeps register reads out
* of the 50 Hz tick. */
reg_read_m(RTL837X_REG_LINKS_STS);
stp_link_now = (uint16_t)sfr_data[1] | ((uint16_t)sfr_data[2] << 8);
if (stp_link_now != stp_link_prev) {
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
if (!(stp_pflags[stp_i] & STP_PF_ENABLED))
continue;
if (!((stp_link_now ^ stp_link_prev) >> stp_i & 1))
continue;
/* Either way the port must stop forwarding first. */
stp_state_set(stp_i, 0b01);
if ((stp_link_now >> stp_i) & 1) {
/* Carrier back: re-run the listen period rather than
* forwarding straight away - the segment may have been
* rewired while we were down. Auto edge still applies. */
port_timers[stp_i] = (uint16_t)stp_fwddelay_s * STP_HZ;
stp_pflags[stp_i] &= ~STP_PF_OPEREDGE;
stp_bpdu_age[stp_i] = 0;
} else {
port_timers[stp_i] = 0;
print_string("STP: link down, port blocking ");
print_byte(stp_i); write_char('\n');
stp_topology_change(stp_i);
}
}
stp_link_prev = stp_link_now;
}
} }
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) { for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
@@ -595,6 +630,11 @@ void stp_setup(void) __banked
print_reg(RTL837X_MSTP_STATES); write_char('\n'); print_reg(RTL837X_MSTP_STATES); write_char('\n');
/* Seed the carrier bitmap, so turning STP on does not report every
* port that was already down as a fresh topology change. */
reg_read_m(RTL837X_REG_LINKS_STS);
stp_link_prev = (uint16_t)sfr_data[1] | ((uint16_t)sfr_data[2] << 8);
stp_claim_root(); stp_claim_root();
/* Take BPDUs to the CPU only - we are a participating bridge now. */ /* Take BPDUs to the CPU only - we are a participating bridge now. */