Input: evdev - Add the events() callback
authorHenrik Rydberg <rydberg@euromail.se>
Wed, 29 Aug 2012 18:48:02 +0000 (20:48 +0200)
committerHenrik Rydberg <rydberg@euromail.se>
Wed, 19 Sep 2012 17:50:18 +0000 (19:50 +0200)
By sending a full frame of events at the same time, the irqsoff
latency at heavy load is brought down from 200 us to 100 us.

Cc: Daniel Kurtz <djkurtz@chromium.org>
Tested-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
Tested-by: Ping Cheng <pingc@wacom.com>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
drivers/input/evdev.c

index a0692c551be52844728a80670c8f576658aa73de..118d0300f1fb72a1324006f544b790a6085affa2 100644 (file)
@@ -54,16 +54,9 @@ struct evdev_client {
 static struct evdev *evdev_table[EVDEV_MINORS];
 static DEFINE_MUTEX(evdev_table_mutex);
 
-static void evdev_pass_event(struct evdev_client *client,
-                            struct input_event *event,
-                            ktime_t mono, ktime_t real)
+static void __pass_event(struct evdev_client *client,
+                        const struct input_event *event)
 {
-       event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
-                                       mono : real);
-
-       /* Interrupts are disabled, just acquire the lock. */
-       spin_lock(&client->buffer_lock);
-
        client->buffer[client->head++] = *event;
        client->head &= client->bufsize - 1;
 
@@ -86,42 +79,74 @@ static void evdev_pass_event(struct evdev_client *client,
                client->packet_head = client->head;
                kill_fasync(&client->fasync, SIGIO, POLL_IN);
        }
+}
+
+static void evdev_pass_values(struct evdev_client *client,
+                       const struct input_value *vals, unsigned int count,
+                       ktime_t mono, ktime_t real)
+{
+       struct evdev *evdev = client->evdev;
+       const struct input_value *v;
+       struct input_event event;
+       bool wakeup = false;
+
+       event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
+                                     mono : real);
+
+       /* Interrupts are disabled, just acquire the lock. */
+       spin_lock(&client->buffer_lock);
+
+       for (v = vals; v != vals + count; v++) {
+               event.type = v->type;
+               event.code = v->code;
+               event.value = v->value;
+               __pass_event(client, &event);
+               if (v->type == EV_SYN && v->code == SYN_REPORT)
+                       wakeup = true;
+       }
 
        spin_unlock(&client->buffer_lock);
+
+       if (wakeup)
+               wake_up_interruptible(&evdev->wait);
 }
 
 /*
- * Pass incoming event to all connected clients.
+ * Pass incoming events to all connected clients.
  */
-static void evdev_event(struct input_handle *handle,
-                       unsigned int type, unsigned int code, int value)
+static void evdev_events(struct input_handle *handle,
+                        const struct input_value *vals, unsigned int count)
 {
        struct evdev *evdev = handle->private;
        struct evdev_client *client;
-       struct input_event event;
        ktime_t time_mono, time_real;
 
        time_mono = ktime_get();
        time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
 
-       event.type = type;
-       event.code = code;
-       event.value = value;
-
        rcu_read_lock();
 
        client = rcu_dereference(evdev->grab);
 
        if (client)
-               evdev_pass_event(client, &event, time_mono, time_real);
+               evdev_pass_values(client, vals, count, time_mono, time_real);
        else
                list_for_each_entry_rcu(client, &evdev->client_list, node)
-                       evdev_pass_event(client, &event, time_mono, time_real);
+                       evdev_pass_values(client, vals, count,
+                                         time_mono, time_real);
 
        rcu_read_unlock();
+}
 
-       if (type == EV_SYN && code == SYN_REPORT)
-               wake_up_interruptible(&evdev->wait);
+/*
+ * Pass incoming event to all connected clients.
+ */
+static void evdev_event(struct input_handle *handle,
+                       unsigned int type, unsigned int code, int value)
+{
+       struct input_value vals[] = { { type, code, value } };
+
+       evdev_events(handle, vals, 1);
 }
 
 static int evdev_fasync(int fd, struct file *file, int on)
@@ -1050,6 +1075,7 @@ MODULE_DEVICE_TABLE(input, evdev_ids);
 
 static struct input_handler evdev_handler = {
        .event          = evdev_event,
+       .events         = evdev_events,
        .connect        = evdev_connect,
        .disconnect     = evdev_disconnect,
        .fops           = &evdev_fops,