ips_timeout performs the forced adapter reinit while holding queue_lock and then tsleeps forever on a completion only the lock-blocked interrupt handler can deliver β permanent watchdog-path deadlock (timer twin of DF-2163)
Summary
The watchdog takes queue_lock exclusively and calls the forced reinit still holding it; the reinit's sync commands wait with plain tsleep (never releases lockmgr locks) while the only wakeup source - the completion interrupt - must first acquire the same queue_lock. LK_CANRECURSE cannot help cross-thread: the softclock thread sleeps forever owning the lock, the IPS ithread blocks, all ips I/O wedges permanently (root-on-ips = whole-system hang). Any card command hung ~100s (failing hardware - exactly what the watchdog exists for) triggers it automatically; DF-2163 is the deliberate ioctl entry. Drop-lock-around-reinit + bounded-waits fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4613 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.3 KB | β raw | |
| VERDICT.md | β | 3.8 KB | β raw | |
| env.txt | β | 273 B | view raw | |
| fix.diff | β | 2.1 KB | view raw | |
| manifest.json | β | 791 B | view raw | |
| verdict.json | β | 3.6 KB | view raw |
DF-4613 β ips_timeout forces adapter reinit while holding queue_lock and then sleeps forever on a completion only the (lock-blocked) interrupt handler can deliver
What
sys/dev/raid/ips/ips.c:331-371 (ips_timeout), ips.c:355 (forced reinit),
ips.c:585 / ips.c:769 (interrupt handlers take the same lock),
sys/dev/raid/ips/ips_commands.c:722,743 (ips_timed_wait(..., 0) = infinite
tsleep) via ips_clear_adapter() called from ips_morpheus_reinit(sc, 1)
(ips.c:516) / ips_copperhead_reinit(sc, 1) (ips.c:738).
Timer-path twin of DF-2163 (which filed the identical pattern reached from the IPS_USER_CMD ioctl in ips_ioctl.c:125-139). This path needs no user action: it fires automatically whenever any card command is stuck for ~100 s.
Sequence (morpheus; copperhead is identical in shape)
- A command hangs on the card.
ips_timeout(10 s callout, softclock thread) decrementstimeouteach tick; at 0 it setsIPS_TIMEOUT, error-completes the command via its callback (ips.c:343-349) β all underlockmgr(&sc->queue_lock, LK_EXCLUSIVE)taken at ips.c:337. - Once no command still has a pending timeout (
state == 0), ips.c:354-355 callssc->ips_adapter_reinit(sc, 1)still holding queue_lock. ips_morpheus_reinit(force=1)resets the card (5 s DELAY + up to 45+240 1-second DELAY busy-waits β themselves under the lock, starving every other callout on the CPU), unmasks interrupts (ips_write_4(OIMR, 0), ips.c:515), thenips_clear_adapter(sc)(ips.c:516).ips_clear_adapterβips_send_config_sync_cmd(ips_commands.c:707-725) issues the sync command and callsips_timed_wait(command, "ipssyn", 0)β timo = 0 = sleep forever (ips_commands.c:43-58; plaintsleep, which does not release lockmgr locks).- The card completes the command β IRQ β
ips_morpheus_intrβlockmgr(&sc->queue_lock, LK_EXCLUSIVE|LK_RETRY)(ips.c:585) β blocks forever: the sleeping softclock thread owns the lock. - The wakeup never happens; the softclock thread never releases the lock;
every subsequent IPS I/O, every ioctl, and every future
ips_timeouttick blocks on queue_lock. Permanent wedge of the controller plus two kernel threads.
LK_CANRECURSE (lockinit at ips_pci.c:154) does not help: the interrupt
handler is a different thread.
Build / run
Requires an IBM ServeRAID (ips) controller: PCI ids 0x1014:0x01BD (morpheus),
0x1014:0x002E / 0x1014:0x0250 / Adaptec 0x9005 (copperhead/marco). The audit
guest is QEMU/virtio and has no such device (env.txt); QEMU has no ServeRAID
device model, so the path cannot be instantiated. Verdict rests on the static
trace above (certain) plus the mechanically identical DF-2163 pattern.
Expected behavior (if hardware were present)
~100 s after any command hangs, the console prints "WARNING: command timeoutβ¦
toaster mode", then "resetting adapterβ¦"; when the reset finishes and
ips_clear_adapter issues the config sync, the system wedges: ps shows the
softclock thread in tsleep ("ipssyn") holding ipslk and the IPS ithread
blocked on ipslk; all I/O to ips(4) logical drives hangs forever; boot
device on ips β hard hang requiring power cycle.
Contents
VERDICT.md,env.txt,fix.diff,manifest.json,verdict.json
VERDICT β DF-4613
Status: not_reproduced (no IBM ServeRAID controller on the audit guest β
env.txt; QEMU provides no device model for 0x1014:0x01BD/0x002E/0x0250 or
0x9005 ServeRAID). The deadlock is proven by line-accurate static trace with
certain confidence; it is the mechanically identical twin of the verified
pattern in DF-2163 (ioctl path), reached here from the watchdog timer with no
user interaction.
Proof chain (every hop cited)
-
The lock is held across the whole recovery.
ips_timeout(ips.c:337):lockmgr(&sc->queue_lock, LK_EXCLUSIVE|LK_RETRY)β¦ips.c:355:if (sc->ips_adapter_reinit(sc, 1))β still inside the critical region (released only at ips.c:370). -
The reinit sleeps waiting for card completion.
ips_morpheus_reinit(sc, force=1)βips_clear_adapter(sc)(ips.c:516; copperhead: ips.c:738) βips_send_config_sync_cmdβips_timed_wait(command, "ipssyn", 0)(ips_commands.c:722); second wait inips_send_error_table_cmd(ips_commands.c:743).ips_timed_waitloopstsleep(&command->completed, 0, id, timo)(ips_commands.c:47-56) β timo 0 = no timeout. DragonFlytsleepdoes not release lockmgr locks (onlylksleepdoes). -
The only wakeup source needs the same lock.
command->completed = 1; wakeup(&command->completed)happens solely inips_wakeup_callback(ips_commands.c:66-72), which runs fromips_morpheus_intr/ips_copperhead_intrβ both of which first takelockmgr(&sc->queue_lock, LK_EXCLUSIVE|LK_RETRY)(ips.c:585, ips.c:769) before draining completions and invoking callbacks (ips.c:573, ips.c:775). -
Cross-thread β CANRECURSE is irrelevant. queue_lock is
lockinit(..., LK_CANRECURSE)(ips_pci.c:154); recursion only helps the owning thread. The interrupt handler is a separate ithread β it blocks, the sleeper never wakes. Softclock handlers are lwkt threads (sys/kern/kern_timeout.c:416-423), so the tsleep is mechanically legal and simply never terminates. -
Additional degradation while the lock is held: morpheus reinit busy waits
DELAY(5000000)+ up to 45Γ1 s + 240Γ1 s (ips.c:487-507) with the lock held in the softclock thread β starving every other callout on that CPU for minutes before the final deadlock even forms.
DF-2163 documented the same lock/sleep inversion entered from
ips_ioctl_cmd (ips_ioctl.c:125-139, tsleep loop) and noted ips_timeout would
be blocked by that hang. DF-4613 is the distinct ips.c entry: the watchdog
itself creates the inversion, no ioctl and no privileges required β the
trigger is purely "a card command did not complete for ~100 s" (failing
hardware, torn cable, firmware stall), exactly the situation the watchdog
exists to recover from. The recovery path cannot succeed by construction: the
config-sync it issues can never complete while its own lock is held.
Impact
Permanent denial of service of the storage controller and two kernel threads (softclock thread of that CPU + IPS ithread). All I/O to ips(4) volumes hangs; if the root filesystem is on an ips drive, the whole system hangs. Requires a hung card command to trigger (hardware fault class), hence Medium (local DoS, unusual precondition) rather than High.
Fix
Drop queue_lock before calling ips_adapter_reinit in ips_timeout
(lockmgr(&sc->queue_lock, LK_RELEASE) at ips.c:355, re-acquire after), and
give the ips_timed_wait calls in ips_clear_adapter's helpers a finite
timeout so a dead card cannot sleep forever. See fix.diff.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableBaseline cannot be exercised on the QEMU/virtio guest (no ServeRAID device model exists upstream), so a patched-kernel A/B run is impossible. fix.diff releases the lock around the reinit and bounds both waits at 60s; it eliminates the inversion by construction (the interrupt handler can then acquire the lock and deliver the wakeup).
fix.diff (ips.c: release/reacquire queue_lock around ips_adapter_reinit; ips_commands.c: ipssyn/ipsetc waits 0 -> 60*hz).
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md five-hop proof chain: ips.c:337 lock, ips.c:355 reinit under lock, ips_commands.c:722/743 tsleep(0), ips_commands.c:66-72 sole wakeup source, ips.c:585/769 interrupt handler blocked on same lock; kern_timeout.c:416-423 softclock is a thread (sleep mechanically legal, never terminates); env.txt (device absent)
PoC changes
none - no PoC possible; QEMU cannot instantiate a ServeRAID controller (PCI 0x1014:0x01BD/0x002E/0x0250/0x9005). Verdict rests on the cited static trace and the mechanically identical DF-2163.
Verified recommended fix
Release queue_lock around the forced ips_adapter_reinit call in ips_timeout and give the ips_timed_wait calls in ips_clear_adapter helpers finite timeouts so a dead card cannot sleep forever.
Verdict
ips_timeout takes sc->queue_lock exclusively (ips.c:337) and, on watchdog expiry, calls sc->ips_adapter_reinit(sc,1) at ips.c:355 still holding it. The forced reinit issues config-sync/error-table commands whose completion it waits for with ips_timed_wait(...,0) - an infinite tsleep (ips_commands.c:722,743) that does not release lockmgr locks. The completion can only be delivered by ips_morpheus_intr/ips_copperhead_intr, which must first acquire the same queue_lock (ips.c:585/769) and therefore blocks forever; the sleeping softclock thread never wakes, never releases the lock, and all IPS I/O plus the watchdog wedge permanently. LK_CANRECURSE (ips_pci.c:154) cannot help cross-thread. This is the timer-path twin of DF-2163 (ioctl path) and needs no user action - only a card command hung ~100s, i.e. exactly the fault the watchdog exists to recover from; by construction the recovery can never complete. Not reproduced on the audit guest: no IBM ServeRAID PCI device exists there (env.txt) and QEMU has no device model for it, so the path cannot be instantiated; the static trace is complete and DF-2163 documents the identical lock/sleep inversion on real hardware.
No comments yet.