aboutsummaryrefslogtreecommitdiffstats
path: root/include
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 /include
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 'include')
-rw-r--r--include/linux/tty.h18
1 files changed, 18 insertions, 0 deletions
diff --git a/include/linux/tty.h b/include/linux/tty.h
index c75d886b0307..189ca80494d1 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -258,6 +258,7 @@ struct tty_struct {
258 unsigned char warned:1; 258 unsigned char warned:1;
259 unsigned char ctrl_status; /* ctrl_lock */ 259 unsigned char ctrl_status; /* ctrl_lock */
260 unsigned int receive_room; /* Bytes free for queue */ 260 unsigned int receive_room; /* Bytes free for queue */
261 int flow_change;
261 262
262 struct tty_struct *link; 263 struct tty_struct *link;
263 struct fasync_struct *fasync; 264 struct fasync_struct *fasync;
@@ -318,6 +319,21 @@ struct tty_file_private {
318 319
319#define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) 320#define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
320 321
322/* Values for tty->flow_change */
323#define TTY_THROTTLE_SAFE 1
324#define TTY_UNTHROTTLE_SAFE 2
325
326static inline void __tty_set_flow_change(struct tty_struct *tty, int val)
327{
328 tty->flow_change = val;
329}
330
331static inline void tty_set_flow_change(struct tty_struct *tty, int val)
332{
333 tty->flow_change = val;
334 smp_mb();
335}
336
321#ifdef CONFIG_TTY 337#ifdef CONFIG_TTY
322extern void console_init(void); 338extern void console_init(void);
323extern void tty_kref_put(struct tty_struct *tty); 339extern void tty_kref_put(struct tty_struct *tty);
@@ -400,6 +416,8 @@ extern int tty_write_room(struct tty_struct *tty);
400extern void tty_driver_flush_buffer(struct tty_struct *tty); 416extern void tty_driver_flush_buffer(struct tty_struct *tty);
401extern void tty_throttle(struct tty_struct *tty); 417extern void tty_throttle(struct tty_struct *tty);
402extern void tty_unthrottle(struct tty_struct *tty); 418extern void tty_unthrottle(struct tty_struct *tty);
419extern int tty_throttle_safe(struct tty_struct *tty);
420extern int tty_unthrottle_safe(struct tty_struct *tty);
403extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws); 421extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws);
404extern void tty_driver_remove_tty(struct tty_driver *driver, 422extern void tty_driver_remove_tty(struct tty_driver *driver,
405 struct tty_struct *tty); 423 struct tty_struct *tty);