staging: quatech_usb2: Potential lost wakeup scenario in TIOCMIWAIT

If the usermode app does an ioctl over this serial device  by
using TIOCMIWAIT, then the code will wait by setting the current
task state to TASK_INTERRUPTIBLE and then calling schedule().
This will be woken up by the qt2_process_modem_status on URB
completion when the port_extra->shadowMSR is set to the new
modem status.

However, this could result in a lost wakeup scenario due to a race
in the logic in the qt2_ioctl(TIOCMIWAIT) loop and the URB completion
for new modem status in qt2_process_modem_status.
Due to this, the usermode app's task will continue to sleep despite a
change in the modem status.

Signed-off-by: Kautuk Consul <consul.kautuk@gmail.com>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Kautuk Consul 2011-09-14 08:56:21 +05:30 committed by Greg Kroah-Hartman
parent e228b7426e
commit e8df1674d3

View file

@ -916,9 +916,10 @@ static int qt2_ioctl(struct tty_struct *tty,
dbg("%s() port %d, cmd == TIOCMIWAIT enter",
__func__, port->number);
prev_msr_value = port_extra->shadowMSR & QT2_SERIAL_MSR_MASK;
barrier();
__set_current_state(TASK_INTERRUPTIBLE);
while (1) {
add_wait_queue(&port_extra->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
dbg("%s(): port %d, cmd == TIOCMIWAIT here\n",
__func__, port->number);
@ -926,9 +927,12 @@ static int qt2_ioctl(struct tty_struct *tty,
/* see if a signal woke us up */
if (signal_pending(current))
return -ERESTARTSYS;
set_current_state(TASK_INTERRUPTIBLE);
msr_value = port_extra->shadowMSR & QT2_SERIAL_MSR_MASK;
if (msr_value == prev_msr_value)
if (msr_value == prev_msr_value) {
__set_current_state(TASK_RUNNING);
return -EIO; /* no change - error */
}
if ((arg & TIOCM_RNG &&
((prev_msr_value & QT2_SERIAL_MSR_RI) ==
(msr_value & QT2_SERIAL_MSR_RI))) ||
@ -941,6 +945,7 @@ static int qt2_ioctl(struct tty_struct *tty,
(arg & TIOCM_CTS &&
((prev_msr_value & QT2_SERIAL_MSR_CTS) ==
(msr_value & QT2_SERIAL_MSR_CTS)))) {
__set_current_state(TASK_RUNNING);
return 0;
}
} /* end inifinite while */