linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Aniroop Mathur <aniroop.mathur@gmail.com>
To: Benjamin Tissoires <benjamin.tissoires@gmail.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>,
	Aniroop Mathur <a.mathur@samsung.com>,
	"linux-input@vger.kernel.org" <linux-input@vger.kernel.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	Peter Hutterer <peter.hutterer@who-t.net>,
	David Herrmann <dh.herrmann@gmail.com>,
	Henrik Rydberg <rydberg@bitmath.org>
Subject: Re: [PATCH] Input: evdev - drop partial events after emptying the buffer
Date: Mon, 4 Jan 2016 22:02:10 +0530	[thread overview]
Message-ID: <CADYu308_ts=WUaaAtW1xeYf=QK8Wqex8iUNjzJ_p2O8PVgJDGg@mail.gmail.com> (raw)
In-Reply-To: <20160103233804.GA5454@jelly.redhat.com>

On Mon, Jan 4, 2016 at 2:22 PM, Benjamin Tissoires
<benjamin.tissoires@gmail.com> wrote:
> On Mon, Jan 4, 2016 at 8:50 AM, Aniroop Mathur <a.mathur@samsung.com> wrote:
>> On Jan 4, 2016 5:08 AM, "Peter Hutterer" <peter.hutterer@who-t.net> wrote:
>>>
>>> On Sat, Jan 02, 2016 at 08:39:21PM -0800, Dmitry Torokhov wrote:
>>> > On Thu, Dec 31, 2015 at 03:36:47AM +0530, Aniroop Mathur wrote:
>>> > > This patch introduces concept to drop partial events in evdev handler
>>> > > itself after emptying the buffer which are dropped by all evdev
>>> > > clients in userspace after SYN_DROPPED occurs.
>>> > > This in turn saves space of partial events in evdev handler buffer
>>> > > and reduces evdev client reading requests.
>>> >
>>> > Let's add a few people who write consumer code to see if this is
>>> > something that they consider useful.
>>>
>>> yeah, it's useful though we already have the code in libevdev to work around
>>> this. Still, it reduces the number of events discarde by the client, so it'll be a net
>>> plus. but, afaict, there's a bug in this implementation.
>>> The doc states: "Client should ignore all events up to and including next
>>> SYN_REPORT event". If you drop partial events, you need to have an empty
>>> SYN_REPORT after the SYN_DROPPED before you start with full events again.
>>> This patch skips that, so after the SYN_DROPPED you have a valid full event
>>> that will be ignored by any client currently capable of handling
>>> SYN_DROPPED.
>>> Example: let's assume a device sending ABS_X/ABS_Y fast enough to cause a
>>> SYN_DROPPED, you may have this queue:
>>>     ABS_X
>>>     ABS_Y
>>>     SYN_REPORT
>>>     ...
>>>     SYN_DROPPED
>>>     ABS_Y      <---- partial event
>>>     SYN_REPORT <---- client discards up to here, sync state
>>>     ABS_X
>>>     ABS_Y
>>>     SYN_REPORT <---- first full event after sync
>>>
>>> With this patch this sequence becomes:
>>>     ABS_X
>>>     ABS_Y
>>>     SYN_REPORT
>>>     ...
>>>     SYN_DROPPED
>>>         [kernel discards ABS_Y + SYN_REPORT as partial event]
>>>     ABS_X
>>>     ABS_Y
>>>     SYN_REPORT <--- client discards up to here, sync state
>>>                <--- there is no event after sync
>>>
>>> That's a change in kernel behaviour and will make all current clients
>>> potentially buggy, you'll really need the empty SYN_REPORT here.
>>>
>>
>> Thank you for your input, Mr. Peter.
>> Actually, there is a need to update the documentation as well after this patch
>> so that clients no more ignore the events after SYN_DROPPED occurs and
>> should read the events normally. I skipped updating the documentation in
>> this patch as I thought of getting a consent first.
>> * SYN_DROPPED:
>>   - Used to indicate buffer overrun in the evdev client's event queue.
>>     Client should ignore all events up to and including next SYN_REPORT
>>     event and query the device (using EVIOCG* ioctls) to obtain its
>>     current state
>>     + From kernel version <4.4.x> onwards, clients do no need to ignore
>>     + events anymore and should read normally as there will be no
>>     + partial events after SYN_DROPPED occurs.
>
> Hi Aniroop,
>
> this just won't do. As Peter said, there are current implementation of
> SYN_DROPPED around, which ignore the events until the next SYN_REPORT.
> If you change the protocol by updating the doc, you will just break
> existing userspace which has not included a check on the kernel
> version (and honestly, checking the kernel version from the userspace
> point of view is just a nightmare when distributions start backporting
> changes here and there).
>
> The kernel rule is "do not break userspace", so we can not accept this.
> Peter suggested you just add an empty SYN_REPORT after SYN_DROPPED
> which would solve the whole problem: clients already handling
> SYN_DROPPED will receive the next valid event, and those who don't (or
> which will be updated) will not have to do anything more.
>
> The only cons I can think of is that multitouch protocol A will be a
> pain to handle with this empty SYN_REPORT if you do not handle the
> SYN_DROPPED as per the doc.
> But on the other hand, if you have a MT protocol A device, you are
> screwed anyway because you need mtdev and so let's use libevdev at
> this point.
>
>>
>> As far as I've worked on client codes, this client code change is easy and
>
> Nope, checking the kernel version is not "easy" as this is not reliable.
>
>> even if some clients miss to update the code then it seems not much of
>> a problem because 8 packets are already dropped so an additional packet
>> would not cause any trouble in case of buffer overrun.
>
> Nope again. In case of a SYN_DROPPED, the client syncs its internal
> state by using ioctls. So if you drop one valid event, you are not in
> sync again and the SYN_DROPPED is moot.
>

Thanks for the explanation Mr. Benjamin.
I understood your concern and I have sent the v2 for this patch.

Regards,
Aniroop Mathur

> Cheers,
> Benjamin
>
>>
>> Regards,
>> Aniroop Mathur
>>
>>> > >
>>> > > Signed-off-by: Aniroop Mathur <a.mathur@samsung.com>
>>> > > ---
>>> > >  drivers/input/evdev.c | 49 +++++++++++++++++++++++++++++++++++++++++++++----
>>> > >  1 file changed, 45 insertions(+), 4 deletions(-)
>>> > >
>>> > > diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
>>> > > index e9ae3d5..e7b612e 100644
>>> > > --- a/drivers/input/evdev.c
>>> > > +++ b/drivers/input/evdev.c
>>> > > @@ -58,6 +58,7 @@ struct evdev_client {
>>> > >     struct list_head node;
>>> > >     unsigned int clk_type;
>>> > >     bool revoked;
>>> > > +   bool drop_pevent; /* specifies whether partial events need to be dropped */
>>> > >     unsigned long *evmasks[EV_CNT]; > >     unsigned int bufsize;
>>> > >     struct input_event buffer[];
>>> > > @@ -192,6 +193,7 @@ static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
>>> > >  {
>>> > >     unsigned long flags;
>>> > >     unsigned int clk_type;
>>> > > +   struct input_event *ev;
>>> > >
>>> > >     switch (clkid) {
>>> > >
>>> > > @@ -218,6 +220,17 @@ static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
>>> > >             spin_lock_irqsave(&client->buffer_lock, flags);
>>> > >
>>> > >             if (client->head != client->tail) {
>>> > > +
>>> > > +                   /*
>>> > > +                    * Set drop_pevent to true if last event packet is
>>> > > +                    * not stored completely in buffer.
>>> > > +                    */
>>> > > +                   client->head--;
>>> > > +                   client->head &= client->bufsize - 1;
>>> > > +                   ev = &client->buffer[client->head];
>>> > > +                   if (!(ev->type == EV_SYN && ev->code == SYN_REPORT))
>>> > > +                           client->drop_pevent = true;
>>> > > +
>>> > >                     client->packet_head = client->head = client->tail;
>>> > >                     __evdev_queue_syn_dropped(client);
>>> > >             }
>>> > > @@ -228,31 +241,51 @@ static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
>>> > >     return 0;
>>> > >  }
>>> > >
>>> > > -static void __pass_event(struct evdev_client *client,
>>> > > +static bool __pass_event(struct evdev_client *client,
>>> > >                      const struct input_event *event)
>>> > >  {
>>> > > +   struct input_event *prev_ev;
>>> > > +
>>> > >     client->buffer[client->head++] = *event;
>>> > >     client->head &= client->bufsize - 1;
>>> > >
>>> > >     if (unlikely(client->head == client->tail)) {
>>> > >             /*
>>> > > -            * This effectively "drops" all unconsumed events, leaving
>>> > > -            * EV_SYN/SYN_DROPPED plus the newest event in the queue.
>>> > > +            * This effectively "drops" all unconsumed events, storing
>>> > > +            * EV_SYN/SYN_DROPPED and the newest event in the queue but
>>> > > +            * only if it is not part of partial packet.
>>> > > +            * Set drop_pevent to true if last event packet is not stored
>>> > > +            * completely in buffer and set to false if SYN_REPORT occurs.
>>> > >              */
>>> > > +
>>> > >             client->tail = (client->head - 2) & (client->bufsize - 1);
>>> > >
>>> > > +           prev_ev = &client->buffer[client->tail];
>>> > > +           if (!(prev_ev->type == EV_SYN && prev_ev->code == SYN_REPORT)) {
>>>
>>> IMO a (prev_ev->type != EV_SYN || prev_ev->code != SYN_REPORT) would be
>>> easier to read than this (!(a && b)).
>>>
>>> Cheers,
>>>    Peter
>>>
>>> > > +                   client->drop_pevent = true;
>>> > > +                   client->head--;
>>> > > +                   client->head &= client->bufsize - 1;
>>> > > +           }
>>> > > +
>>> > >             client->buffer[client->tail].time = event->time;
>>> > >             client->buffer[client->tail].type = EV_SYN;
>>> > >             client->buffer[client->tail].code = SYN_DROPPED;
>>> > >             client->buffer[client->tail].value = 0;
>>> > >
>>> > >             client->packet_head = client->tail;
>>> > > +
>>> > > +           if (event->type == EV_SYN && event->code == SYN_REPORT) {
>>> > > +                   client->drop_pevent = false;
>>> > > +                   return true;
>>> > > +           }
>>> > >     }
>>> > >
>>> > >     if (event->type == EV_SYN && event->code == SYN_REPORT) {
>>> > >             client->packet_head = client->head;
>>> > >             kill_fasync(&client->fasync, SIGIO, POLL_IN);
>>> > >     }
>>> > > +
>>> > > +   return false;
>>> > >  }
>>> > >
>>> > >  static void evdev_pass_values(struct evdev_client *client,
>>> > > @@ -284,10 +317,18 @@ static void evdev_pass_values(struct evdev_client *client,
>>> > >                     wakeup = true;
>>> > >             }
>>> > >
>>> > > +           /* drop partial events until SYN_REPORT occurs */
>>> > > +           if (client->drop_pevent) {
>>> > > +                   if (v->type == EV_SYN && v->code == SYN_REPORT)
>>> > > +                           client->drop_pevent = false;
>>> > > +                   continue;
>>> > > +           }
>>> > > +
>>> > >             event.type = v->type;
>>> > >             event.code = v->code;
>>> > >             event.value = v->value;
>>> > > -           __pass_event(client, &event);
>>> > > +           if (__pass_event(client, &event))
>>> > > +                   wakeup = false;
>>> > >     }
>>> > >
>>> > >     spin_unlock(&client->buffer_lock);
>>> > > --
>>> > > 2.6.2
>>> > >
>>> >
>>> > --
>>> > Dmitry
>>

      reply	other threads:[~2016-01-04 16:32 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-12-30 22:06 [PATCH] Input: evdev - drop partial events after emptying the buffer Aniroop Mathur
2016-01-03  4:39 ` Dmitry Torokhov
2016-01-03 23:38   ` Peter Hutterer
2016-01-04 16:32     ` Aniroop Mathur [this message]

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='CADYu308_ts=WUaaAtW1xeYf=QK8Wqex8iUNjzJ_p2O8PVgJDGg@mail.gmail.com' \
    --to=aniroop.mathur@gmail.com \
    --cc=a.mathur@samsung.com \
    --cc=benjamin.tissoires@gmail.com \
    --cc=dh.herrmann@gmail.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=peter.hutterer@who-t.net \
    --cc=rydberg@bitmath.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).