aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2013-03-06 08:20:52 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-18 19:11:59 -0400
commit70bc126471af30bb115e635512dcf6d86fe6e29a (patch)
treee6fc82736558f3a1b1f1601e237bc395b78a9dcd /drivers/tty
parentc828f679eed393d6925a2b44a4c3fb80a8d657cb (diff)
tty: Add safe tty throttle/unthrottle functions
The tty driver can become stuck throttled due to race conditions between throttle and unthrottle, when the decision to throttle or unthrottle is conditional. The following example helps to illustrate the race: CPU 0 | CPU 1 | if (condition A) | | <processing such that A not true> | if (!condition A) | unthrottle() throttle() | | Note the converse is also possible; ie., CPU 0 | CPU 1 | | if (!condition A) <processing such that A true> | if (condition A) | throttle() | | unthrottle() | Add new throttle/unthrottle functions based on the familiar model of task state and schedule/wake. For example, while (1) { tty_set_flow_change(tty, TTY_THROTTLE_SAFE); if (!condition) break; if (!tty_throttle_safe(tty)) break; } __tty_set_flow_change(tty, 0); In this example, if an unthrottle occurs after the condition is evaluated but before tty_throttle_safe(), then tty_throttle_safe() will return non-zero, looping and forcing the re-evaluation of condition. Reported-by: Vincent Pillet <vincentx.pillet@intel.com> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/tty_ioctl.c64
1 files changed, 64 insertions, 0 deletions
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index d58b92cc187c..132d452578bb 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -106,6 +106,7 @@ void tty_throttle(struct tty_struct *tty)
106 if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && 106 if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&
107 tty->ops->throttle) 107 tty->ops->throttle)
108 tty->ops->throttle(tty); 108 tty->ops->throttle(tty);
109 tty->flow_change = 0;
109 mutex_unlock(&tty->termios_mutex); 110 mutex_unlock(&tty->termios_mutex);
110} 111}
111EXPORT_SYMBOL(tty_throttle); 112EXPORT_SYMBOL(tty_throttle);
@@ -129,11 +130,74 @@ void tty_unthrottle(struct tty_struct *tty)
129 if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && 130 if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
130 tty->ops->unthrottle) 131 tty->ops->unthrottle)
131 tty->ops->unthrottle(tty); 132 tty->ops->unthrottle(tty);
133 tty->flow_change = 0;
132 mutex_unlock(&tty->termios_mutex); 134 mutex_unlock(&tty->termios_mutex);
133} 135}
134EXPORT_SYMBOL(tty_unthrottle); 136EXPORT_SYMBOL(tty_unthrottle);
135 137
136/** 138/**
139 * tty_throttle_safe - flow control
140 * @tty: terminal
141 *
142 * Similar to tty_throttle() but will only attempt throttle
143 * if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental
144 * throttle due to race conditions when throttling is conditional
145 * on factors evaluated prior to throttling.
146 *
147 * Returns 0 if tty is throttled (or was already throttled)
148 */
149
150int tty_throttle_safe(struct tty_struct *tty)
151{
152 int ret = 0;
153
154 mutex_lock(&tty->termios_mutex);
155 if (!test_bit(TTY_THROTTLED, &tty->flags)) {
156 if (tty->flow_change != TTY_THROTTLE_SAFE)
157 ret = 1;
158 else {
159 __set_bit(TTY_THROTTLED, &tty->flags);
160 if (tty->ops->throttle)
161 tty->ops->throttle(tty);
162 }
163 }
164 mutex_unlock(&tty->termios_mutex);
165
166 return ret;
167}
168
169/**
170 * tty_unthrottle_safe - flow control
171 * @tty: terminal
172 *
173 * Similar to tty_unthrottle() but will only attempt unthrottle
174 * if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental
175 * unthrottle due to race conditions when unthrottling is conditional
176 * on factors evaluated prior to unthrottling.
177 *
178 * Returns 0 if tty is unthrottled (or was already unthrottled)
179 */
180
181int tty_unthrottle_safe(struct tty_struct *tty)
182{
183 int ret = 0;
184
185 mutex_lock(&tty->termios_mutex);
186 if (test_bit(TTY_THROTTLED, &tty->flags)) {
187 if (tty->flow_change != TTY_UNTHROTTLE_SAFE)
188 ret = 1;
189 else {
190 __clear_bit(TTY_THROTTLED, &tty->flags);
191 if (tty->ops->unthrottle)
192 tty->ops->unthrottle(tty);
193 }
194 }
195 mutex_unlock(&tty->termios_mutex);
196
197 return ret;
198}
199
200/**
137 * tty_wait_until_sent - wait for I/O to finish 201 * tty_wait_until_sent - wait for I/O to finish
138 * @tty: tty we are waiting for 202 * @tty: tty we are waiting for
139 * @timeout: how long we will wait 203 * @timeout: how long we will wait