Index: sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c =================================================================== --- sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c +++ sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c @@ -266,7 +266,7 @@ { int c; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); AJU_LOCK_ASSERT(sc); while (aju_readable(sc)) { @@ -298,7 +298,7 @@ uint32_t v; uint8_t ch; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); AJU_LOCK_ASSERT(sc); AJU_UNLOCK(sc); @@ -364,7 +364,7 @@ { struct altera_jtag_uart_softc *sc = tty_softc(tp); - tty_assert_locked(tp); + ttydisc_assert_locked(tp); AJU_LOCK(sc); aju_handle_output(sc, tp); @@ -377,7 +377,6 @@ struct altera_jtag_uart_softc *sc = arg; struct tty *tp = sc->ajus_ttyp; - tty_lock(tp); AJU_LOCK(sc); /* @@ -399,7 +398,6 @@ callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, aju_io_callout, sc); AJU_UNLOCK(sc); - tty_unlock(tp); } static void @@ -409,7 +407,6 @@ struct tty *tp = sc->ajus_ttyp; uint32_t v; - tty_lock(tp); AJU_LOCK(sc); v = aju_control_read(sc); if (v & ALTERA_JTAG_UART_CONTROL_AC) { @@ -439,7 +436,6 @@ callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, aju_ac_callout, sc); AJU_UNLOCK(sc); - tty_unlock(tp); } static void @@ -449,7 +445,7 @@ struct tty *tp = sc->ajus_ttyp; uint32_t v; - tty_lock(tp); + ttydisc_lock(tp); AJU_LOCK(sc); v = aju_control_read(sc); if (v & ALTERA_JTAG_UART_CONTROL_RI) { @@ -461,7 +457,7 @@ aju_handle_output(sc, tp); } AJU_UNLOCK(sc); - tty_unlock(tp); + ttydisc_unlock(tp); } int @@ -518,6 +514,8 @@ tty_init_console(tp, 0); } tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit); + /* Grab the lock now for callout work. */ + ttydisc_lock(tp); /* * If we will be using interrupts, enable them now; otherwise, start @@ -528,13 +526,14 @@ aju_intr_readable_enable(sc); AJU_UNLOCK(sc); } else { - callout_init(&sc->ajus_io_callout, 1); + callout_init_mtx(&sc->ajus_io_callout, ttydisc_getlock(tp), 0); callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, aju_io_callout, sc); } - callout_init(&sc->ajus_ac_callout, 1); + callout_init_mtx(&sc->ajus_ac_callout, ttydisc_getlock(tp), 0); callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL, aju_ac_callout, sc); + ttydisc_unlock(tp); return (0); } @@ -545,8 +544,11 @@ /* * If we're using interrupts, disable and release the interrupt - * handler now. Otherwise drain the polling timeout. + * handler now. Otherwise drain the polling timeout. Grab the tty + * lock early to block any requests from userland until we've finished + * detaching. */ + tty_lock(tp); if (sc->ajus_irq_res != NULL) { AJU_LOCK(sc); aju_intr_disable(sc); @@ -558,7 +560,7 @@ callout_drain(&sc->ajus_ac_callout); if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) aju_cons_sc = NULL; - tty_lock(tp); + ttydisc_lock(tp); tty_rel_gone(tp); AJU_LOCK_DESTROY(sc); } Index: sys/dev/bvm/bvm_console.c =================================================================== --- sys/dev/bvm/bvm_console.c +++ sys/dev/bvm/bvm_console.c @@ -110,7 +110,7 @@ if (bvm_consdev.cn_pri != CN_DEAD) { tp = tty_alloc(&bvm_ttydevsw, NULL); - callout_init_mtx(&bvm_timer, tty_getlock(tp), 0); + callout_init_mtx(&bvm_timer, ttydisc_getlock(tp), 0); tty_makedev(tp, NULL, "bvmcons"); } } @@ -118,6 +118,8 @@ static int bvm_tty_open(struct tty *tp) { + + ttydisc_assert_locked(tp); polltime = hz / BVMCONS_POLL_HZ; if (polltime < 1) polltime = 1; @@ -130,7 +132,7 @@ bvm_tty_close(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); callout_stop(&bvm_timer); } @@ -159,7 +161,7 @@ tp = (struct tty *)v; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); while ((c = bvm_cngetc(NULL)) != -1) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); Index: sys/dev/cfe/cfe_console.c =================================================================== --- sys/dev/cfe/cfe_console.c +++ sys/dev/cfe/cfe_console.c @@ -90,7 +90,7 @@ if (cfe_consdev.cn_pri != CN_DEAD && cfe_consdev.cn_name[0] != '\0') { tp = tty_alloc(&cfe_ttydevsw, NULL); - callout_init_mtx(&cfe_timer, tty_getlock(tp), 0); + callout_init_mtx(&cfe_timer, ttydisc_getlock(tp), 0); tty_makedev(tp, NULL, "cfecons"); } } @@ -98,6 +98,8 @@ static int cfe_tty_open(struct tty *tp) { + + ttydisc_assert_locked(tp); polltime = hz / CFECONS_POLL_HZ; if (polltime < 1) polltime = 1; @@ -110,6 +112,7 @@ cfe_tty_close(struct tty *tp) { + ttydisc_assert_locked(tp); callout_stop(&cfe_timer); } @@ -142,7 +145,7 @@ tp = (struct tty *)v; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); while ((c = cfe_cngetc(NULL)) != -1) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); Index: sys/dev/dcons/dcons_os.c =================================================================== --- sys/dev/dcons/dcons_os.c +++ sys/dev/dcons/dcons_os.c @@ -235,13 +235,13 @@ dc = &sc[i]; tp = dc->tty; - tty_lock(tp); + ttydisc_lock(tp); while ((c = dcons_os_checkc_nopoll(dc)) != -1) { ttydisc_rint(tp, c, 0); poll_idle = 0; } ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); } poll_idle++; polltime = hz; Index: sys/dev/gxemul/cons/gxemul_cons.c =================================================================== --- sys/dev/gxemul/cons/gxemul_cons.c +++ sys/dev/gxemul/cons/gxemul_cons.c @@ -281,10 +281,11 @@ tp = tty_alloc(&gxemul_cons_ttydevsw, NULL); tty_init_console(tp, 0); tty_makedev(tp, NULL, "%s", "ttyu0"); - callout_init(&gxemul_cons_callout, 1); + callout_init_mtx(&gxemul_cons_callout, ttydisc_getlock(tp), 0); + ttydisc_lock(tp); callout_reset(&gxemul_cons_callout, gxemul_cons_polltime, gxemul_cons_timeout, tp); - + ttydisc_unlock(tp); } SYSINIT(gxemul_cons_ttyinit, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, gxemul_cons_ttyinit, NULL); @@ -316,7 +317,7 @@ int c; tp = v; - tty_lock(tp); + ttydisc_lock(tp); GC_LOCK(); while (gxemul_cons_readable()) { c = gxemul_cons_read(); @@ -329,7 +330,7 @@ } GC_UNLOCK(); ttydisc_rint_done(tp); - tty_unlock(tp); callout_reset(&gxemul_cons_callout, gxemul_cons_polltime, gxemul_cons_timeout, tp); + ttydisc_unlock(tp); } Index: sys/dev/nmdm/nmdm.c =================================================================== --- sys/dev/nmdm/nmdm.c +++ sys/dev/nmdm/nmdm.c @@ -108,6 +108,7 @@ struct nmdmpart *onp; struct tty *otp; + ttydisc_assert_locked(tp); np = tty_softc(tp); onp = np->np_other; otp = onp->np_tty; @@ -120,13 +121,14 @@ tty_rel_gone(tp); /* Shut down second part. */ - tty_lock(tp); + tty_lock(otp); onp = np->np_other; if (onp == NULL) return; otp = onp->np_tty; tty_rel_gone(otp); tty_lock(tp); + ttydisc_lock(tp); } static void @@ -191,7 +193,10 @@ TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2); callout_init_mtx(&ns->ns_part2.np_callout, &ns->ns_mtx, 0); - /* Create device nodes. */ + /* + * Create device nodes. Both sides can have distinct tty locks, as + * long as they share a ttydisc lock. + */ tp = ns->ns_part1.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part1, &ns->ns_mtx); *end = 'A'; @@ -256,12 +261,16 @@ char c; tp = np->np_tty; - tty_lock(tp); + ttydisc_lock(tp); if (tty_gone(tp)) { - tty_unlock(tp); + ttydisc_unlock(tp); return; } + /* + * We'll be operating on otp while maintaining the tp ttydisc lock; this + * is OK, since they share the same ttydisc lock. + */ otp = np->np_other->np_tty; KASSERT(otp != NULL, ("NULL otp in nmdmstart")); KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); @@ -279,7 +288,7 @@ /* This may happen when we are in detach process. */ if (tty_gone(otp)) { - tty_unlock(otp); + ttydisc_unlock(tp); return; } @@ -294,7 +303,7 @@ ttydisc_rint_done(otp); - tty_unlock(tp); + ttydisc_unlock(tp); } static int @@ -324,6 +333,8 @@ struct tty *tp2; int bpc, rate, speed, i; + /* Must be true for callout manipulation down below. */ + ttydisc_assert_locked(tp); tp2 = np->np_other->np_tty; if (!((t->c_cflag | tp2->t_termios.c_cflag) & CDSR_OFLOW)) { @@ -381,6 +392,7 @@ struct nmdmpart *np = tty_softc(tp); int i = 0; + ttydisc_assert_locked(tp); if (sigon || sigoff) { if (sigon & SER_DTR) np->np_other->np_dcd = 1; Index: sys/dev/ofw/ofw_console.c =================================================================== --- sys/dev/ofw/ofw_console.c +++ sys/dev/ofw/ofw_console.c @@ -102,7 +102,7 @@ return; if (strlen(output) > 0) tty_makealias(tp, "%s", output); - callout_init_mtx(&ofw_timer, tty_getlock(tp), 0); + callout_init_mtx(&ofw_timer, ttydisc_getlock(tp), 0); } } @@ -114,6 +114,8 @@ static int ofwtty_open(struct tty *tp) { + + ttydisc_assert_locked(tp); polltime = hz / OFWCONS_POLL_HZ; if (polltime < 1) polltime = 1; @@ -127,6 +129,7 @@ ofwtty_close(struct tty *tp) { + ttydisc_assert_locked(tp); callout_stop(&ofw_timer); } @@ -136,6 +139,7 @@ int len; u_char buf[OFBURSTLEN]; + ttydisc_assert_locked(tp); for (;;) { len = ttydisc_getc(tp, buf, sizeof buf); if (len == 0) @@ -152,7 +156,7 @@ tp = (struct tty *)v; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); while ((c = ofw_cngetc(NULL)) != -1) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); Index: sys/dev/rp/rp.c =================================================================== --- sys/dev/rp/rp.c +++ sys/dev/rp/rp.c @@ -596,7 +596,7 @@ FIFO one word at a time, pulling apart the character and the status. Update error counters depending on status. */ - tty_lock(tp); + ttydisc_lock(tp); if(ChanStatus & STATMODE) { while(ToRecv) { CharNStat = rp_readch2(cp,sGetTxRxDataIO(cp)); @@ -630,7 +630,7 @@ } } ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); } static void rp_handle_port(struct rp_port *rp) @@ -674,7 +674,7 @@ rp = arg; tp = rp->rp_tty; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); ctl = rp->rp_ctlp; CtlMask = ctl->ctlmask(ctl); if (CtlMask & (1 << rp->rp_aiop)) { @@ -742,7 +742,7 @@ num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++) { rp->rp_tty = tp = tty_alloc(&rp_tty_class, rp); - callout_init_mtx(&rp->rp_timer, tty_getlock(tp), 0); + callout_init_mtx(&rp->rp_timer, ttydisc_getlock(tp), 0); rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; @@ -848,7 +848,9 @@ IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(&rp->rp_channel); + ttydisc_lock(tp); callout_reset(&rp->rp_timer, POLL_INTERVAL, rp_do_poll, rp); + ttydisc_unlock(tp); device_busy(rp->rp_ctlp->dev); return(0); @@ -860,6 +862,7 @@ struct rp_port *rp; rp = tty_softc(tp); + ttydisc_assert_locked(tp); callout_stop(&rp->rp_timer); rphardclose(tp); device_unbusy(rp->rp_ctlp->dev); Index: sys/dev/snp/snp.c =================================================================== --- sys/dev/snp/snp.c +++ sys/dev/snp/snp.c @@ -164,10 +164,14 @@ return (error); tp = ss->snp_tty; - if (tp == NULL || tty_gone(tp)) + if (tp == NULL) + return (EIO); + ttydisc_lock(tp); + if (tty_gone(tp)) { + ttydisc_unlock(tp); return (EIO); + } - tty_lock(tp); for (;;) { error = ttyoutq_read_uio(&ss->snp_outq, tp, uio); if (error != 0 || uio->uio_resid != oresid) @@ -178,7 +182,7 @@ error = EWOULDBLOCK; break; } - error = cv_wait_sig(&ss->snp_outwait, tty_getlock(tp)); + error = cv_wait_sig(&ss->snp_outwait, ttydisc_getlock(tp)); if (error != 0) break; if (tty_gone(tp)) { @@ -186,7 +190,7 @@ break; } } - tty_unlock(tp); + ttydisc_unlock(tp); return (error); } Index: sys/dev/syscons/syscons.c =================================================================== --- sys/dev/syscons/syscons.c +++ sys/dev/syscons/syscons.c @@ -410,6 +410,7 @@ u_char buf[PCBURST]; scr_stat *scp = sc_get_stat(tp); + ttydisc_assert_locked(tp); if (scp->status & SLKED || (scp == scp->sc->cur_scp && scp->sc->blink_in_progress)) return; @@ -748,7 +749,16 @@ scp = sc_get_stat(tp); if (scp == NULL) { + /* + * ttydisc lock isn't sleepable like Giant, so we must go ahead + * and drop it. The tty lock is still held, but that's Giant at + * the moment as it was before the ttydisc lock became distinct. + * This is still likely unsafe, but something to revisit once + * this particular tty device isn't Giant locked anymore. + */ + ttydisc_unlock(tp); scp = SC_STAT(tp) = alloc_scp(sc, SC_VTY(tp)); + ttydisc_lock(tp); if (ISGRAPHSC(scp)) sc_set_pixel_mode(scp, NULL, 0, 0, 16, 8); } @@ -849,9 +859,12 @@ cur_tty = SC_DEV(sc, sc->cur_scp->index); if (!tty_opened_ns(cur_tty)) continue; + ttydisc_lock(cur_tty); - if ((*sc->cur_scp->tsw->te_input)(sc->cur_scp, c, cur_tty)) + if ((*sc->cur_scp->tsw->te_input)(sc->cur_scp, c, cur_tty)) { + ttydisc_unlock(cur_tty); continue; + } switch (KEYFLAGS(c)) { case 0x0000: /* normal key */ @@ -877,6 +890,7 @@ } ttydisc_rint_done(cur_tty); + ttydisc_unlock(cur_tty); } sc->cur_scp->status |= MOUSE_HIDDEN; @@ -4240,10 +4254,12 @@ tp = SC_DEV(scp->sc, scp->sc->cur_scp->index); if (!tty_opened_ns(tp)) return; + ttydisc_lock(tp); rmap = scp->sc->scr_rmap; for (; count > 0; --count) ttydisc_rint(tp, rmap[*p++], 0); ttydisc_rint_done(tp); + ttydisc_unlock(tp); } void @@ -4254,11 +4270,13 @@ tp = SC_DEV(scp->sc, scp->sc->cur_scp->index); if (!tty_opened_ns(tp)) return; + ttydisc_lock(tp); ttydisc_rint_simple(tp, p, count); if (wakeup) { /* XXX: we can't always call ttydisc_rint_done() here! */ ttydisc_rint_done(tp); } + ttydisc_unlock(tp); } void Index: sys/dev/syscons/sysmouse.c =================================================================== --- sys/dev/syscons/sysmouse.c +++ sys/dev/syscons/sysmouse.c @@ -263,7 +263,7 @@ int x, y, z; int i, flags = 0; - tty_lock(sysmouse_tty); + ttydisc_lock(sysmouse_tty); switch (info->operation) { case MOUSE_ACTION: @@ -324,7 +324,7 @@ } ttydisc_rint_done(sysmouse_tty); -done: tty_unlock(sysmouse_tty); +done: ttydisc_unlock(sysmouse_tty); return (flags); } Index: sys/dev/uart/uart_tty.c =================================================================== --- sys/dev/uart/uart_tty.c +++ sys/dev/uart/uart_tty.c @@ -330,7 +330,7 @@ return; tp = sc->sc_u.u_tty.tp; - tty_lock(tp); + ttydisc_lock(tp); if (pend & SER_INT_RXREADY) { while (!uart_rx_empty(sc) && !sc->sc_isquelch) { @@ -366,7 +366,7 @@ if (pend & SER_INT_TXIDLE) uart_tty_outwakeup(tp); ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); } static void @@ -449,7 +449,7 @@ { if (sc->sc_u.u_tty.tp != NULL) - return (tty_getlock(sc->sc_u.u_tty.tp)); + return (ttydisc_getlock(sc->sc_u.u_tty.tp)); else return (NULL); } Index: sys/dev/usb/serial/usb_serial.c =================================================================== --- sys/dev/usb/serial/usb_serial.c +++ sys/dev/usb/serial/usb_serial.c @@ -530,6 +530,7 @@ mtx_unlock(&ucom_mtx); tty_lock(tp); + ttydisc_lock(tp); ucom_close(tp); /* close, if any */ @@ -630,10 +631,11 @@ task->termios_copy = *pt; /* - * Closing the device should be synchronous. + * The tty lock is sleepable and the ttydisc lock is shared with the USB + * parts. We can now sychronously do the USB parts while making sure + * that we're cool from termios standpoint. */ - if (fn == ucom_cfg_close) - usb_proc_mwait(&ssc->sc_tq, t0, t1); + usb_proc_mwait(&ssc->sc_tq, t0, t1); /* * In case of multiple configure requests, Index: sys/dev/virtio/console/virtio_console.c =================================================================== --- sys/dev/virtio/console/virtio_console.c +++ sys/dev/virtio/console/virtio_console.c @@ -95,6 +95,7 @@ #define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx) #define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx) +#define VTCON_PORT_ASSERT(_port, ma) mtx_assert(&(_port)->vtcport_mtx, (ma)) struct vtcon_softc_port { struct vtcon_softc *vcsp_sc; @@ -1293,6 +1294,8 @@ uint32_t len; int i, deq; + /* Effectively the ttydisc lock. */ + VTCON_PORT_ASSERT(port, MA_OWNED); tp = port->vtcport_tty; vq = port->vtcport_invq; @@ -1383,10 +1386,13 @@ { struct vtcon_port *port; + /* Effectively VTCON_PORT_LOCK. */ + ttydisc_assert_locked(tp); port = tty_softc(tp); - if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) + if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) { return (ENXIO); + } vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -1398,6 +1404,8 @@ { struct vtcon_port *port; + /* Effectively VTCON_PORT_LOCK. */ + ttydisc_assert_locked(tp); port = tty_softc(tp); if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) @@ -1413,6 +1421,8 @@ char buf[VTCON_BULK_BUFSZ]; int len; + /* Effectively VTCON_PORT_LOCK. */ + ttydisc_assert_locked(tp); port = tty_softc(tp); if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) Index: sys/dev/xen/console/xen_console.c =================================================================== --- sys/dev/xen/console/xen_console.c +++ sys/dev/xen/console/xen_console.c @@ -515,7 +515,7 @@ cons = tty_softc(tp); - tty_assert_locked(tp); + ttydisc_assert_locked(tp); /* * Don't transmit any character if the buffer is full. Otherwise, @@ -555,7 +555,7 @@ xencons_rx(cons); - tty_lock(tp); + ttydisc_lock(tp); while ((ret = xencons_getc(cons)) != -1) { #ifdef KDB kdb_alt_break(ret, &cons->altbrk); @@ -563,7 +563,7 @@ ttydisc_rint(tp, ret, 0); } ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); /* Try to flush remaining characters if necessary */ xencons_tx_flush(cons, 0); @@ -682,6 +682,7 @@ struct xencons_priv *cons; cons = tty_softc(tp); + ttydisc_assert_locked(tp); callout_stop(&cons->callout); @@ -734,7 +735,7 @@ tty_makedev(tp, NULL, "%s%r", driver_name, 0); device_set_softc(dev, tp); - callout_init_mtx(&cons->callout, tty_getlock(tp), 0); + callout_init_mtx(&cons->callout, ttydisc_getlock(tp), 0); err = cons->ops->init(dev, tp, xencons_intr); if (err != 0) { Index: sys/kern/kern_cons.c =================================================================== --- sys/kern/kern_cons.c +++ sys/kern/kern_cons.c @@ -632,17 +632,17 @@ int c; if (constty != NULL) { - tty_lock(constty); + ttydisc_lock(constty); while ((c = msgbuf_getchar(&consmsgbuf)) != -1) { if (tty_putchar(constty, c) < 0) { - tty_unlock(constty); + ttydisc_unlock(constty); constty = NULL; break; } } if (constty != NULL) - tty_unlock(constty); + ttydisc_unlock(constty); } if (constty != NULL) { callout_reset(&conscallout, hz / constty_wakeups_per_second, Index: sys/kern/kern_proc.c =================================================================== --- sys/kern/kern_proc.c +++ sys/kern/kern_proc.c @@ -861,10 +861,10 @@ */ if (tp != NULL) { - tty_lock(tp); + ttydisc_lock(tp); if (tp->t_session == sp) tty_signal_pgrp(tp, SIGHUP); - tty_unlock(tp); + ttydisc_unlock(tp); } if (ttyvp != NULL) { Index: sys/kern/subr_prf.c =================================================================== --- sys/kern/subr_prf.c +++ sys/kern/subr_prf.c @@ -198,10 +198,10 @@ pca.flags = TOTTY; pca.p_bufr = NULL; va_start(ap, fmt); - tty_lock(pca.tty); + ttydisc_lock(pca.tty); sx_sunlock(&proctree_lock); retval = kvprintf(fmt, putchar, &pca, 10, ap); - tty_unlock(pca.tty); + ttydisc_unlock(pca.tty); va_end(ap); return (retval); } Index: sys/kern/subr_terminal.c =================================================================== --- sys/kern/subr_terminal.c +++ sys/kern/subr_terminal.c @@ -321,7 +321,7 @@ return; c = TCHAR_CHARACTER(c); - tty_lock(tp); + ttydisc_lock(tp); /* * Conversion to UTF-8. */ @@ -353,7 +353,7 @@ ttydisc_rint_simple(tp, str, sizeof str); } ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); } void @@ -365,10 +365,10 @@ if (tp == NULL) return; - tty_lock(tp); + ttydisc_lock(tp); ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); } void @@ -385,10 +385,10 @@ if (str == NULL) return; - tty_lock(tp); + ttydisc_lock(tp); ttydisc_rint_simple(tp, str, strlen(str)); ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); } /* @@ -420,6 +420,7 @@ size_t olen; unsigned int flags = 0; + ttydisc_assert_locked(tp); while ((olen = ttydisc_getc(tp, obuf, sizeof obuf)) > 0) { TERMINAL_LOCK_TTY(tm); if (!(tm->tm_flags & TF_MUTE)) { @@ -478,8 +479,10 @@ * the TTY when handling ioctls. */ tty_unlock(tp); + ttydisc_unlock(tp); error = tm->tm_class->tc_ioctl(tm, cmd, data, td); tty_lock(tp); + ttydisc_lock(tp); return (error); } Index: sys/kern/tty.c =================================================================== --- sys/kern/tty.c +++ sys/kern/tty.c @@ -125,12 +125,14 @@ size_t bs = 0; int error; + tty_assert_locked(tp); + ttydisc_assert_locked(tp); /* Provide an input buffer for 2 seconds of data. */ if (tp->t_termios.c_cflag & CREAD) bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX); error = ttyinq_setsize(&tp->t_inq, tp, bs); if (error != 0) - return (error); + goto out; /* Set low watermark at 10% (when 90% is available). */ tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10; @@ -139,12 +141,13 @@ bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX); error = ttyoutq_setsize(&tp->t_outq, tp, bs); if (error != 0) - return (error); + goto out; /* Set low watermark at 10% (when 90% is available). */ tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10; - return (0); +out: + return (error); } static int @@ -154,6 +157,7 @@ size_t bytes; int error; + ttydisc_assert_locked(tp); if (ttyhook_hashook(tp, getc_inject)) /* buffer is inaccessible */ return (0); @@ -228,21 +232,33 @@ ttydev_leave(struct tty *tp) { + /* + * The locking in this function, like elsewhere, is also a nightmare + * that will be corrected when the tty lock gets converted to a + * sleepable lock. + */ tty_assert_locked(tp); if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) { /* Device is still opened somewhere. */ + if (ttydisc_lock_owned(tp)) + ttydisc_unlock(tp); tty_unlock(tp); return; } + if (!ttydisc_lock_owned(tp)) + ttydisc_lock(tp); tp->t_flags |= TF_OPENCLOSE; /* Remove console TTY. */ if (constty == tp) constty_clear(); - /* Drain any output. */ + /* + * Drain any output. Pick up ttydisc_lock in advance of tty_drain, + * which will perhaps want to sleep and can't handle having both locks. + */ if (!tty_gone(tp)) tty_drain(tp, 1); @@ -262,6 +278,7 @@ tp->t_flags &= ~TF_OPENCLOSE; cv_broadcast(&tp->t_dcdwait); + ttydisc_unlock(tp); tty_rel_free(tp); } @@ -288,6 +305,7 @@ * Block when other processes are currently opening or closing * the TTY. */ + ttydisc_lock(tp); while (tp->t_flags & TF_OPENCLOSE) { error = tty_wait(tp, &tp->t_dcdwait); if (error != 0) { @@ -296,6 +314,7 @@ } } tp->t_flags |= TF_OPENCLOSE; + ttydisc_unlock(tp); /* * Make sure the "tty" and "cua" device cannot be opened at the @@ -319,11 +338,15 @@ } if (!tty_opened(tp)) { - /* Set proper termios flags. */ + /* + * Set proper termios flags. Further mutations of t_termios + * will require the ttydisc lock. + */ if (TTY_CALLOUT(tp, dev)) tp->t_termios = tp->t_termios_init_out; else tp->t_termios = tp->t_termios_init_in; + ttydisc_lock(tp); ttydevsw_param(tp, &tp->t_termios); /* Prevent modem control on callout devices and /dev/console. */ if (TTY_CALLOUT(tp, dev) || dev == dev_console) @@ -338,8 +361,11 @@ ttydisc_open(tp); error = tty_watermarks(tp); + /* tty_watermarks dropped the ttydisc lock. */ if (error != 0) goto done; + } else { + ttydisc_lock(tp); } /* Wait for Carrier Detect. */ @@ -361,7 +387,10 @@ MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || (tp->t_flags & TF_OPENED_OUT) == 0); -done: tp->t_flags &= ~TF_OPENCLOSE; +done: + if (!ttydisc_lock_owned(tp)) + ttydisc_lock(tp); + tp->t_flags &= ~TF_OPENCLOSE; cv_broadcast(&tp->t_dcdwait); ttydev_leave(tp); @@ -375,6 +404,7 @@ struct tty *tp = dev->si_drv1; tty_lock(tp); + ttydisc_lock(tp); /* * Don't actually close the device if it is being used as the @@ -388,6 +418,7 @@ tp->t_flags &= ~(TF_OPENED_IN|TF_OPENED_OUT); if (tp->t_flags & TF_OPENED) { + ttydisc_unlock(tp); tty_unlock(tp); return (0); } @@ -413,8 +444,8 @@ tty_is_ctty(struct tty *tp, struct proc *p) { - tty_assert_locked(tp); - + KASSERT(tty_lock_owned(tp) || ttydisc_lock_owned(tp), + ("neither ttymtx nor ttydiscmtx owned")); return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT); } @@ -427,7 +458,7 @@ int error; MPASS(sig == SIGTTIN || sig == SIGTTOU); - tty_assert_locked(tp); + ttydisc_assert_locked(tp); for (;;) { PROC_LOCK(p); @@ -487,10 +518,18 @@ struct tty *tp = dev->si_drv1; int error; + /* + * We'll pick up the tty lock just long enough to ensure we're alive and + * well, then we'll pick up the ttydisc lock and drop the tty lock -- we + * won't be needing it for the rest of this as we serialize I/O on the + * ttydisc lock. + */ error = ttydev_enter(tp); if (error) goto done; + ttydisc_lock(tp); error = ttydisc_read(tp, uio, ioflag); + ttydisc_unlock(tp); tty_unlock(tp); /* @@ -511,6 +550,7 @@ error = ttydev_enter(tp); if (error) return (error); + ttydisc_lock(tp); if (tp->t_termios.c_lflag & TOSTOP) { error = tty_wait_background(tp, curthread, SIGTTOU); @@ -535,7 +575,8 @@ cv_signal(&tp->t_outserwait); } -done: tty_unlock(tp); +done: ttydisc_unlock(tp); + tty_unlock(tp); return (error); } @@ -586,7 +627,9 @@ * If the ioctl() causes the TTY to be modified, let it * wait in the background. */ + ttydisc_lock(tp); error = tty_wait_background(tp, curthread, SIGTTOU); + ttydisc_unlock(tp); if (error) goto done; } @@ -635,6 +678,7 @@ if (error) return ((events & (POLLIN|POLLRDNORM)) | POLLHUP); + ttydisc_lock(tp); if (events & (POLLIN|POLLRDNORM)) { /* See if we can read something. */ if (ttydisc_read_poll(tp) > 0) @@ -649,6 +693,7 @@ if (ttydisc_write_poll(tp) > 0) revents |= events & (POLLOUT|POLLWRNORM); } + ttydisc_unlock(tp); if (revents == 0) { if (events & (POLLIN|POLLRDNORM)) @@ -697,7 +742,7 @@ { struct tty *tp = kn->kn_hook; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) { kn->kn_flags |= EV_EOF; @@ -721,7 +766,7 @@ { struct tty *tp = kn->kn_hook; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (tty_gone(tp)) { kn->kn_flags |= EV_EOF; @@ -758,12 +803,16 @@ case EVFILT_READ: kn->kn_hook = tp; kn->kn_fop = &tty_kqops_read; + ttydisc_lock(tp); knlist_add(&tp->t_inpoll.si_note, kn, 1); + ttydisc_unlock(tp); break; case EVFILT_WRITE: kn->kn_hook = tp; kn->kn_fop = &tty_kqops_write; + ttydisc_lock(tp); knlist_add(&tp->t_outpoll.si_note, kn, 1); + ttydisc_unlock(tp); break; default: error = EINVAL; @@ -1033,7 +1082,7 @@ } struct tty * -tty_alloc_mutex(struct ttydevsw *tsw, void *sc, struct mtx *mutex) +tty_alloc_mutex(struct ttydevsw *tsw, void *sc, struct mtx *discmtx) { struct tty *tp; @@ -1072,16 +1121,30 @@ cv_init(&tp->t_bgwait, "ttybg"); cv_init(&tp->t_dcdwait, "ttydcd"); - /* Allow drivers to use a custom mutex to lock the TTY. */ - if (mutex != NULL) { - tp->t_mtx = mutex; + if (discmtx == &Giant) { + /* + * This should go away when the syscons/Giant problem is + * resolved. For now, we still have the one driver locked by + * Giant, so we have to special-case it and maintain the old + * behavior. It will get a separate discmtx and maintain Giant + * as the main tty lock. + */ + + tp->t_mtx = discmtx; + discmtx = NULL; } else { - tp->t_mtx = &tp->t_mtxobj; - mtx_init(&tp->t_mtxobj, "ttymtx", NULL, MTX_DEF); + sx_init(&tp->t_sxobj, "ttysx"); } - knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_mtx); - knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx); + if (discmtx != NULL) { + tp->t_discmtx = discmtx; + } else { + tp->t_discmtx = &tp->t_discmtxobj; + mtx_init(&tp->t_discmtxobj, "ttydiscmtx", NULL, MTX_DEF); + } + + knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_discmtx); + knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_discmtx); return (tp); } @@ -1111,8 +1174,11 @@ cv_destroy(&tp->t_dcdwait); cv_destroy(&tp->t_outserwait); - if (tp->t_mtx == &tp->t_mtxobj) - mtx_destroy(&tp->t_mtxobj); + /* We didn't bother initializing the sx if we were given Giant. */ + if (tp->t_mtx == NULL) + sx_destroy(&tp->t_sxobj); + if (tp->t_discmtx == &tp->t_discmtxobj) + mtx_destroy(&tp->t_discmtxobj); ttydevsw_free(tp); free(tp, M_TTY); } @@ -1155,9 +1221,11 @@ MPASS(tp->t_sessioncnt > 0); tty_assert_locked(tp); + ttydisc_lock(tp); if (tp->t_pgrp == pg) tp->t_pgrp = NULL; + ttydisc_unlock(tp); tty_unlock(tp); } @@ -1165,14 +1233,17 @@ tty_rel_sess(struct tty *tp, struct session *sess) { + tty_assert_locked(tp); MPASS(tp->t_sessioncnt > 0); + ttydisc_lock(tp); /* Current session has left. */ if (tp->t_session == sess) { tp->t_session = NULL; MPASS(tp->t_pgrp == NULL); } tp->t_sessioncnt--; + ttydisc_unlock(tp); tty_rel_free(tp); } @@ -1183,6 +1254,8 @@ tty_assert_locked(tp); MPASS(!tty_gone(tp)); + if (!ttydisc_lock_owned(tp)) + ttydisc_lock(tp); /* Simulate carrier removal. */ ttydisc_modem(tp, 0); @@ -1192,6 +1265,7 @@ cv_broadcast(&tp->t_dcdwait); tp->t_flags |= TF_GONE; + ttydisc_unlock(tp); tty_rel_free(tp); } @@ -1233,6 +1307,9 @@ return (EPERM); } + ttydisc_lock(tp); + tp->t_sessioncnt--; + ttydisc_unlock(tp); PROC_LOCK(p); SESS_LOCK(session); vp = session->s_ttyvp; @@ -1241,7 +1318,6 @@ session->s_ttydp = NULL; SESS_UNLOCK(session); - tp->t_sessioncnt--; p->p_flag &= ~P_CONTROLT; PROC_UNLOCK(p); sx_xunlock(&proctree_lock); @@ -1460,7 +1536,7 @@ { struct proc *p; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(sig >= 1 && sig < NSIG); /* Make signals start output again. */ @@ -1479,7 +1555,7 @@ { ksiginfo_t ksi; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(sig >= 1 && sig < NSIG); /* Make signals start output again. */ @@ -1501,6 +1577,7 @@ tty_wakeup(struct tty *tp, int flags) { + ttydisc_assert_locked(tp); if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL) pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); @@ -1521,11 +1598,21 @@ { int error; int revokecnt = tp->t_revokecnt; + bool locktty; - tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); + ttydisc_lock_assert(tp, MA_OWNED | MA_NOTRECURSED); MPASS(!tty_gone(tp)); - error = cv_wait_sig(cv, tp->t_mtx); + locktty = tty_lock_owned(tp); + if (locktty) + tty_unlock(tp); + error = cv_wait_sig(cv, ttydisc_getlock(tp)); + /* If we had the tty lock coming in, relock it. */ + if (locktty) { + ttydisc_unlock(tp); + tty_lock(tp); + ttydisc_lock(tp); + } /* Bail out when the device slipped away. */ if (tty_gone(tp)) @@ -1543,11 +1630,21 @@ { int error; int revokecnt = tp->t_revokecnt; + bool locktty; - tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); + ttydisc_lock_assert(tp, MA_OWNED | MA_NOTRECURSED); MPASS(!tty_gone(tp)); - error = cv_timedwait_sig(cv, tp->t_mtx, hz); + locktty = tty_lock_owned(tp); + if (locktty) + tty_unlock(tp); + error = cv_timedwait_sig(cv, ttydisc_getlock(tp), hz); + /* If we had the tty lock coming in, relock it. */ + if (locktty) { + ttydisc_unlock(tp); + tty_lock(tp); + ttydisc_lock(tp); + } /* Bail out when the device slipped away. */ if (tty_gone(tp)) @@ -1564,6 +1661,7 @@ tty_flush(struct tty *tp, int flags) { + ttydisc_assert_locked(tp); if (flags & FWRITE) { tp->t_flags &= ~TF_HIWAT_OUT; ttyoutq_flush(&tp->t_outq); @@ -1588,10 +1686,14 @@ tty_set_winsize(struct tty *tp, const struct winsize *wsz) { + ttydisc_assert_unlocked(tp); + tty_assert_locked(tp); if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0) return; tp->t_winsize = *wsz; + ttydisc_lock(tp); tty_signal_pgrp(tp, SIGWINCH); + ttydisc_unlock(tp); } static int @@ -1607,30 +1709,42 @@ * shifted. I don't know why. */ case TIOCSDTR: + ttydisc_lock(tp); ttydevsw_modem(tp, SER_DTR, 0); + ttydisc_unlock(tp); return (0); case TIOCCDTR: + ttydisc_lock(tp); ttydevsw_modem(tp, 0, SER_DTR); + ttydisc_unlock(tp); return (0); case TIOCMSET: { int bits = *(int *)data; + ttydisc_lock(tp); ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1); + ttydisc_unlock(tp); return (0); } case TIOCMBIS: { int bits = *(int *)data; + ttydisc_lock(tp); ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0); + ttydisc_unlock(tp); return (0); } case TIOCMBIC: { int bits = *(int *)data; + ttydisc_lock(tp); ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1); + ttydisc_unlock(tp); return (0); } case TIOCMGET: + ttydisc_lock(tp); *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1); + ttydisc_unlock(tp); return (0); case FIOASYNC: @@ -1676,6 +1790,7 @@ case TIOCSETAF: { struct termios *t = data; + ttydisc_lock(tp); /* * Who makes up these funny rules? According to POSIX, * input baud rate is set equal to the output baud rate @@ -1693,8 +1808,10 @@ /* Set terminal flags through tcsetattr(). */ if (cmd == TIOCSETAW || cmd == TIOCSETAF) { error = tty_drain(tp, 0); - if (error) + if (error) { + ttydisc_unlock(tp); return (error); + } if (cmd == TIOCSETAF) tty_flush(tp, FREAD); } @@ -1709,8 +1826,10 @@ tp->t_termios.c_ispeed != t->c_ispeed || tp->t_termios.c_ospeed != t->c_ospeed)) { error = ttydevsw_param(tp, t); - if (error) + if (error) { + ttydisc_unlock(tp); return (error); + } /* XXX: CLOCAL? */ @@ -1720,8 +1839,10 @@ /* Baud rate has changed - update watermarks. */ error = tty_watermarks(tp); - if (error) + if (error) { + ttydisc_unlock(tp); return (error); + } } /* Copy new non-device driver parameters. */ @@ -1752,6 +1873,7 @@ ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP); else ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP); + ttydisc_unlock(tp); return (0); } case TIOCGETD: @@ -1815,13 +1937,16 @@ } /* Connect the session to the TTY. */ + ttydisc_lock(tp); tp->t_session = p->p_session; tp->t_session->s_ttyp = tp; tp->t_sessioncnt++; - sx_xunlock(&proctree_lock); /* Assign foreground process group. */ tp->t_pgrp = p->p_pgrp; + ttydisc_unlock(tp); + sx_xunlock(&proctree_lock); + PROC_LOCK(p); p->p_flag |= P_CONTROLT; PROC_UNLOCK(p); @@ -1856,7 +1981,9 @@ sx_sunlock(&proctree_lock); return (ENOTTY); } + ttydisc_lock(tp); tp->t_pgrp = pg; + ttydisc_unlock(tp); sx_sunlock(&proctree_lock); /* Wake up the background process groups. */ @@ -1870,14 +1997,21 @@ flags = (FREAD|FWRITE); else flags &= (FREAD|FWRITE); + ttydisc_lock(tp); tty_flush(tp, flags); + ttydisc_unlock(tp); return (0); } case TIOCDRAIN: /* Drain TTY output. */ - return tty_drain(tp, 0); + ttydisc_lock(tp); + error = tty_drain(tp, 0); + ttydisc_unlock(tp); + return (error); case TIOCGDRAINWAIT: + ttydisc_lock(tp); *(int *)data = tp->t_drainwait; + ttydisc_unlock(tp); return (0); case TIOCSDRAINWAIT: error = priv_check(td, PRIV_TTY_DRAINWAIT); @@ -1917,22 +2051,32 @@ tty_set_winsize(tp, data); return (0); case TIOCEXCL: + ttydisc_lock(tp); tp->t_flags |= TF_EXCLUDE; + ttydisc_unlock(tp); return (0); case TIOCNXCL: + ttydisc_lock(tp); tp->t_flags &= ~TF_EXCLUDE; + ttydisc_unlock(tp); return (0); case TIOCSTOP: + ttydisc_lock(tp); tp->t_flags |= TF_STOPPED; ttydevsw_pktnotify(tp, TIOCPKT_STOP); + ttydisc_unlock(tp); return (0); case TIOCSTART: + ttydisc_lock(tp); tp->t_flags &= ~TF_STOPPED; ttydevsw_outwakeup(tp); ttydevsw_pktnotify(tp, TIOCPKT_START); + ttydisc_unlock(tp); return (0); case TIOCSTAT: + ttydisc_lock(tp); tty_info(tp); + ttydisc_unlock(tp); return (0); case TIOCSTI: if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI)) @@ -1940,8 +2084,10 @@ if (!tty_is_ctty(tp, td->td_proc) && priv_check(td, PRIV_TTY_STI)) return (EACCES); + ttydisc_lock(tp); ttydisc_rint(tp, *(char *)data, 0); ttydisc_rint_done(tp); + ttydisc_unlock(tp); return (0); } @@ -1962,7 +2108,10 @@ if (tty_gone(tp)) return (ENXIO); + /* tty device driver may change parameters related to I/O. */ + ttydisc_lock(tp); error = ttydevsw_ioctl(tp, cmd, data, td); + ttydisc_unlock(tp); if (error == ENOIOCTL) error = tty_generic_ioctl(tp, cmd, data, fflag, td); @@ -1983,6 +2132,7 @@ tty_checkoutq(struct tty *tp) { + ttydisc_assert_locked(tp); /* 256 bytes should be enough to print a log message. */ return (ttyoutq_bytesleft(&tp->t_outq) >= 256); } @@ -1991,6 +2141,7 @@ tty_hiwat_in_block(struct tty *tp) { + ttydisc_assert_locked(tp); if ((tp->t_flags & TF_HIWAT_IN) == 0 && tp->t_termios.c_iflag & IXOFF && tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) { @@ -2011,6 +2162,7 @@ tty_hiwat_in_unblock(struct tty *tp) { + ttydisc_assert_locked(tp); if (tp->t_flags & TF_HIWAT_IN && tp->t_termios.c_iflag & IXOFF && tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) { @@ -2101,6 +2253,7 @@ if (tp->t_flags & TF_HOOK) goto done3; + ttydisc_lock(tp); tp->t_flags |= TF_HOOK; tp->t_hook = th; tp->t_hooksoftc = softc; @@ -2114,6 +2267,7 @@ if (!ttyhook_hashook(tp, rint) && ttyhook_hashook(tp, rint_bypass)) th->th_rint = ttyhook_defrint; + ttydisc_unlock(tp); done3: tty_unlock(tp); done2: dev_relthread(dev, ref); done1: fdrop(fp, curthread); @@ -2128,11 +2282,13 @@ MPASS(tp->t_flags & TF_HOOK); /* Disconnect the hook. */ + ttydisc_lock(tp); tp->t_flags &= ~TF_HOOK; tp->t_hook = NULL; /* Maybe we need to leave bypass mode. */ ttydisc_optimize(tp); + ttydisc_unlock(tp); /* Maybe deallocate the TTY as well. */ tty_rel_free(tp); Index: sys/kern/tty_info.c =================================================================== --- sys/kern/tty_info.c +++ sys/kern/tty_info.c @@ -266,7 +266,8 @@ char comm[MAXCOMLEN + 1]; struct rusage ru; - tty_assert_locked(tp); + /* ttydisc lock is sufficient for everything we're doing here */ + ttydisc_assert_locked(tp); if (tty_checkoutq(tp) == 0) return; Index: sys/kern/tty_inq.c =================================================================== --- sys/kern/tty_inq.c +++ sys/kern/tty_inq.c @@ -119,6 +119,8 @@ { struct ttyinq_block *tib; + tty_assert_locked(tp); + ttydisc_assert_locked(tp); ti->ti_quota = howmany(size, TTYINQ_DATASIZE); while (ti->ti_quota > ti->ti_nblocks) { @@ -126,15 +128,12 @@ * List is getting bigger. * Add new blocks to the tail of the list. * - * We must unlock the TTY temporarily, because we need - * to allocate memory. This won't be a problem, because - * in the worst case, another thread ends up here, which - * may cause us to allocate too many blocks, but this - * will be caught by the loop below. + * We must unlock the ttydisc, but we're still holding on to + * the tty lock so we should avoid problems. */ - tty_unlock(tp); + ttydisc_unlock(tp); tib = uma_zalloc(ttyinq_zone, M_WAITOK); - tty_lock(tp); + ttydisc_lock(tp); if (tty_gone(tp)) { uma_zfree(ttyinq_zone, tib); @@ -167,6 +166,7 @@ size_t rlen, size_t flen) { + ttydisc_assert_locked(tp); MPASS(rlen <= uio->uio_resid); while (rlen > 0) { @@ -229,10 +229,10 @@ * userspace. We may need to flush trailing * bytes, like EOF characters. */ - tty_unlock(tp); + ttydisc_unlock(tp); error = uiomove(tib->tib_data + cbegin, clen - flen, uio); - tty_lock(tp); + ttydisc_lock(tp); /* Block can now be readded to the list. */ TTYINQ_RECYCLE(ti, tib); @@ -247,9 +247,9 @@ MPASS(ti->ti_begin < TTYINQ_DATASIZE); /* Temporary unlock and copy the data to userspace. */ - tty_unlock(tp); + ttydisc_unlock(tp); error = uiomove(ob, clen - flen, uio); - tty_lock(tp); + ttydisc_lock(tp); } if (error != 0) Index: sys/kern/tty_outq.c =================================================================== --- sys/kern/tty_outq.c +++ sys/kern/tty_outq.c @@ -96,6 +96,8 @@ { struct ttyoutq_block *tob; + tty_assert_locked(tp); + ttydisc_assert_locked(tp); to->to_quota = howmany(size, TTYOUTQ_DATASIZE); while (to->to_quota > to->to_nblocks) { @@ -103,20 +105,12 @@ * List is getting bigger. * Add new blocks to the tail of the list. * - * We must unlock the TTY temporarily, because we need - * to allocate memory. This won't be a problem, because - * in the worst case, another thread ends up here, which - * may cause us to allocate too many blocks, but this - * will be caught by the loop below. + * We must unlock the ttydisc, but we're still holding on to + * the tty lock so we should avoid problems. */ - tty_unlock(tp); + ttydisc_unlock(tp); tob = uma_zalloc(ttyoutq_zone, M_WAITOK); - tty_lock(tp); - - if (tty_gone(tp)) { - uma_zfree(ttyoutq_zone, tob); - return (ENXIO); - } + ttydisc_lock(tp); TTYOUTQ_INSERT_TAIL(to, tob); } @@ -204,6 +198,7 @@ ttyoutq_read_uio(struct ttyoutq *to, struct tty *tp, struct uio *uio) { + ttydisc_assert_locked(tp); while (uio->uio_resid > 0) { int error; struct ttyoutq_block *tob; @@ -247,9 +242,9 @@ to->to_end -= TTYOUTQ_DATASIZE; /* Temporary unlock and copy the data to userspace. */ - tty_unlock(tp); + ttydisc_unlock(tp); error = uiomove(tob->tob_data + cbegin, clen, uio); - tty_lock(tp); + ttydisc_lock(tp); /* Block can now be readded to the list. */ TTYOUTQ_RECYCLE(to, tob); @@ -264,9 +259,9 @@ MPASS(to->to_begin < TTYOUTQ_DATASIZE); /* Temporary unlock and copy the data to userspace. */ - tty_unlock(tp); + ttydisc_unlock(tp); error = uiomove(ob, clen, uio); - tty_lock(tp); + ttydisc_lock(tp); } if (error != 0) Index: sys/kern/tty_pts.c =================================================================== --- sys/kern/tty_pts.c +++ sys/kern/tty_pts.c @@ -84,20 +84,20 @@ * Per-PTS structure. * * List of locks - * (t) locked by tty_lock() + * (d) locked by ttydisc_lock() * (c) const until freeing */ struct pts_softc { int pts_unit; /* (c) Device unit number. */ - unsigned int pts_flags; /* (t) Device flags. */ + unsigned int pts_flags; /* (d) Device flags. */ #define PTS_PKT 0x1 /* Packet mode. */ #define PTS_FINISHED 0x2 /* Return errors on read()/write(). */ - char pts_pkt; /* (t) Unread packet mode data. */ + char pts_pkt; /* (d) Unread packet mode data. */ - struct cv pts_inwait; /* (t) Blocking write() on master. */ - struct selinfo pts_inpoll; /* (t) Select queue for write(). */ - struct cv pts_outwait; /* (t) Blocking read() on master. */ - struct selinfo pts_outpoll; /* (t) Select queue for read(). */ + struct cv pts_inwait; /* (d) Blocking write() on master. */ + struct selinfo pts_inpoll; /* (d) Select queue for write(). */ + struct cv pts_outwait; /* (d) Blocking read() on master. */ + struct selinfo pts_outpoll; /* (d) Select queue for read(). */ #ifdef PTS_EXTERNAL struct cdev *pts_cdev; /* (c) Master device node. */ @@ -122,7 +122,7 @@ if (uio->uio_resid == 0) return (0); - tty_lock(tp); + ttydisc_lock(tp); for (;;) { /* @@ -133,7 +133,7 @@ if (psc->pts_flags & PTS_PKT && psc->pts_pkt) { pkt = psc->pts_pkt; psc->pts_pkt = 0; - tty_unlock(tp); + ttydisc_unlock(tp); error = ureadc(pkt, uio); return (error); @@ -154,11 +154,11 @@ * consumers aren't multithreaded. */ - tty_unlock(tp); + ttydisc_unlock(tp); error = ureadc(TIOCPKT_DATA, uio); if (error) return (error); - tty_lock(tp); + ttydisc_lock(tp); } error = ttydisc_getc_uio(tp, uio); @@ -174,12 +174,12 @@ error = EWOULDBLOCK; break; } - error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx); + error = cv_wait_sig(&psc->pts_outwait, ttydisc_getlock(tp)); if (error != 0) break; } - tty_unlock(tp); + ttydisc_unlock(tp); return (error); } @@ -202,7 +202,7 @@ iblen = MIN(uio->uio_resid, sizeof ib); error = uiomove(ib, iblen, uio); - tty_lock(tp); + ttydisc_lock(tp); if (error != 0) { iblen = 0; goto done; @@ -236,18 +236,19 @@ /* Wake up users on the slave side. */ ttydisc_rint_done(tp); - error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx); + error = cv_wait_sig(&psc->pts_inwait, + ttydisc_getlock(tp)); if (error != 0) goto done; } while (iblen > 0); if (uio->uio_resid == 0) break; - tty_unlock(tp); + ttydisc_unlock(tp); } done: ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); /* * Don't account for the part of the buffer that we couldn't @@ -273,14 +274,14 @@ /* This device supports non-blocking operation. */ return (0); case FIONREAD: - tty_lock(tp); + ttydisc_lock(tp); if (psc->pts_flags & PTS_FINISHED) { /* Force read() to be called. */ *(int *)data = 1; } else { *(int *)data = ttydisc_getc_poll(tp); } - tty_unlock(tp); + ttydisc_unlock(tp); return (0); case FIODGNAME: #ifdef COMPAT_FREEBSD32 @@ -312,9 +313,9 @@ #ifdef PTS_LINUX case TIOCGETA: /* Obtain terminal flags through tcgetattr(). */ - tty_lock(tp); + ttydisc_lock(tp); *(struct termios*)data = tp->t_termios; - tty_unlock(tp); + ttydisc_unlock(tp); return (0); #endif /* PTS_LINUX */ case TIOCSETAF: @@ -339,21 +340,21 @@ #endif /* PTS_COMPAT || PTS_LINUX */ case TIOCGPGRP: /* Get the foreground process group ID. */ - tty_lock(tp); + ttydisc_lock(tp); if (tp->t_pgrp != NULL) *(int *)data = tp->t_pgrp->pg_id; else *(int *)data = NO_PID; - tty_unlock(tp); + ttydisc_unlock(tp); return (0); case TIOCGSID: /* Get the session leader process ID. */ - tty_lock(tp); + ttydisc_lock(tp); if (tp->t_session == NULL) error = ENOTTY; else *(int *)data = tp->t_session->s_sid; - tty_unlock(tp); + ttydisc_unlock(tp); return (error); case TIOCPTMASTER: /* Yes, we are a pseudo-terminal master. */ @@ -364,18 +365,18 @@ if (sig < 1 || sig >= NSIG) return (EINVAL); - tty_lock(tp); + ttydisc_lock(tp); tty_signal_pgrp(tp, sig); - tty_unlock(tp); + ttydisc_unlock(tp); return (0); case TIOCPKT: /* Enable/disable packet mode. */ - tty_lock(tp); + ttydisc_lock(tp); if (*(int *)data) psc->pts_flags |= PTS_PKT; else psc->pts_flags &= ~PTS_PKT; - tty_unlock(tp); + ttydisc_unlock(tp); return (0); } @@ -397,11 +398,11 @@ struct pts_softc *psc = tty_softc(tp); int revents = 0; - tty_lock(tp); + ttydisc_lock(tp); if (psc->pts_flags & PTS_FINISHED) { /* Slave device is not opened. */ - tty_unlock(tp); + ttydisc_unlock(tp); return ((events & (POLLIN|POLLRDNORM)) | POLLHUP); } @@ -435,7 +436,7 @@ selrecord(td, &psc->pts_inpoll); } - tty_unlock(tp); + ttydisc_unlock(tp); return (revents); } @@ -776,8 +777,8 @@ psc->pts_cred = crhold(cred); tp = tty_alloc(&pts_class, psc); - knlist_init_mtx(&psc->pts_inpoll.si_note, tp->t_mtx); - knlist_init_mtx(&psc->pts_outpoll.si_note, tp->t_mtx); + knlist_init_mtx(&psc->pts_inpoll.si_note, ttydisc_getlock(tp)); + knlist_init_mtx(&psc->pts_outpoll.si_note, ttydisc_getlock(tp)); /* Expose the slave device as well. */ tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit); @@ -823,8 +824,8 @@ psc->pts_cred = crhold(cred); tp = tty_alloc(&pts_class, psc); - knlist_init_mtx(&psc->pts_inpoll.si_note, tp->t_mtx); - knlist_init_mtx(&psc->pts_outpoll.si_note, tp->t_mtx); + knlist_init_mtx(&psc->pts_inpoll.si_note, ttydisc_getlock(tp)); + knlist_init_mtx(&psc->pts_outpoll.si_note, ttydisc_getlock(tp)); /* Expose the slave device as well. */ tty_makedev(tp, td->td_ucred, "%s", name); Index: sys/kern/tty_ttydisc.c =================================================================== --- sys/kern/tty_ttydisc.c +++ sys/kern/tty_ttydisc.c @@ -93,6 +93,7 @@ ttydisc_close(struct tty *tp) { + ttydisc_assert_locked(tp); /* Clean up our flags when leaving the discipline. */ tp->t_flags &= ~(TF_STOPPED|TF_HIWAT|TF_ZOMBIE); @@ -327,6 +328,7 @@ int error; tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (uio->uio_resid == 0) return (0); @@ -459,6 +461,7 @@ unsigned int oblen = 0; tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (tp->t_flags & TF_ZOMBIE) return (EIO); @@ -477,18 +480,13 @@ /* Step 1: read data. */ obstart = ob; nlen = MIN(uio->uio_resid, sizeof ob); - tty_unlock(tp); + ttydisc_unlock(tp); error = uiomove(ob, nlen, uio); - tty_lock(tp); + ttydisc_lock(tp); if (error != 0) break; oblen = nlen; - if (tty_gone(tp)) { - error = ENXIO; - break; - } - MPASS(oblen > 0); /* Step 2: process data. */ @@ -573,7 +571,7 @@ void ttydisc_optimize(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (ttyhook_hashook(tp, rint_bypass)) { tp->t_flags |= TF_BYPASS; @@ -594,7 +592,7 @@ ttydisc_modem(struct tty *tp, int open) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (open) cv_broadcast(&tp->t_dcdwait); @@ -842,7 +840,7 @@ char ob[3] = { 0xff, 0x00 }; size_t ol; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); atomic_add_long(&tty_nin, 1); @@ -1085,7 +1083,7 @@ { size_t ret; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(tp->t_flags & TF_BYPASS); @@ -1106,7 +1104,7 @@ ttydisc_rint_done(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (ttyhook_hashook(tp, rint_done)) ttyhook_rint_done(tp); @@ -1122,7 +1120,7 @@ { size_t l; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (ttyhook_hashook(tp, rint_poll)) return ttyhook_rint_poll(tp); @@ -1165,7 +1163,7 @@ ttydisc_getc(struct tty *tp, void *buf, size_t len) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (tp->t_flags & TF_STOPPED) return (0); @@ -1192,7 +1190,7 @@ size_t len; char buf[TTY_STACKBUF]; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (tp->t_flags & TF_STOPPED) return (0); @@ -1212,9 +1210,9 @@ break; /* Copy to userspace. */ - tty_unlock(tp); + ttydisc_unlock(tp); error = uiomove(buf, len, uio); - tty_lock(tp); + ttydisc_lock(tp); if (error != 0) break; @@ -1233,7 +1231,7 @@ ttydisc_getc_poll(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (tp->t_flags & TF_STOPPED) return (0); @@ -1255,7 +1253,7 @@ { size_t i; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (tty_gone(tp)) return (-1); Index: sys/netgraph/ng_tty.c =================================================================== --- sys/netgraph/ng_tty.c +++ sys/netgraph/ng_tty.c @@ -338,8 +338,10 @@ /* notify the TTY that data is ready */ tty_lock(tp); + ttydisc_lock(tp); if (!tty_gone(tp)) ttydevsw_outwakeup(tp); + ttydisc_unlock(tp); tty_unlock(tp); return (0); @@ -412,7 +414,7 @@ size_t total = 0; int error = 0, length; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (sc->hook == NULL) return (0); @@ -459,7 +461,7 @@ struct mbuf *m; int error = 0; - tty_assert_locked(tp); + ttydisc_assert_locked(tp); if (sc->hook == NULL) return (0); Index: sys/powerpc/mambo/mambo_console.c =================================================================== --- sys/powerpc/mambo/mambo_console.c +++ sys/powerpc/mambo/mambo_console.c @@ -91,8 +91,10 @@ polltime = 1; - callout_init(&mambo_callout, 1); + callout_init_mtx(&mambo_callout, ttydisc_getlock(tp), 0); + ttydisc_lock(tp); callout_reset(&mambo_callout, polltime, mambo_timeout, NULL); + ttydisc_unlock(tp); } } @@ -117,11 +119,9 @@ { int c; - tty_lock(tp); while ((c = mambo_cngetc(NULL)) != -1) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); - tty_unlock(tp); callout_reset(&mambo_callout, polltime, mambo_timeout, NULL); } Index: sys/powerpc/powernv/opal_console.c =================================================================== --- sys/powerpc/powernv/opal_console.c +++ sys/powerpc/powernv/opal_console.c @@ -489,11 +489,11 @@ struct tty *tp = sc->tp; int c; - tty_lock(tp); + ttydisc_lock(tp); while ((c = uart_opal_getc(sc)) > 0) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); opal_call(OPAL_POLL_EVENTS, NULL); Index: sys/powerpc/pseries/phyp_console.c =================================================================== --- sys/powerpc/pseries/phyp_console.c +++ sys/powerpc/pseries/phyp_console.c @@ -444,11 +444,11 @@ unsigned char c; int len; - tty_lock(tp); + ttydisc_lock(tp); while ((len = uart_phyp_get(sc, &c, 1)) > 0) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); - tty_unlock(tp); + ttydisc_unlock(tp); if (sc->irqres == NULL) callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); Index: sys/riscv/riscv/riscv_console.c =================================================================== --- sys/riscv/riscv/riscv_console.c +++ sys/riscv/riscv/riscv_console.c @@ -129,8 +129,10 @@ polltime = 1; - callout_init(&riscv_callout, 1); + callout_init_mtx(&riscv_callout, ttydisc_getlock(tp), 0); + ttydisc_lock(tp); callout_reset(&riscv_callout, polltime, riscv_timeout, NULL); + ttydisc_unlock(tp); } } @@ -160,11 +162,9 @@ { int c; - tty_lock(tp); while ((c = riscv_cngetc(NULL)) != -1) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); - tty_unlock(tp); callout_reset(&riscv_callout, polltime, riscv_timeout, NULL); } Index: sys/sys/tty.h =================================================================== --- sys/sys/tty.h +++ sys/sys/tty.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -55,64 +56,89 @@ /* * Per-TTY structure, containing buffers, etc. * + * Under the new locking model, the ttylock is mostly internal to ^/sys/kern. + * It must be acquired when calling tty_rel_gone(), but for the most part + * drivers will not touch it. It is especially wrong for a driver to drop the + * tty lock when entered via ttydevsw methods; it is sleepable so that drivers + * do not need to drop it, keeping entry to tty methods from userland properly + * blocked while the driver is operating. + * + * The lock that drivers may supply is now called the ttydisc lock, which + * replaces most traditional usage of the ttylock in drivers. As the name + * implies, it must be held when calling ttydisc_* methods. The exception is + * when &Giant is passed to tty_alloc_mutex(); this is currently a special hack + * put in place so that syscons can continue operating without the tty layer + * attempting to acquire this sx before Giant. syscons will become properly + * locked in due time, but this is a more complicated feat. + * * List of locks - * (t) locked by t_mtx + * (t) locked by ttylock + * (d) locked by ttydisc lock * (l) locked by tty_list_sx * (c) const until freeing - */ + * (d+t) both locks must be held to write + * + * (d*) locking for tf_flags is more complex. It is generally locked by the + * ttydisc lock, but both locks must be held to mark some flags so that we + * can do some unlocked reads safely with just one or the other. Those flags + * are annotated with a (t) to indicate that they require the ttylock as well. +*/ struct tty { - struct mtx *t_mtx; /* TTY lock. */ - struct mtx t_mtxobj; /* Per-TTY lock (when not borrowing). */ + struct mtx *t_mtx; /* Deprecated TTY lock (Giant). */ + struct sx t_sxobj; /* TTY lock (when not borrowing). */ + struct mtx *t_discmtx; + /* Per-TTY discipline lock (when not borrowing). */ + struct mtx t_discmtxobj; TAILQ_ENTRY(tty) t_list; /* (l) TTY list entry. */ - int t_drainwait; /* (t) TIOCDRAIN timeout seconds. */ - unsigned int t_flags; /* (t) Terminal option flags. */ + int t_drainwait; /* (d) TIOCDRAIN timeout seconds. */ + unsigned int t_flags; /* (d*) Terminal option flags. */ /* Keep flags in sync with db_show_tty and pstat(8). */ #define TF_NOPREFIX 0x00001 /* Don't prepend "tty" to device name. */ #define TF_INITLOCK 0x00002 /* Create init/lock state devices. */ #define TF_CALLOUT 0x00004 /* Create "cua" devices. */ -#define TF_OPENED_IN 0x00008 /* "tty" node is in use. */ -#define TF_OPENED_OUT 0x00010 /* "cua" node is in use. */ -#define TF_OPENED_CONS 0x00020 /* Device in use as console. */ +#define TF_OPENED_IN 0x00008 /* (t) "tty" node is in use. */ +#define TF_OPENED_OUT 0x00010 /* (t) "cua" node is in use. */ +#define TF_OPENED_CONS 0x00020 /* (t) Device in use as console. */ #define TF_OPENED (TF_OPENED_IN|TF_OPENED_OUT|TF_OPENED_CONS) -#define TF_GONE 0x00040 /* Device node is gone. */ -#define TF_OPENCLOSE 0x00080 /* Device is in open()/close(). */ -#define TF_ASYNC 0x00100 /* Asynchronous I/O enabled. */ +#define TF_GONE 0x00040 /* (t) Device node is gone. */ +#define TF_OPENCLOSE 0x00080 /* (t) Device is in open()/close(). */ +#define TF_ASYNC 0x00100 /* (t) Asynchronous I/O enabled. */ #define TF_LITERAL 0x00200 /* Accept the next character literally. */ #define TF_HIWAT_IN 0x00400 /* We've reached the input watermark. */ #define TF_HIWAT_OUT 0x00800 /* We've reached the output watermark. */ #define TF_HIWAT (TF_HIWAT_IN|TF_HIWAT_OUT) #define TF_STOPPED 0x01000 /* Output flow control - stopped. */ -#define TF_EXCLUDE 0x02000 /* Exclusive access. */ +#define TF_EXCLUDE 0x02000 /* (t) Exclusive access. */ #define TF_BYPASS 0x04000 /* Optimized input path. */ #define TF_ZOMBIE 0x08000 /* Modem disconnect received. */ -#define TF_HOOK 0x10000 /* TTY has hook attached. */ -#define TF_BUSY_IN 0x20000 /* Process busy in read() -- not supported. */ -#define TF_BUSY_OUT 0x40000 /* Process busy in write(). */ +#define TF_HOOK 0x10000 /* (t) TTY has hook attached. */ +#define TF_BUSY_IN 0x20000 /* (t) Process busy in read(); not supported. */ +#define TF_BUSY_OUT 0x40000 /* (Process busy in write(). */ #define TF_BUSY (TF_BUSY_IN|TF_BUSY_OUT) - unsigned int t_revokecnt; /* (t) revoke() count. */ + unsigned int t_revokecnt; /* (d+t) revoke() count. */ /* Buffering mechanisms. */ - struct ttyinq t_inq; /* (t) Input queue. */ - size_t t_inlow; /* (t) Input low watermark. */ - struct ttyoutq t_outq; /* (t) Output queue. */ - size_t t_outlow; /* (t) Output low watermark. */ + struct ttyinq t_inq; /* (d) Input queue. */ + size_t t_inlow; /* (d) Input low watermark. */ + struct ttyoutq t_outq; /* (d) Output queue. */ + size_t t_outlow; /* (d) Output low watermark. */ /* Sleeping mechanisms. */ - struct cv t_inwait; /* (t) Input wait queue. */ - struct cv t_outwait; /* (t) Output wait queue. */ - struct cv t_outserwait; /* (t) Serial output wait queue. */ - struct cv t_bgwait; /* (t) Background wait queue. */ - struct cv t_dcdwait; /* (t) Carrier Detect wait queue. */ + struct cv t_inwait; /* (d) Input wait queue. */ + struct cv t_outwait; /* (d) Output wait queue. */ + struct cv t_outserwait; /* (d) Serial output wait queue. */ + struct cv t_bgwait; /* (d) Background wait queue. */ + struct cv t_dcdwait; /* (d) Carrier Detect wait queue. */ /* Polling mechanisms. */ struct selinfo t_inpoll; /* (t) Input poll queue. */ struct selinfo t_outpoll; /* (t) Output poll queue. */ struct sigio *t_sigio; /* (t) Asynchronous I/O. */ - struct termios t_termios; /* (t) I/O processing flags. */ + struct termios t_termios; /* (d+t) I/O processing flags. */ struct winsize t_winsize; /* (t) Window size. */ - unsigned int t_column; /* (t) Current cursor position. */ - unsigned int t_writepos; /* (t) Where input was interrupted. */ + unsigned int t_column; /* (d) Current cursor position. */ + unsigned int t_writepos; /* (d) Where input was interrupted. */ int t_compatflags; /* (t) COMPAT_43TTY flags. */ /* Init/lock-state devices. */ @@ -125,16 +151,16 @@ struct ttyhook *t_hook; /* (t) Capture/inject hook. */ /* Process signal delivery. */ - struct pgrp *t_pgrp; /* (t) Foreground process group. */ - struct session *t_session; /* (t) Associated session. */ - unsigned int t_sessioncnt; /* (t) Backpointing sessions. */ + struct pgrp *t_pgrp; /* (d+t) Foreground process group. */ + struct session *t_session; /* (d+t) Associated session. */ + unsigned int t_sessioncnt; /* (d+t) Backpointing sessions. */ void *t_devswsoftc; /* (c) Soft config, for drivers. */ void *t_hooksoftc; /* (t) Soft config, for hooks. */ struct cdev *t_dev; /* (c) Primary character device. */ - size_t t_prbufsz; /* (t) SIGINFO buffer size. */ - char t_prbuf[]; /* (t) SIGINFO buffer. */ + size_t t_prbufsz; /* (d) SIGINFO buffer size. */ + char t_prbuf[]; /* (d) SIGINFO buffer. */ }; /* @@ -164,20 +190,91 @@ #define TTYUNIT_CALLOUT 0x4 /* Allocation and deallocation. */ +/* + * - tty_alloc: allocate a TTY with internal TTY/discipline locks + * - tty_alloc_mutex: allocate a TTY with a given mutex as the ttydisc lock. + * The exception is if the mutex specified is Giant, it will be used as + * the TTY lock instead and an internal discipline lock will be allocated. + */ struct tty *tty_alloc(struct ttydevsw *tsw, void *softc); struct tty *tty_alloc_mutex(struct ttydevsw *tsw, void *softc, struct mtx *mtx); void tty_rel_pgrp(struct tty *tp, struct pgrp *pgrp); void tty_rel_sess(struct tty *tp, struct session *sess); void tty_rel_gone(struct tty *tp); -#define tty_lock(tp) mtx_lock((tp)->t_mtx) -#define tty_unlock(tp) mtx_unlock((tp)->t_mtx) -#define tty_lock_owned(tp) mtx_owned((tp)->t_mtx) -#define tty_assert_locked(tp) mtx_assert((tp)->t_mtx, MA_OWNED) -#define tty_getlock(tp) ((tp)->t_mtx) +/* + * These will get turned back into macros after the syscons/Giant locking + * situation is resolved. For now, we have to support both kinds of tty lock + * for this one case. + */ +static __inline void +_tty_lock(struct tty *tp) +{ + + if (tp->t_mtx != NULL) + mtx_lock(tp->t_mtx); + else + sx_xlock(&tp->t_sxobj); +} + +static __inline void +_tty_unlock(struct tty *tp) +{ + + if (tp->t_mtx != NULL) + mtx_unlock(tp->t_mtx); + else + sx_xunlock(&tp->t_sxobj); +} + +static __inline int +_tty_lock_owned(struct tty *tp) +{ + + if (tp->t_mtx != NULL) + return (mtx_owned(tp->t_mtx)); + else + return (sx_xlocked(&tp->t_sxobj)); +} + +#if defined(INVARIANTS) || defined(INVARIANTS_SUPPORT) +/* XXX This should go away when the Giant special-case is removed. */ +static __inline void +tty_assert_locked(struct tty *tp) +{ + + if (tp->t_mtx != NULL) + mtx_assert(tp->t_mtx, MA_OWNED); + else + sx_assert(&tp->t_sxobj, SA_XLOCKED); +} + +#else + +#define tty_assert_locked(tp) + +#endif /* defined(INVARIANTS) || defined(INVARIANTS_SUPPORT */ + +#define tty_lock(tp) _tty_lock(tp) +#define tty_unlock(tp) _tty_unlock(tp) +#define tty_lock_owned(tp) _tty_lock_owned(tp) + +/* + * XXX This one is technically wrong as long as syscons is still Giant-locked. + * However, neither the internal tty infrastructure nor syscons will attempt to + * tty_getlock, so we leave it as-is. + */ +#define tty_getlock(tp) (&(tp)->t_sxobj) + +#define ttydisc_lock(tp) mtx_lock((tp)->t_discmtx) +#define ttydisc_unlock(tp) mtx_unlock((tp)->t_discmtx) +#define ttydisc_lock_owned(tp) mtx_owned((tp)->t_discmtx) +#define ttydisc_assert_locked(tp) mtx_assert((tp)->t_discmtx, MA_OWNED) +#define ttydisc_assert_unlocked(tp) mtx_assert((tp)->t_discmtx, MA_NOTOWNED) +#define ttydisc_getlock(tp) ((tp)->t_discmtx) -/* XXX Should migrate users to tty_assert_locked! */ -#define tty_lock_assert(tp, ma) mtx_assert((tp)->t_mtx, (ma)) +/* Internal to tty, preferably... */ +#define ttydisc_lock_assert(tp, ma) mtx_assert((tp)->t_discmtx, (ma)) /* Device node creation. */ int tty_makedevf(struct tty *tp, struct ucred *cred, int flags, Index: sys/sys/ttydevsw.h =================================================================== --- sys/sys/ttydevsw.h +++ sys/sys/ttydevsw.h @@ -87,6 +87,7 @@ { tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return (tp->t_devsw->tsw_open(tp)); @@ -97,6 +98,7 @@ { tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); tp->t_devsw->tsw_close(tp); @@ -106,7 +108,8 @@ ttydevsw_outwakeup(struct tty *tp) { - tty_assert_locked(tp); + /* We may or may not have the tty lock. */ + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); /* Prevent spurious wakeups. */ @@ -120,7 +123,8 @@ ttydevsw_inwakeup(struct tty *tp) { - tty_assert_locked(tp); + /* We may or may not have the tty lock. */ + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); /* Prevent spurious wakeups. */ @@ -135,6 +139,7 @@ { tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return (tp->t_devsw->tsw_ioctl(tp, cmd, data, td)); @@ -155,6 +160,8 @@ ttydevsw_param(struct tty *tp, struct termios *t) { + tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return (tp->t_devsw->tsw_param(tp, t)); @@ -164,6 +171,8 @@ ttydevsw_modem(struct tty *tp, int sigon, int sigoff) { + tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return (tp->t_devsw->tsw_modem(tp, sigon, sigoff)); @@ -183,7 +192,7 @@ ttydevsw_pktnotify(struct tty *tp, char event) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); tp->t_devsw->tsw_pktnotify(tp, event); @@ -193,6 +202,7 @@ ttydevsw_free(struct tty *tp) { + /* Locks are destroyed at this point. */ MPASS(tty_gone(tp)); tp->t_devsw->tsw_free(tty_softc(tp)); @@ -202,7 +212,8 @@ ttydevsw_busy(struct tty *tp) { - tty_assert_locked(tp); + /* We may or may not have the tty lock. */ + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return (tp->t_devsw->tsw_busy(tp)); Index: sys/sys/ttydisc.h =================================================================== --- sys/sys/ttydisc.h +++ sys/sys/ttydisc.h @@ -72,7 +72,7 @@ ttydisc_read_poll(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); return ttyinq_bytescanonicalized(&tp->t_inq); } @@ -81,7 +81,7 @@ ttydisc_write_poll(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); return ttyoutq_bytesleft(&tp->t_outq); } Index: sys/sys/ttyhook.h =================================================================== --- sys/sys/ttyhook.h +++ sys/sys/ttyhook.h @@ -78,7 +78,7 @@ static __inline int ttyhook_rint(struct tty *tp, char c, int flags) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return tp->t_hook->th_rint(tp, c, flags); @@ -87,7 +87,7 @@ static __inline size_t ttyhook_rint_bypass(struct tty *tp, const void *buf, size_t len) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return tp->t_hook->th_rint_bypass(tp, buf, len); @@ -96,7 +96,7 @@ static __inline void ttyhook_rint_done(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); tp->t_hook->th_rint_done(tp); @@ -105,7 +105,7 @@ static __inline size_t ttyhook_rint_poll(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return tp->t_hook->th_rint_poll(tp); @@ -114,7 +114,7 @@ static __inline size_t ttyhook_getc_inject(struct tty *tp, void *buf, size_t len) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return tp->t_hook->th_getc_inject(tp, buf, len); @@ -123,7 +123,7 @@ static __inline void ttyhook_getc_capture(struct tty *tp, const void *buf, size_t len) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); tp->t_hook->th_getc_capture(tp, buf, len); @@ -132,7 +132,7 @@ static __inline size_t ttyhook_getc_poll(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); MPASS(!tty_gone(tp)); return tp->t_hook->th_getc_poll(tp); @@ -141,7 +141,7 @@ static __inline void ttyhook_close(struct tty *tp) { - tty_assert_locked(tp); + ttydisc_assert_locked(tp); tp->t_hook->th_close(tp); }