From a7b12929be6cc55eab2dac3330fa9f5984e12dda Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Mon, 21 May 2012 13:38:42 +0530 Subject: vt: fix race in vt_waitactive() pm_restore_console() is called from the suspend/resume path, and this calls vt_move_to_console(), which calls vt_waitactive(). There's a race in this path which causes the process which requests the suspend to sleep indefinitely waiting for an event which already happened: P1 P2 vt_move_to_console() set_console() schedule_console_callback() vt_waitactive() check n == fg_console +1 console_callback() switch_screen() vt_event_post() // no waiters vt_event_wait() // forever Fix the race by ensuring we're registered for the event before we check if it's already completed. Signed-off-by: Rabin Vincent Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt_ioctl.c | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 6461854..b841f56 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -110,16 +110,7 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new) wake_up_interruptible(&vt_event_waitqueue); } -/** - * vt_event_wait - wait for an event - * @vw: our event - * - * Waits for an event to occur which completes our vt_event_wait - * structure. On return the structure has wv->done set to 1 for success - * or 0 if some event such as a signal ended the wait. - */ - -static void vt_event_wait(struct vt_event_wait *vw) +static void __vt_event_queue(struct vt_event_wait *vw) { unsigned long flags; /* Prepare the event */ @@ -129,8 +120,18 @@ static void vt_event_wait(struct vt_event_wait *vw) spin_lock_irqsave(&vt_event_lock, flags); list_add(&vw->list, &vt_events); spin_unlock_irqrestore(&vt_event_lock, flags); +} + +static void __vt_event_wait(struct vt_event_wait *vw) +{ /* Wait for it to pass */ wait_event_interruptible(vt_event_waitqueue, vw->done); +} + +static void __vt_event_dequeue(struct vt_event_wait *vw) +{ + unsigned long flags; + /* Dequeue it */ spin_lock_irqsave(&vt_event_lock, flags); list_del(&vw->list); @@ -138,6 +139,22 @@ static void vt_event_wait(struct vt_event_wait *vw) } /** + * vt_event_wait - wait for an event + * @vw: our event + * + * Waits for an event to occur which completes our vt_event_wait + * structure. On return the structure has wv->done set to 1 for success + * or 0 if some event such as a signal ended the wait. + */ + +static void vt_event_wait(struct vt_event_wait *vw) +{ + __vt_event_queue(vw); + __vt_event_wait(vw); + __vt_event_dequeue(vw); +} + +/** * vt_event_wait_ioctl - event ioctl handler * @arg: argument to ioctl * @@ -177,10 +194,14 @@ int vt_waitactive(int n) { struct vt_event_wait vw; do { - if (n == fg_console + 1) - break; vw.event.event = VT_EVENT_SWITCH; - vt_event_wait(&vw); + __vt_event_queue(&vw); + if (n == fg_console + 1) { + __vt_event_dequeue(&vw); + break; + } + __vt_event_wait(&vw); + __vt_event_dequeue(&vw); if (vw.done == 0) return -EINTR; } while (vw.event.newev != n); -- cgit v1.1