# DF-4316 — lge_stop()/lge_tick() callout AB-BA deadlock (DF-3715 family, lge member)

**What**: `lge_tick()` blocks in `lwkt_serialize_enter(ifp->if_serializer)`
(if_lge.c:984) and re-arms itself inside that serializer (:1017). Every
in-tree caller of `lge_stop()` — which does `callout_stop(&sc->lge_stat_timer)`
(:1442) — holds that same serializer (lge_detach :576-580, lge_init :1211 via
SIOCSIFFLAGS :1387 / lge_rxeoc :933 / lge_watchdog :1425, lge_intr :1029,
lge_ioctl :1391). DragonFly's `callout_stop()` is synchronous
(kern_timeout.c:1091 → sync=1 → ssleep "costp" :920): it sleeps until an
INPROG callback returns. A `lge_tick` dequeued while a stop-path thread holds
the serializer therefore parks inside `lwkt_serialize_enter` forever, and the
stopper sleeps in `costp` forever holding the serializer — permanent AB-BA
deadlock. Collateral: the interface serializer is never released (every later
lge path blocks) and the parked CPU's softclock thread stops delivering all
callouts on that CPU.

This is the same protocol defect proven for em(4) in DF-3715; this pack
proves it deterministically for lge's exact callout shape.

## Build / run / expected

Everything runs on the QEMU guest (DragonFly 6.5-DEVELOPMENT x86_64, 6 vCPU):

```
# on host
dfbsd-qemu/vm.sh up                      # if down
# copy lge_callout_replica.c + Makefile to guest:/root/lgerep
dfbsd-qemu/vm.sh run_root 'cd /root/lgerep && make'          # see build.log

# negative control (recommended-fix shape: try-enter + retry):
dfbsd-qemu/vm.sh run_root 'kldload /root/lgerep/lge_callout_replica.ko; \
    sysctl kern.lgereplica.arm=1; sleep 2; sysctl kern.lgereplica.stop_fix=1'
# EXPECT (dmesg): "lgereplica[fix]: RESULT pending=0 ... does NOT deadlock"
#                and the sysctl command returns (exit 0).

# positive test (exact lge_tick / lge_stop shape):
dfbsd-qemu/vm.sh run_root '(sysctl kern.lgereplica.stop_lge=1 &); sleep 25; \
    ps -axo pid,stat,wchan,command | grep stop_lge; dmesg | tail -8'
# EXPECT (dmesg): "handler PARKED on our serializer ... entering callout_stop()"
#                "IF THIS IS THE LAST lgereplica LINE, ..." -- then silence
#      (ps):      "<pid> D4 costp sysctl kern.lgereplica.stop_lge=1"
# The sysctl never returns; the session that ran it wedges; a second ssh
# session still works (6 vCPU guest) and shows the D4/costp thread.
# After the test the guest is deliberately wedged -> vm.sh reset with-src.
```

On real lge hardware the trigger is: `ifconfig lge0 down` (or up →
lge_init→lge_stop), `kldunload if_lge`, watchdog reinit, or RXCMDFIFO-empty
reinit, with the 1 Hz stat tick firing inside the stop-path's serialized
section. The guest has no LXT1001 NIC (`ifconfig -l` → `vtnet0 lo0`), so the
in-driver instance cannot be fired there — hence the kernel-module replica of
lge's exact callout protocol.

## Files
- `lge_callout_replica.c` — KLD replica (lge_tick shape + fix-shape control)
- `Makefile`, `build.sh`, `run.sh`
- `build.log` — full module build (BUILD_RC=0, -Werror)
- `run.log` — control + deadlock sessions + post-wedge captures
- `env.txt` — guest uname / NICs / compiler
- `fix.diff` — lge_tick try-enter + 1-tick retry (never blocks)
- `VERDICT.md`, `manifest.json`, `verdict.json`
