DF-4762 / fix.diff
# DF-4762 fix — serialize RX chunk processing against fwe_stop() teardown NOT applied to sys/ (read-only audit tree). Authored after source-level verification; not runtime-validated (no FireWire hardware on the audit guest) — see VERDICT.md. Design notes: * DragonFly lwkt serializers are NOT recursive (sys/sys/serialize.h:42), so fwohci_rbuf_update() must snapshot `hand` under the serializer and invoke it after releasing it; fwe_as_input() revalidates the channel after it acquires the serializer itself. * fwe_stop() must close the fwohci_intr() guard windows (FWXFERQ_OPEN at fwohci.c:1813, FWXFERQ_HANDLER at fwohci.c:2183) BEFORE the 1-second tsleep inside fwohci_irx_disable(), and all chunk-queue mutation in fwohci_rbuf_update() must happen under the xferq serializer so it cannot interleave with the kfree(). --- a/sys/bus/firewire/firewirereg.h +++ b/sys/bus/firewire/firewirereg.h @@ -200,5 +200,6 @@ struct fw_xferq { struct kqinfo rkq; caddr_t sc; void (*hand) (struct fw_xferq *); + struct lwkt_serializer *slz; /* optional: guards chunk qs + hand */ }; struct fw_bulkxfer{ --- a/sys/bus/firewire/fwohci.c +++ b/sys/bus/firewire/fwohci.c @@ -2143,6 +2143,8 @@ fwohci_rbuf_update(struct fwohci_softc *sc, int dmach) ir = fc->ir[dmach]; ldesc = sc->ir[dmach].ndesc - 1; #if 0 dump_db(sc, dmach); #endif + if (ir->slz != NULL) + lwkt_serialize_enter(ir->slz); crit_enter(); fwdma_sync_multiseg_all(sc->ir[dmach].am, BUS_DMASYNC_POSTREAD); while ((chunk = STAILQ_FIRST(&ir->stdma)) != NULL) { @@ -2181,9 +2183,15 @@ fwohci_rbuf_update(struct fwohci_softc *sc, int dmach) w++; } crit_exit(); - if (w) { - if (ir->flag & FWXFERQ_HANDLER) - ir->hand(ir); + hand = (w ? ir->hand : NULL); /* snapshot under slz */ + if (ir->slz != NULL) + lwkt_serialize_exit(ir->slz); + if (w) { + if (hand != NULL) + hand(ir); /* handler re-checks under its own serializer */ else wakeup(ir); } } --- a/sys/dev/netif/fwe/if_fwe.c +++ b/sys/dev/netif/fwe/if_fwe.c @@ -261,11 +261,24 @@ fwe_stop(struct fwe_softc *fwe) if (fwe->dma_ch >= 0) { xferq = fc->ir[fwe->dma_ch]; + /* + * Stop accepting new RX completions BEFORE disabling the + * DMA: fwohci_intr()/fwohci_rbuf_update() gate on + * FWXFERQ_OPEN and FWXFERQ_HANDLER, and fwohci_irx_disable() + * sleeps for up to 1s with those flags still set otherwise. + * All chunk-queue mutation in fwohci_rbuf_update() happens + * under xferq->slz (== if_serializer), so after we hold the + * serializer no completion can reference bulkxfer anymore. + */ + xferq->flag &= ~(FWXFERQ_OPEN | FWXFERQ_HANDLER | + FWXFERQ_STREAM | FWXFERQ_CHTAGMASK); + xferq->hand = NULL; + if (xferq->flag & FWXFERQ_RUNNING) fc->irx_disable(fc, fwe->dma_ch); xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM | FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK); xferq->hand = NULL; + xferq->slz = NULL; @@ -331,6 +344,7 @@ fwe_init(void *arg) /* register fwe_input handler */ xferq->sc = (caddr_t) fwe; xferq->hand = fwe_as_input; + xferq->slz = ifp->if_serializer; xferq->bnchunk = rx_queue_len; @@ -541,6 +555,13 @@ fwe_as_input(struct fw_xferq *xferq) fwe = (struct fwe_softc *)xferq->sc; ifp = &fwe->fwe_if; lwkt_serialize_enter(ifp->if_serializer); + if (xferq->hand != fwe_as_input) { + /* channel was torn down while we waited for the serializer */ + lwkt_serialize_exit(ifp->if_serializer); + return; + } while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) { |