# DF-5532 — usb_proc_mwait() starvation: wakeup gated on whole-queue-empty

File: `sys/bus/u4b/usb_process.c` (pass 2)

## What this demonstrates

`usb_proc_mwait()` (usb_process.c:363-394) documents "returns when the
message pointed to by pm is no longer on a queue", but the ONLY wakeups
for `up_drain` are:

1. the dispatcher's **empty-queue** transition (usb_process.c:163-167), and
2. `usb_proc_drain()` teardown (usb_process.c:445-447).

So if the queue is continuously refilled by *any* other message source,
a waiter sleeps forever (cv_wait, no PCATCH → uninterruptible D-state)
**long after its own messages have completed**.

## Harness (family evidence class: production-code KLD rig)

`dfharness.c` drives the *production* `usb_proc_*` machinery:

- a "churn" message pair whose callback does trivial work, drops the
  lock for 1 tick (as every real cfg callback does around its USB
  request) and re-queues itself → queue never empties, no slow
  callback anywhere;
- a "victim" message pair that the `dfwait` kernel thread msignal()s
  and mwait()s — the stand-in for the unprivileged-syscall waiters of
  the production consumers (`ucom_queue_command()`,
  usb_serial.c:655-672, tcsetattr/close on /dev/ttyU*;
  `usbd_transfer_unsetup()`, usb_transfer.c:1286).

## Build / run (on the QEMU guest, as root)

```
cd /root/dfharness
make                                    # dfharness.ko
kldload ./dfharness.ko
sleep 3     && sysctl kern.dfstarv.report   # victim done, waiter ASLEEP
sleep 6     && sysctl kern.dfstarv.report   # ... still asleep
sysctl kern.dfstarv.stop=1                # let the queue drain
sleep 1     && sysctl kern.dfstarv.report   # waiter woke; starve_ms shown
kldunload dfharness
```

## Expected (baseline, unpatched kernel)

```
T+3s : victim_ran=1  waiter_done=0   (victim completed at ~T+0.5s)
T+9s : victim_ran=1  waiter_done=0   churn_count≈1000 (10ms/msg, no stall)
stop : waiter_done=1  starve_ms≈19500  (woke 20ms after the queue could empty)
```

`starve_ms` is how long the waiter overslept its own messages'
completion. It is bounded only by when the churn happens to stop —
in production the churn is sustained USB traffic/driver polls, so the
wait is unbounded.

### Extreme variant (churn_ticks=0, spin mode)

An *instantly* self-requeuing message monopolizes the usb_process
thread AND its lock: observed 8.9e9 callbacks over 275 s, victim never
executed, other threads starved even in lockmgr, guest userland (sshd)
starved. See `run.spin.log`.

## Fix

`fix.diff` (validated on a patched kernel): broadcast `up_drain` after
every completed-message dequeue (`if (up->up_dsleep)` guard → zero cost
when nobody waits). Also carries the DF-5533 cold-path fix.
