tty: mxser: Use the new locking rules to fix setserial properly

Propogate the init/shutdown mutex through the setserial logic. Use the proper
locks for the various bits still using the BKL. Kill the BKL in this driver.

Updated to fix the bug noted by Dan Carpenter

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Alan Cox 2009-11-30 13:17:41 +00:00 committed by Greg Kroah-Hartman
parent 6769140d30
commit 07f86c03fe

View file

@ -23,7 +23,6 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/tty.h> #include <linux/tty.h>
@ -1229,6 +1228,7 @@ static int mxser_set_serial_info(struct tty_struct *tty,
struct serial_struct __user *new_info) struct serial_struct __user *new_info)
{ {
struct mxser_port *info = tty->driver_data; struct mxser_port *info = tty->driver_data;
struct tty_port *port = &info->port;
struct serial_struct new_serial; struct serial_struct new_serial;
speed_t baud; speed_t baud;
unsigned long sl_flags; unsigned long sl_flags;
@ -1244,7 +1244,7 @@ static int mxser_set_serial_info(struct tty_struct *tty,
new_serial.port != info->ioaddr) new_serial.port != info->ioaddr)
return -EINVAL; return -EINVAL;
flags = info->port.flags & ASYNC_SPD_MASK; flags = port->flags & ASYNC_SPD_MASK;
if (!capable(CAP_SYS_ADMIN)) { if (!capable(CAP_SYS_ADMIN)) {
if ((new_serial.baud_base != info->baud_base) || if ((new_serial.baud_base != info->baud_base) ||
@ -1258,16 +1258,17 @@ static int mxser_set_serial_info(struct tty_struct *tty,
* OK, past this point, all the error checking has been done. * OK, past this point, all the error checking has been done.
* At this point, we start making changes..... * At this point, we start making changes.....
*/ */
info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) | port->flags = ((port->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS)); (new_serial.flags & ASYNC_FLAGS));
info->port.close_delay = new_serial.close_delay * HZ / 100; port->close_delay = new_serial.close_delay * HZ / 100;
info->port.closing_wait = new_serial.closing_wait * HZ / 100; port->closing_wait = new_serial.closing_wait * HZ / 100;
tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
? 1 : 0; if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
(new_serial.baud_base != info->baud_base || (new_serial.baud_base != info->baud_base ||
new_serial.custom_divisor != new_serial.custom_divisor !=
info->custom_divisor)) { info->custom_divisor)) {
if (new_serial.custom_divisor == 0)
return -EINVAL;
baud = new_serial.baud_base / new_serial.custom_divisor; baud = new_serial.baud_base / new_serial.custom_divisor;
tty_encode_baud_rate(tty, baud, baud); tty_encode_baud_rate(tty, baud, baud);
} }
@ -1277,18 +1278,16 @@ static int mxser_set_serial_info(struct tty_struct *tty,
process_txrx_fifo(info); process_txrx_fifo(info);
if (info->port.flags & ASYNC_INITIALIZED) { if (test_bit(ASYNCB_INITIALIZED, &port->flags)) {
if (flags != (info->port.flags & ASYNC_SPD_MASK)) { if (flags != (port->flags & ASYNC_SPD_MASK)) {
spin_lock_irqsave(&info->slock, sl_flags); spin_lock_irqsave(&info->slock, sl_flags);
mxser_change_speed(tty, NULL); mxser_change_speed(tty, NULL);
spin_unlock_irqrestore(&info->slock, sl_flags); spin_unlock_irqrestore(&info->slock, sl_flags);
} }
} else { } else {
mutex_lock(&info->port.mutex); retval = mxser_activate(port, tty);
retval = mxser_activate(&info->port, tty);
if (retval == 0) if (retval == 0)
set_bit(ASYNCB_INITIALIZED, &info->port.flags); set_bit(ASYNCB_INITIALIZED, &port->flags);
mutex_unlock(&info->port.mutex);
} }
return retval; return retval;
} }
@ -1478,7 +1477,8 @@ static int __init mxser_read_register(int port, unsigned short *regs)
static int mxser_ioctl_special(unsigned int cmd, void __user *argp) static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
{ {
struct mxser_port *port; struct mxser_port *ip;
struct tty_port *port;
struct tty_struct *tty; struct tty_struct *tty;
int result, status; int result, status;
unsigned int i, j; unsigned int i, j;
@ -1494,38 +1494,39 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
case MOXA_CHKPORTENABLE: case MOXA_CHKPORTENABLE:
result = 0; result = 0;
lock_kernel();
for (i = 0; i < MXSER_BOARDS; i++) for (i = 0; i < MXSER_BOARDS; i++)
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) for (j = 0; j < MXSER_PORTS_PER_BOARD; j++)
if (mxser_boards[i].ports[j].ioaddr) if (mxser_boards[i].ports[j].ioaddr)
result |= (1 << i); result |= (1 << i);
unlock_kernel();
return put_user(result, (unsigned long __user *)argp); return put_user(result, (unsigned long __user *)argp);
case MOXA_GETDATACOUNT: case MOXA_GETDATACOUNT:
lock_kernel(); /* The receive side is locked by port->slock but it isn't
clear that an exact snapshot is worth copying here */
if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log))) if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log)))
ret = -EFAULT; ret = -EFAULT;
unlock_kernel();
return ret; return ret;
case MOXA_GETMSTATUS: { case MOXA_GETMSTATUS: {
struct mxser_mstatus ms, __user *msu = argp; struct mxser_mstatus ms, __user *msu = argp;
lock_kernel();
for (i = 0; i < MXSER_BOARDS; i++) for (i = 0; i < MXSER_BOARDS; i++)
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) {
port = &mxser_boards[i].ports[j]; ip = &mxser_boards[i].ports[j];
port = &ip->port;
memset(&ms, 0, sizeof(ms)); memset(&ms, 0, sizeof(ms));
if (!port->ioaddr) mutex_lock(&port->mutex);
if (!ip->ioaddr)
goto copy; goto copy;
tty = tty_port_tty_get(&port->port); tty = tty_port_tty_get(port);
if (!tty || !tty->termios) if (!tty || !tty->termios)
ms.cflag = port->normal_termios.c_cflag; ms.cflag = ip->normal_termios.c_cflag;
else else
ms.cflag = tty->termios->c_cflag; ms.cflag = tty->termios->c_cflag;
tty_kref_put(tty); tty_kref_put(tty);
status = inb(port->ioaddr + UART_MSR); spin_lock_irq(&ip->slock);
status = inb(ip->ioaddr + UART_MSR);
spin_unlock_irq(&ip->slock);
if (status & UART_MSR_DCD) if (status & UART_MSR_DCD)
ms.dcd = 1; ms.dcd = 1;
if (status & UART_MSR_DSR) if (status & UART_MSR_DSR)
@ -1533,13 +1534,11 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
if (status & UART_MSR_CTS) if (status & UART_MSR_CTS)
ms.cts = 1; ms.cts = 1;
copy: copy:
if (copy_to_user(msu, &ms, sizeof(ms))) { mutex_unlock(&port->mutex);
unlock_kernel(); if (copy_to_user(msu, &ms, sizeof(ms)))
return -EFAULT; return -EFAULT;
}
msu++; msu++;
} }
unlock_kernel();
return 0; return 0;
} }
case MOXA_ASPP_MON_EXT: { case MOXA_ASPP_MON_EXT: {
@ -1551,41 +1550,48 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
if (!me) if (!me)
return -ENOMEM; return -ENOMEM;
lock_kernel();
for (i = 0, p = 0; i < MXSER_BOARDS; i++) { for (i = 0, p = 0; i < MXSER_BOARDS; i++) {
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) { for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) {
if (p >= ARRAY_SIZE(me->rx_cnt)) { if (p >= ARRAY_SIZE(me->rx_cnt)) {
i = MXSER_BOARDS; i = MXSER_BOARDS;
break; break;
} }
port = &mxser_boards[i].ports[j]; ip = &mxser_boards[i].ports[j];
if (!port->ioaddr) port = &ip->port;
continue;
status = mxser_get_msr(port->ioaddr, 0, p); mutex_lock(&port->mutex);
if (!ip->ioaddr) {
mutex_unlock(&port->mutex);
continue;
}
spin_lock_irq(&ip->slock);
status = mxser_get_msr(ip->ioaddr, 0, p);
if (status & UART_MSR_TERI) if (status & UART_MSR_TERI)
port->icount.rng++; ip->icount.rng++;
if (status & UART_MSR_DDSR) if (status & UART_MSR_DDSR)
port->icount.dsr++; ip->icount.dsr++;
if (status & UART_MSR_DDCD) if (status & UART_MSR_DDCD)
port->icount.dcd++; ip->icount.dcd++;
if (status & UART_MSR_DCTS) if (status & UART_MSR_DCTS)
port->icount.cts++; ip->icount.cts++;
port->mon_data.modem_status = status; ip->mon_data.modem_status = status;
me->rx_cnt[p] = port->mon_data.rxcnt; me->rx_cnt[p] = ip->mon_data.rxcnt;
me->tx_cnt[p] = port->mon_data.txcnt; me->tx_cnt[p] = ip->mon_data.txcnt;
me->up_rxcnt[p] = port->mon_data.up_rxcnt; me->up_rxcnt[p] = ip->mon_data.up_rxcnt;
me->up_txcnt[p] = port->mon_data.up_txcnt; me->up_txcnt[p] = ip->mon_data.up_txcnt;
me->modem_status[p] = me->modem_status[p] =
port->mon_data.modem_status; ip->mon_data.modem_status;
tty = tty_port_tty_get(&port->port); spin_unlock_irq(&ip->slock);
tty = tty_port_tty_get(&ip->port);
if (!tty || !tty->termios) { if (!tty || !tty->termios) {
cflag = port->normal_termios.c_cflag; cflag = ip->normal_termios.c_cflag;
iflag = port->normal_termios.c_iflag; iflag = ip->normal_termios.c_iflag;
me->baudrate[p] = tty_termios_baud_rate(&port->normal_termios); me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios);
} else { } else {
cflag = tty->termios->c_cflag; cflag = tty->termios->c_cflag;
iflag = tty->termios->c_iflag; iflag = tty->termios->c_iflag;
@ -1604,16 +1610,15 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
if (iflag & (IXON | IXOFF)) if (iflag & (IXON | IXOFF))
me->flowctrl[p] |= 0x0C; me->flowctrl[p] |= 0x0C;
if (port->type == PORT_16550A) if (ip->type == PORT_16550A)
me->fifo[p] = 1; me->fifo[p] = 1;
opmode = inb(port->opmode_ioaddr) >> opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2);
((p % 4) * 2);
opmode &= OP_MODE_MASK; opmode &= OP_MODE_MASK;
me->iftype[p] = opmode; me->iftype[p] = opmode;
mutex_unlock(&port->mutex);
} }
} }
unlock_kernel();
if (copy_to_user(argp, me, sizeof(*me))) if (copy_to_user(argp, me, sizeof(*me)))
ret = -EFAULT; ret = -EFAULT;
kfree(me); kfree(me);
@ -1650,6 +1655,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct mxser_port *info = tty->driver_data; struct mxser_port *info = tty->driver_data;
struct tty_port *port = &info->port;
struct async_icount cnow; struct async_icount cnow;
unsigned long flags; unsigned long flags;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
@ -1674,20 +1680,20 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
opmode != RS422_MODE && opmode != RS422_MODE &&
opmode != RS485_4WIRE_MODE) opmode != RS485_4WIRE_MODE)
return -EFAULT; return -EFAULT;
lock_kernel();
mask = ModeMask[p]; mask = ModeMask[p];
shiftbit = p * 2; shiftbit = p * 2;
spin_lock_irq(&info->slock);
val = inb(info->opmode_ioaddr); val = inb(info->opmode_ioaddr);
val &= mask; val &= mask;
val |= (opmode << shiftbit); val |= (opmode << shiftbit);
outb(val, info->opmode_ioaddr); outb(val, info->opmode_ioaddr);
unlock_kernel(); spin_unlock_irq(&info->slock);
} else { } else {
lock_kernel();
shiftbit = p * 2; shiftbit = p * 2;
spin_lock_irq(&info->slock);
opmode = inb(info->opmode_ioaddr) >> shiftbit; opmode = inb(info->opmode_ioaddr) >> shiftbit;
spin_unlock_irq(&info->slock);
opmode &= OP_MODE_MASK; opmode &= OP_MODE_MASK;
unlock_kernel();
if (put_user(opmode, (int __user *)argp)) if (put_user(opmode, (int __user *)argp))
return -EFAULT; return -EFAULT;
} }
@ -1700,14 +1706,14 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
switch (cmd) { switch (cmd) {
case TIOCGSERIAL: case TIOCGSERIAL:
lock_kernel(); mutex_lock(&port->mutex);
retval = mxser_get_serial_info(tty, argp); retval = mxser_get_serial_info(tty, argp);
unlock_kernel(); mutex_unlock(&port->mutex);
return retval; return retval;
case TIOCSSERIAL: case TIOCSSERIAL:
lock_kernel(); mutex_lock(&port->mutex);
retval = mxser_set_serial_info(tty, argp); retval = mxser_set_serial_info(tty, argp);
unlock_kernel(); mutex_unlock(&port->mutex);
return retval; return retval;
case TIOCSERGETLSR: /* Get line status register */ case TIOCSERGETLSR: /* Get line status register */
return mxser_get_lsr_info(info, argp); return mxser_get_lsr_info(info, argp);
@ -1753,31 +1759,33 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
case MOXA_HighSpeedOn: case MOXA_HighSpeedOn:
return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp); return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp);
case MOXA_SDS_RSTICOUNTER: case MOXA_SDS_RSTICOUNTER:
lock_kernel(); spin_lock_irq(&info->slock);
info->mon_data.rxcnt = 0; info->mon_data.rxcnt = 0;
info->mon_data.txcnt = 0; info->mon_data.txcnt = 0;
unlock_kernel(); spin_unlock_irq(&info->slock);
return 0; return 0;
case MOXA_ASPP_OQUEUE:{ case MOXA_ASPP_OQUEUE:{
int len, lsr; int len, lsr;
lock_kernel();
len = mxser_chars_in_buffer(tty); len = mxser_chars_in_buffer(tty);
spin_lock(&info->slock);
lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE; lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE;
spin_unlock_irq(&info->slock);
len += (lsr ? 0 : 1); len += (lsr ? 0 : 1);
unlock_kernel();
return put_user(len, (int __user *)argp); return put_user(len, (int __user *)argp);
} }
case MOXA_ASPP_MON: { case MOXA_ASPP_MON: {
int mcr, status; int mcr, status;
lock_kernel(); spin_lock(&info->slock);
status = mxser_get_msr(info->ioaddr, 1, tty->index); status = mxser_get_msr(info->ioaddr, 1, tty->index);
mxser_check_modem_status(tty, info, status); mxser_check_modem_status(tty, info, status);
mcr = inb(info->ioaddr + UART_MCR); mcr = inb(info->ioaddr + UART_MCR);
spin_unlock(&info->slock);
if (mcr & MOXA_MUST_MCR_XON_FLAG) if (mcr & MOXA_MUST_MCR_XON_FLAG)
info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD; info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD;
else else
@ -1792,7 +1800,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD; info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD;
else else
info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD; info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD;
unlock_kernel();
if (copy_to_user(argp, &info->mon_data, if (copy_to_user(argp, &info->mon_data,
sizeof(struct mxser_mon))) sizeof(struct mxser_mon)))
return -EFAULT; return -EFAULT;
@ -1951,6 +1959,7 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
{ {
struct mxser_port *info = tty->driver_data; struct mxser_port *info = tty->driver_data;
unsigned long orig_jiffies, char_time; unsigned long orig_jiffies, char_time;
unsigned long flags;
int lsr; int lsr;
if (info->type == PORT_UNKNOWN) if (info->type == PORT_UNKNOWN)
@ -1990,19 +1999,21 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
timeout, char_time); timeout, char_time);
printk("jiff=%lu...", jiffies); printk("jiff=%lu...", jiffies);
#endif #endif
lock_kernel(); spin_lock_irqsave(&info->slock, flags);
while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) { while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) {
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...", lsr, jiffies); printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
#endif #endif
spin_unlock_irqrestore(&info->slock, flags);
schedule_timeout_interruptible(char_time); schedule_timeout_interruptible(char_time);
spin_lock_irqsave(&info->slock, flags);
if (signal_pending(current)) if (signal_pending(current))
break; break;
if (timeout && time_after(jiffies, orig_jiffies + timeout)) if (timeout && time_after(jiffies, orig_jiffies + timeout))
break; break;
} }
spin_unlock_irqrestore(&info->slock, flags);
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
unlock_kernel();
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);