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 | |
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>
-rw-r--r-- | drivers/char/keyboard.c | 202 | ||||
-rw-r--r-- | drivers/input/input.c | 37 | ||||
-rw-r--r-- | include/linux/input.h | 10 |
3 files changed, 148 insertions, 101 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 | ||
diff --git a/drivers/input/input.c b/drivers/input/input.c index cc763c96fada..5d6421bde4ba 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c | |||
@@ -1651,6 +1651,38 @@ void input_unregister_handler(struct input_handler *handler) | |||
1651 | EXPORT_SYMBOL(input_unregister_handler); | 1651 | EXPORT_SYMBOL(input_unregister_handler); |
1652 | 1652 | ||
1653 | /** | 1653 | /** |
1654 | * input_handler_for_each_handle - handle iterator | ||
1655 | * @handler: input handler to iterate | ||
1656 | * @data: data for the callback | ||
1657 | * @fn: function to be called for each handle | ||
1658 | * | ||
1659 | * Iterate over @bus's list of devices, and call @fn for each, passing | ||
1660 | * it @data and stop when @fn returns a non-zero value. The function is | ||
1661 | * using RCU to traverse the list and therefore may be usind in atonic | ||
1662 | * contexts. The @fn callback is invoked from RCU critical section and | ||
1663 | * thus must not sleep. | ||
1664 | */ | ||
1665 | int input_handler_for_each_handle(struct input_handler *handler, void *data, | ||
1666 | int (*fn)(struct input_handle *, void *)) | ||
1667 | { | ||
1668 | struct input_handle *handle; | ||
1669 | int retval = 0; | ||
1670 | |||
1671 | rcu_read_lock(); | ||
1672 | |||
1673 | list_for_each_entry_rcu(handle, &handler->h_list, h_node) { | ||
1674 | retval = fn(handle, data); | ||
1675 | if (retval) | ||
1676 | break; | ||
1677 | } | ||
1678 | |||
1679 | rcu_read_unlock(); | ||
1680 | |||
1681 | return retval; | ||
1682 | } | ||
1683 | EXPORT_SYMBOL(input_handler_for_each_handle); | ||
1684 | |||
1685 | /** | ||
1654 | * input_register_handle - register a new input handle | 1686 | * input_register_handle - register a new input handle |
1655 | * @handle: handle to register | 1687 | * @handle: handle to register |
1656 | * | 1688 | * |
@@ -1683,7 +1715,7 @@ int input_register_handle(struct input_handle *handle) | |||
1683 | * we can't be racing with input_unregister_handle() | 1715 | * we can't be racing with input_unregister_handle() |
1684 | * and so separate lock is not needed here. | 1716 | * and so separate lock is not needed here. |
1685 | */ | 1717 | */ |
1686 | list_add_tail(&handle->h_node, &handler->h_list); | 1718 | list_add_tail_rcu(&handle->h_node, &handler->h_list); |
1687 | 1719 | ||
1688 | if (handler->start) | 1720 | if (handler->start) |
1689 | handler->start(handle); | 1721 | handler->start(handle); |
@@ -1706,7 +1738,7 @@ void input_unregister_handle(struct input_handle *handle) | |||
1706 | { | 1738 | { |
1707 | struct input_dev *dev = handle->dev; | 1739 | struct input_dev *dev = handle->dev; |
1708 | 1740 | ||
1709 | list_del_init(&handle->h_node); | 1741 | list_del_rcu(&handle->h_node); |
1710 | 1742 | ||
1711 | /* | 1743 | /* |
1712 | * Take dev->mutex to prevent race with input_release_device(). | 1744 | * Take dev->mutex to prevent race with input_release_device(). |
@@ -1714,6 +1746,7 @@ void input_unregister_handle(struct input_handle *handle) | |||
1714 | mutex_lock(&dev->mutex); | 1746 | mutex_lock(&dev->mutex); |
1715 | list_del_rcu(&handle->d_node); | 1747 | list_del_rcu(&handle->d_node); |
1716 | mutex_unlock(&dev->mutex); | 1748 | mutex_unlock(&dev->mutex); |
1749 | |||
1717 | synchronize_rcu(); | 1750 | synchronize_rcu(); |
1718 | } | 1751 | } |
1719 | EXPORT_SYMBOL(input_unregister_handle); | 1752 | EXPORT_SYMBOL(input_unregister_handle); |
diff --git a/include/linux/input.h b/include/linux/input.h index 56d8e048c646..db563bbac9dd 100644 --- a/include/linux/input.h +++ b/include/linux/input.h | |||
@@ -1021,9 +1021,12 @@ struct ff_effect { | |||
1021 | * @keycodesize: size of elements in keycode table | 1021 | * @keycodesize: size of elements in keycode table |
1022 | * @keycode: map of scancodes to keycodes for this device | 1022 | * @keycode: map of scancodes to keycodes for this device |
1023 | * @setkeycode: optional method to alter current keymap, used to implement | 1023 | * @setkeycode: optional method to alter current keymap, used to implement |
1024 | * sparse keymaps. If not supplied default mechanism will be used | 1024 | * sparse keymaps. If not supplied default mechanism will be used. |
1025 | * The method is being called while holding event_lock and thus must | ||
1026 | * not sleep | ||
1025 | * @getkeycode: optional method to retrieve current keymap. If not supplied | 1027 | * @getkeycode: optional method to retrieve current keymap. If not supplied |
1026 | * default mechanism will be used | 1028 | * default mechanism will be used. The method is being called while |
1029 | * holding event_lock and thus must not sleep | ||
1027 | * @ff: force feedback structure associated with the device if device | 1030 | * @ff: force feedback structure associated with the device if device |
1028 | * supports force feedback effects | 1031 | * supports force feedback effects |
1029 | * @repeat_key: stores key code of the last key pressed; used to implement | 1032 | * @repeat_key: stores key code of the last key pressed; used to implement |
@@ -1295,6 +1298,9 @@ void input_unregister_device(struct input_dev *); | |||
1295 | int __must_check input_register_handler(struct input_handler *); | 1298 | int __must_check input_register_handler(struct input_handler *); |
1296 | void input_unregister_handler(struct input_handler *); | 1299 | void input_unregister_handler(struct input_handler *); |
1297 | 1300 | ||
1301 | int input_handler_for_each_handle(struct input_handler *, void *data, | ||
1302 | int (*fn)(struct input_handle *, void *)); | ||
1303 | |||
1298 | int input_register_handle(struct input_handle *); | 1304 | int input_register_handle(struct input_handle *); |
1299 | void input_unregister_handle(struct input_handle *); | 1305 | void input_unregister_handle(struct input_handle *); |
1300 | 1306 | ||