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>
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index 9dac516..3d92306 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -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 @@
 		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 @@
 			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 @@
 		 * 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 @@
 
 	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 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 @@
 
 	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 @@
 				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 @@
 		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)
-					continue;
+				ip = &mxser_boards[i].ports[j];
+				port = &ip->port;
 
-				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)
-					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 @@
 				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 @@
 		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 @@
 					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 @@
 
 	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 @@
 	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 @@
 			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 @@
 {
 	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 @@
 		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);