[Bug 298369] LinuxKPI/iwlwifi: TX task drains itself in lkpi_lsta_free, blocking unload and causing a deadlkres panic

From: <bugzilla-noreply_at_freebsd.org>
Date: Thu, 10 Sep 2026 16:32:31 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=298369

            Bug ID: 298369
           Summary: LinuxKPI/iwlwifi: TX task drains itself in
                    lkpi_lsta_free, blocking unload and causing a
                    deadlkres panic
           Product: Base System
           Version: 16.0-CURRENT
          Hardware: amd64
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: wireless
          Assignee: wireless@FreeBSD.org
          Reporter: oleglelchuk@gmail.com

My kernel dump shows a LinuxKPI Wi-Fi transmit task synchronously waiting for
its own completion during station destruction. It retains the device's wiphy
mutex, blocking scanning and driver unload. The deadlock watchdog eventually
panics after the scanner has waited for 30 minutes.

Observed incident and environment:

FreeBSD 16.0-CURRENT amd64, GENERIC, main-n288971-18a5549bcb81-dirty, built
September 8, 2026 at 21:32:59 EDT. The dump records September 10 at 11:15:14
EDT as its dump time and has status good. The installed wireless driver is
if_iwlwifi; its TX callback in the dump is iwl_mvm_mac_tx.

I no longer needed wireless networking and issued `kldunload if_iwlwifi`. The
command hung, but the machine remained reachable by SSH, apparently through my
RTL8156B Ethernet adapter. I restored local access and subsequently issued
`reboot`; the kernel panic followed. This is an account of one observed
incident, not a tested sequence that reliably reproduces the bug. I have not
reproduced it with an unmodified kernel.

My kernel includes a local combined patch, including asynchronous iwlwifi
hardware-recovery support. The relevant patch/source comparison and crash-time
counters are described below. An LLM analyzed my saved dump using kgdb,
matching installed kernel/debug symbols, and local and upstream source. No fix
has been implemented or tested for this incident.

The recorded panic is:

deadlres_td_sleep_q: possible deadlock detected for 0xfffff8010aaf7780
(iwlwifi0 net80211 t), blocked for 1800293 ticks

The dump has hz=1000 and slptime_threshold=1800. The panic therefore reports a
scanner wait of 1800.293 seconds. The scanner is a secondary victim of the
transmit task's self-deadlock.

The following is an abbreviated kgdb stack for the lock owner, TID 100075,
`kernel/thread taskq`. Frames are shown in debugger order, innermost first;
unrelated scheduler frames and optimized argument values are omitted:

taskqueue_drain(queue=0xfffff8004ac37a00, task=0xfffff8022f831820)
lkpi_lsta_free(lsta=0xfffff8022f831800, ni=0xfffffe019c7ea000)
lkpi_ic_node_free(ni=0xfffffe019c7ea000)
ieee80211_tx_complete(...)
_lkpi_ieee80211_free_txskb(...)
linuxkpi_ieee80211_free_txskb(...)
lkpi_80211_txq_tx_one(lsta=0xfffff8022f831800, ...)
lkpi_80211_txq_task(ctx=0xfffff8022f831800, ...)
taskqueue_run_locked(queue=0xfffff8004ac37a00)
taskqueue_thread_loop(...)

The same outer taskqueue_run_locked frame has:

tb_running = 0xfffff8022f831820
tb_canceling = true
tb_wanted = true

The address of &lsta->txq_task is also 0xfffff8022f831820. Thus the current
callback, the station's TX task, and the task being drained are the same
object. The worker sleeps with wmesg="tq_drain" on its own active taskqueue
bookkeeping at 0xfffffe00d7176e50.

The device's wiphy mutex is at 0xfffffe0188511080. Its sx_lock value is
0xfffff80002c38784; masking SX flag bits gives owner thread 0xfffff80002c38780,
which is TID 100075 above. The separate scanner, TID 102013, has
td_wchan=0xfffffe0188511080 and td_wmesg="&wiphy->mtx", with this stack:

_sx_xlock_hard
_sx_xlock
lkpi_ic_scan_start
scan_start
taskqueue_run_locked
taskqueue_thread_loop

The source explains the cycle. lkpi_80211_txq_tx_one holds wiphy across
lkpi_80211_mo_tx. Packet disposal reaches ieee80211_tx_complete, whose final
node-reference release invokes lkpi_ic_node_free and lkpi_lsta_free. The latter
attempts to cancel and drain lsta->txq_task. Because the callback itself is
currently executing that task, cancellation finds it busy and the drain cannot
complete. The callback cannot return while waiting inside its own destruction
path.

The saved packet has frame_control=0xc0 (outgoing deauthentication), a STATION
interface, and TX flags 0x20 without TX_OFFCHAN (0x10). lsta->added_to_drv is
false. These fields satisfy iwl_mvm_mac_tx's ordinary synchronous
management-frame drop condition. Compiler-elided driver frames mean the exact
historical choice among earlier drop guards is not directly recorded. The
subsequent packet disposal and task self-wait are directly established.

The kldunload thread is waiting for the same wiphy mutex in lkpi_ic_vap_delete,
called through ieee80211_ifdetach, linuxkpi_ieee80211_unregister_hw, and
iwlwifi device/module teardown. Its particular mutex wait began 147.442 seconds
after the TX worker's current wait began. This does not establish when I issued
the unload command: the unload may have initiated teardown that exposed the
problem. The dump does not establish that reboot created the deadlock; its
recorded panic trigger is the watchdog.

Local patch qualification and upstream comparison:

- The central functions lkpi_lsta_free, lkpi_ic_node_free,
lkpi_80211_txq_tx_one, _lkpi_ieee80211_free_txskb, and
linuxkpi_ieee80211_free_txskb are byte-identical to the unpatched base revision
18a5549bcb81. The scan lock acquisition, net80211 completion/reference code,
taskqueue code, and iwlwifi mvm/mac80211.c are also unchanged by the local
patch.
- The local recovery implementation's restart_requests, restart_successes, and
restart_failures are all zero. restart_work.work_queue=NULL and state=0: no
accepted hardware-recovery/replay work ran for this hardware instance.
- restart_flags=24 means STOPPED|QUIESCED, consistent with the observed
unload/teardown stack. Patch-added teardown hooks did execute. This does not
exclude indirect timing effects of the local patch or claim a stock-kernel
reproduction.
- Upstream main checked September 10 at
d55eb2bed6a7d8b55cfc120ccd535ca798175764 still has the same relevant LinuxKPI
and iwlwifi files as the panic's upstream base. The station cleanup loop
remains here:
 
https://github.com/freebsd/freebsd-src/blob/d55eb2bed6a7d8b55cfc120ccd535ca798175764/sys/compat/linuxkpi/common/src/linux_80211.c#L1163

The drain loop was introduced in 0936c648ad0ee5152dc19f261e77fe9c1833fe05
(review D43753). This is historical context, not a claim about the first
release in which this exact sequence became possible.

Related commit 3c38dce87ecd2c87744e4b7ff1904ee841f88a47 fixes final
node-reference release under wiphy during state teardown. It is already present
in this kernel and does not change the observed TX-task self-drain. Bug 279717
concerns firmware recovery; bug 208860 has a different net80211 taskqueue wait
chain. Searches of Bugzilla, GitHub freebsd-src, and reviews.freebsd.org found
no exact report or applicable proposed fix for this self-drain, but a duplicate
under different wording may exist.

Expected behavior is that station cleanup and driver unload complete without
synchronously waiting for the currently executing task itself. Merely releasing
wiphy would leave the self-wait intact; simply skipping the drain would need a
lifetime audit because the transmit function accesses lsta after the driver
callback returns. This is a diagnosis, not a proposed or validated code fix.

I have retained the full vmcore locally. The essential task/lock identities and
abbreviated stacks are included above; no packet payloads, credentials, or
unrelated process memory are included in this report.

-- 
You are receiving this mail because:
You are the assignee for the bug.