DF-2000 / fix.diff
diff --git a/sys/dev/netif/ic/if_ic.c b/sys/dev/netif/ic/if_ic.c --- a/sys/dev/netif/ic/if_ic.c +++ b/sys/dev/netif/ic/if_ic.c @@ -82,6 +82,8 @@ int ic_xfercnt; int ic_iferrs; + + struct lwkt_token ic_tok; /* serializes obuf/ifbuf/cp vs SIOCSIFMTU */ }; static devclass_t ic_devclass; @@ -130,6 +132,8 @@ struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); struct ifnet *ifp = &sc->ic_if; + lwkt_token_init(&sc->ic_tok, "ic_tok"); + sc->ic_addr = PCF_MASTER_ADDRESS; /* XXX only PCF masters */ ifp->if_softc = sc; @@ -203,6 +207,14 @@ break; case SIOCSIFMTU: + /* + * icoutput() and icintr() dereference sc->ic_obuf / sc->ic_ifbuf + * (and capture sc->ic_cp from ic_ifbuf). Serialize the buffer + * swap-and-free against both paths so a concurrent TX/RX on another + * CPU never holds a stale pointer into the freed M_DEVBUF chunk. + */ + lwkt_gettoken(&sc->ic_tok); + /* save previous buffers */ iptr = sc->ic_ifbuf; optr = sc->ic_obuf; @@ -220,6 +232,8 @@ kfree(optr,M_DEVBUF); sc->ic_if.if_mtu = ifr->ifr_mtu; + + lwkt_reltoken(&sc->ic_tok); break; case SIOCGIFMTU: @@ -260,6 +274,8 @@ crit_enter(); + lwkt_gettoken(&sc->ic_tok); + switch (event) { case INTR_GENERAL: @@ -326,6 +342,7 @@ panic("%s: unknown event (%d)!", __func__, event); } + lwkt_reltoken(&sc->ic_tok); crit_exit(); } @@ -348,6 +365,8 @@ crit_enter(); + lwkt_gettoken(&sc->ic_tok); + /* already sending? */ if (sc->ic_sending) { IFNET_STAT_INC(ifp, oerrors, 1); @@ -386,6 +405,12 @@ crit_exit(); + /* + * sc->ic_obuf is read by iicbus_block_write below; keep the token + * across it so a concurrent SIOCSIFMTU cannot kfree the buffer + * out from under the in-flight bus write. + */ + /* send the packet */ if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf, len + ICHDRLEN, &sent)) @@ -398,10 +423,13 @@ sc->ic_sending = 0; + lwkt_reltoken(&sc->ic_tok); + return (0); error: m_freem(m); + lwkt_reltoken(&sc->ic_tok); crit_exit(); return(0); |