DF-5191 / fix.diff
diff --git a/sys/dev/serial/sio/sio_pccard.c b/sys/dev/serial/sio/sio_pccard.c --- a/sys/dev/serial/sio/sio_pccard.c +++ b/sys/dev/serial/sio/sio_pccard.c @@ -114,22 +114,45 @@ static int sio_pccard_detach(device_t dev) { struct com_s *com; + struct tty *tp; com = (struct com_s *) device_get_softc(dev); if (com == NULL) { device_printf(dev, "NULL com in siounload\n"); return (0); } + + /* + * DF-5191: every sio core path that touches com->gone, com->tp or + * com->ibuf runs under tty_token (sioopen, sioclose, sioparam, + * siosetwater "called with tty_token held"). Detach must take the + * same token or it races open/close in flight and double-frees + * com->ibuf. + * + * Sequence: publish com->gone under the token (stops new work), + * DROP the token before draining the callouts (their callbacks + * acquire tty_token themselves: siodtrwakeup, siobusycheck -- + * draining under the token would deadlock), then re-acquire for + * the teardown. The com->gone guards added to the callbacks keep + * them from re-arming after the drain (DF-2191). + */ + lwkt_gettoken(&tty_token); com->gone = 1; + lwkt_reltoken(&tty_token); + + callout_drain(&com->dtr_ch); /* DF-2191 */ + callout_drain(&com->busy_ch); /* DF-2191 */ + + lwkt_gettoken(&tty_token); + crit_enter(); + tp = com->tp; if (com->irqres) { bus_teardown_intr(dev, com->irqres, com->cookie); bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres); + com->irqres = NULL; } if (com->ioportres) { bus_release_resource(dev, SYS_RES_IOPORT, 0, com->ioportres); + com->ioportres = NULL; } - if (com->tp && (com->tp->t_state & TS_ISOPEN)) { + if (tp && (tp->t_state & TS_ISOPEN)) { device_printf(dev, "still open, forcing close\n"); - ttyclose(com->tp); + ttyclose(tp); } else { if (com->ibuf != NULL) { kfree(com->ibuf, M_DEVBUF); + com->ibuf = NULL; /* DF-3902 */ + com->ibufold = NULL; } } + crit_exit(); + lwkt_reltoken(&tty_token); device_printf(dev, "unloaded\n"); return (0); } diff --git a/sys/dev/serial/sio/sio.c b/sys/dev/serial/sio/sio.c --- a/sys/dev/serial/sio/sio.c +++ b/sys/dev/serial/sio/sio.c @@ -1605,6 +1605,9 @@ siobusycheck(void *chan) lwkt_gettoken(&tty_token); com = (struct com_s *)chan; + if (com->gone) { /* DF-2191: detach in flight */ + lwkt_reltoken(&tty_token); + return; + } /* * Clear TS_BUSY if low-level output is complete. * spl locking is sufficient because siointr1() does not set CS_BUSY. @@ -1660,6 +1663,9 @@ siodtrwakeup(void *chan) lwkt_gettoken(&tty_token); com = (struct com_s *)chan; + if (com->gone) { /* DF-2191: detach in flight */ + lwkt_reltoken(&tty_token); + return; + } com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); lwkt_reltoken(&tty_token); |