nic: report a transfer that does not complete instead of carrying on

The bounded waits were silent: on timeout the code went straight back to
its caller, and handle_rx() then read a frame the DMA may never have
delivered, which is worse than waiting longer.

Each of the three transfers now says so on the console, the two RX ones
report failure to handle_rx(), and handle_rx() acknowledges the packet
and gives up on it rather than parsing whatever is in the buffer. The
guard variable moved to the top of its function, so the block that held
it and its indentation are gone.
This commit is contained in:
d00f
2026-08-19 16:20:34 +02:00
parent 803d9c1242
commit 62f80f0ad2
+32 -18
View File
@@ -621,17 +621,21 @@ void get_random_32(void)
* data will be stored in the rx_header structure * data will be stored in the rx_header structure
* len is the length of data to be transferred * len is the length of data to be transferred
*/ */
void nic_rx_header(uint16_t ring_ptr) bool nic_rx_header(uint16_t ring_ptr)
{ {
uint16_t buffer = (uint16_t) &rx_headers[0]; uint16_t buffer = (uint16_t) &rx_headers[0];
uint16_t guard = 0;
SFR_NIC_DATA_U16LE = buffer; SFR_NIC_DATA_U16LE = buffer;
SFR_NIC_RING_U16LE = ring_ptr; SFR_NIC_RING_U16LE = ring_ptr;
SFR_NIC_CTRL = 1; SFR_NIC_CTRL = 1;
/* Bounded, cf. nic_tx_packet: a stuck NIC DMA must not freeze the loop */ while (SFR_NIC_CTRL != 0) {
{ if (++guard == 0) {
uint16_t rx_guard = 0; print_string("NIC: RX header transfer did not complete\n");
do { } while (SFR_NIC_CTRL != 0 && ++rx_guard != 0); return false;
} }
}
return true;
} }
@@ -641,8 +645,10 @@ void nic_rx_header(uint16_t ring_ptr)
* data will be returned in the xmem buffer points to * data will be returned in the xmem buffer points to
* ring_ptr is the current position of the RX Ring on the ASIC side * ring_ptr is the current position of the RX Ring on the ASIC side
*/ */
void nic_rx_packet(register uint16_t buffer, register uint16_t ring_ptr) bool nic_rx_packet(register uint16_t buffer, register uint16_t ring_ptr)
{ {
uint16_t guard = 0;
SFR_NIC_DATA_U16LE = buffer; SFR_NIC_DATA_U16LE = buffer;
SFR_NIC_RING_U16LE = ring_ptr; SFR_NIC_RING_U16LE = ring_ptr;
@@ -654,11 +660,13 @@ void nic_rx_packet(register uint16_t buffer, register uint16_t ring_ptr)
print_short(len); print_short(len);
#endif #endif
SFR_NIC_CTRL = len; SFR_NIC_CTRL = len;
/* Bounded, cf. nic_tx_packet: a stuck NIC DMA must not freeze the loop */ while (SFR_NIC_CTRL != 0) {
{ if (++guard == 0) {
uint16_t rx_guard = 0; print_string("NIC: RX transfer did not complete\n");
do { } while (SFR_NIC_CTRL != 0 && ++rx_guard != 0); return false;
} }
}
return true;
} }
@@ -668,6 +676,7 @@ void nic_rx_packet(register uint16_t buffer, register uint16_t ring_ptr)
void nic_tx_packet(uint16_t ring_ptr) void nic_tx_packet(uint16_t ring_ptr)
{ {
uint16_t len; uint16_t len;
uint16_t guard = 0;
/* If we have a management VLAN, we have inserted a dot1Q-tag into the frame and /* If we have a management VLAN, we have inserted a dot1Q-tag into the frame and
* the frame starts at the beginning of uip_buf with the RTL TX descriptor, * the frame starts at the beginning of uip_buf with the RTL TX descriptor,
@@ -702,12 +711,11 @@ void nic_tx_packet(uint16_t ring_ptr)
len += 0xf; len += 0xf;
len >>= 3; len >>= 3;
SFR_NIC_CTRL = len; SFR_NIC_CTRL = len;
/* Bounded wait: the NIC normally consumes the frame in microseconds, and while (SFR_NIC_CTRL != 0) {
* an unbounded spin here would freeze the main loop for good if it ever if (++guard == 0) {
* did not. Dropping one frame is recoverable, a frozen switch is not. */ print_string("NIC: TX transfer did not complete\n");
{ return;
uint16_t tx_guard = 0; }
do { } while (SFR_NIC_CTRL != 0 && ++tx_guard != 0);
} }
} }
@@ -1133,7 +1141,10 @@ void handle_rx(void)
uint16_t ring_ptr = ((uint16_t)sfr_data[2]) << 8; uint16_t ring_ptr = ((uint16_t)sfr_data[2]) << 8;
ring_ptr |= sfr_data[3]; ring_ptr |= sfr_data[3];
ring_ptr <<= 3; ring_ptr <<= 3;
nic_rx_header(ring_ptr); if (!nic_rx_header(ring_ptr)) {
REG_SET(RTL837X_REG_NIC_RXCMD, 1);
return;
}
#ifdef RXTXDBG #ifdef RXTXDBG
__xdata uint8_t *ptr = rx_headers; __xdata uint8_t *ptr = rx_headers;
print_string("RX on port "); print_byte(rx_headers[3] & 0xf); print_string("RX on port "); print_byte(rx_headers[3] & 0xf);
@@ -1143,7 +1154,10 @@ void handle_rx(void)
write_char(' '); write_char(' ');
} }
#endif #endif
nic_rx_packet((uint16_t) &uip_buf[0], ring_ptr + 8); if (!nic_rx_packet((uint16_t) &uip_buf[0], ring_ptr + 8)) {
REG_SET(RTL837X_REG_NIC_RXCMD, 1);
return;
}
#ifdef RXTXDBG #ifdef RXTXDBG
print_string("\n<< "); print_string("\n<< ");