# DF-2870 — taskqueue_free() terminate lost-wakeup → permanent deadlock

## What
`taskqueue_thread_loop()` (subr_taskqueue.c:617-619) parks in
`TQ_SLEEP(tq, tq, "tqthr")` after `taskqueue_run()` **without re-checking
TQ_FLAGS_ACTIVE**. `taskqueue_terminate()` (subr_taskqueue.c:141-147)
delivers `wakeup(tq)` after dropping the queue spinlock; if the worker is
executing a task function at that moment (not on the sleep queue), the wakeup
is lost. When the func returns, the worker parks unconditionally, the
terminator is asleep on `tq_threads`, and nobody is left to wake either —
`taskqueue_free()` never returns and the worker thread is leaked.

Trigger: `taskqueue_free()` called while any task of the queue is in flight
(dequeued, ta_func executing). Every in-tree `taskqueue_free()` consumer
(wlan ieee80211.c:464, ~15 network/storage drivers) reaches this window when
tearing down a busy queue.

## Build (on the guest as root)
same out-of-tree KLD pattern: see build.sh

## Run
```
kldload /root/poc/tqdead/tqdead.ko
sleep 15
dmesg | grep tqdead
ps -axH -o stat,wchan,comm | grep -E "tqdead"
```

## Expected output (stock kernel — reproduced)
```
tqdead: queue 0xfffff8008da44e88 started (0)
tqdead: task in-flight; calling taskqueue_free()...
tqdead: releasing in-flight task func
tqdead: RESULT: DEADLOCK CONFIRMED - taskqueue_free() stuck for 10+ seconds;
        ctrl_td=0xfffff8008df45b80 (wchan 0xfffff8008d560c80), worker parked forever
--- ps ---
B2     taskqueu tqdeadctrl     <- asleep in "taskqueue_terminate" forever
B4     tqthr    tqdead         <- worker parked forever
```
100% deterministic (no timing race needed — the harness blocks the func
until after free() has parked).

NOTE: the wedged module cannot be unloaded; reset the guest afterwards.

## Fixed kernel
worker re-checks TQ_FLAGS_ACTIVE before parking → `taskqueue_free RETURNED
(not reproduced)` and the module unloads cleanly (see run.fixed.log).
