tty: mxser: Use the new locking rules to fix setserial properly
authorAlan Cox <alan@linux.intel.com>
Mon, 30 Nov 2009 13:17:41 +0000 (13:17 +0000)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Dec 2009 23:18:07 +0000 (15:18 -0800)
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>
drivers/char/mxser.c

index 9dac516..3d92306 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/errno.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
-#include <linux/smp_lock.h>
 #include <linux/timer.h>
 #include <linux/interrupt.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 mxser_port *info = tty->driver_data;
+       struct tty_port *port = &info->port;
        struct serial_struct new_serial;
        speed_t baud;
        unsigned long sl_flags;
@@ -1244,7 +1244,7 @@ static int mxser_set_serial_info(struct tty_struct *tty,
                        new_serial.port != info->ioaddr)
                return -EINVAL;
 
-       flags = info->port.flags & ASYNC_SPD_MASK;
+       flags = port->flags & ASYNC_SPD_MASK;
 
        if (!capable(CAP_SYS_ADMIN)) {
                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.
                 * 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));
-               info->port.close_delay = new_serial.close_delay * HZ / 100;
-               info->port.closing_wait = new_serial.closing_wait * HZ / 100;
-               tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY)
-                                                               ? 1 : 0;
-               if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
+               port->close_delay = new_serial.close_delay * HZ / 100;
+               port->closing_wait = new_serial.closing_wait * HZ / 100;
+               tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+               if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
                                (new_serial.baud_base != info->baud_base ||
                                new_serial.custom_divisor !=
                                info->custom_divisor)) {
+                       if (new_serial.custom_divisor == 0)
+                               return -EINVAL;
                        baud = new_serial.baud_base / new_serial.custom_divisor;
                        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);
 
-       if (info->port.flags & ASYNC_INITIALIZED) {
-               if (flags != (info->port.flags & ASYNC_SPD_MASK)) {
+       if (test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+               if (flags != (port->flags & ASYNC_SPD_MASK)) {
                        spin_lock_irqsave(&info->slock, sl_flags);
                        mxser_change_speed(tty, NULL);
                        spin_unlock_irqrestore(&info->slock, sl_flags);
                }
        } else {
-               mutex_lock(&info->port.mutex);
-               retval = mxser_activate(&info->port, tty);
+               retval = mxser_activate(port, tty);
                if (retval == 0)
-                       set_bit(ASYNCB_INITIALIZED, &info->port.flags);
-               mutex_unlock(&info->port.mutex);
+                       set_bit(ASYNCB_INITIALIZED, &port->flags);
        }
        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)
 {
-       struct mxser_port *port;
+       struct mxser_port *ip;
+       struct tty_port *port;
        struct tty_struct *tty;
        int result, status;
        unsigned int i, j;
@@ -1494,38 +1494,39 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
 
        case MOXA_CHKPORTENABLE:
                result = 0;
-               lock_kernel();
                for (i = 0; i < MXSER_BOARDS; i++)
                        for (j = 0; j < MXSER_PORTS_PER_BOARD; j++)
                                if (mxser_boards[i].ports[j].ioaddr)
                                        result |= (1 << i);
-               unlock_kernel();
                return put_user(result, (unsigned long __user *)argp);
        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)))
                        ret = -EFAULT;
-               unlock_kernel();
                return ret;
        case MOXA_GETMSTATUS: {
                struct mxser_mstatus ms, __user *msu = argp;
-               lock_kernel();
                for (i = 0; i < MXSER_BOARDS; i++)
                        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));
 
-                               if (!port->ioaddr)
+                               mutex_lock(&port->mutex);
+                               if (!ip->ioaddr)
                                        goto copy;
                                
-                               tty = tty_port_tty_get(&port->port);
+                               tty = tty_port_tty_get(port);
 
                                if (!tty || !tty->termios)
-                                       ms.cflag = port->normal_termios.c_cflag;
+                                       ms.cflag = ip->normal_termios.c_cflag;
                                else
                                        ms.cflag = tty->termios->c_cflag;
                                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)
                                        ms.dcd = 1;
                                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)
                                        ms.cts = 1;
                        copy:
-                               if (copy_to_user(msu, &ms, sizeof(ms))) {
-                                       unlock_kernel();
+                               mutex_unlock(&port->mutex);
+                               if (copy_to_user(msu, &ms, sizeof(ms)))
                                        return -EFAULT;
-                               }
                                msu++;
                        }
-               unlock_kernel();
                return 0;
        }
        case MOXA_ASPP_MON_EXT: {
@@ -1551,41 +1550,48 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
                if (!me)
                        return -ENOMEM;
 
-               lock_kernel();
                for (i = 0, p = 0; i < MXSER_BOARDS; i++) {
                        for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) {
                                if (p >= ARRAY_SIZE(me->rx_cnt)) {
                                        i = MXSER_BOARDS;
                                        break;
                                }
-                               port = &mxser_boards[i].ports[j];
-                               if (!port->ioaddr)
+                               ip = &mxser_boards[i].ports[j];
+                               port = &ip->port;
+
+                               mutex_lock(&port->mutex);
+                               if (!ip->ioaddr) {
+                                       mutex_unlock(&port->mutex);
                                        continue;
+                               }
 
-                               status = mxser_get_msr(port->ioaddr, 0, p);
+                               spin_lock_irq(&ip->slock);
+                               status = mxser_get_msr(ip->ioaddr, 0, p);
 
                                if (status & UART_MSR_TERI)
-                                       port->icount.rng++;
+                                       ip->icount.rng++;
                                if (status & UART_MSR_DDSR)
-                                       port->icount.dsr++;
+                                       ip->icount.dsr++;
                                if (status & UART_MSR_DDCD)
-                                       port->icount.dcd++;
+                                       ip->icount.dcd++;
                                if (status & UART_MSR_DCTS)
-                                       port->icount.cts++;
+                                       ip->icount.cts++;
 
-                               port->mon_data.modem_status = status;
-                               me->rx_cnt[p] = port->mon_data.rxcnt;
-                               me->tx_cnt[p] = port->mon_data.txcnt;
-                               me->up_rxcnt[p] = port->mon_data.up_rxcnt;
-                               me->up_txcnt[p] = port->mon_data.up_txcnt;
+                               ip->mon_data.modem_status = status;
+                               me->rx_cnt[p] = ip->mon_data.rxcnt;
+                               me->tx_cnt[p] = ip->mon_data.txcnt;
+                               me->up_rxcnt[p] = ip->mon_data.up_rxcnt;
+                               me->up_txcnt[p] = ip->mon_data.up_txcnt;
                                me->modem_status[p] =
-                                       port->mon_data.modem_status;
-                               tty = tty_port_tty_get(&port->port);
+                                       ip->mon_data.modem_status;
+                               spin_unlock_irq(&ip->slock);
+
+                               tty = tty_port_tty_get(&ip->port);
 
                                if (!tty || !tty->termios) {
-                                       cflag = port->normal_termios.c_cflag;
-                                       iflag = port->normal_termios.c_iflag;
-                                       me->baudrate[p] = tty_termios_baud_rate(&port->normal_termios);
+                                       cflag = ip->normal_termios.c_cflag;
+                                       iflag = ip->normal_termios.c_iflag;
+                                       me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios);
                                } else {
                                        cflag = tty->termios->c_cflag;
                                        iflag = tty->termios->c_iflag;
@@ -1604,16 +1610,15 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
                                if (iflag & (IXON | IXOFF))
                                        me->flowctrl[p] |= 0x0C;
 
-                               if (port->type == PORT_16550A)
+                               if (ip->type == PORT_16550A)
                                        me->fifo[p] = 1;
 
-                               opmode = inb(port->opmode_ioaddr) >>
-                                               ((p % 4) * 2);
+                               opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2);
                                opmode &= OP_MODE_MASK;
                                me->iftype[p] = opmode;
+                               mutex_unlock(&port->mutex);
                        }
                }
-               unlock_kernel();
                if (copy_to_user(argp, me, sizeof(*me)))
                        ret = -EFAULT;
                kfree(me);
@@ -1650,6 +1655,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
                unsigned int cmd, unsigned long arg)
 {
        struct mxser_port *info = tty->driver_data;
+       struct tty_port *port = &info->port;
        struct async_icount cnow;
        unsigned long flags;
        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 != RS485_4WIRE_MODE)
                                return -EFAULT;
-                       lock_kernel();
                        mask = ModeMask[p];
                        shiftbit = p * 2;
+                       spin_lock_irq(&info->slock);
                        val = inb(info->opmode_ioaddr);
                        val &= mask;
                        val |= (opmode << shiftbit);
                        outb(val, info->opmode_ioaddr);
-                       unlock_kernel();
+                       spin_unlock_irq(&info->slock);
                } else {
-                       lock_kernel();
                        shiftbit = p * 2;
+                       spin_lock_irq(&info->slock);
                        opmode = inb(info->opmode_ioaddr) >> shiftbit;
+                       spin_unlock_irq(&info->slock);
                        opmode &= OP_MODE_MASK;
-                       unlock_kernel();
                        if (put_user(opmode, (int __user *)argp))
                                return -EFAULT;
                }
@@ -1700,14 +1706,14 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
 
        switch (cmd) {
        case TIOCGSERIAL:
-               lock_kernel();
+               mutex_lock(&port->mutex);
                retval = mxser_get_serial_info(tty, argp);
-               unlock_kernel();
+               mutex_unlock(&port->mutex);
                return retval;
        case TIOCSSERIAL:
-               lock_kernel();
+               mutex_lock(&port->mutex);
                retval = mxser_set_serial_info(tty, argp);
-               unlock_kernel();
+               mutex_unlock(&port->mutex);
                return retval;
        case TIOCSERGETLSR:     /* Get line status register */
                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:
                return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp);
        case MOXA_SDS_RSTICOUNTER:
-               lock_kernel();
+               spin_lock_irq(&info->slock);
                info->mon_data.rxcnt = 0;
                info->mon_data.txcnt = 0;
-               unlock_kernel();
+               spin_unlock_irq(&info->slock);
                return 0;
 
        case MOXA_ASPP_OQUEUE:{
                int len, lsr;
 
-               lock_kernel();
                len = mxser_chars_in_buffer(tty);
+               spin_lock(&info->slock);
                lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE;
+               spin_unlock_irq(&info->slock);
                len += (lsr ? 0 : 1);
-               unlock_kernel();
 
                return put_user(len, (int __user *)argp);
        }
        case MOXA_ASPP_MON: {
                int mcr, status;
 
-               lock_kernel();
+               spin_lock(&info->slock);
                status = mxser_get_msr(info->ioaddr, 1, tty->index);
                mxser_check_modem_status(tty, info, status);
 
                mcr = inb(info->ioaddr + UART_MCR);
+               spin_unlock(&info->slock);
+
                if (mcr & MOXA_MUST_MCR_XON_FLAG)
                        info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD;
                else
@@ -1792,7 +1800,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
                        info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD;
                else
                        info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD;
-               unlock_kernel();
+
                if (copy_to_user(argp, &info->mon_data,
                                sizeof(struct mxser_mon)))
                        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;
        unsigned long orig_jiffies, char_time;
+       unsigned long flags;
        int lsr;
 
        if (info->type == PORT_UNKNOWN)
@@ -1990,19 +1999,21 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
                timeout, char_time);
        printk("jiff=%lu...", jiffies);
 #endif
-       lock_kernel();
+       spin_lock_irqsave(&info->slock, flags);
        while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) {
 #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
                printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
 #endif
+               spin_unlock_irqrestore(&info->slock, flags);
                schedule_timeout_interruptible(char_time);
+               spin_lock_irqsave(&info->slock, flags);
                if (signal_pending(current))
                        break;
                if (timeout && time_after(jiffies, orig_jiffies + timeout))
                        break;
        }
+       spin_unlock_irqrestore(&info->slock, flags);
        set_current_state(TASK_RUNNING);
-       unlock_kernel();
 
 #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
        printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);