# DF-4365 — pcn_stop()/pcn_tick() AB-BA callout deadlock (DF-3715 family, pcn(4) instance)

**File:** `sys/dev/netif/pcn/if_pcn.c` · **Severity:** Medium · **Class:** CWE-667 improper locking / kernel wedge (DoS)

## Bug (source)

`pcn_stat_timer` is a **plain (non-MPSAFE)** callout (`callout_init` at
`if_pcn.c:577`) whose callback `pcn_tick()` does a **blocking**
`lwkt_serialize_enter(ifp->if_serializer)` (`if_pcn.c:879`), self-re-arms at
1 Hz (`callout_reset(..., hz, pcn_tick, sc)` at `if_pcn.c:896`), and is torn
down by `callout_stop(&sc->pcn_stat_timer)` in `pcn_stop()`
(`if_pcn.c:1329`) — **while every caller of `pcn_stop()` holds that same
serializer**: `pcn_detach` (`:641-643`), `pcn_shutdown` (`:1374-1376`),
`pcn_init` (`:1106`, reached from `pcn_ioctl` SIOCSIFFLAGS `:1273/:1276`,
`pcn_watchdog` `:1306-1310`, `pcn_intr` ERR `:929`), and `pcn_intr` directly
(`:912-914`, interrupt context).

DragonFly's `callout_stop()` is **synchronous**: it ssleep()'s on
`'costp'` (`kern_timeout.c:920`) until an INPROG callback finishes
(`kern_timeout.c:567`). A tick dequeued and parked in
`lwkt_serialize_enter()` (`lwkt_serialize.c:105`) on the serializer held by
the `pcn_stop()` caller therefore deadlocks against it (AB-BA): the stopper
sleeps forever, the tick sleeps forever, that CPU's softclock stops
delivering ALL callouts, and — because the callout is non-MPSAFE — the
parked softclock thread holds the Giant MP lock while it sleeps.

**Trigger on real hardware:** `ifconfig pcnX down` (or up/down cycling,
MTU/flags change, watchdog reinit, detach, suspend/resume, system shutdown)
racing the 1 Hz tick.

## Reproduce (guest, protocol replica — the guest has no PCnet NIC)

```
./build.sh     # ships + builds pcn_callout_replica{,_fixed}.ko on the guest
./run.sh       # loads replica, arms tick, fires pcn_stop shape in background
```

Expected (buggy protocol): serial console prints

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

and no further pcnreplica lines. Fresh ssh session shows the stopper in
`D0`/wchan `costp` and one `(softclock N)` thread in `B`/wchan `slize`.
The guest then fails to shut down cleanly.

Fix-shape validation (`pcn_callout_replica_fixed.c`, fresh guest): 308
try-enter retries during a 3 s serializer hold, `callout_stop RETURNED,
pending=0`, guest healthy.

Afterwards: `vm.sh reset with-src` (the wedge survives clean shutdown).

## Fix

`fix.diff` — make `pcn_tick()` non-blocking (`lwkt_serialize_try()` + 1-tick
retry), the same shape validated for dc(4) in DF-3850.
