diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-16 12:03:43 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-16 12:03:43 -0700 |
commit | 15bdb5652689d51cc0316de61774d2732472d9e1 (patch) | |
tree | fb79ca4d6bf8d46c466bc1f1c085b7c3ee6c7683 /drivers/char/pty.c | |
parent | 98523d4630865c407d3787fd592e5e399488b93b (diff) | |
parent | 1a2c4b3147ac0645605d6def2855478861d9361b (diff) |
Merge branch 'serial'
* serial:
imx: Check for NULL pointer deref before calling tty_encode_baud_rate
atmel_serial: fix hang in set_termios when crtscts is enabled
MAINTAINERS: update 8250 section, give Alan Cox a name
tty: fix sanity check
pty: Narrow the race on ldisc locking
tty: fix unused warning when TCGETX is not defined
ldisc: debug aids
ldisc: Make sure the ldisc isn't active when we close it
tty: Fix leaks introduced by the shift to separate ldisc objects
Fix conflicts in drivers/char/pty.c due to earlier version of the ldisc
race narrowing.
Diffstat (limited to 'drivers/char/pty.c')
-rw-r--r-- | drivers/char/pty.c | 57 |
1 files changed, 42 insertions, 15 deletions
diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 3910ce112a9..daebe1ba43d 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -95,23 +95,34 @@ static void pty_unthrottle(struct tty_struct *tty) * a count. * * FIXME: Our pty_write method is called with our ldisc lock held but - * not our partners. We can't just take the other one blindly without - * risking deadlocks. + * not our partners. We can't just wait on the other one blindly without + * risking deadlocks. At some point when everything has settled down we need + * to look into making pty_write at least able to sleep over an ldisc change. + * + * The return on no ldisc is a bit counter intuitive but the logic works + * like this. During an ldisc change the other end will flush its buffers. We + * thus return the full length which is identical to the case where we had + * proper locking and happened to queue the bytes just before the flush during + * the ldisc change. */ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct tty_struct *to = tty->link; - int c; + struct tty_ldisc *ld; + int c = count; - if (!to || !to->ldisc || tty->stopped) + if (!to || tty->stopped) return 0; - - c = to->receive_room; - if (c > count) - c = count; - to->ldisc->ops->receive_buf(to, buf, NULL, c); - + ld = tty_ldisc_ref(to); + + if (ld) { + c = to->receive_room; + if (c > count) + c = count; + ld->ops->receive_buf(to, buf, NULL, c); + tty_ldisc_deref(ld); + } return c; } @@ -145,14 +156,23 @@ static int pty_write_room(struct tty_struct *tty) static int pty_chars_in_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; - int count; + struct tty_ldisc *ld; + int count = 0; /* We should get the line discipline lock for "tty->link" */ - if (!to || !to->ldisc || !to->ldisc->ops->chars_in_buffer) + if (!to) + return 0; + /* We cannot take a sleeping reference here without deadlocking with + an ldisc change - but it doesn't really matter */ + ld = tty_ldisc_ref(to); + if (ld == NULL) return 0; /* The ldisc must report 0 if no characters available to be read */ - count = to->ldisc->ops->chars_in_buffer(to); + if (ld->ops->chars_in_buffer) + count = ld->ops->chars_in_buffer(to); + + tty_ldisc_deref(ld); if (tty->driver->subtype == PTY_TYPE_SLAVE) return count; @@ -182,12 +202,19 @@ static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; unsigned long flags; + struct tty_ldisc *ld; + + if (!to) + return; + ld = tty_ldisc_ref(to); - if (!to || !to->ldisc) + /* The other end is changing discipline */ + if (!ld) return; - if (to->ldisc->ops->flush_buffer) + if (ld->ops->flush_buffer) to->ldisc->ops->flush_buffer(to); + tty_ldisc_deref(ld); if (to->packet) { spin_lock_irqsave(&tty->ctrl_lock, flags); |