# DF-5184 — wake_up_bit() ignores the bit number and wakes exactly ONE waiter on the word **File:** `sys/dev/drm/linux_wait.c:97-101` **Severity:** Low (latent API-contract divergence; self-healing today) **Verification:** analysis-only (skipped per contract — Low severity, kernel- internal, no deterministic in-tree multi-waiter trigger; the file's other defects dominate observable behavior) ## Analysis ```c void wake_up_bit(void *addr, int bit) { wakeup_one(addr); } ``` The Linux contract (`wake_up_bit`) wakes all waiters waiting on `(addr, bit)` — the wait identity is the (address, bit-number) pair via a hashed wait table. This shim: 1. **ignores `bit` entirely** — the wake identity is just the `word` pointer (the waiters `tsleep(word, ...)` at linux_wait.c:118). Any waiter on *any* bit of the word is a valid `wakeup_one` target. 2. **wakes exactly one waiter** (`wakeup_one`) regardless of how many waiters are queued on that word. Consequences when two threads sleep on the same word (different bits, or the same bit): * the wake can be delivered to the *wrong* waiter (wrong-bit wake); the true waiter is not woken by this call; * with N waiters on one bit, N−1 waiters are not woken by a single `wake_up_bit` — under Linux semantics each would be woken. The wrong-waiter case does *not* currently escape as a spurious success: the woken thread's do-while loop re-checks `test_bit()` and goes back to sleep, so the observable effect is a stolen wakeup (added latency / reliance on the caller's timeout), and the stranded waiter is accidentally rescued by the 1 Hz polling that DF-5183 documents (`MAX_SCHEDULE_TIMEOUT` truncation). In-tree usage keeps at most one `wait_on_bit*` sleeper per word at a time: both i915 wait sites (i915_irq.c:3199 HANDOFF and i915_irq.c:3341 RESET_ENGINE+n) execute under the I915_RESET_BACKOFF serialization (i915_irq.c:3324-3331), so the fanout defect is latent with current callers. Reachable in the future by any driver that waits on two bits of one `unsigned long` concurrently — a pattern the Linux API explicitly supports and drm code (e.g. the i915 reset flags word) is shaped for. ## Suggested fix Encode the bit into the sleep identity or use an exclusive wakeup keyed per (word, bit), and wake all waiters of that pair: ```diff --- a/sys/dev/drm/linux_wait.c +++ b/sys/dev/drm/linux_wait.c @@ -97,7 +97,8 @@ void wake_up_bit(void *addr, int bit) { - wakeup_one(addr); + /* Linux semantics: wake all waiters on (addr, bit). */ + wakeup((void *)((uintptr_t)addr ^ (bit + 1))); } ``` with the matching `tsleep()` ident in `wait_on_bit_timeout()` (and `wait_on_bit()`), e.g. `tsleep((void *)((uintptr_t)word ^ (bit + 1)), ...)`. (XOR-folding the bit into the ident keeps per-bit wake identity; `wakeup()` restores the all-waiters fanout. Collisions with unrelated sleep idents are no worse than the current single-word ident.)