Commit a7b12929be6cc55eab2dac3330fa9f5984e12dda
Committed by
Greg Kroah-Hartman
1 parent
ed85c60457
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
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>
Showing 1 changed file with 34 additions and 13 deletions Side-by-side Diff
drivers/tty/vt/vt_ioctl.c
... | ... | @@ -110,16 +110,7 @@ |
110 | 110 | wake_up_interruptible(&vt_event_waitqueue); |
111 | 111 | } |
112 | 112 | |
113 | -/** | |
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) | |
113 | +static void __vt_event_queue(struct vt_event_wait *vw) | |
123 | 114 | { |
124 | 115 | unsigned long flags; |
125 | 116 | /* Prepare the event */ |
126 | 117 | |
... | ... | @@ -129,8 +120,18 @@ |
129 | 120 | spin_lock_irqsave(&vt_event_lock, flags); |
130 | 121 | list_add(&vw->list, &vt_events); |
131 | 122 | spin_unlock_irqrestore(&vt_event_lock, flags); |
123 | +} | |
124 | + | |
125 | +static void __vt_event_wait(struct vt_event_wait *vw) | |
126 | +{ | |
132 | 127 | /* Wait for it to pass */ |
133 | 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 | 135 | /* Dequeue it */ |
135 | 136 | spin_lock_irqsave(&vt_event_lock, flags); |
136 | 137 | list_del(&vw->list); |
... | ... | @@ -138,6 +139,22 @@ |
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 | 158 | * vt_event_wait_ioctl - event ioctl handler |
142 | 159 | * @arg: argument to ioctl |
143 | 160 | * |
144 | 161 | |
... | ... | @@ -177,10 +194,14 @@ |
177 | 194 | { |
178 | 195 | struct vt_event_wait vw; |
179 | 196 | do { |
180 | - if (n == fg_console + 1) | |
181 | - break; | |
182 | 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 | 205 | if (vw.done == 0) |
185 | 206 | return -EINTR; |
186 | 207 | } while (vw.event.newev != n); |