diff options
| author | Nick Price <nick@spun.io> | 2026-07-19 16:40:46 +0000 |
|---|---|---|
| committer | Adrian Chadd <adrian@FreeBSD.org> | 2026-07-19 16:40:46 +0000 |
| commit | 73cd4048807dc0b3a5329bfc9a80ea4bf2d975fd (patch) | |
| tree | 4d5669d47bda4261f06b8c099f59c0d09975608b | |
| parent | dcede1ec9de3b24fc9fbeeebc99ca34d226995f9 (diff) | |
aq(4): drop errored RX frames instead of resetting the interface
aq_isc_rxd_pkt_get() returned EBADMSG when a receive descriptor's
MAC/receive-error bit (rx_stat bit 0) was set. iflib treats any error
from isc_rxd_pkt_get() as a fatal ring fault and answers with
IFC_DO_RESET -- a full interface reinitialization. A per-frame receive
error is not a ring fault: on a marginal link or cable the Atlantic
delivers errored frames continuously, so each one triggered another
reset and the interface reset-stormed itself into carrying no traffic
instead of merely dropping the bad frames.
The Atlantic delivers errored frames to the host by design (Linux drops
them in software via buff->is_error), and iflib offers no per-frame
error return that isn't a reset. Follow the vmxnet3 model: on a receive
error zero the fragment lengths and return success. iflib then discards
the packet (assemble_segments() excludes zero-length fragments) while
still recycling the descriptors through the refill path -- no reset.
Also drop frames flagged with an RX-DMA fault (rdm_err), not just the
MAC-error bit; and keep iri_len non-zero on that drop path, since iflib
asserts iri_len != 0.
The genuinely structural errors -- more segments than isc_rx_nsegments,
or a pkt_len inconsistent with the descriptor count -- still return
EBADMSG, since those indicate a confused ring where a reset is the right
recovery.
Reviewed by: adrian
Differential Revision: https://reviews.freebsd.org/D58136
| -rw-r--r-- | sys/dev/aq/aq_ring.c | 26 |
1 files changed, 21 insertions, 5 deletions
diff --git a/sys/dev/aq/aq_ring.c b/sys/dev/aq/aq_ring.c index 8307fb0b3474..6afec86c63bc 100644 --- a/sys/dev/aq/aq_ring.c +++ b/sys/dev/aq/aq_ring.c @@ -333,11 +333,13 @@ aq_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri) if_t ifp; int cidx, rc = 0, i; size_t len, total_len; + bool is_error = false; AQ_DBG_ENTERA("[%d] start=%d", ring->index, ri->iri_cidx); cidx = ri->iri_cidx; ifp = iflib_get_ifp(aq_dev->ctx); i = 0; + total_len = 0; do { if (i >= aq_dev->sctx->isc_rx_nsegments) @@ -348,11 +350,15 @@ aq_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri) trace_aq_rx_descr(ring->index, cidx, (volatile uint64_t *)rx_desc); - if ((rx_desc->wb.rx_stat & BIT(0)) != 0) - goto rx_err; + /* MAC error (rx_stat) or RX-DMA fault (rdm_err) -> drop. */ + if ((rx_desc->wb.rx_stat & BIT(0)) != 0 || rx_desc->wb.rdm_err) + is_error = true; if (!rx_desc->wb.eop) { len = ring->rx_buf_size; + } else if (is_error) { + total_len = le32toh(rx_desc->wb.pkt_len); + len = 0; } else { total_len = le32toh(rx_desc->wb.pkt_len); if (total_len < (size_t)i * ring->rx_buf_size) @@ -375,6 +381,19 @@ aq_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri) cidx = aq_next(cidx, ring->rx_size - 1); } while (!rx_desc->wb.eop); + ri->iri_nfrags = i; + ri->iri_len = total_len; + + /* Per-frame RX error: drop (zero-length frags), don't reset. */ + if (is_error) { + counter_u64_add(ring->stats.rx_err, 1); + for (i = 0; i < ri->iri_nfrags; i++) + ri->iri_frags[i].irf_len = 0; + if (ri->iri_len == 0) + ri->iri_len = 1; /* iflib asserts iri_len != 0 */ + goto exit; + } + if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0) { aq_rx_set_cso_flags(rx_desc, ri); } @@ -383,9 +402,6 @@ aq_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri) ri->iri_flowid = le32toh(rx_desc->wb.rss_hash); } - ri->iri_len = total_len; - ri->iri_nfrags = i; - counter_u64_add(ring->stats.rx_bytes, total_len); counter_u64_add(ring->stats.rx_pkts, 1); goto exit; |
