⬢ DragonFlyBSD Kernel Audit
DF-3796 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/virtual/amazon/ena/ena.c b/sys/dev/virtual/amazon/ena/ena.c
index 97aed1c..46f6fb8 100644
--- a/sys/dev/virtual/amazon/ena/ena.c
+++ b/sys/dev/virtual/amazon/ena/ena.c
@@ -1058,12 +1058,17 @@ ena_free_rx_bufs(struct ena_adapter *adapter, unsigned int qid)
 	struct ena_ring *rx_ring = &adapter->rx_ring[qid];
 	unsigned int i;
 
+	/* DF-3796: the deferred RX cleanup task may be running concurrently
+	 * (it takes this lock); serialize buffer freeing with it, exactly
+	 * like ena_free_tx_bufs() does for the TX side. */
+	ENA_RING_MTX_LOCK(rx_ring);
 	for (i = 0; i < rx_ring->ring_size; i++) {
 		struct ena_rx_buffer *rx_info = &rx_ring->rx_buffer_info[i];
 
 		if (rx_info->mbuf != NULL)
 			ena_free_rx_mbuf(adapter, rx_ring, rx_info);
 	}
+	ENA_RING_MTX_UNLOCK(rx_ring);
 }
 
 /**
@@ -2610,6 +2615,20 @@ ena_down(struct ena_adapter *adapter)
 
 		callout_drain(&adapter->timer_service);
 
+		/*
+		 * DF-3796: drain any pending/running deferred RX cleanup tasks
+		 * before tearing down the IO queues and buffers they operate on.
+		 * Only the (now-drained) timer enqueues them, so they cannot
+		 * reappear after this point.
+		 */
+		for (int i = 0; i < adapter->num_queues; i++) {
+			struct ena_ring *rx_ring = &adapter->rx_ring[i];
+			while (taskqueue_cancel(rx_ring->cmpl_tq,
+			    &rx_ring->cmpl_task, NULL) != 0)
+				taskqueue_drain(rx_ring->cmpl_tq,
+				    &rx_ring->cmpl_task);
+		}
+
 		adapter->up = false;
 		ifq_set_oactive(&adapter->ifp->if_snd);
 		adapter->ifp->if_flags &= ~IFF_RUNNING;