aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/igc/if_igc.c
diff options
context:
space:
mode:
authorKevin Bowling <kbowling@FreeBSD.org>2026-07-25 23:57:42 +0000
committerKevin Bowling <kbowling@FreeBSD.org>2026-08-02 04:26:34 +0000
commit7db1936ca0df93ed2db2fc42e9619e33222202a8 (patch)
tree15867e98834e36872cded1744ab6fbdfc1fc5d58 /sys/dev/igc/if_igc.c
parentd182de314510e3e0cfee05753fb7babe4c01a758 (diff)
igc: make AIM counter sampling coherent
Sample free-running counters by delta instead of clearing them from the interrupt filter, which can race their producers. Publish byte and packet counts together at the TX and RX doorbells so each sample is coherent. Aggregate every TX ring assigned to the interrupt vector so unequal RX and TX queue counts are safe. Count RX bytes only after a frame is accepted. (cherry picked from commit 2290ea7f4311e899019fe77bf7c7775033af6b24)
Diffstat (limited to 'sys/dev/igc/if_igc.c')
-rw-r--r--sys/dev/igc/if_igc.c97
1 files changed, 72 insertions, 25 deletions
diff --git a/sys/dev/igc/if_igc.c b/sys/dev/igc/if_igc.c
index 8c9ec0566a38..78a97660649c 100644
--- a/sys/dev/igc/if_igc.c
+++ b/sys/dev/igc/if_igc.c
@@ -144,7 +144,7 @@ static int igc_get_rs(SYSCTL_HANDLER_ARGS);
static void igc_print_debug_info(struct igc_softc *);
static int igc_is_valid_ether_addr(u8 *);
static void igc_neweitr(struct igc_softc *, struct igc_rx_queue *,
- struct tx_ring *, struct rx_ring *);
+ struct rx_ring *);
static int igc_sysctl_tso_tcp_flags_mask(SYSCTL_HANDLER_ARGS);
/* Management and WOL Support */
static void igc_get_hw_control(struct igc_softc *);
@@ -893,6 +893,47 @@ igc_if_init(if_ctx_t ctx)
igc_set_eee_i225(&sc->hw, true, true, true);
}
+/*
+ * RX publishes its byte and packet counters as one snapshot when iflib
+ * returns descriptors to hardware. This also covers watchdog-driven RX
+ * processing, which can run while the interrupt vector is unmasked.
+ */
+static __inline void
+igc_aim_rx_delta(struct rx_ring *rxr, u32 *bytes, u32 *packets)
+{
+ uint64_t snapshot;
+ u32 now_bytes, now_packets;
+
+ snapshot = atomic_load_acq_64(&rxr->rx_aim_snapshot);
+ now_bytes = snapshot >> 32;
+ now_packets = (u32)snapshot;
+ *bytes = now_bytes - rxr->rx_bytes_last;
+ *packets = now_packets - rxr->rx_packets_last;
+ rxr->rx_bytes_last = now_bytes;
+ rxr->rx_packets_last = now_packets;
+}
+
+/*
+ * TX publishes its byte and packet counters as one snapshot at the doorbell,
+ * because encapsulation can overlap the interrupt filter. The two halves
+ * remain independent free running u32 counters, so their deltas are correct
+ * across wrap.
+ */
+static __inline void
+igc_aim_tx_delta(struct tx_ring *txr, u32 *bytes, u32 *packets)
+{
+ uint64_t snapshot;
+ u32 now_bytes, now_packets;
+
+ snapshot = atomic_load_acq_64(&txr->tx_aim_snapshot);
+ now_bytes = snapshot >> 32;
+ now_packets = (u32)snapshot;
+ *bytes = now_bytes - txr->tx_bytes_last;
+ *packets = now_packets - txr->tx_packets_last;
+ txr->tx_bytes_last = now_bytes;
+ txr->tx_packets_last = now_packets;
+}
+
enum eitr_latency_target {
eitr_latency_disabled = 0,
eitr_latency_lowest = 1,
@@ -906,16 +947,32 @@ enum eitr_latency_target {
*********************************************************************/
static void
igc_neweitr(struct igc_softc *sc, struct igc_rx_queue *que,
- struct tx_ring *txr, struct rx_ring *rxr)
+ struct rx_ring *rxr)
{
struct igc_hw *hw = &sc->hw;
- unsigned long bytes, bytes_per_packet, packets;
- unsigned long rxbytes, rxpackets, txbytes, txpackets;
+ struct igc_tx_queue *tx_que;
+ u32 bytes, bytes_per_packet, packets;
+ u32 ringbytes, ringpackets, rxbytes, rxpackets, txbytes, txpackets;
u32 neweitr;
u8 nextlatency;
+ int i;
- rxbytes = atomic_load_long(&rxr->rx_bytes);
- txbytes = atomic_load_long(&txr->tx_bytes);
+ igc_aim_rx_delta(rxr, &rxbytes, &rxpackets);
+
+ /*
+ * A vector can service more than one TX ring when iflib is configured
+ * with unequal RX and TX queue counts. Sample every ring routed to
+ * this vector rather than treating the vector as a TX queue index.
+ */
+ txbytes = txpackets = 0;
+ for (i = 0; i < sc->tx_num_queues; i++) {
+ tx_que = &sc->tx_queues[i];
+ if (tx_que->msix != que->msix)
+ continue;
+ igc_aim_tx_delta(&tx_que->txr, &ringbytes, &ringpackets);
+ txbytes += ringbytes;
+ txpackets += ringpackets;
+ }
/* Idle, do nothing */
if (txbytes == 0 && rxbytes == 0)
@@ -938,15 +995,13 @@ igc_neweitr(struct igc_softc *sc, struct igc_rx_queue *que,
goto igc_set_next_eitr;
}
- bytes = bytes_per_packet = 0;
+ bytes = bytes_per_packet = packets = 0;
/* Get largest values from the associated tx and rx ring */
- txpackets = atomic_load_long(&txr->tx_packets);
if (txpackets != 0) {
bytes = txbytes;
bytes_per_packet = txbytes / txpackets;
packets = txpackets;
}
- rxpackets = atomic_load_long(&rxr->rx_packets);
if (rxpackets != 0) {
bytes = lmax(bytes, rxbytes);
bytes_per_packet =
@@ -1048,7 +1103,6 @@ igc_intr(void *arg)
struct igc_softc *sc = arg;
struct igc_hw *hw = &sc->hw;
struct igc_rx_queue *que = &sc->rx_queues[0];
- struct tx_ring *txr = &sc->tx_queues[0].txr;
struct rx_ring *rxr = &que->rxr;
if_ctx_t ctx = sc->ctx;
u32 reg_icr;
@@ -1081,13 +1135,7 @@ igc_intr(void *arg)
if (reg_icr & IGC_ICR_RXO)
sc->rx_overruns++;
- igc_neweitr(sc, que, txr, rxr);
-
- /* Reset state */
- txr->tx_bytes = 0;
- txr->tx_packets = 0;
- rxr->rx_bytes = 0;
- rxr->rx_packets = 0;
+ igc_neweitr(sc, que, rxr);
return (FILTER_SCHEDULE_THREAD);
}
@@ -1122,18 +1170,11 @@ igc_msix_que(void *arg)
{
struct igc_rx_queue *que = arg;
struct igc_softc *sc = que->sc;
- struct tx_ring *txr = &sc->tx_queues[que->msix].txr;
struct rx_ring *rxr = &que->rxr;
++que->irqs;
- igc_neweitr(sc, que, txr, rxr);
-
- /* Reset state */
- txr->tx_bytes = 0;
- txr->tx_packets = 0;
- rxr->rx_bytes = 0;
- rxr->rx_packets = 0;
+ igc_neweitr(sc, que, rxr);
return (FILTER_SCHEDULE_THREAD);
}
@@ -2027,6 +2068,9 @@ igc_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs,
/* Set up some basics */
struct tx_ring *txr = &que->txr;
+ KASSERT(__is_aligned(&txr->tx_aim_snapshot, sizeof(uint64_t)),
+ ("%s: misaligned TX AIM snapshot %p", __func__,
+ &txr->tx_aim_snapshot));
txr->sc = que->sc = sc;
que->me = txr->me = i;
@@ -2079,6 +2123,9 @@ igc_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs,
for (i = 0, que = sc->rx_queues; i < nrxqsets; i++, que++) {
/* Set up some basics */
struct rx_ring *rxr = &que->rxr;
+ KASSERT(__is_aligned(&rxr->rx_aim_snapshot, sizeof(uint64_t)),
+ ("%s: misaligned RX AIM snapshot %p", __func__,
+ &rxr->rx_aim_snapshot));
rxr->sc = que->sc = sc;
rxr->que = que;
que->me = rxr->me = i;