svn commit: r259441 - head/sys/kern
Jilles Tjoelker
jilles at stack.nl
Sun Feb 16 23:03:10 UTC 2014
On Mon, Dec 16, 2013 at 12:50:15AM +0000, Marcel Moolenaar wrote:
> Author: marcel
> Date: Mon Dec 16 00:50:14 2013
> New Revision: 259441
> URL: http://svnweb.freebsd.org/changeset/base/259441
> Log:
> Properly drain the TTY when both revoke(2) and close(2) end up closing
> the TTY. In such a case, ttydev_close() is called multiple times and
> each time, t_revokecnt is incremented and cv_broadcast() is called for
> both the t_outwait and t_inwait condition variables.
> Let's say revoke(2) comes in first and gets to call tty_drain() from
> ttydev_leave(). Let's say that the revoke comes from init(8) as the
> result of running "shutdown -r now". Since shutdown prints various
> messages to the console before announing that the machine will reboot
> immediately, let's also say that the output queue is not empty and
> that tty_drain() has something to do. Let's assume this all happens
> on a 9600 baud serial console, so it takes a time to drain.
> The shutdown command will exit(2) and as such will end up closing
> stdout. Let's say this close will come in second, bump t_revokecnt
> and call tty_wakeup(). This has tty_wait() return prematurely and
> the next thing that will happen is that the thread doing revoke(2)
> will flush the TTY. Since the drain wasn't complete, the flush will
> effectively drop whatever is left in t_outq.
> This change takes into account that tty_drain() will return ERESTART
> due to the fact that t_revokecnt was bumped and in that case simply
> call tty_drain() again. The thread in question is already performing
> the close so it can safely finish draining the TTY before destroying
> the TTY structure.
> Now all messages from shutdown will be printed on the serial console.
> Obtained from: Juniper Networks, Inc.
> Modified:
> head/sys/kern/tty.c
> Modified: head/sys/kern/tty.c
> ==============================================================================
> --- head/sys/kern/tty.c Sun Dec 15 23:49:42 2013 (r259440)
> +++ head/sys/kern/tty.c Mon Dec 16 00:50:14 2013 (r259441)
> @@ -191,8 +191,10 @@ ttydev_leave(struct tty *tp)
>
> /* Drain any output. */
> MPASS((tp->t_flags & TF_STOPPED) == 0);
> - if (!tty_gone(tp))
> - tty_drain(tp);
> + if (!tty_gone(tp)) {
> + while (tty_drain(tp) == ERESTART)
> + ;
> + }
>
> ttydisc_close(tp);
>
Unfortunately, this is only correct if the [ERESTART] is because of the
(tp->t_revokecnt != revokecnt) check. If cv_wait_sig() returned
ERESTART, it will return ERESTART immediately the next time it is
called, until the signal has been handled. Therefore, if a process
performing the last close of a TTY via close(2) receives a signal with
SA_RESTART set, it will spin until the data has drained. Formerly, the
data would be discarded when the signal was received (without reflecting
this in close(2)'s return value, since ttydev_leave() returns nothing
and ttydev_close() always returns 0).
One way to fix this would be to replicate the revokecnt check into
ttydev_leave() so that tty_drain() is only retried if t_revokecnt
changed.
Discarding data because of a signal is not ideal, but I think leaving a
process unkillable because of a drain that will never complete is worse.
A process that cares about data in TTY buffers can use tcdrain(3) and
handle signals and errors normally.
--
Jilles Tjoelker
More information about the svn-src-head
mailing list