From 66d2a5952eab875f1286e04f738ef029afdaf013 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 1 Dec 2009 21:54:35 -0800 Subject: 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 Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'drivers/input') 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 @@ -1650,6 +1650,38 @@ void input_unregister_handler(struct input_handler *handler) } EXPORT_SYMBOL(input_unregister_handler); +/** + * input_handler_for_each_handle - handle iterator + * @handler: input handler to iterate + * @data: data for the callback + * @fn: function to be called for each handle + * + * Iterate over @bus's list of devices, and call @fn for each, passing + * it @data and stop when @fn returns a non-zero value. The function is + * using RCU to traverse the list and therefore may be usind in atonic + * contexts. The @fn callback is invoked from RCU critical section and + * thus must not sleep. + */ +int input_handler_for_each_handle(struct input_handler *handler, void *data, + int (*fn)(struct input_handle *, void *)) +{ + struct input_handle *handle; + int retval = 0; + + rcu_read_lock(); + + list_for_each_entry_rcu(handle, &handler->h_list, h_node) { + retval = fn(handle, data); + if (retval) + break; + } + + rcu_read_unlock(); + + return retval; +} +EXPORT_SYMBOL(input_handler_for_each_handle); + /** * input_register_handle - register a new input handle * @handle: handle to register @@ -1683,7 +1715,7 @@ int input_register_handle(struct input_handle *handle) * we can't be racing with input_unregister_handle() * and so separate lock is not needed here. */ - list_add_tail(&handle->h_node, &handler->h_list); + list_add_tail_rcu(&handle->h_node, &handler->h_list); if (handler->start) handler->start(handle); @@ -1706,7 +1738,7 @@ void input_unregister_handle(struct input_handle *handle) { struct input_dev *dev = handle->dev; - list_del_init(&handle->h_node); + list_del_rcu(&handle->h_node); /* * Take dev->mutex to prevent race with input_release_device(). @@ -1714,6 +1746,7 @@ void input_unregister_handle(struct input_handle *handle) mutex_lock(&dev->mutex); list_del_rcu(&handle->d_node); mutex_unlock(&dev->mutex); + synchronize_rcu(); } EXPORT_SYMBOL(input_unregister_handle); -- cgit v1.2.3