Page MenuHomeFreeBSD

D58266.id183391.diff
No OneTemporary

D58266.id183391.diff

diff --git a/share/man/man4/iflib.4 b/share/man/man4/iflib.4
--- a/share/man/man4/iflib.4
+++ b/share/man/man4/iflib.4
@@ -1,4 +1,4 @@
-.Dd January 7, 2026
+.Dd August 4, 2026
.Dt IFLIB 4
.Os
.Sh NAME
@@ -128,6 +128,15 @@
Doing so usually increases the transmit throughput by reducing the number of
transmit interrupts.
Setting this to a non-zero value will disable the use of this feature.
+.It Va net.iflib.tx_watchdog_periods
+Number of consecutive
+.Va net.iflib.timer_default
+intervals for which a transmit queue must stay frozen,
+that is, hold descriptors the hardware has not reported as completed
+while none are reclaimed,
+before iflib asks the hardware whether it has completions pending.
+If it reports none, the interface is reset.
+Setting this to zero disables the check.
.El
.Pp
These
diff --git a/sys/net/iflib.c b/sys/net/iflib.c
--- a/sys/net/iflib.c
+++ b/sys/net/iflib.c
@@ -374,7 +374,7 @@
uint8_t ift_txd_size[8];
uint64_t ift_processed;
uint64_t ift_cleaned;
- uint64_t ift_cleaned_prev;
+ uint64_t ift_processed_prev;
#if MEMORY_LOGGING
uint64_t ift_enqueued;
uint64_t ift_dequeued;
@@ -404,9 +404,20 @@
#endif /* DEV_NETMAP */
if_txsd_vec_t ift_sds;
+ /*
+ * TX watchdog state, updated once per iflib_timer period. The
+ * period count saturates instead of wrapping, and is 16 bits
+ * wide so that it still reaches any value
+ * net.iflib.tx_watchdog_periods is plausibly set to; an 8-bit
+ * counter would silently disable the check for a threshold
+ * above 255.
+ */
+ qidx_t ift_outstanding_prev;
+ uint16_t ift_wdog_armed;
uint8_t ift_qstatus;
uint8_t ift_closed;
uint8_t ift_update_freq;
+ uint8_t ift_spare0; /* pad to the next pointer boundary */
struct iflib_filter_info ift_filter_info;
bus_dma_tag_t ift_buf_tag;
bus_dma_tag_t ift_tso_buf_tag;
@@ -569,6 +580,20 @@
static int iflib_timer_default = 1000;
SYSCTL_INT(_net_iflib, OID_AUTO, timer_default, CTLFLAG_RW,
&iflib_timer_default, 0, "number of ticks between iflib_timer calls");
+/*
+ * Consecutive timer periods a TX queue must stay frozen - see
+ * iflib_timer(), which defines that state - before the hardware is
+ * asked whether it has completions pending. Four periods is roughly
+ * two seconds with the default timer interval: a healthy queue on
+ * hardware that coalesces completion reports (e.g. 8254x,
+ * TXDCTL.WTHRESH) stays frozen for at most two (measured on 82541PI),
+ * a wedged one until it is reset.
+ */
+static int iflib_tx_watchdog_periods = 4;
+SYSCTL_INT(_net_iflib, OID_AUTO, tx_watchdog_periods, CTLFLAG_RWTUN,
+ &iflib_tx_watchdog_periods, 0,
+ "consecutive frozen timer periods before a TX queue is checked for "
+ "a hang (0 disables the check)");
#if IFLIB_DEBUG_COUNTERS
@@ -2403,25 +2428,89 @@
return;
/*
- ** Check on the state of the TX queue(s), this
- ** can be done without the lock because its RO
- ** and the HUNG state will be static if set.
- */
+ * Check on the state of the TX queue(s); this can be done
+ * without the lock: the counters the check reads are only
+ * advanced by the queue's tx task and a stale read just
+ * delays the verdict by one timer period.
+ */
if (this_tick - txq->ift_last_timer_tick >= iflib_timer_default) {
+ qidx_t outstanding;
+ bool frozen;
+
txq->ift_last_timer_tick = this_tick;
IFDI_TIMER(ctx, txq->ift_id);
- if ((txq->ift_qstatus == IFLIB_QUEUE_HUNG) &&
- ((txq->ift_cleaned_prev == txq->ift_cleaned) ||
- (sctx->isc_pause_frames == 0)))
- goto hung;
-
- if (txq->ift_qstatus != IFLIB_QUEUE_IDLE &&
- ifmp_ring_is_stalled(txq->ift_br)) {
- KASSERT(ctx->ifc_link_state == LINK_STATE_UP,
- ("queue can't be marked as hung if interface is down"));
- txq->ift_qstatus = IFLIB_QUEUE_HUNG;
+
+ /*
+ * Descriptors the hardware has not reported as
+ * completed: neither harvested as credits
+ * (ift_processed) nor reclaimed (ift_cleaned accounts
+ * the difference to ift_in_use). The tail whose
+ * report-status request is still deferred is never
+ * reported and must not count (ift_rs_pending
+ * over-counts it by one per packet).
+ */
+ outstanding = txq->ift_in_use -
+ (qidx_t)(txq->ift_processed - txq->ift_cleaned);
+
+ /*
+ * The queue is frozen while it has descriptors the
+ * hardware has not reported as completed and none
+ * were reclaimed over the period; the link must be
+ * up, with no pause frames and no pending doorbell
+ * (the laggard check below rings it).
+ *
+ * Being frozen is not a fault - the hardware may
+ * defer marking descriptors as completed
+ * indefinitely, and 8254x hardware does so for a
+ * quiet queue - therefore the check arms only when a
+ * frozen queue also takes on new work, and acts only
+ * once it has stayed frozen for
+ * net.iflib.tx_watchdog_periods consecutive periods.
+ */
+ frozen = outstanding > txq->ift_rs_pending &&
+ txq->ift_processed == txq->ift_processed_prev &&
+ txq->ift_db_pending == 0 &&
+ sctx->isc_pause_frames == 0 &&
+ ctx->ifc_link_state == LINK_STATE_UP;
+ if (!frozen)
+ txq->ift_wdog_armed = 0;
+ else if (txq->ift_wdog_armed > 0 ||
+ outstanding > txq->ift_outstanding_prev) {
+ if (txq->ift_wdog_armed < UINT16_MAX)
+ txq->ift_wdog_armed++;
}
- txq->ift_cleaned_prev = txq->ift_cleaned;
+
+ /*
+ * Frozen long enough: ask the hardware. Completions
+ * ready but unharvested for this long mean the
+ * completion interrupt went missing - kick the
+ * queue's task. Nothing ready, although the queue
+ * kept taking on work, means it is hung.
+ */
+ if (iflib_tx_watchdog_periods > 0 &&
+ txq->ift_wdog_armed >= iflib_tx_watchdog_periods) {
+ bus_dmamap_sync(txq->ift_ifdi->idi_tag,
+ txq->ift_ifdi->idi_map, BUS_DMASYNC_POSTREAD);
+ if (ctx->isc_txd_credits_update(ctx->ifc_softc,
+ txq->ift_id, false) == 0) {
+ device_printf(ctx->ifc_dev,
+ "Watchdog timeout (TX: %d desc "
+ "avail: %d pidx: %d) -- resetting\n",
+ txq->ift_id, TXQ_AVAIL(txq),
+ txq->ift_pidx);
+ STATE_LOCK(ctx);
+ if_setdrvflagbits(ctx->ifc_ifp,
+ IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
+ ctx->ifc_flags |=
+ (IFC_DO_WATCHDOG | IFC_DO_RESET);
+ iflib_admin_intr_deferred(ctx);
+ STATE_UNLOCK(ctx);
+ return;
+ }
+ GROUPTASK_ENQUEUE(&txq->ift_task);
+ }
+ txq->ift_outstanding_prev = outstanding;
+ txq->ift_processed_prev = txq->ift_processed;
}
/* handle any laggards */
if (txq->ift_db_pending)
@@ -2431,17 +2520,6 @@
if (if_getdrvflags(ctx->ifc_ifp) & IFF_DRV_RUNNING)
callout_reset_on(&txq->ift_timer, iflib_timer_default, iflib_timer,
txq, txq->ift_timer.c_cpu);
- return;
-
- hung:
- device_printf(ctx->ifc_dev,
- "Watchdog timeout (TX: %d desc avail: %d pidx: %d) -- resetting\n",
- txq->ift_id, TXQ_AVAIL(txq), txq->ift_pidx);
- STATE_LOCK(ctx);
- if_setdrvflagbits(ctx->ifc_ifp, IFF_DRV_OACTIVE, IFF_DRV_RUNNING);
- ctx->ifc_flags |= (IFC_DO_WATCHDOG | IFC_DO_RESET);
- iflib_admin_intr_deferred(ctx);
- STATE_UNLOCK(ctx);
}
static uint16_t
@@ -2625,6 +2703,9 @@
iflib_txsd_free(ctx, txq, j);
}
txq->ift_processed = txq->ift_cleaned = txq->ift_cidx_processed = 0;
+ txq->ift_processed_prev = 0;
+ txq->ift_outstanding_prev = 0;
+ txq->ift_wdog_armed = 0;
txq->ift_in_use = txq->ift_gen = txq->ift_no_desc_avail = 0;
if (sctx->isc_flags & IFLIB_PRESERVE_TX_INDICES)
txq->ift_cidx = txq->ift_pidx;

File Metadata

Mime Type
text/plain
Expires
Sat, Aug 15, 12:14 AM (21 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36770828
Default Alt Text
D58266.id183391.diff (7 KB)

Event Timeline