diff options
author | Rabin Vincent <rabin.vincent@stericsson.com> | 2012-05-21 04:08:42 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-06-14 20:10:08 -0400 |
commit | 2fc46915ecfbc51afcf995901f6ade7c3d503a25 (patch) | |
tree | 8b7b2a931020acdd349e2eb793f4e8da85239d37 /drivers/tty/vt | |
parent | 057eb856eda1d957c0f1155eaa90739221f809a7 (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')
-rw-r--r-- | drivers/tty/vt/vt_ioctl.c | 47 |
1 files changed, 34 insertions, 13 deletions
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 64618547be11..b841f56d2e66 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 | /** | 113 | static 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 | |||
122 | static 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 | |||
125 | static 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 | |||
131 | static 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 | |||
150 | static 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); |