Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F107873272
D24459.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
64 KB
Referenced Files
None
Subscribers
None
D24459.diff
View Options
diff --git a/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c b/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
--- a/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
+++ b/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
@@ -263,7 +263,7 @@
{
int c;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
AJU_LOCK_ASSERT(sc);
while (aju_readable(sc)) {
@@ -295,7 +295,7 @@
uint32_t v;
uint8_t ch;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
AJU_LOCK_ASSERT(sc);
AJU_UNLOCK(sc);
@@ -361,7 +361,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);
@@ -374,7 +374,6 @@
struct altera_jtag_uart_softc *sc = arg;
struct tty *tp = sc->ajus_ttyp;
- tty_lock(tp);
AJU_LOCK(sc);
/*
@@ -396,7 +395,6 @@
callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
aju_io_callout, sc);
AJU_UNLOCK(sc);
- tty_unlock(tp);
}
static void
@@ -406,7 +404,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) {
@@ -436,7 +433,6 @@
callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
aju_ac_callout, sc);
AJU_UNLOCK(sc);
- tty_unlock(tp);
}
static void
@@ -446,7 +442,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) {
@@ -458,7 +454,7 @@
aju_handle_output(sc, tp);
}
AJU_UNLOCK(sc);
- tty_unlock(tp);
+ ttydisc_unlock(tp);
}
int
@@ -515,6 +511,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
@@ -525,13 +523,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);
}
@@ -542,8 +541,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);
@@ -555,7 +557,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);
}
diff --git a/sys/dev/cfe/cfe_console.c b/sys/dev/cfe/cfe_console.c
--- a/sys/dev/cfe/cfe_console.c
+++ b/sys/dev/cfe/cfe_console.c
@@ -87,7 +87,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");
}
}
@@ -95,6 +95,8 @@
static int
cfe_tty_open(struct tty *tp)
{
+
+ ttydisc_assert_locked(tp);
polltime = hz / CFECONS_POLL_HZ;
if (polltime < 1)
polltime = 1;
@@ -107,6 +109,7 @@
cfe_tty_close(struct tty *tp)
{
+ ttydisc_assert_locked(tp);
callout_stop(&cfe_timer);
}
@@ -139,7 +142,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);
diff --git a/sys/dev/dcons/dcons_os.c b/sys/dev/dcons/dcons_os.c
--- a/sys/dev/dcons/dcons_os.c
+++ b/sys/dev/dcons/dcons_os.c
@@ -233,13 +233,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;
diff --git a/sys/dev/nmdm/nmdm.c b/sys/dev/nmdm/nmdm.c
--- a/sys/dev/nmdm/nmdm.c
+++ b/sys/dev/nmdm/nmdm.c
@@ -106,6 +106,7 @@
struct nmdmpart *onp;
struct tty *otp;
+ ttydisc_assert_locked(tp);
np = tty_softc(tp);
onp = np->np_other;
otp = onp->np_tty;
@@ -118,13 +119,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
@@ -189,7 +191,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';
@@ -254,12 +259,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"));
@@ -277,7 +286,7 @@
/* This may happen when we are in detach process. */
if (tty_gone(otp)) {
- tty_unlock(otp);
+ ttydisc_unlock(tp);
return;
}
@@ -291,8 +300,7 @@
}
ttydisc_rint_done(otp);
-
- tty_unlock(tp);
+ ttydisc_unlock(tp);
}
static int
@@ -322,6 +330,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)) {
@@ -379,6 +389,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;
@@ -386,14 +397,12 @@
np->np_other->np_dcd = 0;
ttydisc_modem(np->np_other->np_tty, np->np_other->np_dcd);
-
return (0);
} else {
if (np->np_dcd)
i |= SER_DCD;
if (np->np_other->np_dcd)
i |= SER_DTR;
-
return (i);
}
}
diff --git a/sys/dev/ofw/ofw_console.c b/sys/dev/ofw/ofw_console.c
--- a/sys/dev/ofw/ofw_console.c
+++ b/sys/dev/ofw/ofw_console.c
@@ -100,7 +100,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);
}
}
@@ -112,6 +112,8 @@
static int
ofwtty_open(struct tty *tp)
{
+
+ ttydisc_assert_locked(tp);
polltime = hz / OFWCONS_POLL_HZ;
if (polltime < 1)
polltime = 1;
@@ -125,6 +127,7 @@
ofwtty_close(struct tty *tp)
{
+ ttydisc_assert_locked(tp);
callout_stop(&ofw_timer);
}
@@ -134,6 +137,7 @@
int len;
u_char buf[OFBURSTLEN];
+ ttydisc_assert_locked(tp);
for (;;) {
len = ttydisc_getc(tp, buf, sizeof buf);
if (len == 0)
@@ -150,7 +154,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);
diff --git a/sys/dev/snp/snp.c b/sys/dev/snp/snp.c
--- a/sys/dev/snp/snp.c
+++ b/sys/dev/snp/snp.c
@@ -162,10 +162,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)
@@ -176,7 +180,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)) {
@@ -184,7 +188,7 @@
break;
}
}
- tty_unlock(tp);
+ ttydisc_unlock(tp);
return (error);
}
@@ -212,11 +216,11 @@
if (error != 0)
return (error);
- tty_lock(tp);
+ ttydisc_lock(tp);
/* Driver could have abandoned the TTY in the mean time. */
if (tty_gone(tp)) {
- tty_unlock(tp);
+ ttydisc_unlock(tp);
return (ENXIO);
}
@@ -228,7 +232,7 @@
ttydisc_rint_simple(tp, in, len);
ttydisc_rint_done(tp);
- tty_unlock(tp);
+ ttydisc_unlock(tp);
}
return (0);
@@ -276,7 +280,9 @@
/* Now that went okay, allocate a buffer for the queue. */
tp = ss->snp_tty;
tty_lock(tp);
+ ttydisc_lock(tp);
ttyoutq_setsize(&ss->snp_outq, tp, SNP_OUTPUT_BUFSIZE);
+ ttydisc_unlock(tp);
tty_unlock(tp);
return (0);
diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c
--- a/sys/dev/syscons/syscons.c
+++ b/sys/dev/syscons/syscons.c
@@ -408,6 +408,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;
@@ -746,7 +747,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);
}
@@ -846,9 +856,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 */
@@ -874,8 +887,8 @@
}
ttydisc_rint_done(cur_tty);
+ ttydisc_unlock(cur_tty);
}
-
sc->cur_scp->status |= MOUSE_HIDDEN;
done:
@@ -4239,10 +4252,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
@@ -4253,11 +4268,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);
}
/*
diff --git a/sys/dev/syscons/sysmouse.c b/sys/dev/syscons/sysmouse.c
--- a/sys/dev/syscons/sysmouse.c
+++ b/sys/dev/syscons/sysmouse.c
@@ -260,7 +260,7 @@
int x, y, z;
int i, flags = 0;
- tty_lock(sysmouse_tty);
+ ttydisc_lock(sysmouse_tty);
switch (info->operation) {
case MOUSE_ACTION:
@@ -323,7 +323,7 @@
}
ttydisc_rint_done(sysmouse_tty);
-done: tty_unlock(sysmouse_tty);
+done: ttydisc_unlock(sysmouse_tty);
return (flags);
}
diff --git a/sys/dev/uart/uart_tty.c b/sys/dev/uart/uart_tty.c
--- a/sys/dev/uart/uart_tty.c
+++ b/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
@@ -459,7 +459,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);
}
diff --git a/sys/dev/usb/serial/usb_serial.c b/sys/dev/usb/serial/usb_serial.c
--- a/sys/dev/usb/serial/usb_serial.c
+++ b/sys/dev/usb/serial/usb_serial.c
@@ -526,6 +526,7 @@
mtx_unlock(&ucom_mtx);
tty_lock(tp);
+ ttydisc_lock(tp);
ucom_close(tp); /* close, if any */
@@ -626,10 +627,12 @@
task->termios_copy = *pt;
/*
- * Closing or opening the device should be synchronous.
+ * The tty lock is sleepable and the ttydisc lock is shared with the USB
+ * parts. We can now asychronously do the USB parts while making sure
+ * that we're not violation termios guarantees about the state of the
+ * hardware when we return.
*/
- if (fn == ucom_cfg_close || fn == ucom_cfg_open)
- usb_proc_mwait(&ssc->sc_tq, t0, t1);
+ usb_proc_mwait(&ssc->sc_tq, t0, t1);
/*
* In case of multiple configure requests,
diff --git a/sys/dev/virtio/console/virtio_console.c b/sys/dev/virtio/console/virtio_console.c
--- a/sys/dev/virtio/console/virtio_console.c
+++ b/sys/dev/virtio/console/virtio_console.c
@@ -92,6 +92,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;
@@ -1306,6 +1307,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;
@@ -1396,13 +1399,14 @@
{
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);
-
return (0);
}
@@ -1411,8 +1415,9 @@
{
struct vtcon_port *port;
+ /* Effectively VTCON_PORT_LOCK. */
+ ttydisc_assert_locked(tp);
port = tty_softc(tp);
-
if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
return;
@@ -1426,6 +1431,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)
diff --git a/sys/dev/xen/console/xen_console.c b/sys/dev/xen/console/xen_console.c
--- a/sys/dev/xen/console/xen_console.c
+++ b/sys/dev/xen/console/xen_console.c
@@ -517,7 +517,7 @@
cons = tty_softc(tp);
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
/*
* Don't transmit any character if the buffer is full. Otherwise,
@@ -557,7 +557,7 @@
xencons_rx(cons);
- tty_lock(tp);
+ ttydisc_lock(tp);
while ((ret = xencons_getc(cons)) != -1) {
#ifdef KDB
kdb_alt_break(ret, &cons->altbrk);
@@ -565,7 +565,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);
@@ -684,6 +684,7 @@
struct xencons_priv *cons;
cons = tty_softc(tp);
+ ttydisc_assert_locked(tp);
callout_stop(&cons->callout);
@@ -736,7 +737,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) {
diff --git a/sys/kern/kern_cons.c b/sys/kern/kern_cons.c
--- a/sys/kern/kern_cons.c
+++ b/sys/kern/kern_cons.c
@@ -603,16 +603,16 @@
int size = consmsgbuf_size;
void *buf = NULL;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (constty == tp)
return (0);
if (constty != NULL)
return (EBUSY);
if (consbuf == NULL) {
- tty_unlock(tp);
+ ttydisc_unlock(tp);
buf = malloc(size, M_TTYCONS, M_WAITOK);
- tty_lock(tp);
+ ttydisc_lock(tp);
}
mtx_lock(&constty_mtx);
if (constty != NULL) {
@@ -628,7 +628,7 @@
constty = tp;
mtx_unlock(&constty_mtx);
- callout_init_mtx(&conscallout, tty_getlock(tp), 0);
+ callout_init_mtx(&conscallout, ttydisc_getlock(tp), 0);
constty_timeout(tp);
return (0);
}
@@ -641,7 +641,7 @@
{
int c;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (constty != tp)
return (ENXIO);
callout_stop(&conscallout);
@@ -666,7 +666,7 @@
struct tty *tp = arg;
int c;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
if (tty_putchar(tp, c) < 0) {
constty_clear(tp);
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -945,10 +945,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) {
diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c
--- a/sys/kern/subr_prf.c
+++ b/sys/kern/subr_prf.c
@@ -205,10 +205,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);
}
diff --git a/sys/kern/subr_terminal.c b/sys/kern/subr_terminal.c
--- a/sys/kern/subr_terminal.c
+++ b/sys/kern/subr_terminal.c
@@ -317,7 +317,7 @@
return;
c = TCHAR_CHARACTER(c);
- tty_lock(tp);
+ ttydisc_lock(tp);
/*
* Conversion to UTF-8.
*/
@@ -349,7 +349,7 @@
ttydisc_rint_simple(tp, str, sizeof str);
}
ttydisc_rint_done(tp);
- tty_unlock(tp);
+ ttydisc_unlock(tp);
}
void
@@ -361,10 +361,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
@@ -381,10 +381,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);
}
/*
@@ -416,6 +416,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)) {
@@ -473,9 +474,11 @@
* deallocate TTYs. This means it's safe to temporarily unlock
* the TTY when handling ioctls.
*/
+ ttydisc_unlock(tp);
tty_unlock(tp);
error = tm->tm_class->tc_ioctl(tm, cmd, data, td);
tty_lock(tp);
+ ttydisc_lock(tp);
if ((error == 0) && (cmd == CONS_CLRHIST)) {
/*
* Scrollback history has been successfully cleared,
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -126,12 +126,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;
@@ -140,12 +142,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
@@ -155,6 +158,7 @@
size_t bytes;
int error;
+ ttydisc_assert_locked(tp);
if (ttyhook_hashook(tp, getc_inject))
/* buffer is inaccessible */
return (0);
@@ -229,20 +233,31 @@
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. */
constty_clear(tp);
- /* 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);
@@ -259,6 +274,7 @@
tp->t_flags &= ~TF_OPENCLOSE;
cv_broadcast(&tp->t_dcdwait);
+ ttydisc_unlock(tp);
tty_rel_free(tp);
}
@@ -285,6 +301,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) {
@@ -293,6 +310,7 @@
}
}
tp->t_flags |= TF_OPENCLOSE;
+ ttydisc_unlock(tp);
/*
* Make sure the "tty" and "cua" device cannot be opened at the
@@ -316,11 +334,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)
@@ -335,8 +357,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. */
@@ -358,7 +383,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);
@@ -372,6 +400,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
@@ -385,6 +414,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 +443,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 +457,7 @@
int error;
MPASS(sig == SIGTTIN || sig == SIGTTOU);
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
p = td->td_proc;
for (;;) {
@@ -504,10 +534,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);
/*
@@ -528,6 +566,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);
@@ -554,7 +593,8 @@
cv_signal(&tp->t_outserwait);
}
-done: tty_unlock(tp);
+done: ttydisc_unlock(tp);
+ tty_unlock(tp);
return (error);
}
@@ -605,7 +645,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;
}
@@ -654,6 +696,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)
@@ -668,6 +711,7 @@
if (ttydisc_write_poll(tp) > 0)
revents |= events & (POLLOUT|POLLWRNORM);
}
+ ttydisc_unlock(tp);
if (revents == 0) {
if (events & (POLLIN|POLLRDNORM))
@@ -716,7 +760,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;
@@ -740,7 +784,7 @@
{
struct tty *tp = kn->kn_hook;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (tty_gone(tp)) {
kn->kn_flags |= EV_EOF;
@@ -777,12 +821,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;
@@ -1052,7 +1100,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;
@@ -1091,16 +1139,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 {
+ sx_init(&tp->t_sxobj, "ttysx");
+ }
+
+ if (discmtx != NULL) {
+ tp->t_discmtx = discmtx;
} else {
- tp->t_mtx = &tp->t_mtxobj;
- mtx_init(&tp->t_mtxobj, "ttymtx", NULL, MTX_DEF);
+ 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_mtx);
- knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx);
+ knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_discmtx);
+ knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_discmtx);
return (tp);
}
@@ -1132,8 +1194,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);
}
@@ -1176,9 +1241,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);
}
@@ -1186,14 +1253,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);
}
@@ -1204,6 +1274,8 @@
tty_assert_locked(tp);
MPASS(!tty_gone(tp));
+ if (!ttydisc_lock_owned(tp))
+ ttydisc_lock(tp);
/* Simulate carrier removal. */
ttydisc_modem(tp, 0);
@@ -1213,6 +1285,7 @@
cv_broadcast(&tp->t_dcdwait);
tp->t_flags |= TF_GONE;
+ ttydisc_unlock(tp);
tty_rel_free(tp);
}
@@ -1254,6 +1327,9 @@
return (EPERM);
}
+ ttydisc_lock(tp);
+ tp->t_sessioncnt--;
+ ttydisc_unlock(tp);
PROC_LOCK(p);
SESS_LOCK(session);
vp = session->s_ttyvp;
@@ -1262,7 +1338,6 @@
session->s_ttydp = NULL;
SESS_UNLOCK(session);
- tp->t_sessioncnt--;
p->p_flag &= ~P_CONTROLT;
PROC_UNLOCK(p);
sx_xunlock(&proctree_lock);
@@ -1501,7 +1576,7 @@
struct proc *p;
struct session *s;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
MPASS(sig >= 1 && sig < NSIG);
/* Make signals start output again. */
@@ -1527,7 +1602,7 @@
{
ksiginfo_t ksi;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
MPASS(sig >= 1 && sig < NSIG);
/* Make signals start output again. */
@@ -1550,6 +1625,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));
@@ -1570,11 +1646,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))
@@ -1592,11 +1678,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))
@@ -1613,6 +1709,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);
@@ -1637,10 +1734,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
@@ -1656,30 +1757,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:
@@ -1726,6 +1839,7 @@
struct termios *t = data;
bool canonicalize = false;
+ ttydisc_lock(tp);
/*
* Who makes up these funny rules? According to POSIX,
* input baud rate is set equal to the output baud rate
@@ -1743,8 +1857,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);
}
@@ -1759,8 +1875,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? */
@@ -1770,8 +1888,10 @@
/* Baud rate has changed - update watermarks. */
error = tty_watermarks(tp);
- if (error)
+ if (error) {
+ ttydisc_unlock(tp);
return (error);
+ }
}
/*
@@ -1817,6 +1937,7 @@
ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP);
else
ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP);
+ ttydisc_unlock(tp);
return (0);
}
case TIOCGETD:
@@ -1880,12 +2001,15 @@
}
/* Connect the session to the TTY. */
+ ttydisc_lock(tp);
tp->t_session = p->p_session;
tp->t_session->s_ttyp = tp;
tp->t_sessioncnt++;
/* Assign foreground process group. */
tp->t_pgrp = p->p_pgrp;
+ ttydisc_unlock(tp);
+
PROC_LOCK(p);
p->p_flag |= P_CONTROLT;
PROC_UNLOCK(p);
@@ -1921,7 +2045,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. */
@@ -1935,14 +2061,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);
@@ -1969,23 +2102,33 @@
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;
tp->t_termios.c_lflag &= ~FLUSHO;
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))
@@ -1993,8 +2136,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);
}
@@ -2015,7 +2160,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);
@@ -2036,6 +2184,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);
}
@@ -2044,6 +2193,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) {
@@ -2064,6 +2214,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) {
@@ -2163,6 +2314,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;
@@ -2176,6 +2328,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);
@@ -2190,11 +2343,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);
diff --git a/sys/kern/tty_info.c b/sys/kern/tty_info.c
--- a/sys/kern/tty_info.c
+++ b/sys/kern/tty_info.c
@@ -295,7 +295,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;
diff --git a/sys/kern/tty_inq.c b/sys/kern/tty_inq.c
--- a/sys/kern/tty_inq.c
+++ b/sys/kern/tty_inq.c
@@ -116,6 +116,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) {
@@ -123,15 +125,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);
@@ -164,6 +163,7 @@
size_t rlen, size_t flen)
{
+ ttydisc_assert_locked(tp);
/* rlen includes flen, flen bytes will be trimmed from the end. */
MPASS(rlen - flen <= uio->uio_resid);
@@ -235,10 +235,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);
@@ -253,9 +253,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)
diff --git a/sys/kern/tty_outq.c b/sys/kern/tty_outq.c
--- a/sys/kern/tty_outq.c
+++ b/sys/kern/tty_outq.c
@@ -93,6 +93,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) {
@@ -100,20 +102,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);
}
@@ -201,6 +195,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;
@@ -244,9 +239,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);
@@ -261,9 +256,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)
diff --git a/sys/kern/tty_pts.c b/sys/kern/tty_pts.c
--- a/sys/kern/tty_pts.c
+++ b/sys/kern/tty_pts.c
@@ -81,20 +81,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. */
@@ -119,7 +119,7 @@
if (uio->uio_resid == 0)
return (0);
- tty_lock(tp);
+ ttydisc_lock(tp);
for (;;) {
/*
@@ -130,7 +130,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);
@@ -151,11 +151,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);
@@ -171,12 +171,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);
}
@@ -199,7 +199,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;
@@ -233,18 +233,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
@@ -270,9 +271,9 @@
/* This device supports non-blocking operation. */
return (0);
case FIONREAD:
- tty_lock(tp);
+ ttydisc_lock(tp);
*(int *)data = ttydisc_getc_poll(tp);
- tty_unlock(tp);
+ ttydisc_unlock(tp);
return (0);
case FIODGNAME:
#ifdef COMPAT_FREEBSD32
@@ -304,9 +305,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:
@@ -331,21 +332,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. */
@@ -356,18 +357,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);
}
@@ -389,11 +390,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);
}
@@ -427,7 +428,7 @@
selrecord(td, &psc->pts_inpoll);
}
- tty_unlock(tp);
+ ttydisc_unlock(tp);
return (revents);
}
@@ -768,8 +769,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);
@@ -815,8 +816,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);
diff --git a/sys/kern/tty_ttydisc.c b/sys/kern/tty_ttydisc.c
--- a/sys/kern/tty_ttydisc.c
+++ b/sys/kern/tty_ttydisc.c
@@ -98,6 +98,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);
tp->t_termios.c_lflag &= ~FLUSHO;
@@ -411,6 +412,7 @@
int error;
tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (uio->uio_resid == 0)
return (0);
@@ -543,6 +545,7 @@
unsigned int oblen = 0;
tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (tp->t_flags & TF_ZOMBIE)
return (EIO);
@@ -567,18 +570,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. */
@@ -669,7 +667,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;
@@ -690,7 +688,7 @@
ttydisc_modem(struct tty *tp, int open)
{
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (open)
cv_broadcast(&tp->t_dcdwait);
@@ -1015,7 +1013,7 @@
char ob[3] = { 0xff, 0x00 };
size_t ol;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
atomic_add_long(&tty_nin, 1);
@@ -1272,7 +1270,7 @@
{
size_t ret;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
MPASS(tp->t_flags & TF_BYPASS);
@@ -1293,7 +1291,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);
@@ -1309,7 +1307,7 @@
{
size_t l;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (ttyhook_hashook(tp, rint_poll))
return ttyhook_rint_poll(tp);
@@ -1352,7 +1350,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);
@@ -1379,7 +1377,7 @@
size_t len;
char buf[TTY_STACKBUF];
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (tp->t_flags & TF_STOPPED)
return (0);
@@ -1399,9 +1397,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;
@@ -1420,7 +1418,7 @@
ttydisc_getc_poll(struct tty *tp)
{
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (tp->t_flags & TF_STOPPED)
return (0);
@@ -1442,7 +1440,7 @@
{
size_t i;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (tty_gone(tp))
return (-1);
diff --git a/sys/netgraph/ng_tty.c b/sys/netgraph/ng_tty.c
--- a/sys/netgraph/ng_tty.c
+++ b/sys/netgraph/ng_tty.c
@@ -336,8 +336,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);
@@ -410,7 +412,7 @@
size_t total = 0;
int error = 0, length;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (sc->hook == NULL)
return (0);
@@ -457,7 +459,7 @@
struct mbuf *m;
int error = 0;
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
if (sc->hook == NULL)
return (0);
diff --git a/sys/powerpc/mambo/mambo_console.c b/sys/powerpc/mambo/mambo_console.c
--- a/sys/powerpc/mambo/mambo_console.c
+++ b/sys/powerpc/mambo/mambo_console.c
@@ -88,8 +88,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);
}
}
@@ -114,11 +116,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);
}
diff --git a/sys/powerpc/powernv/opal_console.c b/sys/powerpc/powernv/opal_console.c
--- a/sys/powerpc/powernv/opal_console.c
+++ b/sys/powerpc/powernv/opal_console.c
@@ -528,11 +528,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);
diff --git a/sys/powerpc/pseries/phyp_console.c b/sys/powerpc/pseries/phyp_console.c
--- a/sys/powerpc/pseries/phyp_console.c
+++ b/sys/powerpc/pseries/phyp_console.c
@@ -447,11 +447,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);
diff --git a/sys/riscv/riscv/riscv_console.c b/sys/riscv/riscv/riscv_console.c
--- a/sys/riscv/riscv/riscv_console.c
+++ b/sys/riscv/riscv/riscv_console.c
@@ -126,8 +126,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);
}
}
@@ -157,11 +159,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);
}
diff --git a/sys/sys/tty.h b/sys/sys/tty.h
--- a/sys/sys/tty.h
+++ b/sys/sys/tty.h
@@ -36,6 +36,7 @@
#include <sys/queue.h>
#include <sys/lock.h>
#include <sys/mutex.h>
+#include <sys/sx.h>
#include <sys/condvar.h>
#include <sys/selinfo.h>
#include <sys/_termios.h>
@@ -53,64 +54,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; /* TTY discipline lock. */
+ /* 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. */
@@ -123,16 +149,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. */
};
/*
@@ -162,20 +188,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,
diff --git a/sys/sys/ttydevsw.h b/sys/sys/ttydevsw.h
--- a/sys/sys/ttydevsw.h
+++ b/sys/sys/ttydevsw.h
@@ -85,6 +85,7 @@
{
tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
MPASS(!tty_gone(tp));
return (tp->t_devsw->tsw_open(tp));
@@ -95,6 +96,7 @@
{
tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
MPASS(!tty_gone(tp));
tp->t_devsw->tsw_close(tp);
@@ -104,7 +106,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. */
@@ -118,7 +121,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. */
@@ -133,6 +137,7 @@
{
tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
MPASS(!tty_gone(tp));
return (tp->t_devsw->tsw_ioctl(tp, cmd, data, td));
@@ -153,6 +158,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));
@@ -162,6 +169,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));
@@ -181,7 +190,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);
@@ -191,6 +200,7 @@
ttydevsw_free(struct tty *tp)
{
+ /* Locks are destroyed at this point. */
MPASS(tty_gone(tp));
tp->t_devsw->tsw_free(tty_softc(tp));
@@ -200,7 +210,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));
diff --git a/sys/sys/ttydisc.h b/sys/sys/ttydisc.h
--- a/sys/sys/ttydisc.h
+++ b/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);
}
diff --git a/sys/sys/ttyhook.h b/sys/sys/ttyhook.h
--- a/sys/sys/ttyhook.h
+++ b/sys/sys/ttyhook.h
@@ -76,7 +76,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);
@@ -85,7 +85,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);
@@ -94,7 +94,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);
@@ -103,7 +103,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);
@@ -112,7 +112,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);
@@ -121,7 +121,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);
@@ -130,7 +130,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);
@@ -139,7 +139,7 @@
static __inline void
ttyhook_close(struct tty *tp)
{
- tty_assert_locked(tp);
+ ttydisc_assert_locked(tp);
tp->t_hook->th_close(tp);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Jan 19, 9:53 PM (20 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15961719
Default Alt Text
D24459.diff (64 KB)
Attached To
Mode
D24459: (WIP) tty: split the tty lock up, make the primary tty lock sleepable
Attached
Detach File
Event Timeline
Log In to Comment