Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F166412189
D58266.id183099.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
7 KB
Referenced Files
None
Subscribers
None
D58266.id183099.diff
View Options
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 July 30, 2026
.Dt IFLIB 4
.Os
.Sh NAME
@@ -128,6 +128,14 @@
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 appear frozen
+before iflib checks it for a transmit hang.
+If the hardware then reports no pending completions,
+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
@@ -312,6 +312,10 @@
/* The minimum descriptors per second before we start coalescing */
#define IFLIB_MIN_DESC_SEC 16384
#define IFLIB_DEFAULT_TX_UPDATE_FREQ 16
+/*
+ * The queue states below and ift_qstatus no longer participate in
+ * the TX watchdog decision and are scheduled for removal.
+ */
#define IFLIB_QUEUE_IDLE 0
#define IFLIB_QUEUE_HUNG 1
#define IFLIB_QUEUE_WORKING 2
@@ -374,7 +378,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 +408,26 @@
#endif /* DEV_NETMAP */
if_txsd_vec_t ift_sds;
+ /*
+ * TX watchdog state, updated once per iflib_timer period: the
+ * writeback-owed descriptor count at the end of the previous
+ * period, and the number of consecutive frozen periods since
+ * the queue armed. 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. These two fields and ift_spare0 below
+ * live here rather than with the counters they are derived
+ * from because that region is packed: an insertion there adds
+ * padding, while here it consumes padding this struct already
+ * had.
+ */
+ 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 +590,21 @@
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 iflib_timer periods for which a suspect TX queue must
+ * stay frozen before it is declared hung. A healthy queue on
+ * hardware with descriptor-writeback coalescing (e.g. 8254x,
+ * TXDCTL.WTHRESH) stays frozen for at most two periods (measured on
+ * 82541PI); a wedged queue stays frozen until it is reset. This is
+ * a threshold in time, not in device work: with the default timer
+ * interval the verdict falls after roughly two seconds. Zero
+ * disables the check.
+ */
+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 declared hung "
+ "(0 disables the check)");
#if IFLIB_DEBUG_COUNTERS
@@ -2403,25 +2439,81 @@
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 handed to the hardware for which a
+ * completion writeback is still owed: those 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 (ift_rs_pending over-counts it by one per
+ * packet) never writes back and must not count as
+ * owed.
+ */
+ outstanding = txq->ift_in_use -
+ (qidx_t)(txq->ift_processed - txq->ift_cleaned);
+
+ /*
+ * A period is frozen if writeback-owed work exists
+ * and no completions were harvested since the last
+ * period, with the link up, no pause frames seen,
+ * and no doorbell pending (the laggard check below
+ * rings it).
+ *
+ * Frozen alone is a normal state: hardware with
+ * descriptor-writeback coalescing (e.g. 8254x,
+ * TXDCTL.WTHRESH) legitimately withholds the final
+ * report-status writeback of a quiet queue
+ * indefinitely, so a constant owed tail must never
+ * trigger the watchdog. Only a queue that received
+ * new work across a frozen period (the owed count
+ * grew) arms, and the verdict waits until 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;
+
+ /*
+ * Armed long enough - ask the hardware: if
+ * completions are ready but nobody harvested them
+ * for this long, the completion interrupt went
+ * missing - kick the queue's task instead of
+ * resetting; if it reported nothing although the
+ * owed count grew, the queue 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)
+ goto hung;
+ 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)
@@ -2625,6 +2717,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;
txq->ift_npending = txq->ift_db_pending = 0;
txq->ift_rs_pending = 0;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Aug 14, 12:06 PM (9 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36702815
Default Alt Text
D58266.id183099.diff (7 KB)
Attached To
Mode
D58266: iflib: restore TX watchdog functionality
Attached
Detach File
Event Timeline
Log In to Comment