summaryrefslogtreecommitdiffstats
path: root/sys/kern/tty.c
diff options
context:
space:
mode:
authorbde <bde@FreeBSD.org>1995-07-30 12:39:42 +0000
committerbde <bde@FreeBSD.org>1995-07-30 12:39:42 +0000
commit817ff6d360efa18b42436d4ee46b8bee38315b17 (patch)
tree5d0eae0751ab4d6891b2edb481b05df4a958f90d /sys/kern/tty.c
parent117bc8a1c40f1d6d82ceb9154a481aac2780d463 (diff)
downloadFreeBSD-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.c61
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));
}
}
OpenPOWER on IntegriCloud