Commit 4ba8b8ae authored by Kenny Levinsen's avatar Kenny Levinsen Committed by Dmitry Torokhov

Input: evdev - per-client waitgroups

All evdev clients share a common waitgroup. On new input events, all
clients waiting on this waitgroup are woken up, even those filtering out
the events, possibly more than once per event. This leads to duplicated
and unwanted wakeups.

Split the shared waitgroup into per-client waitgroups for more
fine-grained wakeups.
Signed-off-by: default avatarKenny Levinsen <kl@kl.wtf>
Link: https://lore.kernel.org/r/20200429184126.2155-1-kl@kl.wtfSigned-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 470d154a
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
struct evdev { struct evdev {
int open; int open;
struct input_handle handle; struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client __rcu *grab; struct evdev_client __rcu *grab;
struct list_head client_list; struct list_head client_list;
spinlock_t client_lock; /* protects client_list */ spinlock_t client_lock; /* protects client_list */
...@@ -43,6 +42,7 @@ struct evdev_client { ...@@ -43,6 +42,7 @@ struct evdev_client {
unsigned int tail; unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */ unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */ spinlock_t buffer_lock; /* protects access to buffer, head and tail */
wait_queue_head_t wait;
struct fasync_struct *fasync; struct fasync_struct *fasync;
struct evdev *evdev; struct evdev *evdev;
struct list_head node; struct list_head node;
...@@ -245,7 +245,6 @@ static void evdev_pass_values(struct evdev_client *client, ...@@ -245,7 +245,6 @@ static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count, const struct input_value *vals, unsigned int count,
ktime_t *ev_time) ktime_t *ev_time)
{ {
struct evdev *evdev = client->evdev;
const struct input_value *v; const struct input_value *v;
struct input_event event; struct input_event event;
struct timespec64 ts; struct timespec64 ts;
...@@ -282,7 +281,7 @@ static void evdev_pass_values(struct evdev_client *client, ...@@ -282,7 +281,7 @@ static void evdev_pass_values(struct evdev_client *client,
spin_unlock(&client->buffer_lock); spin_unlock(&client->buffer_lock);
if (wakeup) if (wakeup)
wake_up_interruptible_poll(&evdev->wait, wake_up_interruptible_poll(&client->wait,
EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM); EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM);
} }
...@@ -426,11 +425,11 @@ static void evdev_hangup(struct evdev *evdev) ...@@ -426,11 +425,11 @@ static void evdev_hangup(struct evdev *evdev)
struct evdev_client *client; struct evdev_client *client;
spin_lock(&evdev->client_lock); spin_lock(&evdev->client_lock);
list_for_each_entry(client, &evdev->client_list, node) list_for_each_entry(client, &evdev->client_list, node) {
kill_fasync(&client->fasync, SIGIO, POLL_HUP); kill_fasync(&client->fasync, SIGIO, POLL_HUP);
wake_up_interruptible_poll(&client->wait, EPOLLHUP | EPOLLERR);
}
spin_unlock(&evdev->client_lock); spin_unlock(&evdev->client_lock);
wake_up_interruptible_poll(&evdev->wait, EPOLLHUP | EPOLLERR);
} }
static int evdev_release(struct inode *inode, struct file *file) static int evdev_release(struct inode *inode, struct file *file)
...@@ -479,6 +478,7 @@ static int evdev_open(struct inode *inode, struct file *file) ...@@ -479,6 +478,7 @@ static int evdev_open(struct inode *inode, struct file *file)
if (!client) if (!client)
return -ENOMEM; return -ENOMEM;
init_waitqueue_head(&client->wait);
client->bufsize = bufsize; client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock); spin_lock_init(&client->buffer_lock);
client->evdev = evdev; client->evdev = evdev;
...@@ -595,7 +595,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, ...@@ -595,7 +595,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
break; break;
if (!(file->f_flags & O_NONBLOCK)) { if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(evdev->wait, error = wait_event_interruptible(client->wait,
client->packet_head != client->tail || client->packet_head != client->tail ||
!evdev->exist || client->revoked); !evdev->exist || client->revoked);
if (error) if (error)
...@@ -613,7 +613,7 @@ static __poll_t evdev_poll(struct file *file, poll_table *wait) ...@@ -613,7 +613,7 @@ static __poll_t evdev_poll(struct file *file, poll_table *wait)
struct evdev *evdev = client->evdev; struct evdev *evdev = client->evdev;
__poll_t mask; __poll_t mask;
poll_wait(file, &evdev->wait, wait); poll_wait(file, &client->wait, wait);
if (evdev->exist && !client->revoked) if (evdev->exist && !client->revoked)
mask = EPOLLOUT | EPOLLWRNORM; mask = EPOLLOUT | EPOLLWRNORM;
...@@ -946,7 +946,7 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client, ...@@ -946,7 +946,7 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
client->revoked = true; client->revoked = true;
evdev_ungrab(evdev, client); evdev_ungrab(evdev, client);
input_flush_device(&evdev->handle, file); input_flush_device(&evdev->handle, file);
wake_up_interruptible_poll(&evdev->wait, EPOLLHUP | EPOLLERR); wake_up_interruptible_poll(&client->wait, EPOLLHUP | EPOLLERR);
return 0; return 0;
} }
...@@ -1358,7 +1358,6 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, ...@@ -1358,7 +1358,6 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
INIT_LIST_HEAD(&evdev->client_list); INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock); spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex); mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
evdev->exist = true; evdev->exist = true;
dev_no = minor; dev_no = minor;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment