aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/input.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2009-12-02 00:54:35 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2009-12-02 00:57:48 -0500
commit66d2a5952eab875f1286e04f738ef029afdaf013 (patch)
tree6d30e807108ef7d2a56ec43271c45acc357df699 /drivers/input/input.c
parent6ee88d713fb75ab191515f66edffa4e866386565 (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/input.c')
-rw-r--r--drivers/input/input.c37
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)
1651EXPORT_SYMBOL(input_unregister_handler); 1651EXPORT_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 */
1665int 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}
1683EXPORT_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}
1719EXPORT_SYMBOL(input_unregister_handle); 1752EXPORT_SYMBOL(input_unregister_handle);