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 | |
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.
-rw-r--r-- | sys/kern/tty.c | 61 | ||||
-rw-r--r-- | sys/sys/tty.h | 9 |
2 files changed, 31 insertions, 39 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)); } } diff --git a/sys/sys/tty.h b/sys/sys/tty.h index c3843f4..121926f 100644 --- a/sys/sys/tty.h +++ b/sys/sys/tty.h @@ -36,7 +36,7 @@ * SUCH DAMAGE. * * @(#)tty.h 8.6 (Berkeley) 1/21/94 - * $Id: tty.h,v 1.24 1995/07/22 16:45:22 bde Exp $ + * $Id: tty.h,v 1.25 1995/07/29 13:40:13 bde Exp $ */ #ifndef _SYS_TTY_H_ @@ -130,7 +130,7 @@ struct tty { #endif /* These flags are kept in t_state. */ -#define TS_ASLEEP 0x00001 /* Process waiting for tty. */ +#define TS_SO_OLOWAT 0x00001 /* Wake up when output <= low water. */ #define TS_ASYNC 0x00002 /* Tty in async I/O mode. */ #define TS_BUSY 0x00004 /* Draining output. */ #define TS_CARR_ON 0x00008 /* Carrier is present. */ @@ -161,6 +161,8 @@ struct tty { */ #define TS_CAN_BYPASS_L_RINT 0x20000 /* device in "raw" mode */ +/* Extras. */ +#define TS_SO_OCOMPLETE 0x080000 /* Wake up when output completes. */ /* Character type information. */ #define ORDINARY 0 @@ -201,7 +203,8 @@ struct speedtab { /* Unique sleep addresses. */ #define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq) /* XXX overloaded */ -#define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) /* XXX overloaded */ +#define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq.c_cl) +#define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) #define TSA_PTC_READ(tp) ((void *)&(tp)->t_outq.c_cf) #define TSA_PTC_WRITE(tp) ((void *)&(tp)->t_rawq.c_cl) #define TSA_PTS_READ(tp) ((void *)&(tp)->t_canq) |