diff options
author | bde <bde@FreeBSD.org> | 1995-07-30 12:39:42 +0000 |
---|---|---|
committer | bde <bde@FreeBSD.org> | 1995-07-30 12:39:42 +0000 |
commit | 817ff6d360efa18b42436d4ee46b8bee38315b17 (patch) | |
tree | 5d0eae0751ab4d6891b2edb481b05df4a958f90d /sys/kern/tty.c | |
parent | 117bc8a1c40f1d6d82ceb9154a481aac2780d463 (diff) | |
download | FreeBSD-src-817ff6d360efa18b42436d4ee46b8bee38315b17.zip FreeBSD-src-817ff6d360efa18b42436d4ee46b8bee38315b17.tar.gz |
Split TS_ASLEEP (sleep on output [below low water])into TS_SO_OLOWAT (sleep
on output below low water) and TS_SO_OCOMPLETE (sleep on output complete).
Most of the support for this has already been committed. Drivers should
call ttwwakeup() to handle wakeups whenever output is below low water
(and some output event causes this condition to be checked) or TS_BUSY is
cleared.
tty.c:
Fix the livelock in ttywait() properly by sleeping on output complete, not
on output below low water.
Use ttwwakeup() instead of separate select and output wakeups for all
wakeups of writers.
Add wakeups of writers for output flushes and carrier/clocal transitions.
Don't go to sleep in ttycheckoutq() if ttstart() reduces the queue to below
low water.
Use the timeout built into tsleep() in ttycheckoutq().
Optimize the select wakeup in ttwwakeup(). It seems reasonable to know
too much about the internals of tp->t_wsel now that the knowledge is
localised in tty.c.
Diffstat (limited to 'sys/kern/tty.c')
-rw-r--r-- | sys/kern/tty.c | 61 |
1 files changed, 25 insertions, 36 deletions
diff --git a/sys/kern/tty.c b/sys/kern/tty.c index c59aaca..0df9786 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -36,7 +36,7 @@ * SUCH DAMAGE. * * @(#)tty.c 8.8 (Berkeley) 1/21/94 - * $Id: tty.c,v 1.57 1995/07/22 16:45:07 bde Exp $ + * $Id: tty.c,v 1.58 1995/07/29 13:35:33 bde Exp $ */ /*- @@ -833,6 +833,7 @@ ttioctl(tp, cmd, data, flag) #endif ttwakeup(tp); } + ttwwakeup(tp); tp->t_cflag = t->c_cflag; tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; @@ -961,7 +962,7 @@ ttioctl(tp, cmd, data, flag) if (error) return (error); tp->t_timeout = *(int *)data * hz; - wakeup(TSA_OLOWAT(tp)); + ttwwakeup(tp); break; case TIOCGDRAINWAIT: *(int *)data = tp->t_timeout / hz; @@ -1055,30 +1056,13 @@ ttywait(tp) while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && (ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL)) && tp->t_oproc) { - /* - * XXX the call to t_oproc() can cause livelock. - * - * If two processes wait for output to drain from the same - * tty, and the amount of output to drain is <= tp->t_lowat, - * then the processes will take turns uselessly waking each - * other up until the output drains, all running at spltty() - * so that even interrupts on other terminals are blocked. - * - * Skipping the call when TS_BUSY is set avoids the problem - * for current drivers but isn't "right". There is no - * problem for ptys - we only get woken up when the output - * queue is actually reduced. Hardware ttys should be - * handled similarly. There would still be excessive - * wakeups for output below low water when we only care - * about output complete. - */ - if (!ISSET(tp->t_state, TS_BUSY)) - (*tp->t_oproc)(tp); + (*tp->t_oproc)(tp); if ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && (ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL))) { - SET(tp->t_state, TS_ASLEEP); - error = ttysleep(tp, TSA_OLOWAT(tp), TTOPRI | PCATCH, - "ttywai", tp->t_timeout); + SET(tp->t_state, TS_SO_OCOMPLETE); + error = ttysleep(tp, TSA_OCOMPLETE(tp), + TTOPRI | PCATCH, "ttywai", + tp->t_timeout); if (error == EWOULDBLOCK) error = EIO; if (error) @@ -1133,8 +1117,7 @@ ttyflush(tp, rw) } if (rw & FWRITE) { FLUSHQ(&tp->t_outq); - wakeup(TSA_OLOWAT(tp)); - selwakeup(&tp->t_wsel); + ttwwakeup(tp); } if ((rw & FREAD) && ISSET(tp->t_state, TS_TBLOCK) && tp->t_rawq.c_cc < TTYHOG/5) { @@ -1294,6 +1277,7 @@ ttymodem(tp, flag) */ SET(tp->t_state, TS_CARR_ON); ttwakeup(tp); + ttwwakeup(tp); } return (1); } @@ -1640,14 +1624,14 @@ ttycheckoutq(tp, wait) if (tp->t_outq.c_cc > hiwat + 200) while (tp->t_outq.c_cc > hiwat) { ttstart(tp); + if (tp->t_outq.c_cc <= hiwat) + break; if (wait == 0 || curproc->p_siglist != oldsig) { splx(s); return (0); } - timeout((void (*)__P((void *)))wakeup, - (void *)&tp->t_outq, hz); - SET(tp->t_state, TS_ASLEEP); - (void) tsleep(TSA_OLOWAT(tp), PZERO - 1, "ttoutq", 0); + SET(tp->t_state, TS_SO_OLOWAT); + tsleep(TSA_OLOWAT(tp), PZERO - 1, "ttoutq", hz); } splx(s); return (1); @@ -1838,7 +1822,7 @@ ovhiwat: uio->uio_resid += cc; return (uio->uio_resid == cnt ? EWOULDBLOCK : 0); } - SET(tp->t_state, TS_ASLEEP); + SET(tp->t_state, TS_SO_OLOWAT); error = ttysleep(tp, TSA_OLOWAT(tp), TTOPRI | PCATCH, "ttywri", tp->t_timeout); splx(s); @@ -2038,12 +2022,17 @@ ttwwakeup(tp) register struct tty *tp; { - if (tp->t_outq.c_cc <= tp->t_lowat) { - if (tp->t_state & TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup(TSA_OLOWAT(tp)); - } + if (tp->t_wsel.si_pid != 0 && tp->t_outq.c_cc <= tp->t_lowat) selwakeup(&tp->t_wsel); + if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) == + TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) { + CLR(tp->t_state, TS_SO_OCOMPLETE); + wakeup(TSA_OCOMPLETE(tp)); + } + if (ISSET(tp->t_state, TS_SO_OLOWAT) && + tp->t_outq.c_cc <= tp->t_lowat) { + CLR(tp->t_state, TS_SO_OLOWAT); + wakeup(TSA_OLOWAT(tp)); } } |