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.
This commit is contained in:
Linus Torvalds 2009-06-16 12:03:43 -07:00
commit 15bdb56526
7 changed files with 63 additions and 25 deletions

View File

@ -157,9 +157,10 @@ S: Maintained
F: drivers/net/r8169.c F: drivers/net/r8169.c
8250/16?50 (AND CLONE UARTS) SERIAL DRIVER 8250/16?50 (AND CLONE UARTS) SERIAL DRIVER
P: Alan Cox
M: alan@lxorguk.ukuu.org.uk
L: linux-serial@vger.kernel.org L: linux-serial@vger.kernel.org
W: http://serial.sourceforge.net W: http://serial.sourceforge.net
M: alan@lxorguk.ukuu.org.uk
S: Odd Fixes S: Odd Fixes
F: drivers/serial/8250* F: drivers/serial/8250*
F: include/linux/serial_8250.h F: include/linux/serial_8250.h

View File

@ -95,23 +95,34 @@ static void pty_unthrottle(struct tty_struct *tty)
* a count. * a count.
* *
* FIXME: Our pty_write method is called with our ldisc lock held but * 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 * not our partners. We can't just wait on the other one blindly without
* risking deadlocks. * 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, static int pty_write(struct tty_struct *tty, const unsigned char *buf,
int count) int count)
{ {
struct tty_struct *to = tty->link; 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; return 0;
ld = tty_ldisc_ref(to);
c = to->receive_room; if (ld) {
if (c > count) c = to->receive_room;
c = count; if (c > count)
to->ldisc->ops->receive_buf(to, buf, NULL, c); c = count;
ld->ops->receive_buf(to, buf, NULL, c);
tty_ldisc_deref(ld);
}
return c; 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) static int pty_chars_in_buffer(struct tty_struct *tty)
{ {
struct tty_struct *to = tty->link; 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" */ /* 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; return 0;
/* The ldisc must report 0 if no characters available to be read */ /* 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) if (tty->driver->subtype == PTY_TYPE_SLAVE)
return count; return count;
@ -182,12 +202,19 @@ static void pty_flush_buffer(struct tty_struct *tty)
{ {
struct tty_struct *to = tty->link; struct tty_struct *to = tty->link;
unsigned long flags; unsigned long flags;
struct tty_ldisc *ld;
if (!to || !to->ldisc) if (!to)
return;
ld = tty_ldisc_ref(to);
/* The other end is changing discipline */
if (!ld)
return; return;
if (to->ldisc->ops->flush_buffer) if (ld->ops->flush_buffer)
to->ldisc->ops->flush_buffer(to); to->ldisc->ops->flush_buffer(to);
tty_ldisc_deref(ld);
if (to->packet) { if (to->packet) {
spin_lock_irqsave(&tty->ctrl_lock, flags); spin_lock_irqsave(&tty->ctrl_lock, flags);

View File

@ -1263,7 +1263,9 @@ static int tty_reopen(struct tty_struct *tty)
tty->count++; tty->count++;
tty->driver = driver; /* N.B. why do this every time?? */ tty->driver = driver; /* N.B. why do this every time?? */
mutex_lock(&tty->ldisc_mutex);
WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
mutex_unlock(&tty->ldisc_mutex);
return 0; return 0;
} }

View File

@ -947,7 +947,6 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
void __user *p = (void __user *)arg; void __user *p = (void __user *)arg;
int ret = 0; int ret = 0;
struct ktermios kterm; struct ktermios kterm;
struct termiox ktermx;
if (tty->driver->type == TTY_DRIVER_TYPE_PTY && if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER) tty->driver->subtype == PTY_TYPE_MASTER)
@ -1049,7 +1048,8 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
return ret; return ret;
#endif #endif
#ifdef TCGETX #ifdef TCGETX
case TCGETX: case TCGETX: {
struct termiox ktermx;
if (real_tty->termiox == NULL) if (real_tty->termiox == NULL)
return -EINVAL; return -EINVAL;
mutex_lock(&real_tty->termios_mutex); mutex_lock(&real_tty->termios_mutex);
@ -1058,6 +1058,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
if (copy_to_user(p, &ktermx, sizeof(struct termiox))) if (copy_to_user(p, &ktermx, sizeof(struct termiox)))
ret = -EFAULT; ret = -EFAULT;
return ret; return ret;
}
case TCSETX: case TCSETX:
return set_termiox(real_tty, p, 0); return set_termiox(real_tty, p, 0);
case TCSETXW: case TCSETXW:

View File

@ -207,6 +207,7 @@ static void tty_ldisc_put(struct tty_ldisc *ld)
ldo->refcount--; ldo->refcount--;
module_put(ldo->owner); module_put(ldo->owner);
spin_unlock_irqrestore(&tty_ldisc_lock, flags); spin_unlock_irqrestore(&tty_ldisc_lock, flags);
WARN_ON(ld->refcount);
kfree(ld); kfree(ld);
} }
@ -793,6 +794,8 @@ void tty_ldisc_hangup(struct tty_struct *tty)
/* Avoid racing set_ldisc */ /* Avoid racing set_ldisc */
mutex_lock(&tty->ldisc_mutex); mutex_lock(&tty->ldisc_mutex);
/* Switch back to N_TTY */ /* Switch back to N_TTY */
tty_ldisc_halt(tty);
tty_ldisc_wait_idle(tty);
tty_ldisc_reinit(tty); tty_ldisc_reinit(tty);
/* At this point we have a closed ldisc and we want to /* At this point we have a closed ldisc and we want to
reopen it. We could defer this to the next open but reopen it. We could defer this to the next open but

View File

@ -1104,11 +1104,13 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
/* update the per-port timeout */ /* update the per-port timeout */
uart_update_timeout(port, termios->c_cflag, baud); uart_update_timeout(port, termios->c_cflag, baud);
/* save/disable interrupts and drain transmitter */ /*
* save/disable interrupts. The tty layer will ensure that the
* transmitter is empty if requested by the caller, so there's
* no need to wait for it here.
*/
imr = UART_GET_IMR(port); imr = UART_GET_IMR(port);
UART_PUT_IDR(port, -1); UART_PUT_IDR(port, -1);
while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY))
cpu_relax();
/* disable receiver and transmitter */ /* disable receiver and transmitter */
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);

View File

@ -924,11 +924,13 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
rational_best_approximation(16 * div * baud, sport->port.uartclk, rational_best_approximation(16 * div * baud, sport->port.uartclk,
1 << 16, 1 << 16, &num, &denom); 1 << 16, 1 << 16, &num, &denom);
tdiv64 = sport->port.uartclk; if (port->info && port->info->port.tty) {
tdiv64 *= num; tdiv64 = sport->port.uartclk;
do_div(tdiv64, denom * 16 * div); tdiv64 *= num;
tty_encode_baud_rate(sport->port.info->port.tty, do_div(tdiv64, denom * 16 * div);
(speed_t)tdiv64, (speed_t)tdiv64); tty_encode_baud_rate(sport->port.info->port.tty,
(speed_t)tdiv64, (speed_t)tdiv64);
}
num -= 1; num -= 1;
denom -= 1; denom -= 1;