Since f6afed726b00 the TX-hang check in iflib_timer() has required a
queue state other than IFLIB_QUEUE_IDLE, but nothing ever sets
IFLIB_QUEUE_WORKING, so IFLIB_QUEUE_HUNG has been unreachable ever
since: stalled TX queues are not detected, not reported, and not
reset - the TX watchdog of every iflib(4) driver has been dead code.
Instead of resurrecting the queue-state machine, detect the hang
directly, arming on growth rather than persistence: a queue becomes
suspect only when descriptors owed a completion writeback - beyond
the tail whose report-status request is still deferred - increased
across a timer period in which no completions were harvested, while
the link was up, no pause frames were received, and no doorbell was
pending. Once armed, the queue must stay frozen for
IFLIB_WDOG_ARMED_PERIODS consecutive periods; then ask the hardware
via the driver's read-only credits peek (isc_txd_credits_update with
clear=false, the same call the mp_ring can_drain callback makes
routinely): if completions are ready but were not harvested for this
long, the completion interrupt went missing - kick the queue's task
instead of resetting; if the hardware reported nothing although the
queue kept receiving work, it is hung and the existing watchdog
reset machinery takes over.
Neither software counters alone nor mere persistence of unharvested
work can make this decision. iflib reclaims lazily (up to
isc_tx_nsegments completed descriptors stay unharvested
indefinitely) and defers report-status requests, so "descriptors in
use" and "no cleaning progress" are normal states of an idle healthy
queue. And hardware with descriptor-writeback coalescing (e.g.
8254x, TXDCTL.WTHRESH) legitimately withholds the final writeback of
a quiet queue indefinitely, so a zero credits peek is a normal idle
state, not a hang indicator: persistence-armed detection reset
healthy interfaces on every traffic lull (field-tested on 82541PI).
Only growth of the writeback-owed count across frozen periods
separates a wedged queue from a coalescing one. The four-period
threshold is a threshold in time, not in device work: a period is
one iflib_timer interval (hz/2 by default), so the verdict falls
after roughly two seconds of frozen growth. It was calibrated from
counter traces on that old and slow hardware, where healthy
coalescing always cleared within two periods; newer hardware posts
completion writebacks far sooner and exits the frozen state earlier,
so the threshold needs no recalibration for more modern devices.
A queue whose link is down is never flagged - preserving what
f6afed726b00 fixed.
The IFLIB_QUEUE_* states no longer participate in the watchdog
decision; they will be removed in a followup commit.
PR: 220997, 239240
Fixes: f6afed726b00 ("iflib: Prevent watchdog from resetting idle queues")
Suggested by: gallatin (mxge-style detection)
MFC after: 1 month
Assisted-by: Claude Code (Fable 5)