diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2009-12-02 00:54:35 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2009-12-02 00:57:48 -0500 |
commit | 66d2a5952eab875f1286e04f738ef029afdaf013 (patch) | |
tree | 6d30e807108ef7d2a56ec43271c45acc357df699 /drivers/char | |
parent | 6ee88d713fb75ab191515f66edffa4e866386565 (diff) |
Input: keyboard - fix lack of locking when traversing handler->h_list
Keyboard handler should not attempt to traverse handler->h_list on
its own, without any locking, otherwise it races with registering
and unregistering of input handles which leads to crashes.
Introduce input_handler_for_each_handle() helper that allows safely
iterate over all handles attached to a particular handler and switch
keyboard handler to use it.
Reported-by: Jim Paradis <jparadis@redhat.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/keyboard.c | 202 |
1 files changed, 105 insertions, 97 deletions
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index ca090e57e161..ff8e9345f3c9 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c | |||
@@ -46,8 +46,6 @@ | |||
46 | 46 | ||
47 | extern void ctrl_alt_del(void); | 47 | extern void ctrl_alt_del(void); |
48 | 48 | ||
49 | #define to_handle_h(n) container_of(n, struct input_handle, h_node) | ||
50 | |||
51 | /* | 49 | /* |
52 | * Exported functions/variables | 50 | * Exported functions/variables |
53 | */ | 51 | */ |
@@ -191,78 +189,85 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier); | |||
191 | * etc.). So this means that scancodes for the extra function keys won't | 189 | * etc.). So this means that scancodes for the extra function keys won't |
192 | * be valid for the first event device, but will be for the second. | 190 | * be valid for the first event device, but will be for the second. |
193 | */ | 191 | */ |
192 | |||
193 | struct getset_keycode_data { | ||
194 | unsigned int scancode; | ||
195 | unsigned int keycode; | ||
196 | int error; | ||
197 | }; | ||
198 | |||
199 | static int getkeycode_helper(struct input_handle *handle, void *data) | ||
200 | { | ||
201 | struct getset_keycode_data *d = data; | ||
202 | |||
203 | d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode); | ||
204 | |||
205 | return d->error == 0; /* stop as soon as we successfully get one */ | ||
206 | } | ||
207 | |||
194 | int getkeycode(unsigned int scancode) | 208 | int getkeycode(unsigned int scancode) |
195 | { | 209 | { |
196 | struct input_handle *handle; | 210 | struct getset_keycode_data d = { scancode, 0, -ENODEV }; |
197 | int keycode; | ||
198 | int error = -ENODEV; | ||
199 | 211 | ||
200 | list_for_each_entry(handle, &kbd_handler.h_list, h_node) { | 212 | input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper); |
201 | error = input_get_keycode(handle->dev, scancode, &keycode); | ||
202 | if (!error) | ||
203 | return keycode; | ||
204 | } | ||
205 | 213 | ||
206 | return error; | 214 | return d.error ?: d.keycode; |
215 | } | ||
216 | |||
217 | static int setkeycode_helper(struct input_handle *handle, void *data) | ||
218 | { | ||
219 | struct getset_keycode_data *d = data; | ||
220 | |||
221 | d->error = input_set_keycode(handle->dev, d->scancode, d->keycode); | ||
222 | |||
223 | return d->error == 0; /* stop as soon as we successfully set one */ | ||
207 | } | 224 | } |
208 | 225 | ||
209 | int setkeycode(unsigned int scancode, unsigned int keycode) | 226 | int setkeycode(unsigned int scancode, unsigned int keycode) |
210 | { | 227 | { |
211 | struct input_handle *handle; | 228 | struct getset_keycode_data d = { scancode, keycode, -ENODEV }; |
212 | int error = -ENODEV; | ||
213 | 229 | ||
214 | list_for_each_entry(handle, &kbd_handler.h_list, h_node) { | 230 | input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper); |
215 | error = input_set_keycode(handle->dev, scancode, keycode); | ||
216 | if (!error) | ||
217 | break; | ||
218 | } | ||
219 | 231 | ||
220 | return error; | 232 | return d.error; |
221 | } | 233 | } |
222 | 234 | ||
223 | /* | 235 | /* |
224 | * Making beeps and bells. | 236 | * Making beeps and bells. |
225 | */ | 237 | */ |
226 | static void kd_nosound(unsigned long ignored) | 238 | |
239 | static int kd_sound_helper(struct input_handle *handle, void *data) | ||
227 | { | 240 | { |
228 | struct input_handle *handle; | 241 | unsigned int *hz = data; |
242 | struct input_dev *dev = handle->dev; | ||
229 | 243 | ||
230 | list_for_each_entry(handle, &kbd_handler.h_list, h_node) { | 244 | if (test_bit(EV_SND, dev->evbit)) { |
231 | if (test_bit(EV_SND, handle->dev->evbit)) { | 245 | if (test_bit(SND_TONE, dev->sndbit)) |
232 | if (test_bit(SND_TONE, handle->dev->sndbit)) | 246 | input_inject_event(handle, EV_SND, SND_TONE, *hz); |
233 | input_inject_event(handle, EV_SND, SND_TONE, 0); | 247 | if (test_bit(SND_BELL, handle->dev->sndbit)) |
234 | if (test_bit(SND_BELL, handle->dev->sndbit)) | 248 | input_inject_event(handle, EV_SND, SND_BELL, *hz ? 1 : 0); |
235 | input_inject_event(handle, EV_SND, SND_BELL, 0); | ||
236 | } | ||
237 | } | 249 | } |
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static void kd_nosound(unsigned long ignored) | ||
255 | { | ||
256 | static unsigned int zero; | ||
257 | |||
258 | input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper); | ||
238 | } | 259 | } |
239 | 260 | ||
240 | static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0); | 261 | static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0); |
241 | 262 | ||
242 | void kd_mksound(unsigned int hz, unsigned int ticks) | 263 | void kd_mksound(unsigned int hz, unsigned int ticks) |
243 | { | 264 | { |
244 | struct list_head *node; | 265 | del_timer_sync(&kd_mksound_timer); |
245 | 266 | ||
246 | del_timer(&kd_mksound_timer); | 267 | input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper); |
247 | 268 | ||
248 | if (hz) { | 269 | if (hz && ticks) |
249 | list_for_each_prev(node, &kbd_handler.h_list) { | 270 | mod_timer(&kd_mksound_timer, jiffies + ticks); |
250 | struct input_handle *handle = to_handle_h(node); | ||
251 | if (test_bit(EV_SND, handle->dev->evbit)) { | ||
252 | if (test_bit(SND_TONE, handle->dev->sndbit)) { | ||
253 | input_inject_event(handle, EV_SND, SND_TONE, hz); | ||
254 | break; | ||
255 | } | ||
256 | if (test_bit(SND_BELL, handle->dev->sndbit)) { | ||
257 | input_inject_event(handle, EV_SND, SND_BELL, 1); | ||
258 | break; | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | if (ticks) | ||
263 | mod_timer(&kd_mksound_timer, jiffies + ticks); | ||
264 | } else | ||
265 | kd_nosound(0); | ||
266 | } | 271 | } |
267 | EXPORT_SYMBOL(kd_mksound); | 272 | EXPORT_SYMBOL(kd_mksound); |
268 | 273 | ||
@@ -270,27 +275,34 @@ EXPORT_SYMBOL(kd_mksound); | |||
270 | * Setting the keyboard rate. | 275 | * Setting the keyboard rate. |
271 | */ | 276 | */ |
272 | 277 | ||
273 | int kbd_rate(struct kbd_repeat *rep) | 278 | static int kbd_rate_helper(struct input_handle *handle, void *data) |
274 | { | 279 | { |
275 | struct list_head *node; | 280 | struct input_dev *dev = handle->dev; |
276 | unsigned int d = 0; | 281 | struct kbd_repeat *rep = data; |
277 | unsigned int p = 0; | 282 | |
278 | 283 | if (test_bit(EV_REP, dev->evbit)) { | |
279 | list_for_each(node, &kbd_handler.h_list) { | 284 | |
280 | struct input_handle *handle = to_handle_h(node); | 285 | if (rep[0].delay > 0) |
281 | struct input_dev *dev = handle->dev; | 286 | input_inject_event(handle, |
282 | 287 | EV_REP, REP_DELAY, rep[0].delay); | |
283 | if (test_bit(EV_REP, dev->evbit)) { | 288 | if (rep[0].period > 0) |
284 | if (rep->delay > 0) | 289 | input_inject_event(handle, |
285 | input_inject_event(handle, EV_REP, REP_DELAY, rep->delay); | 290 | EV_REP, REP_PERIOD, rep[0].period); |
286 | if (rep->period > 0) | 291 | |
287 | input_inject_event(handle, EV_REP, REP_PERIOD, rep->period); | 292 | rep[1].delay = dev->rep[REP_DELAY]; |
288 | d = dev->rep[REP_DELAY]; | 293 | rep[1].period = dev->rep[REP_PERIOD]; |
289 | p = dev->rep[REP_PERIOD]; | ||
290 | } | ||
291 | } | 294 | } |
292 | rep->delay = d; | 295 | |
293 | rep->period = p; | 296 | return 0; |
297 | } | ||
298 | |||
299 | int kbd_rate(struct kbd_repeat *rep) | ||
300 | { | ||
301 | struct kbd_repeat data[2] = { *rep }; | ||
302 | |||
303 | input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper); | ||
304 | *rep = data[1]; /* Copy currently used settings */ | ||
305 | |||
294 | return 0; | 306 | return 0; |
295 | } | 307 | } |
296 | 308 | ||
@@ -998,36 +1010,36 @@ static inline unsigned char getleds(void) | |||
998 | return leds; | 1010 | return leds; |
999 | } | 1011 | } |
1000 | 1012 | ||
1013 | static int kbd_update_leds_helper(struct input_handle *handle, void *data) | ||
1014 | { | ||
1015 | unsigned char leds = *(unsigned char *)data; | ||
1016 | |||
1017 | if (test_bit(EV_LED, handle->dev->evbit)) { | ||
1018 | input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); | ||
1019 | input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); | ||
1020 | input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); | ||
1021 | input_inject_event(handle, EV_SYN, SYN_REPORT, 0); | ||
1022 | } | ||
1023 | |||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1001 | /* | 1027 | /* |
1002 | * This routine is the bottom half of the keyboard interrupt | 1028 | * This is the tasklet that updates LED state on all keyboards |
1003 | * routine, and runs with all interrupts enabled. It does | 1029 | * attached to the box. The reason we use tasklet is that we |
1004 | * console changing, led setting and copy_to_cooked, which can | 1030 | * need to handle the scenario when keyboard handler is not |
1005 | * take a reasonably long time. | 1031 | * registered yet but we already getting updates form VT to |
1006 | * | 1032 | * update led state. |
1007 | * Aside from timing (which isn't really that important for | ||
1008 | * keyboard interrupts as they happen often), using the software | ||
1009 | * interrupt routines for this thing allows us to easily mask | ||
1010 | * this when we don't want any of the above to happen. | ||
1011 | * This allows for easy and efficient race-condition prevention | ||
1012 | * for kbd_start => input_inject_event(dev, EV_LED, ...) => ... | ||
1013 | */ | 1033 | */ |
1014 | |||
1015 | static void kbd_bh(unsigned long dummy) | 1034 | static void kbd_bh(unsigned long dummy) |
1016 | { | 1035 | { |
1017 | struct list_head *node; | ||
1018 | unsigned char leds = getleds(); | 1036 | unsigned char leds = getleds(); |
1019 | 1037 | ||
1020 | if (leds != ledstate) { | 1038 | if (leds != ledstate) { |
1021 | list_for_each(node, &kbd_handler.h_list) { | 1039 | input_handler_for_each_handle(&kbd_handler, &leds, |
1022 | struct input_handle *handle = to_handle_h(node); | 1040 | kbd_update_leds_helper); |
1023 | input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); | 1041 | ledstate = leds; |
1024 | input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); | ||
1025 | input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); | ||
1026 | input_inject_event(handle, EV_SYN, SYN_REPORT, 0); | ||
1027 | } | ||
1028 | } | 1042 | } |
1029 | |||
1030 | ledstate = leds; | ||
1031 | } | 1043 | } |
1032 | 1044 | ||
1033 | DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); | 1045 | DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); |
@@ -1370,15 +1382,11 @@ static void kbd_disconnect(struct input_handle *handle) | |||
1370 | */ | 1382 | */ |
1371 | static void kbd_start(struct input_handle *handle) | 1383 | static void kbd_start(struct input_handle *handle) |
1372 | { | 1384 | { |
1373 | unsigned char leds = ledstate; | ||
1374 | |||
1375 | tasklet_disable(&keyboard_tasklet); | 1385 | tasklet_disable(&keyboard_tasklet); |
1376 | if (leds != 0xff) { | 1386 | |
1377 | input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); | 1387 | if (ledstate != 0xff) |
1378 | input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); | 1388 | kbd_update_leds_helper(handle, &ledstate); |
1379 | input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); | 1389 | |
1380 | input_inject_event(handle, EV_SYN, SYN_REPORT, 0); | ||
1381 | } | ||
1382 | tasklet_enable(&keyboard_tasklet); | 1390 | tasklet_enable(&keyboard_tasklet); |
1383 | } | 1391 | } |
1384 | 1392 | ||