The wakeup condition needs to be checked after adding the task to the waitqueue and updating the task state to avoid missing a racing modem status update or disconnect.
Fixes: e68453ed28c5 ("greybus: uart-gb: now builds, more framework added") Cc: stable@vger.kernel.org # 4.9 Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Johan Hovold johan@kernel.org --- drivers/staging/greybus/uart.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c index 24b4dab069c3..bcfea0a02155 100644 --- a/drivers/staging/greybus/uart.c +++ b/drivers/staging/greybus/uart.c @@ -643,7 +643,10 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) return -EINVAL;
- do { + add_wait_queue(&gb_tty->wioctl, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&gb_tty->read_lock); old = gb_tty->oldcount; new = gb_tty->iocount; @@ -657,18 +660,20 @@ static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) if ((arg & TIOCM_RI) && (old.rng != new.rng)) break;
- add_wait_queue(&gb_tty->wioctl, &wait); - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - remove_wait_queue(&gb_tty->wioctl, &wait); if (gb_tty->disconnected) { - if (arg & TIOCM_CD) - break; retval = -ENODEV; - } else if (signal_pending(current)) { + break; + } + + schedule(); + + if (signal_pending(current)) { retval = -ERESTARTSYS; + break; } - } while (!retval); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&gb_tty->wioctl, &wait);
return retval; }