diff options
| -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); |
