Commit 79c1faa4 authored by Peter Hurley's avatar Peter Hurley Committed by Greg Kroah-Hartman

tty: Remove tty_wait_until_sent_from_close()

tty_wait_until_sent_from_close() drops the tty lock while waiting
for the tty driver to finish sending previously accepted data (ie.,
data remaining in its write buffer and transmit fifo).

tty_wait_until_sent_from_close() was added by commit a57a7bf3
("TTY: define tty_wait_until_sent_from_close") to prevent the entire
tty subsystem from being unable to open new ttys while waiting for
one tty to close while output drained.

However, since commit 0911261d ("tty: Don't take tty_mutex for tty
count changes"), holding a tty lock while closing does not prevent other
ttys from being opened/closed/hung up, but only prevents lifetime event
changes for the tty under lock.

Holding the tty lock while waiting for output to drain does prevent
parallel non-blocking opens (O_NONBLOCK) from advancing or returning
while the tty lock is held. However, all parallel opens _already_
block even if the tty lock is dropped while closing and the parallel
open advances. Blocking in open has been in mainline since at least 2.6.29
(see tty_port_block_til_ready(); note the test for O_NONBLOCK is _after_
the wait while ASYNC_CLOSING).

IOW, before this patch a non-blocking open will sleep anyway for the
_entire_ duration of a parallel hardware shutdown, and when it wakes, the
error return will cause a release of its tty, and it will restart with
a fresh attempt to open. Similarly with a blocking open that is already
waiting; when it's woken, the hardware shutdown has already completed
to ASYNC_INITIALIZED is not set, which forces a release and restart as
well.

So, holding the tty lock across the _entire_ close (which is what this
patch does), even while waiting for output to drain, is equivalent to
the current outcome wrt parallel opens.

Cc: Alan Cox <alan@linux.intel.com>
Cc: David Laight <David.Laight@aculab.com>
CC: Arnd Bergmann <arnd@arndb.de>
CC: Karsten Keil <isdn@linux-pingi.de>
CC: linuxppc-dev@lists.ozlabs.org
Signed-off-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 32ede4a5
...@@ -1582,7 +1582,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) ...@@ -1582,7 +1582,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp)
* line status register. * line status register.
*/ */
if (port->flags & ASYNC_INITIALIZED) { if (port->flags & ASYNC_INITIALIZED) {
tty_wait_until_sent_from_close(tty, 3000); /* 30 seconds timeout */ tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
/* /*
* Before we drop DTR, make sure the UART transmitter * Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially * has completely drained; this is especially
......
...@@ -418,7 +418,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) ...@@ -418,7 +418,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
* there is no buffered data otherwise sleeps on a wait queue * there is no buffered data otherwise sleeps on a wait queue
* waking periodically to check chars_in_buffer(). * waking periodically to check chars_in_buffer().
*/ */
tty_wait_until_sent_from_close(tty, HVC_CLOSE_WAIT); tty_wait_until_sent(tty, HVC_CLOSE_WAIT);
} else { } else {
if (hp->port.count < 0) if (hp->port.count < 0)
printk(KERN_ERR "hvc_close %X: oops, count is %d\n", printk(KERN_ERR "hvc_close %X: oops, count is %d\n",
......
...@@ -1230,7 +1230,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) ...@@ -1230,7 +1230,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
irq = hvcsd->vdev->irq; irq = hvcsd->vdev->irq;
spin_unlock_irqrestore(&hvcsd->lock, flags); spin_unlock_irqrestore(&hvcsd->lock, flags);
tty_wait_until_sent_from_close(tty, HVCS_CLOSE_WAIT); tty_wait_until_sent(tty, HVCS_CLOSE_WAIT);
/* /*
* This line is important because it tells hvcs_open that this * This line is important because it tells hvcs_open that this
......
...@@ -463,10 +463,7 @@ static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) ...@@ -463,10 +463,7 @@ static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty)
schedule_timeout_interruptible(timeout); schedule_timeout_interruptible(timeout);
} }
/* Caller holds tty lock. /* Caller holds tty lock. */
* NB: may drop and reacquire tty lock (in tty_wait_until_sent_from_close())
* so tty and tty port may have changed state (but not hung up or reopened).
*/
int tty_port_close_start(struct tty_port *port, int tty_port_close_start(struct tty_port *port,
struct tty_struct *tty, struct file *filp) struct tty_struct *tty, struct file *filp)
{ {
...@@ -502,7 +499,7 @@ int tty_port_close_start(struct tty_port *port, ...@@ -502,7 +499,7 @@ int tty_port_close_start(struct tty_port *port,
if (tty->flow_stopped) if (tty->flow_stopped)
tty_driver_flush_buffer(tty); tty_driver_flush_buffer(tty);
if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent_from_close(tty, port->closing_wait); tty_wait_until_sent(tty, port->closing_wait);
if (port->drain_delay) if (port->drain_delay)
tty_port_drain_delay(port, tty); tty_port_drain_delay(port, tty);
} }
...@@ -543,10 +540,6 @@ EXPORT_SYMBOL(tty_port_close_end); ...@@ -543,10 +540,6 @@ EXPORT_SYMBOL(tty_port_close_end);
* tty_port_close * tty_port_close
* *
* Caller holds tty lock * Caller holds tty lock
*
* NB: may drop and reacquire tty lock (in tty_port_close_start()->
* tty_wait_until_sent_from_close()) so tty and tty_port may have changed
* state (but not hung up or reopened).
*/ */
void tty_port_close(struct tty_port *port, struct tty_struct *tty, void tty_port_close(struct tty_port *port, struct tty_struct *tty,
struct file *filp) struct file *filp)
......
...@@ -656,24 +656,6 @@ extern void __lockfunc tty_unlock(struct tty_struct *tty); ...@@ -656,24 +656,6 @@ extern void __lockfunc tty_unlock(struct tty_struct *tty);
extern void __lockfunc tty_lock_slave(struct tty_struct *tty); extern void __lockfunc tty_lock_slave(struct tty_struct *tty);
extern void __lockfunc tty_unlock_slave(struct tty_struct *tty); extern void __lockfunc tty_unlock_slave(struct tty_struct *tty);
extern void tty_set_lock_subclass(struct tty_struct *tty); extern void tty_set_lock_subclass(struct tty_struct *tty);
/*
* this shall be called only from where BTM is held (like close)
*
* We need this to ensure nobody waits for us to finish while we are waiting.
* Without this we were encountering system stalls.
*
* This should be indeed removed with BTM removal later.
*
* Locking: BTM required. Nobody is allowed to hold port->mutex.
*/
static inline void tty_wait_until_sent_from_close(struct tty_struct *tty,
long timeout)
{
tty_unlock(tty); /* tty->ops->close holds the BTM, drop it while waiting */
tty_wait_until_sent(tty, timeout);
tty_lock(tty);
}
/* /*
* wait_event_interruptible_tty -- wait for a condition with the tty lock held * wait_event_interruptible_tty -- wait for a condition with the tty lock held
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment