# DF-4112 — nge(4): callout AB-BA deadlock — nge_tick blocks on the serializer nge_stop stops the callout under

## What this is

`sys/dev/netif/nge/if_nge.c` arms `sc->nge_stat_timer` with a plain
`callout_init()` (if_nge.c:731) — MP-safe on this kernel
(sys/kern/kern_timeout.c:770) — and re-arms it every `hz` from
`nge_init` (if_nge.c:1717).  The callback, `nge_tick()`
(if_nge.c:1366-1406), takes the driver serializer **by blocking**
(`lwkt_serialize_enter(ifp->if_serializer)`, if_nge.c:1372 → parks in
`tsleep "slize"`, sys/kern/lwkt_serialize.c:227-237) and re-arms itself
*inside* the serializer (if_nge.c:1403).

`nge_stop()` (if_nge.c:2094) calls `callout_stop(&sc->nge_stat_timer)`
while its caller **holds that same serializer** (nge_stop is reached
under the serializer from `nge_ioctl` if_nge.c:2031, `nge_init` :1716,
`nge_watchdog` :2067, `nge_detach` :934, and `nge_intr` :1491 — the
latter is the interrupt thread itself).

DragonFly's `callout_stop()` synchronously waits for an in-progress
callback (`ssleep "costp"`, sys/kern/kern_timeout.c:920,1091).  A
callback parked on the stopper's serializer deadlocks against it: both
threads wedge permanently, the interface serializer is never released,
and the parked softclock thread stops delivering all callouts for its
CPU.  Same mechanism as DF-3715 (em(4)); re-proven here with the exact
nge shape.

## Build / run (verification on the audit guest)

```
# guest has no nge(4)/DP83820 hardware (vtnet0 lo0 only), so the driver
# protocol is replicated verbatim by an out-of-tree module.

scp tlnge_replica.c Makefile root@guest:/root/kmod/
vm.sh run_root 'cd /root/kmod && make'
vm.sh run_root 'kldload /root/kmod/tlnge_replica.ko; sysctl kern.ngereplica.arm=1; sleep 3; \
                (sysctl kern.ngereplica.stop_blk=1 >/dev/null 2>&1 &); sleep 6; \
                dmesg | grep "replica\[nge\]" | tail -3; \
                ps -o pid,stat,wchan,command -ax | grep stop_blk | grep -v grep'
```

## Expected / observed

* dmesg ends with
  `replica[nge]: handler PARKED on our serializer (fired=8 ran=7) - entering callout_stop()`
  then the marker — and **nothing after** (permanent).
* stopper thread `D5`, wchan `costp` (kern_timeout.c:920).
* Fix-shape control (`stop_fix`: try-enter + 1-tick retry, the proposed
  fix): `callout_stop` under the serializer **returns**, `pending=0`,
  firing fully stalled — fix mechanism validated on this kernel.
* On real nge(4) hardware the trigger is `ifconfig nge0 down` / MTU
  change / detach / link-watchdog reinit racing the 1-second tick.

## Trigger on hardware

`ifconfig nge0 down` (SIOCSIFFLAGS), SIOCSIFMTU reinit, `kldunload
if_nge`, nge_watchdog recovery, or an unwanted interrupt while the
interface is down (`nge_intr` → `nge_stop` directly, if_nge.c:1490-1493)
— racing the once-per-second nge_tick.
