]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/2.6.36.2/tty-restore-tty_ldisc_wait_idle.patch
fixes for 4.19
[thirdparty/kernel/stable-queue.git] / releases / 2.6.36.2 / tty-restore-tty_ldisc_wait_idle.patch
1 From 100eeae2c5ce23b4db93ff320ee330ef1d740151 Mon Sep 17 00:00:00 2001
2 From: Jiri Slaby <jslaby@suse.cz>
3 Date: Sun, 31 Oct 2010 23:17:51 +0100
4 Subject: TTY: restore tty_ldisc_wait_idle
5
6 From: Jiri Slaby <jslaby@suse.cz>
7
8 commit 100eeae2c5ce23b4db93ff320ee330ef1d740151 upstream.
9
10 It was removed in 65b770468e98 (tty-ldisc: turn ldisc user count into
11 a proper refcount), but we need to wait for last user to quit the
12 ldisc before we close it in tty_set_ldisc.
13
14 Otherwise weird things start to happen. There might be processes
15 waiting in tty_read->n_tty_read on tty->read_wait for input to appear
16 and at that moment, a change of ldisc is fatal. n_tty_close is called,
17 it frees read_buf and the waiting process is still in the middle of
18 reading and goes nuts after it is woken.
19
20 Previously we prevented close to happen when others are in ldisc ops
21 by tty_ldisc_wait_idle in tty_set_ldisc. But the commit above removed
22 that. So revoke the change and test whether there is 1 user (=we), and
23 allow the close then.
24
25 We can do that without ldisc/tty locks, because nobody else can open
26 the device due to TTY_LDISC_CHANGING bit set, so we in fact wait for
27 everybody to leave.
28
29 I don't understand why tty_ldisc_lock would be needed either when the
30 counter is an atomic variable, so this is a lockless
31 tty_ldisc_wait_idle.
32
33 On the other hand, if we fail to wait (timeout or signal), we have to
34 reenable the halted ldiscs, so we take ldisc lock and reuse the setup
35 path at the end of tty_set_ldisc.
36
37 Signed-off-by: Jiri Slaby <jslaby@suse.cz>
38 Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
39 Tested-by: Sebastian Andrzej Siewior <bigeasy@breakpoint.cc>
40 LKML-Reference: <20101031104136.GA511@Chamillionaire.breakpoint.cc>
41 LKML-Reference: <1287669539-22644-1-git-send-email-jslaby@suse.cz>
42 Cc: Alan Cox <alan@linux.intel.com>
43 Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
44
45 ---
46 drivers/char/tty_ldisc.c | 29 +++++++++++++++++++++++++++++
47 1 file changed, 29 insertions(+)
48
49 --- a/drivers/char/tty_ldisc.c
50 +++ b/drivers/char/tty_ldisc.c
51 @@ -47,6 +47,7 @@
52
53 static DEFINE_SPINLOCK(tty_ldisc_lock);
54 static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
55 +static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_idle);
56 /* Line disc dispatch table */
57 static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
58
59 @@ -83,6 +84,7 @@ static void put_ldisc(struct tty_ldisc *
60 return;
61 }
62 local_irq_restore(flags);
63 + wake_up(&tty_ldisc_idle);
64 }
65
66 /**
67 @@ -531,6 +533,23 @@ static int tty_ldisc_halt(struct tty_str
68 }
69
70 /**
71 + * tty_ldisc_wait_idle - wait for the ldisc to become idle
72 + * @tty: tty to wait for
73 + *
74 + * Wait for the line discipline to become idle. The discipline must
75 + * have been halted for this to guarantee it remains idle.
76 + */
77 +static int tty_ldisc_wait_idle(struct tty_struct *tty)
78 +{
79 + int ret;
80 + ret = wait_event_interruptible_timeout(tty_ldisc_idle,
81 + atomic_read(&tty->ldisc->users) == 1, 5 * HZ);
82 + if (ret < 0)
83 + return ret;
84 + return ret > 0 ? 0 : -EBUSY;
85 +}
86 +
87 +/**
88 * tty_set_ldisc - set line discipline
89 * @tty: the terminal to set
90 * @ldisc: the line discipline
91 @@ -634,8 +653,17 @@ int tty_set_ldisc(struct tty_struct *tty
92
93 flush_scheduled_work();
94
95 + retval = tty_ldisc_wait_idle(tty);
96 +
97 tty_lock();
98 mutex_lock(&tty->ldisc_mutex);
99 +
100 + /* handle wait idle failure locked */
101 + if (retval) {
102 + tty_ldisc_put(new_ldisc);
103 + goto enable;
104 + }
105 +
106 if (test_bit(TTY_HUPPED, &tty->flags)) {
107 /* We were raced by the hangup method. It will have stomped
108 the ldisc data and closed the ldisc down */
109 @@ -669,6 +697,7 @@ int tty_set_ldisc(struct tty_struct *tty
110
111 tty_ldisc_put(o_ldisc);
112
113 +enable:
114 /*
115 * Allow ldisc referencing to occur again
116 */