diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 51acfe39a438f125837e55504d2fd6c7652ac593..27aad94223321718f4d86f5847f0554dad4011cc 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1607,7 +1607,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->port.close_wait, + wait_event_interruptible_tty(info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 5e9a81d8ebcfcd74ea5191e48c84b5e5d6a145da..be28391adb79b7de338594ad0a9cd5d865de5fdf 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -954,7 +954,7 @@ static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned l * order of opens and closes may not be preserved across shared * memory, so we must wait until it is complete. */ - wait_event_interruptible(portp->raw_wait, + wait_event_interruptible_tty(portp->raw_wait, !test_bit(ST_CLOSING, &portp->state)); if (signal_pending(current)) { return -ERESTARTSYS; @@ -989,7 +989,7 @@ static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned l set_bit(ST_OPENING, &portp->state); spin_unlock_irqrestore(&brd_lock, flags); - wait_event_interruptible(portp->raw_wait, + wait_event_interruptible_tty(portp->raw_wait, !test_bit(ST_OPENING, &portp->state)); if (signal_pending(current)) rc = -ERESTARTSYS; @@ -1020,7 +1020,7 @@ static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned * occurs on this port. */ if (wait) { - wait_event_interruptible(portp->raw_wait, + wait_event_interruptible_tty(portp->raw_wait, !test_bit(ST_CLOSING, &portp->state)); if (signal_pending(current)) { return -ERESTARTSYS; @@ -1052,7 +1052,7 @@ static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned * to come back. */ rc = 0; - wait_event_interruptible(portp->raw_wait, + wait_event_interruptible_tty(portp->raw_wait, !test_bit(ST_CLOSING, &portp->state)); if (signal_pending(current)) rc = -ERESTARTSYS; @@ -1073,6 +1073,10 @@ static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback) { + /* + * no need for wait_event_tty because clearing ST_CMDING cannot block + * on BTM + */ wait_event_interruptible(portp->raw_wait, !test_bit(ST_CMDING, &portp->state)); if (signal_pending(current)) diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index f4bd2591e39fd6975d92183aefa039c0c33ab748..a98290d7a2c581b1add26df5fcede3d484e9cbd6 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -1079,7 +1079,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible(pInfo->read_wait, + wait_event_interruptible_tty(pInfo->read_wait, (pMsg = remove_msg(pInfo, pClient))); } diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index a3bd1d0b66cfe3fbba3bf9592c0bfbab26bb88a3..35eb30402f1889c4e7778e50c96463c652976075 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -231,7 +231,7 @@ int tty_port_block_til_ready(struct tty_port *port, /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - wait_event_interruptible(port->close_wait, + wait_event_interruptible_tty(port->close_wait, !(port->flags & ASYNC_CLOSING)); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index cf87c533622906047b72c0dc94d8a00f009ee1c1..2bbeaaea46e9b7765ce983374fb125b7dd5422c9 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -133,7 +133,7 @@ static void vt_event_wait(struct vt_event_wait *vw) list_add(&vw->list, &vt_events); spin_unlock_irqrestore(&vt_event_lock, flags); /* Wait for it to pass */ - wait_event_interruptible(vt_event_waitqueue, vw->done); + wait_event_interruptible_tty(vt_event_waitqueue, vw->done); /* Dequeue it */ spin_lock_irqsave(&vt_event_lock, flags); list_del(&vw->list); @@ -1761,10 +1761,13 @@ int vt_move_to_console(unsigned int vt, int alloc) return -EIO; } release_console_sem(); + tty_lock(); if (vt_waitactive(vt + 1)) { pr_debug("Suspend: Can't switch VCs."); + tty_unlock(); return -EINTR; } + tty_unlock(); return prev; } diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c index f848e188deae305b939a8367efc3bf709e5b69e3..94bfb9f238e1add705413662ae12f8fbc15b71d9 100644 --- a/drivers/serial/crisv10.c +++ b/drivers/serial/crisv10.c @@ -3992,7 +3992,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->close_wait, + wait_event_interruptible_tty(info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) @@ -4150,7 +4150,7 @@ rs_open(struct tty_struct *tty, struct file * filp) */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->close_wait, + wait_event_interruptible_tty(info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? diff --git a/include/linux/tty.h b/include/linux/tty.h index 6ead6b60c7439bad4150756dac01e8aeddc65b85..955d72ea71c015de359a8cc048eb7f970be5a860 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -607,5 +607,47 @@ static inline void tty_unlock(void) __releases(kernel_lock) } #define tty_locked() (kernel_locked()) +/* + * wait_event_interruptible_tty -- wait for a condition with the tty lock held + * + * The condition we are waiting for might take a long time to + * become true, or might depend on another thread taking the + * BTM. In either case, we need to drop the BTM to guarantee + * forward progress. This is a leftover from the conversion + * from the BKL and should eventually get removed as the BTM + * falls out of use. + * + * Do not use in new code. + */ +#define wait_event_interruptible_tty(wq, condition) \ +({ \ + int __ret = 0; \ + if (!(condition)) { \ + __wait_event_interruptible_tty(wq, condition, __ret); \ + } \ + __ret; \ +}) + +#define __wait_event_interruptible_tty(wq, condition, ret) \ +do { \ + DEFINE_WAIT(__wait); \ + \ + for (;;) { \ + prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (!signal_pending(current)) { \ + tty_unlock(); \ + schedule(); \ + tty_lock(); \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + finish_wait(&wq, &__wait); \ +} while (0) + + #endif /* __KERNEL__ */ #endif