summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@gmail.com>2014-07-20 20:40:02 +0200
committerDavid Herrmann <dh.herrmann@gmail.com>2014-08-13 14:27:00 +0200
commitb481e4ad3478b94e2467d240256d2ead75662b25 (patch)
treeeec40886a6098fe0bff3312fae2fd0c75d3d0b22
parente339aee2db2e037ff3431d068adedb4b222e2f73 (diff)
Input: evdev - drop redundant list-lockinginput-next
evdev->client_list is rcu-protected. We need the client_lock only to protect against concurrent writes. However, all paths that access client_list already lock evdev->mutex. Therefore, drop client_lock and use evdev->mutex as list-protection. This also drops several helper functions that are called only once. Most of them are fairly trivial so there's little reason to extract them. This is needed to get better control over evdev->mutex locking. Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
-rw-r--r--drivers/input/evdev.c126
1 files changed, 40 insertions, 86 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 73b9b95adb21..168f972b9eb1 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -34,7 +34,6 @@ struct evdev {
wait_queue_head_t wait;
struct evdev_client __rcu *grab;
struct list_head client_list;
- spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
struct cdev cdev;
@@ -430,69 +429,6 @@ static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
return 0;
}
-static void evdev_attach_client(struct evdev *evdev,
- struct evdev_client *client)
-{
- spin_lock(&evdev->client_lock);
- list_add_tail_rcu(&client->node, &evdev->client_list);
- spin_unlock(&evdev->client_lock);
-}
-
-static void evdev_detach_client(struct evdev *evdev,
- struct evdev_client *client)
-{
- spin_lock(&evdev->client_lock);
- list_del_rcu(&client->node);
- spin_unlock(&evdev->client_lock);
- synchronize_rcu();
-}
-
-static int evdev_open_device(struct evdev *evdev)
-{
- int retval;
-
- retval = mutex_lock_interruptible(&evdev->mutex);
- if (retval)
- return retval;
-
- if (!evdev->exist)
- retval = -ENODEV;
- else if (!evdev->open++) {
- retval = input_open_device(&evdev->handle);
- if (retval)
- evdev->open--;
- }
-
- mutex_unlock(&evdev->mutex);
- return retval;
-}
-
-static void evdev_close_device(struct evdev *evdev)
-{
- mutex_lock(&evdev->mutex);
-
- if (evdev->exist && !--evdev->open)
- input_close_device(&evdev->handle);
-
- mutex_unlock(&evdev->mutex);
-}
-
-/*
- * Wake up users waiting for IO so they can disconnect from
- * dead device.
- */
-static void evdev_hangup(struct evdev *evdev)
-{
- struct evdev_client *client;
-
- spin_lock(&evdev->client_lock);
- list_for_each_entry(client, &evdev->client_list, node)
- kill_fasync(&client->fasync, SIGIO, POLL_HUP);
- spin_unlock(&evdev->client_lock);
-
- wake_up_interruptible(&evdev->wait);
-}
-
static int evdev_release(struct inode *inode, struct file *file)
{
struct evdev_client *client = file->private_data;
@@ -501,9 +437,12 @@ static int evdev_release(struct inode *inode, struct file *file)
mutex_lock(&evdev->mutex);
evdev_ungrab(evdev, client);
+ list_del_rcu(&client->node);
+ if (evdev->exist && !--evdev->open)
+ input_close_device(&evdev->handle);
mutex_unlock(&evdev->mutex);
- evdev_detach_client(evdev, client);
+ synchronize_rcu();
for (i = 0; i < EV_CNT; ++i)
kfree(client->evmasks[i]);
@@ -513,8 +452,6 @@ static int evdev_release(struct inode *inode, struct file *file)
else
kfree(client);
- evdev_close_device(evdev);
-
return 0;
}
@@ -545,19 +482,38 @@ static int evdev_open(struct inode *inode, struct file *file)
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
client->evdev = evdev;
- evdev_attach_client(evdev, client);
- error = evdev_open_device(evdev);
+ error = mutex_lock_interruptible(&evdev->mutex);
if (error)
- goto err_free_client;
+ goto err_free;
+
+ list_add_tail_rcu(&client->node, &evdev->client_list);
+
+ if (!evdev->exist) {
+ error = -ENODEV;
+ goto err_detach;
+ }
+
+ if (!evdev->open++) {
+ error = input_open_device(&evdev->handle);
+ if (error) {
+ evdev->open--;
+ goto err_detach;
+ }
+ }
+
+ mutex_unlock(&evdev->mutex);
file->private_data = client;
nonseekable_open(inode, file);
return 0;
- err_free_client:
- evdev_detach_client(evdev, client);
+err_detach:
+ list_del_rcu(&client->node);
+ mutex_unlock(&evdev->mutex);
+ synchronize_rcu();
+err_free:
kfree(client);
return error;
}
@@ -1384,24 +1340,23 @@ static const struct file_operations evdev_fops = {
.llseek = no_llseek,
};
-/*
- * Mark device non-existent. This disables writes, ioctls and
- * prevents new users from opening the device. Already posted
- * blocking reads will stay, however new ones will fail.
- */
-static void evdev_mark_dead(struct evdev *evdev)
+static void evdev_cleanup(struct evdev *evdev)
{
+ struct input_handle *handle = &evdev->handle;
+ struct evdev_client *client;
+
+ /*
+ * Mark device non-existent to disable writes, ioctls and new users.
+ * Then wake up running users that wait for I/O so they can disconnect
+ * from the dead device.
+ */
mutex_lock(&evdev->mutex);
evdev->exist = false;
+ list_for_each_entry(client, &evdev->client_list, node)
+ kill_fasync(&client->fasync, SIGIO, POLL_HUP);
mutex_unlock(&evdev->mutex);
-}
-static void evdev_cleanup(struct evdev *evdev)
-{
- struct input_handle *handle = &evdev->handle;
-
- evdev_mark_dead(evdev);
- evdev_hangup(evdev);
+ wake_up_interruptible(&evdev->wait);
cdev_del(&evdev->cdev);
@@ -1438,7 +1393,6 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
}
INIT_LIST_HEAD(&evdev->client_list);
- spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
evdev->exist = true;