# DF-3906 — xl(4) AB-BA callout deadlock: xl_stop() vs xl_stats_update()

**File:** `sys/dev/netif/xl/if_xl.c` (pass 2, GLM 5.3)
**Family:** DF-3715 / 3738 / 3760 / 3776 / 3806 / 3850 / 3903 (**10th
instance**) — this is the 3Com 3c90x xl(4) member; aggravating delta
(like dc/sk): `xl_stat_timer` is a **non-MPSAFE** callout.

## What to run (on the audit QEMU guest, root)

```sh
md5 /usr/src/sys/dev/netif/xl/if_xl.c          # 50da613cb89ad7c4e36eca8304bee221 == audited tree
./build.sh                                      # xl_callout_replica.ko (+ _fixed.ko)
kldload /root/kmod/xl_callout_replica.ko
sysctl kern.xlreplica_arm=1                     # arm the xl_stats_update-shaped 1-tick timer
sleep 1
(sysctl kern.xlreplica_stop=1 >/dev/null 2>&1 &)   # xl_stop shape: NEVER RETURNS
```

## Expected

The background `sysctl` never returns. A second session shows the stopper
thread in state `D`, wchan `costp` (the synchronous `callout_stop()` wait,
`sys/kern/kern_timeout.c:920`), and one `(softclock N)` thread in state
`B`, wchan `slize` (parked in `lwkt_serialize_enter`). Serial console
(`dfbsd-qemu/boot.log`):

```
xlreplica[arm]: xl_stat_timer armed (1 tick), waiting
xlreplica[stop]: tick INPROG and PARKED on the serializer (fired=2 ran=1)
xlreplica[stop]: IF THIS IS THE LAST xlreplica LINE callout_stop NEVER RETURNED (AB-BA deadlock)
```

No further `xlreplica` lines appear: `callout_stop()` never returned. The
wedge survives a clean shutdown attempt (the parked softclock thread holds
the Giant MP lock because `xl_stat_timer` is a plain, non-MPSAFE callout,
`if_xl.c:1320`); the guest must be force-reset.

The guest has no 3c90x hardware, so the driver itself is exercised by a
kernel module (`xl_callout_replica.c`) that replicates if_xl.c's exact
callout/serializer protocol (same callout flavor, same blocking enter,
same self re-arm inside the serialized body, same `callout_stop` under
the serializer). The deadlock is in that generic protocol.

## Fix-shape validation (fresh guest)

```sh
kldload /root/kmod/xl_callout_replica_fixed.ko   # lwkt_serialize_try + 1-tick retry (fix.diff shape)
sysctl kern.xlrepfix_arm=1; sleep 1
(sysctl kern.xlrepfix_stop=1 >/dev/null 2>&1 &)  # holds serializer 3s, then callout_stop
```

Expected (observed): `xlrepfix[stop]: callout_stop RETURNED (fixed shape):
fired=312 ran=5 retry=307 pending=0` — under cross-CPU contention the tick
retries instead of parking and `callout_stop` returns.
