DF-3990 / fix.diff
--- a/sys/dev/netif/ale/if_ale.c +++ b/sys/dev/netif/ale/if_ale.c @@ -2023,7 +2023,14 @@ sc->ale_cdata.ale_tx_cmb_map, BUS_DMASYNC_POSTREAD); prod = *sc->ale_cdata.ale_tx_cmb & TPD_CNT_MASK; } else - prod = CSR_READ_2(sc, ALE_TPD_CONS_IDX); + /* + * DF-3990: TPD_CONS_IDX is a 16-bit device register. + * Mask it into the ring so a garbage value cannot walk + * cons past every queued descriptor (over-reclaim of + * mbufs the NIC still owns). + */ + prod = CSR_READ_2(sc, ALE_TPD_CONS_IDX) & + (ALE_TX_RING_CNT - 1); cons = sc->ale_cdata.ale_tx_cons; /* * Go through our Tx list and free mbufs for those @@ -2159,6 +2166,15 @@ uint32_t length, prod, seqno, status, vtags; int prog; + /* + * DF-3990 (DF-3761/3778/3934 family): bound the work done per + * interrupt so a line-rate flood (or faulty device keeping the + * CMB producer ahead) cannot spin this handler forever holding + * ifp->if_serializer. ALE_PROC_DEFAULT was defined for exactly + * this and never wired up. + */ + int ale_rx_budget = ALE_PROC_DEFAULT; + rx_page = &sc->ale_cdata.ale_rx_page[sc->ale_cdata.ale_rx_curp]; bus_dmamap_sync(rx_page->cmb_tag, rx_page->cmb_map, BUS_DMASYNC_POSTREAD); @@ -2177,6 +2193,11 @@ for (prog = 0; ; prog++) { if (rx_page->cons >= prod) break; + if (prog >= ale_rx_budget) { + /* Leave the rest for the next interrupt; the IM + * timer / INTR_RX_PKT will re-arm us. */ + break; + } rs = (struct rx_rs *)(rx_page->page_addr + rx_page->cons); seqno = ALE_RX_SEQNO(le32toh(rs->seqno)); if (sc->ale_cdata.ale_rx_seqno != seqno) { |