Commit cdda911c authored by Jeff Brown's avatar Jeff Brown Committed by Dmitry Torokhov

Input: evdev - only signal polls on full packets

This patch modifies evdev so that it only becomes readable when
the buffer contains an EV_SYN/SYN_REPORT event.

On SMP systems, it is possible for an evdev client blocked on poll()
to wake up and read events from the evdev ring buffer at the same
rate as they are enqueued.  This can result in high CPU usage,
particularly for MT devices, because the client ends up reading
events one at a time instead of reading complete packets.

We eliminate this problem by making the device readable only when
the buffer contains at least one complete packet.  This causes
clients to block until the entire packet is available.
Signed-off-by: default avatarJeff Brown <jeffbrown@android.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 03351ff4
...@@ -41,6 +41,7 @@ struct evdev { ...@@ -41,6 +41,7 @@ struct evdev {
struct evdev_client { struct evdev_client {
unsigned int head; unsigned int head;
unsigned int tail; 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 */ spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync; struct fasync_struct *fasync;
struct evdev *evdev; struct evdev *evdev;
...@@ -72,12 +73,16 @@ static void evdev_pass_event(struct evdev_client *client, ...@@ -72,12 +73,16 @@ static void evdev_pass_event(struct evdev_client *client,
client->buffer[client->tail].type = EV_SYN; client->buffer[client->tail].type = EV_SYN;
client->buffer[client->tail].code = SYN_DROPPED; client->buffer[client->tail].code = SYN_DROPPED;
client->buffer[client->tail].value = 0; client->buffer[client->tail].value = 0;
}
spin_unlock(&client->buffer_lock); client->packet_head = client->tail;
}
if (event->type == EV_SYN) if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;
kill_fasync(&client->fasync, SIGIO, POLL_IN); kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
spin_unlock(&client->buffer_lock);
} }
/* /*
...@@ -387,12 +392,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, ...@@ -387,12 +392,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
if (count < input_event_size()) if (count < input_event_size())
return -EINVAL; return -EINVAL;
if (client->head == client->tail && evdev->exist && if (client->packet_head == client->tail && evdev->exist &&
(file->f_flags & O_NONBLOCK)) (file->f_flags & O_NONBLOCK))
return -EAGAIN; return -EAGAIN;
retval = wait_event_interruptible(evdev->wait, retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist); client->packet_head != client->tail || !evdev->exist);
if (retval) if (retval)
return retval; return retval;
...@@ -421,7 +426,7 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait) ...@@ -421,7 +426,7 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
poll_wait(file, &evdev->wait, wait); poll_wait(file, &evdev->wait, wait);
mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
if (client->head != client->tail) if (client->packet_head != client->tail)
mask |= POLLIN | POLLRDNORM; mask |= POLLIN | POLLRDNORM;
return mask; return mask;
......
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