提交 38db8979 编写于 作者: A Alan Cox 提交者: Linus Torvalds

tty: throttling race fix

The tty throttling code can race due to the lock drops. It takes very high
loads but this has been observed and verified by Rob Duncan.

The basic problem is that on an SMP box we can go

	CPU #1				CPU #2
	need to throttle ?
	suppose we should		buffer space cleared
					are we throttled
					yes ? - unthrottle
	call throttle method

This changeet take the termios lock to protect against this. The termios
lock isn't the initial obvious candidate but many implementations of throttle
methods already need to poke around their own termios structures (and nobody
really locks them against a racing change of flow control).

This does mean that anyone who is setting tty->low_latency = 1 and then
calling tty_flip_buffer_push from their unthrottle method is going to end up
collapsing in a pile of locks. However we've removed all the known bogus
users of low_latency = 1 and such use isn't safe anyway for other reasons so
catching it would be an improvement.
Signed-off-by: NAlan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
上级 5b0ed526
...@@ -97,14 +97,19 @@ EXPORT_SYMBOL(tty_driver_flush_buffer); ...@@ -97,14 +97,19 @@ EXPORT_SYMBOL(tty_driver_flush_buffer);
* @tty: terminal * @tty: terminal
* *
* Indicate that a tty should stop transmitting data down the stack. * Indicate that a tty should stop transmitting data down the stack.
* Takes the termios mutex to protect against parallel throttle/unthrottle
* and also to ensure the driver can consistently reference its own
* termios data at this point when implementing software flow control.
*/ */
void tty_throttle(struct tty_struct *tty) void tty_throttle(struct tty_struct *tty)
{ {
mutex_lock(&tty->termios_mutex);
/* check TTY_THROTTLED first so it indicates our state */ /* check TTY_THROTTLED first so it indicates our state */
if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&
tty->ops->throttle) tty->ops->throttle)
tty->ops->throttle(tty); tty->ops->throttle(tty);
mutex_unlock(&tty->termios_mutex);
} }
EXPORT_SYMBOL(tty_throttle); EXPORT_SYMBOL(tty_throttle);
...@@ -113,13 +118,21 @@ EXPORT_SYMBOL(tty_throttle); ...@@ -113,13 +118,21 @@ EXPORT_SYMBOL(tty_throttle);
* @tty: terminal * @tty: terminal
* *
* Indicate that a tty may continue transmitting data down the stack. * Indicate that a tty may continue transmitting data down the stack.
* Takes the termios mutex to protect against parallel throttle/unthrottle
* and also to ensure the driver can consistently reference its own
* termios data at this point when implementing software flow control.
*
* Drivers should however remember that the stack can issue a throttle,
* then change flow control method, then unthrottle.
*/ */
void tty_unthrottle(struct tty_struct *tty) void tty_unthrottle(struct tty_struct *tty)
{ {
mutex_lock(&tty->termios_mutex);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
tty->ops->unthrottle) tty->ops->unthrottle)
tty->ops->unthrottle(tty); tty->ops->unthrottle(tty);
mutex_unlock(&tty->termios_mutex);
} }
EXPORT_SYMBOL(tty_unthrottle); EXPORT_SYMBOL(tty_unthrottle);
......
...@@ -127,7 +127,8 @@ ...@@ -127,7 +127,8 @@
* the line discipline are close to full, and it should somehow * the line discipline are close to full, and it should somehow
* signal that no more characters should be sent to the tty. * signal that no more characters should be sent to the tty.
* *
* Optional: Always invoke via tty_throttle(); * Optional: Always invoke via tty_throttle(), called under the
* termios lock.
* *
* void (*unthrottle)(struct tty_struct * tty); * void (*unthrottle)(struct tty_struct * tty);
* *
...@@ -135,7 +136,8 @@ ...@@ -135,7 +136,8 @@
* that characters can now be sent to the tty without fear of * that characters can now be sent to the tty without fear of
* overrunning the input buffers of the line disciplines. * overrunning the input buffers of the line disciplines.
* *
* Optional: Always invoke via tty_unthrottle(); * Optional: Always invoke via tty_unthrottle(), called under the
* termios lock.
* *
* void (*stop)(struct tty_struct *tty); * void (*stop)(struct tty_struct *tty);
* *
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册