# DF-4918 — drain_workqueue() sleeps holding worker_lock → permanent kernel-thread/workqueue deadlock

`sys/dev/drm/linux_workqueue.c:276-281`:

```c
lockmgr(&worker->worker_lock, LK_EXCLUSIVE);
while (!STAILQ_EMPTY(&worker->ws_list_head)) {
        tsleep(&drain_workqueue, 0, "wkdrain", 1);   /* plain tsleep */
}
lockmgr(&worker->worker_lock, LK_RELEASE);
```

Plain `tsleep()` does **not** release held locks (only `lksleep()` does).
The worker thread needs that very lock to dequeue and run the remaining
works (`process_all_work()` re-acquires at linux_workqueue.c:91), so once
`drain_workqueue()` acquires the lock of a worker whose queue is
non-empty:

* the drainer loops forever, one tick at a time, **holding the lock**;
* the worker blocks forever in `lockmgr()` (wchan `lwq`);
* every later `queue_work()` on the same workqueue blocks forever at
  linux_workqueue.c:134 — cascading wedge of arbitrary kernel threads.

Distinct from DF-1977 ("drain does not wait for in-flight works" — returns
too early): here drain **never returns** and permanently wedges the wq.
Root cause is different (sleep-with-lock-held vs missing in-flight wait).

Reached in-tree from `destroy_workqueue()` (linux_workqueue.c:246) — every
DRM driver teardown — and from direct `drain_workqueue()` callers, with
any works still pending on the queue at drain time.

## Build (guest, root)

```sh
kldload drm                # workqueue shim lives in drm.ko
cd /tmp/df4918 && make
```

## Run

```sh
kldload /tmp/df4918/df4918.ko
sysctl hw.df4918_test=1    # ~16s; prints per-second state
sysctl hw.df4918_status    # re-print frozen state
```

## Expected

Unpatched (baseline run.log / ps output):

```
[DF4918] t+ 3s..14s: drain_done=0 filler_done=0 slow_done=1 probe_done=1
[DF4918] DEADLOCK CONFIRMED: drain_workqueue() never returned, ...
ps -ax -o wchan:   lwq      B1  df4918wq      <- worker blocked on worker_lock
                  wkdrain  B2  df4918drain    <- drainer asleep HOLDING it
```

Patched (run_fixed.log): `drain_done=1 filler_done=1` within seconds,
guest healthy.

The wedge is permanent (threads park; module refuses unload) — reset the
guest afterwards: `vm.sh reset with-src`.
