# DF-3717 VERDICT — mxge_detach() AB-BA deadlock (callout_stop vs mxge_tick)

**Finding.** `mxge_detach()` (sys/dev/netif/mxge/if_mxge.c:4463-4468) calls
synchronous `callout_stop(&sc->co_hdl)` while holding
`sc->main_serialize` (via `ifnet_serialize_all`, :4463 — main_serialize is
`sc->serializes[0]`, installed at :4133).  The `mxge_tick` callback
(:3712-3750) *begins* with a blocking `lwkt_serialize_enter(&sc->main_serialize)`
(:3720) and re-arms itself inside the lock (:3747).  DragonFly's
`callout_stop` (kern_timeout.c:1091 → `_callout_cancel_or_stop(..., sync=1)`)
sleeps on `"costp"` (kern_timeout.c:920) until an INPROG callback finishes;
the parked callback sleeps in `lwkt_serialize_enter` (lwkt_serialize.c:84-90,
wchan `"slize"`) until the stopper releases the serializer.  If the tick
(armed at :4445, every `mxge_ticks` = hz/2 by default) fires in the window
after the detaching thread acquired main_serialize, the two threads wedge
permanently and the callback's CPU loses its softclock thread (every callout
on that CPU stops firing: timers, tsleep timeouts).

**Verification.** Guest has no Myricom hardware (no 0x14c1 PCI device;
`if_mxge.ko` not loaded), so the driver path is hardware-gated — per family
convention (DF-3713/3714/3715) the mechanism was proven with a kernel-module
replica of the exact protocol on the same kernel primitives:

1. **Fix shape** (detach with `callout_stop_async` under the serializer +
   `callout_terminate` after release — mirroring fixed :4468 + existing
   :4474): tick parked on the serializer; stop returned immediately;
   `pending=0`; callback completed (`ran=9`).  NO deadlock — the
   recommended fix is validated on-guest.
2. **Shipped shape** (:4463-4468 exactly): tick parked; `callout_stop`
   entered and **never returned**.  Live state: stopper `D1 costp`
   (inside kern_timeout.c:920 wait) holding the serializer; softclock of
   the test CPU `B2 slize` (inside lwkt_serialize_enter) — both halves of
   the AB-BA visible in `ps`.  This is the same mechanism DF-3715 proved
   for em(4); the mxge locus is the detach path rather than em_stop.

**Classification.** Per family convention: `status=inconclusive`,
`reproduced=0`, `impact=none`, `confidence=certain` — the mxge-specific
runtime requires hardware the guest lacks; the underlying kernel-primitive
mechanism is proven deterministically (three clean fix-shape runs, one
decisive shipped-shape wedge, full state capture, guest reset afterwards).

**Trigger conditions on real hardware.** Any `mxge_detach()` on a live
device — kldunload if_mxge/kldunload mxge_fw_*, devd/ACPI-driven device
removal, surprise hotplug — racing the hz/2 tick.  Root-initiated action
required (as with DF-3715), but the result is a permanent system-wide
degradation, not a clean unload: two wedged kernel threads and one CPU's
callout subsystem dead.

**Harness incidents (documented, not affecting the decisive run).**
v1 re-armed a terminated callout → unrelated DID_INIT assertion panic;
v2's tsleep-timeout poll froze on the parked softclock (itself evidence
that the parked softclock stops servicing tsleep timeouts).  Both fixed;
final replica replicates if_mxge.c's protocol one-to-one and uses a bounded
busy-wait observer.  Guest left clean (`vm.sh reset with-src`).

## 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.
