aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/vt/vt_ioctl.c
diff options
context:
space:
mode:
authorRabin Vincent <rabin.vincent@stericsson.com>2012-05-21 04:08:42 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-26 16:37:02 -0400
commita7b12929be6cc55eab2dac3330fa9f5984e12dda (patch)
treea8bcc11178f8d2dda31c4c7ce509a1315d55f87a /drivers/tty/vt/vt_ioctl.c
parented85c60457a8e6bfe4604f8d3d343e70e30aaa3e (diff)
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 <rabin.vincent@stericsson.com> Acked-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/vt/vt_ioctl.c')
-rw-r--r--drivers/tty/vt/vt_ioctl.c47
1 files changed, 34 insertions, 13 deletions
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index 64618547be1..b841f56d2e6 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)
110 wake_up_interruptible(&vt_event_waitqueue); 110 wake_up_interruptible(&vt_event_waitqueue);
111} 111}
112 112
113/** 113static void __vt_event_queue(struct vt_event_wait *vw)
114 * vt_event_wait - wait for an event
115 * @vw: our event
116 *
117 * Waits for an event to occur which completes our vt_event_wait
118 * structure. On return the structure has wv->done set to 1 for success
119 * or 0 if some event such as a signal ended the wait.
120 */
121
122static void vt_event_wait(struct vt_event_wait *vw)
123{ 114{
124 unsigned long flags; 115 unsigned long flags;
125 /* Prepare the event */ 116 /* Prepare the event */
@@ -129,8 +120,18 @@ static void vt_event_wait(struct vt_event_wait *vw)
129 spin_lock_irqsave(&vt_event_lock, flags); 120 spin_lock_irqsave(&vt_event_lock, flags);
130 list_add(&vw->list, &vt_events); 121 list_add(&vw->list, &vt_events);
131 spin_unlock_irqrestore(&vt_event_lock, flags); 122 spin_unlock_irqrestore(&vt_event_lock, flags);
123}
124
125static void __vt_event_wait(struct vt_event_wait *vw)
126{
132 /* Wait for it to pass */ 127 /* Wait for it to pass */
133 wait_event_interruptible(vt_event_waitqueue, vw->done); 128 wait_event_interruptible(vt_event_waitqueue, vw->done);
129}
130
131static void __vt_event_dequeue(struct vt_event_wait *vw)
132{
133 unsigned long flags;
134
134 /* Dequeue it */ 135 /* Dequeue it */
135 spin_lock_irqsave(&vt_event_lock, flags); 136 spin_lock_irqsave(&vt_event_lock, flags);
136 list_del(&vw->list); 137 list_del(&vw->list);
@@ -138,6 +139,22 @@ static void vt_event_wait(struct vt_event_wait *vw)
138} 139}
139 140
140/** 141/**
142 * vt_event_wait - wait for an event
143 * @vw: our event
144 *
145 * Waits for an event to occur which completes our vt_event_wait
146 * structure. On return the structure has wv->done set to 1 for success
147 * or 0 if some event such as a signal ended the wait.
148 */
149
150static void vt_event_wait(struct vt_event_wait *vw)
151{
152 __vt_event_queue(vw);
153 __vt_event_wait(vw);
154 __vt_event_dequeue(vw);
155}
156
157/**
141 * vt_event_wait_ioctl - event ioctl handler 158 * vt_event_wait_ioctl - event ioctl handler
142 * @arg: argument to ioctl 159 * @arg: argument to ioctl
143 * 160 *
@@ -177,10 +194,14 @@ int vt_waitactive(int n)
177{ 194{
178 struct vt_event_wait vw; 195 struct vt_event_wait vw;
179 do { 196 do {
180 if (n == fg_console + 1)
181 break;
182 vw.event.event = VT_EVENT_SWITCH; 197 vw.event.event = VT_EVENT_SWITCH;
183 vt_event_wait(&vw); 198 __vt_event_queue(&vw);
199 if (n == fg_console + 1) {
200 __vt_event_dequeue(&vw);
201 break;
202 }
203 __vt_event_wait(&vw);
204 __vt_event_dequeue(&vw);
184 if (vw.done == 0) 205 if (vw.done == 0)
185 return -EINTR; 206 return -EINTR;
186 } while (vw.event.newev != n); 207 } while (vw.event.newev != n);