diff options
Diffstat (limited to 'drivers/char/pty.c')
-rw-r--r-- | drivers/char/pty.c | 53 |
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 | */ |
101 | static int pty_write(struct tty_struct *tty, const unsigned char *buf, | 108 | static 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) | |||
145 | static int pty_chars_in_buffer(struct tty_struct *tty) | 156 | static 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); |