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 commita57a7bf3fc
("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 commit0911261d4c
("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: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
32ede4a517
commit
79c1faa451
|
@ -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)
|
||||||
* 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)
|
||||||
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)
|
||||||
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,
|
||||||
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);
|
||||||
* 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);
|
||||||
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
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue