All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Henrik Rydberg" <rydberg@euromail.se>
To: Daniel Kurtz <djkurtz@chromium.org>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>,
	Jiri Kosina <jkosina@suse.cz>,
	linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 06/19] Input: Send events one packet at a time
Date: Sat, 25 Aug 2012 21:38:10 +0200	[thread overview]
Message-ID: <20120825193810.GA4732@polaris.bitmath.org> (raw)
In-Reply-To: <CAGS+omC3x31Yq0c10AZzMtfaytUkhTRgkBnhekm=YdzAC5M8vA@mail.gmail.com>

Hi Daniel,

> On Mon, Aug 13, 2012 at 5:42 AM, Henrik Rydberg <rydberg@euromail.se> wrote:
> > On heavy event loads, such as a multitouch driver, the irqsoff latency
> > can be as high as 250 us.  By accumulating a frame worth of data
> > before passing it on, the latency can be dramatically reduced.  As a
> > side effect, the special EV_SYN handling can be removed, since the
> > frame is now atomic.
> 
> This patch(set) is very interesting and exciting.  Thanks!
> Some questions and comments inline...
> 
> >
> > This patch adds the events() handler callback and uses it if it
> > exists. The latency is improved by 50 us even without the callback.
> 
> How much of the savings is just from reducing the number of
> add_input_randomness()  calls from 1-per-input_value to 1-per-frame?

Some, but the largest saving comes from calling down to evdev more sparsely.

> Could you achieve similar savings by only calling add_input_randomness
> on the first input_value after each EV_SYN/SYN_REPORT (ie "when sync =
> true")?

It might make a bit of a difference, because of the additional locks,
but I have not tried it explicitly.

> > @@ -90,46 +90,81 @@ static void input_stop_autorepeat(struct input_dev *dev)
> >   * filtered out, through all open handles. This function is called with
> >   * dev->event_lock held and interrupts disabled.
> >   */
> > -static void input_pass_event(struct input_dev *dev,
> > -                            unsigned int type, unsigned int code, int value)
> > +static size_t input_to_handler(struct input_handle *handle,
> > +                                   struct input_value *vals, size_t count)
> 
> The only thing that is a little strange with this function is that it
> actually changes the 'vals' array due to in-place filtering.

Hm, yes, I did not want to allocate additional memory for the
filtering stuff. It is only used in a few (one?) place, and TBH, it is
not on my list of favorite pieces of code. I would rather see that
api modified than working towards more elaborate filtering schemes.

> It means
> that input_to_handler can't handle const arrays of vals, which may
> have a performance impact in some cases (like key repeat).  You are
> relying on this behavior since you want to pass the final filtered
> input_value array to ->events() without copying, but this seems to be
> optimizing the 'filtered' case relative to the (normal?) unfiltered
> behavior.  Probably not worth changing, though.

Right.

> 
> >  {
> > -       struct input_handler *handler;
> > -       struct input_handle *handle;
> > +       struct input_handler *handler = handle->handler;
> > +       struct input_value *end = vals;
> > +       struct input_value *v;
> >
> > -       rcu_read_lock();
> > +       for (v = vals; v != vals + count; v++) {
> > +               if (handler->filter &&
> 
> if (handler->filter == false), you could skip the whole loop and just
> assign end = vals + count.

True - in principle, but currently a suboptimization.

> Also, the original version assumed that if a handler had the grab, it
> couldn't be a filter, and would skip filtering entirely.
> 
> > +                   handler->filter(handle, v->type, v->code, v->value))
> 
> Maybe we can have a handler->filter_events(handle, vals, count) that
> returns the number of events left after filtering.
> This would allow more sophisticated filtering that could inspect an
> entire frame.

Possibly. Still, the notion of filtering as information-sharing
between drivers on the input bus is not one of my favorites. IMHO,
focus should be on getting the data out of the kernel as quickly as
possible.

> 
> > +                       continue;
> > +               if (end != v)
> > +                       *end = *v;
> > +               end++;
> > +       }
> >
> > -       handle = rcu_dereference(dev->grab);
> > -       if (handle)
> > -               handle->handler->event(handle, type, code, value);
> > -       else {
> > -               bool filtered = false;
> > +       count = end - vals;
> > +       if (!count)
> > +               return 0;
> >
> > -               list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
> > -                       if (!handle->open)
> > -                               continue;
> 
> In the original version, one handler would not call both ->filter()
> and ->event().
> I'm not sure if that was a bug or a feature.  But, you now make it possible.
> However, this opens up the possibility of a filter handler processing
> events via its ->event that would get filtered out by a later
> handler's filter.

True, but it does not change any of the existing usages of filtering.

> In sum, I think if we assume a handler only has either ->filter or
> ->event (->events), then we can split this function into two, one that
> only does filtering on filters, and one that only passes the resulting
> filtered events.
> 
> > +       if (handler->events)
> > +               handler->events(handle, vals, count);
> > +       else
> > +               for (v = vals; v != end; v++)
> > +                       handler->event(handle, v->type, v->code, v->value);
> > +
> > +       return count;
> > +}

My standpoint is clear by now, so I shall not repeat myself. :-)

> > @@ -326,14 +331,35 @@ static void input_handle_event(struct input_dev *dev,
> >                 break;
> >         }
> >
> > -       if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
> > -               dev->sync = false;
> > +       return disposition;
> > +}
> > +
> > +static void input_handle_event(struct input_dev *dev,
> > +                              unsigned int type, unsigned int code, int value)
> > +{
> > +       struct input_value *v;
> > +       int disp;
> > +
> > +       disp = input_get_disposition(dev, type, code, value);
> >
> > -       if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
> > +       if ((disp & INPUT_PASS_TO_DEVICE) && dev->event)
> >                 dev->event(dev, type, code, value);
> >
> > -       if (disposition & INPUT_PASS_TO_HANDLERS)
> > -               input_pass_event(dev, type, code, value);
> > +       if (!dev->vals)
> > +               return;
> > +
> > +       if (disp & INPUT_PASS_TO_HANDLERS) {
> > +               v = &dev->vals[dev->num_vals++];
> > +               v->type = type;
> > +               v->code = code;
> > +               v->value = value;
> > +       }
> > +
> > +       if ((disp & INPUT_FLUSH) || (dev->num_vals >= dev->max_vals)) {
> > +               if (dev->num_vals >= 2)
> 
> I'm not sure about this check.  What if the previous "frame" had
> dev->max_vals + 1 events, and so dev->max_vals of them (all but the
> SYN_REPORT) were already passed.
> We would not get that frame's SYN_REPORT all by itself, so "disp &
> INPUT_FLUSH" is true, but dev->num_vals == 1.  We still want to pass
> the SYN_REPORT immediately, and not save until we get another full
> frame.
> 
> Is this even possible?

Yes, it is possible, if the driver is misconfigured with respect to
the input buffer size. I have ignored that possibility in a few other
places as well (keyboard repeat for one). You are probably right in
that it should be handled somehow, but I would rather make sure the
buffer is always large enough. The atomicity of the frame is really
what makes things go faster.

> 
> > +                       input_pass_values(dev, dev->vals, dev->num_vals);
> > +               dev->num_vals = 0;
> > +       }
> >  }
> >
> >  /**
> > @@ -361,7 +387,6 @@ void input_event(struct input_dev *dev,
> >         if (is_event_supported(type, dev->evbit, EV_MAX)) {
> >
> >                 spin_lock_irqsave(&dev->event_lock, flags);
> > -               add_input_randomness(type, code, value);
> >                 input_handle_event(dev, type, code, value);
> >                 spin_unlock_irqrestore(&dev->event_lock, flags);
> >         }
> > @@ -842,8 +867,7 @@ int input_set_keycode(struct input_dev *dev,
> >             __test_and_clear_bit(old_keycode, dev->key)) {
> >
> >                 input_pass_event(dev, EV_KEY, old_keycode, 0);
> > -               if (dev->sync)
> > -                       input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
> > +               input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
> >         }
> >
> >   out:
> > @@ -1425,6 +1449,7 @@ static void input_dev_release(struct device *device)
> >         input_ff_destroy(dev);
> >         input_mt_destroy_slots(dev);
> >         kfree(dev->absinfo);
> > +       kfree(dev->vals);
> >         kfree(dev);
> >
> >         module_put(THIS_MODULE);
> > @@ -1845,6 +1870,14 @@ int input_register_device(struct input_dev *dev)
> >         if (dev->hint_events_per_packet < packet_size)
> >                 dev->hint_events_per_packet = packet_size;
> >
> > +       dev->num_vals = 0;
> > +       dev->max_vals = max(dev->hint_events_per_packet, packet_size);
> > +
> > +       kfree(dev->vals);
> How could this already be non-NULL?  Is it possible to re-register a device?

Uhm, you are probably right.

> A huge optimization to input event processing is pretty exciting!

Thanks for the review, I will send out a new patchset taking all the
response so far into account.

  reply	other threads:[~2012-08-25 19:33 UTC|newest]

Thread overview: 45+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-08-12 21:42 [PATCH 00/19] Input and HID updates for 3.7 Henrik Rydberg
2012-08-12 21:42 ` [PATCH 01/19] Input: Break out MT data Henrik Rydberg
2012-08-12 21:42 ` [PATCH 02/19] Input: Improve the events-per-packet estimate Henrik Rydberg
2012-08-14 19:32   ` Ping Cheng
2012-08-14 19:53     ` Dmitry Torokhov
2012-08-14 20:50       ` Ping Cheng
2012-08-14 21:12         ` Dmitry Torokhov
2012-08-15  0:54           ` Ping Cheng
2012-08-14 20:01     ` Henrik Rydberg
2012-08-14 21:06       ` Ping Cheng
2012-08-12 21:42 ` [PATCH 03/19] Input: Remove redundant packet estimates Henrik Rydberg
2012-08-12 21:42 ` [PATCH 04/19] Input: Make sure we follow all EV_KEY events Henrik Rydberg
2012-08-12 21:42 ` [PATCH 05/19] Input: Move autorepeat to the event-passing phase Henrik Rydberg
2012-08-12 21:42 ` [PATCH 06/19] Input: Send events one packet at a time Henrik Rydberg
2012-08-24  4:03   ` Daniel Kurtz
2012-08-25 19:38     ` Henrik Rydberg [this message]
2012-08-12 21:42 ` [PATCH 07/19] Input: evdev - Add the events() callback Henrik Rydberg
2012-08-24  4:07   ` Daniel Kurtz
2012-08-25 19:46     ` Henrik Rydberg
2012-08-12 21:42 ` [PATCH 08/19] Input: MT - Add flags to input_mt_init_slots() Henrik Rydberg
2012-08-12 21:42 ` [PATCH 09/19] Input: MT - Handle frame synchronization in core Henrik Rydberg
2012-08-15 23:28   ` Ping Cheng
2012-08-16 18:07     ` Henrik Rydberg
2012-08-16 19:22       ` Ping Cheng
2012-08-16 20:05         ` Henrik Rydberg
2012-08-16 19:58       ` Ping Cheng
2012-08-20 13:36   ` Benjamin Tissoires
2012-08-20 15:53     ` Henrik Rydberg
2012-08-12 21:42 ` [PATCH 10/19] Input: MT - Add in-kernel tracking Henrik Rydberg
2012-08-12 21:42 ` [PATCH 11/19] Input: MT - Add slot assignment by id Henrik Rydberg
2012-08-12 21:42 ` [PATCH 12/19] Input: bcm5974 - Preparatory renames Henrik Rydberg
2012-08-12 21:42 ` [PATCH 13/19] Input: bcm5974 - Drop pressure and width emulation Henrik Rydberg
2012-08-12 21:42 ` [PATCH 14/19] Input: bcm5974 - Drop the logical dimensions Henrik Rydberg
2012-08-12 21:42 ` [PATCH 15/19] Input: bcm5974 - Convert to MT-B Henrik Rydberg
2012-08-12 21:42 ` [PATCH 16/19] HID: hid-multitouch: Remove misleading null test Henrik Rydberg
2012-08-20 13:35   ` Benjamin Tissoires
2012-08-12 21:42 ` [PATCH 17/19] HID: Only dump input if someone is listening Henrik Rydberg
2012-08-12 21:42 ` [PATCH 18/19] HID: Add an input configured notification callback Henrik Rydberg
2012-08-12 21:42 ` [PATCH 19/19] HID: multitouch: Remove the redundant touch state Henrik Rydberg
2012-08-20 13:36   ` Benjamin Tissoires
2012-08-20 16:01     ` Henrik Rydberg
2012-08-22 20:58     ` [PATCH v2] " Henrik Rydberg
2012-08-28 22:25       ` Jiri Kosina
2012-08-29 13:36         ` Benjamin Tissoires
2012-08-29 17:18           ` Jiri Kosina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120825193810.GA4732@polaris.bitmath.org \
    --to=rydberg@euromail.se \
    --cc=djkurtz@chromium.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=jkosina@suse.cz \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.