mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-08-30 14:52:51 +08:00
stp: management failsafe (commit-confirm) + bounded NIC waits
Enabling STP on a bridge whose management rides an in-band VLAN can cut
off that very management - and not only by our own blocking: on this
network the upstream TP-Link Easy Smart switch's "loop prevention"
reacted to our BPDU hellos by blocking ITS port towards us while our
ASIC was all-forwarding, isolating the whole segment until a power
cycle. Recoverable only by going quiet.
Add a commit-confirm watchdog: while STP is enabled, any HTTP request
re-arms a countdown ("stp failsafe <seconds>", default 180, 0 disables);
if management stays silent for the whole window, STP disables itself,
which also stops BPDU TX so a neighbour's loop protection can release
its block. The web UI polls /stp.json every 2 s, so an open browser
naturally keeps the watchdog re-armed. The trip is reported via
/stp.json (fs, fsT) and as a warning on the Spanning Tree page.
Deliberately not conditioned on our own MSTP port states - the incident
above proves the uplink can be dead while every local port forwards.
Also bound the NIC DMA busy-waits (nic_tx_packet, nic_rx_header,
nic_rx_packet): an unbounded spin on SFR_NIC_CTRL freezes the entire
main loop (timers, HTTP, ARP) if the ASIC ever fails to consume a
transfer; give up after ~65k polls and drop the frame instead.
Hardware-verified end to end: with priority 15 against a live RSTP
bridge the uplink died 6 s after "stp on" and the network recovered BY
ITSELF 66 s later (trip at 45 s + neighbour release), fsT=1, LACP and
LAN intact. Telemetry via syslog-to-edge-port host confirmed the full
chain: countdown 44->4, trip, hello TX stopping at the trip.
(cherry picked from commit 1fa9775156fd6d7ebfdda2382f73430b86601230)
This commit is contained in:
+20
-3
@@ -621,7 +621,11 @@ void nic_rx_header(uint16_t ring_ptr)
|
||||
SFR_NIC_DATA_U16LE = buffer;
|
||||
SFR_NIC_RING_U16LE = ring_ptr;
|
||||
SFR_NIC_CTRL = 1;
|
||||
do { } while (SFR_NIC_CTRL != 0);
|
||||
/* Bounded, cf. nic_tx_packet: a stuck NIC DMA must not freeze the loop */
|
||||
{
|
||||
uint16_t rx_guard = 0;
|
||||
do { } while (SFR_NIC_CTRL != 0 && ++rx_guard != 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -644,7 +648,11 @@ void nic_rx_packet(register uint16_t buffer, register uint16_t ring_ptr)
|
||||
print_short(len);
|
||||
#endif
|
||||
SFR_NIC_CTRL = len;
|
||||
do { } while (SFR_NIC_CTRL != 0);
|
||||
/* Bounded, cf. nic_tx_packet: a stuck NIC DMA must not freeze the loop */
|
||||
{
|
||||
uint16_t rx_guard = 0;
|
||||
do { } while (SFR_NIC_CTRL != 0 && ++rx_guard != 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -688,7 +696,16 @@ void nic_tx_packet(uint16_t ring_ptr)
|
||||
len += 0xf;
|
||||
len >>= 3;
|
||||
SFR_NIC_CTRL = len;
|
||||
do { } while (SFR_NIC_CTRL != 0);
|
||||
/* Bounded wait: normally the NIC consumes the frame in microseconds, but
|
||||
* when the egress port is held in an MSTP non-forwarding state the ASIC
|
||||
* has been observed to never complete the TX - an unbounded spin here
|
||||
* then freezes the entire main loop (no STP/LACP timers, no HTTP, no
|
||||
* ARP) until a power cycle. Give up after ~65k polls and drop the frame:
|
||||
* losing one packet is recoverable, a frozen switch is not. */
|
||||
{
|
||||
uint16_t tx_guard = 0;
|
||||
do { } while (SFR_NIC_CTRL != 0 && ++tx_guard != 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user