# DF-5182 — wait_event family lost wakeup (check-before-interlock + unconditional autoremove unlink)

**File:** `sys/dev/drm/linux_wait.c` (+ macro in `sys/dev/drm/include/linux/wait.h:110-169`)
**Severity:** Medium — spurious timeouts / stalled waits in drm sync paths
**Verdict:** REPRODUCED (deterministic instantiation, 9/9 windowed rounds)

## What this is

A KLD module (`lwqdemo.ko`) that compiles the **real, unmodified**
`sys/dev/drm/linux_wait.c` into the module (the guest kernel does not
include drm) and drives the real `wait_event_timeout()` macro from
`<linux/wait.h>` against it.

## Build

```sh
cd /root/lwqdemo
make SYSDIR=/usr/src/sys        # -> lwqdemo.ko  (-Werror clean)
```

`lwqdemo.c`, `Makefile` and a pristine copy of `sys/dev/drm/linux_wait.c`
must be in the directory. See `build.sh`.

## Run

```sh
kldload ./lwqdemo.ko
# ... experiments run for ~25 s (8 misreports cap) ...
dmesg | grep LWQ            # MISREPORT lines + SUMMARY
kldunload lwqdemo           # allowed after SUMMARY printed
```

See `run.sh`.

## Expected output (decisive lines)

```
LWQ: MISREPORT #1 (windowed) dt=199 ticks: wait_event_timeout returned 0 (timeout)
     but condition gen 1 != 0 is TRUE - the in-window wake was lost and the rescue
     wake could not find the auto-removed entry
...
LWQ: ================= SUMMARY =================
LWQ: rounds_done=17 fast=8 control_ok=8 misreports=9 rescues_fired=9
LWQ: verdict: REPRODUCED - ... control rounds behave correctly
```

* every **windowed** round (wake delivered between the condition's state
  read and `tsleep_interlock`) burns its full 2 s timeout and returns 0
  ("condition never became true") although `gen` was bumped *before* the
  wake — 9/9 rounds;
* a **rescue** `wake_up()` fired ~100 µs into each stranded round and
  could not reach the sleeper (`rescues_fired=9`, still misreported) —
  the wait entry had been unlinked by `autoremove_wake_function()`;
* every **control** round (wake delivered after the interlock) returned
  immediately with `ret > 0` — 8/8, proving the harness plumbing and that
  out-of-window wakes behave correctly.

The log also contains the earlier *statistical* attempt (same module
family, timing-only waker): **1,666,602 rounds, 0 misreports** — negative
evidence that matters: an external timing-only waker cannot beat the
waiter's condition→interlock path, so the bug is entered in production
via condition width or waiter preemption (see VERDICT.md).

## Experiment 2 (shared module) — DF-5183

The same module proves the `MAX_SCHEDULE_TIMEOUT` truncation:

```
LWQ-F3: tsleep(timo=(int)MAX_SCHEDULE_TIMEOUT=-1) returned rv=35 after 2 ticks
        with NO waker - 'infinite' timeout actually expires every tick
```
