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); |
