Re: [RFC 2/8] HID: usbhid: update LED fields unlocked

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 16 Jul 2013, Benjamin Tissoires wrote:

> > Report fields can be updated from HID drivers unlocked via
> > hid_set_field(). It is protected by input_lock in HID core so only a
> > single input event is handled at a time. USBHID can thus update the field
> > unlocked and doesn't conflict with any HID vendor/device drivers. Note,
> > many HID drivers make heavy use of hid_set_field() in that way.
> >
> > But usbhid also schedules a work to gather multiple LED changes in a
> > single report. Hence, we used to lock the LED field update so the work can
> > read a consistent state. However, hid_set_field() only writes a single
> > integer field, which is guaranteed to be allocated all the time. So the
> > worst possible race-condition is a garbage read on the LED field.
> >
> > Therefore, there is no need to protect the update. In fact, the only thing
> > that is prevented by locking hid_set_field(), is an LED update while the
> > scheduled work currently writes an older LED update out. However, this
> > means, a new work is scheduled directly when the old one is done writing
> > the new state to the device. So we actually _win_ by not protecting the
> > write and allowing the write to be combined with the current write. A new
> > worker is still scheduled, but will not write any new state. So the LED
> > will not blink unnecessarily on the device.
> >
> > Assume we have the LED set to 0. Two request come in which enable the LED
> > and immediately disable it. The current situation with two CPUs would be:
> >
> >   usb_hidinput_input_event()       |      hid_led()
> >   ---------------------------------+----------------------------------
> >     spin_lock(&usbhid->lock);
> >     hid_set_field(1);
> >     spin_unlock(&usbhid->lock);
> >     schedule_work(...);
> >                                       spin_lock(&usbhid->lock);
> >                                       __usbhid_submit_report(..1..);
> >                                       spin_unlock(&usbhid->lock);
> >     spin_lock(&usbhid->lock);
> >     hid_set_field(0);
> >     spin_unlock(&usbhid->lock);
> >     schedule_work(...);
> >                                       spin_lock(&usbhid->lock);
> >                                       __usbhid_submit_report(..0..);
> >                                       spin_unlock(&usbhid->lock);
> >
> > With the locking removed, we _might_ end up with (look at the changed
> > __usbhid_submit_report() parameters in the first try!):
> >
> >   usb_hidinput_input_event()       |      hid_led()
> >   ---------------------------------+----------------------------------
> >     hid_set_field(1);
> >     schedule_work(...);
> >                                       spin_lock(&usbhid->lock);
> >     hid_set_field(0);
> >     schedule_work(...);
> >                                       __usbhid_submit_report(..0..);
> >                                       spin_unlock(&usbhid->lock);
> >
> >                                       ... next work ...
> >
> >                                       spin_lock(&usbhid->lock);
> >                                       __usbhid_submit_report(..0..);
> >                                       spin_unlock(&usbhid->lock);
> >
> > As one can see, we no longer send the "LED ON" signal as it is disabled
> > immediately afterwards and the following "LED OFF" request overwrites the
> > pending "LED ON".
> >
> > It is important to note that hid_set_field() is not atomic, so we might
> > also end up with any other value. But that doesn't matter either as we
> > _always_ schedule the next work with a correct value and schedule_work()
> > acts as memory barrier, anyways. So in the worst case, we run
> > __usbhid_submit_report(..<garbage>..) in the first case and the following
> > __usbhid_submit_report() will write the correct value. But LED states are
> > booleans so any garbage will be converted to either 0 or 1 and the remote
> > device will never see invalid requests.
> >
> > Why all this? It avoids any custom locking around hid_set_field() in
> > usbhid and finally allows us to provide a generic hidinput_input_event()
> > handler for all HID transport drivers.
> >
> > Signed-off-by: David Herrmann <dh.herrmann@xxxxxxxxx>
> > ---
> 
> Hi David,
> 
> that was a very good commit message!
> 
> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx>

I love this patch :) Thanks David, thanks Benjamin.

-- 
Jiri Kosina
SUSE Labs
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux