aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/pty.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/pty.c')
-rw-r--r--drivers/char/pty.c53
1 files changed, 40 insertions, 13 deletions
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 5acd29e6e043..daebe1ba43d4 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -95,23 +95,34 @@ static void pty_unthrottle(struct tty_struct *tty)
95 * a count. 95 * a count.
96 * 96 *
97 * FIXME: Our pty_write method is called with our ldisc lock held but 97 * FIXME: Our pty_write method is called with our ldisc lock held but
98 * not our partners. We can't just take the other one blindly without 98 * not our partners. We can't just wait on the other one blindly without
99 * risking deadlocks. 99 * risking deadlocks. At some point when everything has settled down we need
100 * to look into making pty_write at least able to sleep over an ldisc change.
101 *
102 * The return on no ldisc is a bit counter intuitive but the logic works
103 * like this. During an ldisc change the other end will flush its buffers. We
104 * thus return the full length which is identical to the case where we had
105 * proper locking and happened to queue the bytes just before the flush during
106 * the ldisc change.
100 */ 107 */
101static int pty_write(struct tty_struct *tty, const unsigned char *buf, 108static int pty_write(struct tty_struct *tty, const unsigned char *buf,
102 int count) 109 int count)
103{ 110{
104 struct tty_struct *to = tty->link; 111 struct tty_struct *to = tty->link;
105 int c; 112 struct tty_ldisc *ld;
113 int c = count;
106 114
107 if (!to || tty->stopped) 115 if (!to || tty->stopped)
108 return 0; 116 return 0;
109 117 ld = tty_ldisc_ref(to);
110 c = to->receive_room; 118
111 if (c > count) 119 if (ld) {
112 c = count; 120 c = to->receive_room;
113 to->ldisc->ops->receive_buf(to, buf, NULL, c); 121 if (c > count)
114 122 c = count;
123 ld->ops->receive_buf(to, buf, NULL, c);
124 tty_ldisc_deref(ld);
125 }
115 return c; 126 return c;
116} 127}
117 128
@@ -145,14 +156,23 @@ static int pty_write_room(struct tty_struct *tty)
145static int pty_chars_in_buffer(struct tty_struct *tty) 156static int pty_chars_in_buffer(struct tty_struct *tty)
146{ 157{
147 struct tty_struct *to = tty->link; 158 struct tty_struct *to = tty->link;
148 int count; 159 struct tty_ldisc *ld;
160 int count = 0;
149 161
150 /* We should get the line discipline lock for "tty->link" */ 162 /* We should get the line discipline lock for "tty->link" */
151 if (!to || !to->ldisc->ops->chars_in_buffer) 163 if (!to)
164 return 0;
165 /* We cannot take a sleeping reference here without deadlocking with
166 an ldisc change - but it doesn't really matter */
167 ld = tty_ldisc_ref(to);
168 if (ld == NULL)
152 return 0; 169 return 0;
153 170
154 /* The ldisc must report 0 if no characters available to be read */ 171 /* The ldisc must report 0 if no characters available to be read */
155 count = to->ldisc->ops->chars_in_buffer(to); 172 if (ld->ops->chars_in_buffer)
173 count = ld->ops->chars_in_buffer(to);
174
175 tty_ldisc_deref(ld);
156 176
157 if (tty->driver->subtype == PTY_TYPE_SLAVE) 177 if (tty->driver->subtype == PTY_TYPE_SLAVE)
158 return count; 178 return count;
@@ -182,12 +202,19 @@ static void pty_flush_buffer(struct tty_struct *tty)
182{ 202{
183 struct tty_struct *to = tty->link; 203 struct tty_struct *to = tty->link;
184 unsigned long flags; 204 unsigned long flags;
205 struct tty_ldisc *ld;
185 206
186 if (!to) 207 if (!to)
187 return; 208 return;
209 ld = tty_ldisc_ref(to);
210
211 /* The other end is changing discipline */
212 if (!ld)
213 return;
188 214
189 if (to->ldisc->ops->flush_buffer) 215 if (ld->ops->flush_buffer)
190 to->ldisc->ops->flush_buffer(to); 216 to->ldisc->ops->flush_buffer(to);
217 tty_ldisc_deref(ld);
191 218
192 if (to->packet) { 219 if (to->packet) {
193 spin_lock_irqsave(&tty->ctrl_lock, flags); 220 spin_lock_irqsave(&tty->ctrl_lock, flags);