⬢ DragonFlyBSD Kernel Audit
DF-3990 / fix.diff
← back to finding ↓ download raw
--- 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) {