ChangeSet 1.1002.3.14, 2003/02/25 11:12:34-08:00, greg@kroah.com [PATCH] USB: fix potential races in mct_u232 now that there's no locks in the usb-serial core. drivers/usb/serial/mct_u232.c | 106 ++++++++++++++++++++++++++++++------------ 1 files changed, 76 insertions(+), 30 deletions(-) diff -Nru a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c --- a/drivers/usb/serial/mct_u232.c Fri Feb 28 14:50:33 2003 +++ b/drivers/usb/serial/mct_u232.c Fri Feb 28 14:50:33 2003 @@ -171,6 +171,7 @@ struct mct_u232_private { + spinlock_t lock; unsigned long control_state; /* Modem Line Setting (TIOCM) */ unsigned char last_lcr; /* Line Control Register */ unsigned char last_lsr; /* Line Status Register */ @@ -306,8 +307,9 @@ /* allocate the private data structure */ priv = kmalloc(sizeof(struct mct_u232_private), GFP_KERNEL); if (!priv) - return (-1); /* error */ + return -ENOMEM; /* set initial values for control structures */ + spin_lock_init(&priv->lock); priv->control_state = 0; priv->last_lsr = 0; priv->last_msr = 0; @@ -339,6 +341,10 @@ struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); int retval = 0; + unsigned long control_state; + unsigned long flags; + unsigned char last_lcr; + unsigned char last_msr; dbg("%s port %d", __FUNCTION__, port->number); @@ -355,20 +361,27 @@ * sure if this is really necessary. But it should not harm * either. */ + spin_lock_irqsave(&priv->lock, flags); if (port->tty->termios->c_cflag & CBAUD) priv->control_state = TIOCM_DTR | TIOCM_RTS; else priv->control_state = 0; - mct_u232_set_modem_ctrl(serial, priv->control_state); priv->last_lcr = (MCT_U232_DATA_BITS_8 | MCT_U232_PARITY_NONE | MCT_U232_STOP_BITS_1); - mct_u232_set_line_ctrl(serial, priv->last_lcr); + control_state = priv->control_state; + last_lcr = priv->last_lcr; + spin_unlock_irqrestore(&priv->lock, flags); + mct_u232_set_modem_ctrl(serial, control_state); + mct_u232_set_line_ctrl(serial, last_lcr); /* Read modem status and update control state */ - mct_u232_get_modem_stat(serial, &priv->last_msr); + mct_u232_get_modem_stat(serial, &last_msr); + spin_lock_irqsave(&priv->lock, flags); + priv->last_msr = last_msr; mct_u232_msr_to_state(&priv->control_state, priv->last_msr); + spin_unlock_irqrestore(&priv->lock, flags); { /* Puh, that's dirty */ @@ -523,6 +536,7 @@ struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int status; + unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); @@ -567,6 +581,7 @@ * The interrupt-in pipe signals exceptional conditions (modem line * signal changes and errors). data[0] holds MSR, data[1] holds LSR. */ + spin_lock_irqsave(&priv->lock, flags); priv->last_msr = data[MCT_U232_MSR_INDEX]; /* Record Control Line states */ @@ -597,6 +612,7 @@ } } #endif + spin_unlock_irqrestore(&priv->lock, flags); exit: status = usb_submit_urb (urb, GFP_ATOMIC); if (status) @@ -614,7 +630,16 @@ unsigned int old_iflag = old_termios->c_iflag; unsigned int cflag = port->tty->termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; - + unsigned long flags; + unsigned long control_state; + unsigned char last_lcr; + + /* get a local copy of the current port settings */ + spin_lock_irqsave(&priv->lock, flags); + control_state = priv->control_state; + last_lcr = priv->last_lcr; + spin_unlock_irqrestore(&priv->lock, flags); + /* * Update baud rate */ @@ -622,12 +647,12 @@ /* reassert DTR and (maybe) RTS on transition from B0 */ if( (old_cflag & CBAUD) == B0 ) { dbg("%s: baud was B0", __FUNCTION__); - priv->control_state |= TIOCM_DTR; + control_state |= TIOCM_DTR; /* don't set RTS if using hardware flow control */ if (!(old_cflag & CRTSCTS)) { - priv->control_state |= TIOCM_RTS; + control_state |= TIOCM_RTS; } - mct_u232_set_modem_ctrl(serial, priv->control_state); + mct_u232_set_modem_ctrl(serial, control_state); } switch(cflag & CBAUD) { @@ -659,8 +684,8 @@ if ((cflag & CBAUD) == B0 ) { dbg("%s: baud is B0", __FUNCTION__); /* Drop RTS and DTR */ - priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); - mct_u232_set_modem_ctrl(serial, priv->control_state); + control_state &= ~(TIOCM_DTR | TIOCM_RTS); + mct_u232_set_modem_ctrl(serial, control_state); } } @@ -672,36 +697,36 @@ || (cflag & CSTOPB) != (old_cflag & CSTOPB) ) { - priv->last_lcr = 0; + last_lcr = 0; /* set the parity */ if (cflag & PARENB) - priv->last_lcr |= (cflag & PARODD) ? + last_lcr |= (cflag & PARODD) ? MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN; else - priv->last_lcr |= MCT_U232_PARITY_NONE; + last_lcr |= MCT_U232_PARITY_NONE; /* set the number of data bits */ switch (cflag & CSIZE) { case CS5: - priv->last_lcr |= MCT_U232_DATA_BITS_5; break; + last_lcr |= MCT_U232_DATA_BITS_5; break; case CS6: - priv->last_lcr |= MCT_U232_DATA_BITS_6; break; + last_lcr |= MCT_U232_DATA_BITS_6; break; case CS7: - priv->last_lcr |= MCT_U232_DATA_BITS_7; break; + last_lcr |= MCT_U232_DATA_BITS_7; break; case CS8: - priv->last_lcr |= MCT_U232_DATA_BITS_8; break; + last_lcr |= MCT_U232_DATA_BITS_8; break; default: err("CSIZE was not CS5-CS8, using default of 8"); - priv->last_lcr |= MCT_U232_DATA_BITS_8; + last_lcr |= MCT_U232_DATA_BITS_8; break; } /* set the number of stop bits */ - priv->last_lcr |= (cflag & CSTOPB) ? + last_lcr |= (cflag & CSTOPB) ? MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; - mct_u232_set_line_ctrl(serial, priv->last_lcr); + mct_u232_set_line_ctrl(serial, last_lcr); } /* @@ -714,11 +739,17 @@ /* Drop DTR/RTS if no flow control otherwise assert */ if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS) ) - priv->control_state |= TIOCM_DTR | TIOCM_RTS; + control_state |= TIOCM_DTR | TIOCM_RTS; else - priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); - mct_u232_set_modem_ctrl(serial, priv->control_state); + control_state &= ~(TIOCM_DTR | TIOCM_RTS); + mct_u232_set_modem_ctrl(serial, control_state); } + + /* save off the modified port settings */ + spin_lock_irqsave(&priv->lock, flags); + priv->control_state = control_state; + priv->last_lcr = last_lcr; + spin_unlock_irqrestore(&priv->lock, flags); } /* mct_u232_set_termios */ @@ -726,10 +757,15 @@ { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); - unsigned char lcr = priv->last_lcr; + unsigned char lcr; + unsigned long flags; dbg("%sstate=%d", __FUNCTION__, break_state); + spin_lock_irqsave(&priv->lock, flags); + lcr = priv->last_lcr; + spin_unlock_irqrestore(&priv->lock, flags); + if (break_state) lcr |= MCT_U232_SET_BREAK; @@ -743,13 +779,19 @@ struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); int mask; + unsigned long control_state; + unsigned long flags; dbg("%scmd=0x%x", __FUNCTION__, cmd); + spin_lock_irqsave(&priv->lock, flags); + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + /* Based on code from acm.c and others */ switch (cmd) { case TIOCMGET: - return put_user(priv->control_state, (unsigned long *) arg); + return put_user(control_state, (unsigned long *) arg); break; case TIOCMSET: /* Turns on and off the lines as specified by the mask */ @@ -762,20 +804,24 @@ /* RTS needs set */ if( ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) || (cmd == TIOCMBIS) ) - priv->control_state |= TIOCM_RTS; + control_state |= TIOCM_RTS; else - priv->control_state &= ~TIOCM_RTS; + control_state &= ~TIOCM_RTS; } if ((cmd == TIOCMSET) || (mask & TIOCM_DTR)) { /* DTR needs set */ if( ((cmd == TIOCMSET) && (mask & TIOCM_DTR)) || (cmd == TIOCMBIS) ) - priv->control_state |= TIOCM_DTR; + control_state |= TIOCM_DTR; else - priv->control_state &= ~TIOCM_DTR; + control_state &= ~TIOCM_DTR; } - mct_u232_set_modem_ctrl(serial, priv->control_state); + mct_u232_set_modem_ctrl(serial, control_state); + + spin_lock_irqsave(&priv->lock, flags); + priv->control_state = control_state; + spin_unlock_irqrestore(&priv->lock, flags); break; case TIOCMIWAIT: