Fix draining in ttydev_leave():
1. ERESTART is not only returned when the revoke count changed. It is also returned when a signal is received. While a change in the revoke count should be ignored, a signal should not. 2. Waiting until the output queue is entirely drained can cause a hang when the underlying device is stuck or broken. Have tty_drain() take care of this by telling it when we're leaving. When leaving, tty_drain() will use a timed wait to address point 2 above and it will check the revoke count to handle point 1 above. The timeout is set to 1 second, which is arbitrary and long enough to expect a change in the output queue. Discussed with: jilles@ Reported by: Yamagi Burmeister <lists@yamagi.org>
This commit is contained in:
parent
cdfd89cea1
commit
383f423be1
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=272789
@ -123,9 +123,10 @@ tty_watermarks(struct tty *tp)
|
||||
}
|
||||
|
||||
static int
|
||||
tty_drain(struct tty *tp)
|
||||
tty_drain(struct tty *tp, int leaving)
|
||||
{
|
||||
int error;
|
||||
size_t bytesused;
|
||||
int error, revokecnt;
|
||||
|
||||
if (ttyhook_hashook(tp, getc_inject))
|
||||
/* buffer is inaccessible */
|
||||
@ -134,11 +135,27 @@ tty_drain(struct tty *tp)
|
||||
while (ttyoutq_bytesused(&tp->t_outq) > 0) {
|
||||
ttydevsw_outwakeup(tp);
|
||||
/* Could be handled synchronously. */
|
||||
if (ttyoutq_bytesused(&tp->t_outq) == 0)
|
||||
bytesused = ttyoutq_bytesused(&tp->t_outq);
|
||||
if (bytesused == 0)
|
||||
return (0);
|
||||
|
||||
/* Wait for data to be drained. */
|
||||
error = tty_wait(tp, &tp->t_outwait);
|
||||
if (leaving) {
|
||||
revokecnt = tp->t_revokecnt;
|
||||
error = tty_timedwait(tp, &tp->t_outwait, hz);
|
||||
switch (error) {
|
||||
case ERESTART:
|
||||
if (revokecnt != tp->t_revokecnt)
|
||||
error = 0;
|
||||
break;
|
||||
case EWOULDBLOCK:
|
||||
if (ttyoutq_bytesused(&tp->t_outq) < bytesused)
|
||||
error = 0;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
error = tty_wait(tp, &tp->t_outwait);
|
||||
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
@ -191,10 +208,8 @@ ttydev_leave(struct tty *tp)
|
||||
|
||||
/* Drain any output. */
|
||||
MPASS((tp->t_flags & TF_STOPPED) == 0);
|
||||
if (!tty_gone(tp)) {
|
||||
while (tty_drain(tp) == ERESTART)
|
||||
;
|
||||
}
|
||||
if (!tty_gone(tp))
|
||||
tty_drain(tp, 1);
|
||||
|
||||
ttydisc_close(tp);
|
||||
|
||||
@ -1528,7 +1543,7 @@ tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag,
|
||||
|
||||
/* Set terminal flags through tcsetattr(). */
|
||||
if (cmd == TIOCSETAW || cmd == TIOCSETAF) {
|
||||
error = tty_drain(tp);
|
||||
error = tty_drain(tp, 0);
|
||||
if (error)
|
||||
return (error);
|
||||
if (cmd == TIOCSETAF)
|
||||
@ -1707,7 +1722,7 @@ tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag,
|
||||
}
|
||||
case TIOCDRAIN:
|
||||
/* Drain TTY output. */
|
||||
return tty_drain(tp);
|
||||
return tty_drain(tp, 0);
|
||||
case TIOCCONS:
|
||||
/* Set terminal as console TTY. */
|
||||
if (*(int *)data) {
|
||||
|
Loading…
Reference in New Issue
Block a user