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/input | |
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/input')
-rw-r--r-- | drivers/input/input.c | 37 |
1 files changed, 35 insertions, 2 deletions
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); |