diff mbox

[07/19] Input: evdev - Add the events() callback

Message ID 1344807757-2217-8-git-send-email-rydberg@euromail.se (mailing list archive)
State New, archived
Headers show

Commit Message

Henrik Rydberg Aug. 12, 2012, 9:42 p.m. UTC
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.

Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
---
 drivers/input/evdev.c | 68 +++++++++++++++++++++++++++++++++++----------------
 1 file changed, 47 insertions(+), 21 deletions(-)

Comments

Daniel Kurtz Aug. 24, 2012, 4:07 a.m. UTC | #1
On Mon, Aug 13, 2012 at 5:42 AM, Henrik Rydberg <rydberg@euromail.se> wrote:
> 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.
>
> Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
> ---
>  drivers/input/evdev.c | 68 +++++++++++++++++++++++++++++++++++----------------
>  1 file changed, 47 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
> index a0692c5..1bf4ce5 100644
> --- a/drivers/input/evdev.c
> +++ b/drivers/input/evdev.c
> @@ -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, size_t 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, size_t 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);

Hi Henrik,

Reading the time just once and applying it as the timestamp to an
entire frame is very nice.
However, is it ever possible for the SYN_REPORT to get delayed until
the next batch of input_values, therefore breaking the assumption that
the SYN_REPORT timestamp applies to the rest of the input_values for
its frame?

Also, bonus points if the input driver could set this input frame
timestamp based on when it first saw a hardware interrupt rather then
when evdev gets around to sending the frame to userspace.  This could
potentially remove a lot of the timing jitter userspace sees when
computing ballistics based on input event timestamps.

Thanks!
-Daniel

>
>         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,
> --
> 1.7.11.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Henrik Rydberg Aug. 25, 2012, 7:46 p.m. UTC | #2
> Reading the time just once and applying it as the timestamp to an
> entire frame is very nice.
> However, is it ever possible for the SYN_REPORT to get delayed until
> the next batch of input_values, therefore breaking the assumption that
> the SYN_REPORT timestamp applies to the rest of the input_values for
> its frame?

Yes, but see reply to previous patch.

> Also, bonus points if the input driver could set this input frame
> timestamp based on when it first saw a hardware interrupt rather then
> when evdev gets around to sending the frame to userspace.  This could
> potentially remove a lot of the timing jitter userspace sees when
> computing ballistics based on input event timestamps.

In principle, yes (it has been discussed before), but in practise some
devices provide timestamps and some not, and the scale and granularity
may vary. In addition, desktop userland (read X input) does not even
use the kernel timestamp, so the effect would not even be seen without
a synchronized effort. I am not saying it is a bad idea, but it has
some details to get straight before it becomes useful.

Thanks,
Henrik
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index a0692c5..1bf4ce5 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -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, size_t 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, size_t 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,