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: Kenny Levinsen <kl@kl.wtf> Link: https://lore.kernel.org/r/20200429184126.2155-1-kl@kl.wtf Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
parent
470d154a62
commit
4ba8b8aec5
|
@ -28,7 +28,6 @@
|
|||
struct evdev {
|
||||
int open;
|
||||
struct input_handle handle;
|
||||
wait_queue_head_t wait;
|
||||
struct evdev_client __rcu *grab;
|
||||
struct list_head client_list;
|
||||
spinlock_t client_lock; /* protects client_list */
|
||||
|
@ -43,6 +42,7 @@ struct evdev_client {
|
|||
unsigned int tail;
|
||||
unsigned int packet_head; /* [future] position of the first element of next packet */
|
||||
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
|
||||
wait_queue_head_t wait;
|
||||
struct fasync_struct *fasync;
|
||||
struct evdev *evdev;
|
||||
struct list_head node;
|
||||
|
@ -245,7 +245,6 @@ static void evdev_pass_values(struct evdev_client *client,
|
|||
const struct input_value *vals, unsigned int count,
|
||||
ktime_t *ev_time)
|
||||
{
|
||||
struct evdev *evdev = client->evdev;
|
||||
const struct input_value *v;
|
||||
struct input_event event;
|
||||
struct timespec64 ts;
|
||||
|
@ -282,7 +281,7 @@ static void evdev_pass_values(struct evdev_client *client,
|
|||
spin_unlock(&client->buffer_lock);
|
||||
|
||||
if (wakeup)
|
||||
wake_up_interruptible_poll(&evdev->wait,
|
||||
wake_up_interruptible_poll(&client->wait,
|
||||
EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM);
|
||||
}
|
||||
|
||||
|
@ -426,11 +425,11 @@ 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)
|
||||
list_for_each_entry(client, &evdev->client_list, node) {
|
||||
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
||||
wake_up_interruptible_poll(&client->wait, EPOLLHUP | EPOLLERR);
|
||||
}
|
||||
spin_unlock(&evdev->client_lock);
|
||||
|
||||
wake_up_interruptible_poll(&evdev->wait, EPOLLHUP | EPOLLERR);
|
||||
}
|
||||
|
||||
static int evdev_release(struct inode *inode, struct file *file)
|
||||
|
@ -479,6 +478,7 @@ static int evdev_open(struct inode *inode, struct file *file)
|
|||
if (!client)
|
||||
return -ENOMEM;
|
||||
|
||||
init_waitqueue_head(&client->wait);
|
||||
client->bufsize = bufsize;
|
||||
spin_lock_init(&client->buffer_lock);
|
||||
client->evdev = evdev;
|
||||
|
@ -595,7 +595,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
|
|||
break;
|
||||
|
||||
if (!(file->f_flags & O_NONBLOCK)) {
|
||||
error = wait_event_interruptible(evdev->wait,
|
||||
error = wait_event_interruptible(client->wait,
|
||||
client->packet_head != client->tail ||
|
||||
!evdev->exist || client->revoked);
|
||||
if (error)
|
||||
|
@ -613,7 +613,7 @@ static __poll_t evdev_poll(struct file *file, poll_table *wait)
|
|||
struct evdev *evdev = client->evdev;
|
||||
__poll_t mask;
|
||||
|
||||
poll_wait(file, &evdev->wait, wait);
|
||||
poll_wait(file, &client->wait, wait);
|
||||
|
||||
if (evdev->exist && !client->revoked)
|
||||
mask = EPOLLOUT | EPOLLWRNORM;
|
||||
|
@ -946,7 +946,7 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
|
|||
client->revoked = true;
|
||||
evdev_ungrab(evdev, client);
|
||||
input_flush_device(&evdev->handle, file);
|
||||
wake_up_interruptible_poll(&evdev->wait, EPOLLHUP | EPOLLERR);
|
||||
wake_up_interruptible_poll(&client->wait, EPOLLHUP | EPOLLERR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1358,7 +1358,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;
|
||||
|
||||
dev_no = minor;
|
||||
|
|
Loading…
Reference in New Issue