Re: [PATCH v1] counter: interrupt-cnt: add counter_push_event()

From: William Breathitt Gray
Date: Wed Dec 29 2021 - 04:26:17 EST


On Mon, Dec 27, 2021 at 09:16:31AM -0600, David Lechner wrote:
> On 12/24/21 10:07 PM, William Breathitt Gray wrote:
> > On Wed, Dec 15, 2021 at 10:08:53AM +0100, David Jander wrote:
> >>
> >> Dear William,
> >>
> >> On Wed, 15 Dec 2021 17:48:26 +0900
> >> William Breathitt Gray <vilhelm.gray@xxxxxxxxx> wrote:
> >>
> >>> On Wed, Dec 08, 2021 at 05:10:35PM +0100, David Jander wrote:
> >>>>
> >>>> Dear Uwe,
> >>>>
> >>>> On Wed, 8 Dec 2021 14:59:02 +0100
> >>>> Uwe Kleine-König <u.kleine-koenig@xxxxxxxxxxxxxx> wrote:
> >>>>
> >>>>> Hello David,
> >>>>>
> >>>>> On Tue, Dec 07, 2021 at 08:16:02AM +0100, David Jander wrote:
> >>>>>> On Mon, 6 Dec 2021 13:24:18 -0600
> >>>>>> David Lechner <david@xxxxxxxxxxxxxx> wrote:
> >>>>>>
> >>>>>>> On 11/24/21 7:58 PM, William Breathitt Gray wrote:
> >>>>>>>> On Wed, Nov 24, 2021 at 08:27:20AM +0100, Oleksij Rempel wrote:
> >>>>>>>>> Hi William,
> >>>>>>>>>
> >>>>>>>>> On Wed, Nov 24, 2021 at 03:09:05PM +0900, William Breathitt Gray wrote:
> >>>>>>>>>> On Tue, Nov 23, 2021 at 02:45:40PM +0100, Oleksij Rempel wrote:
> >>>>>>>>>>> Add counter_push_event() to notify user space about new pulses
> >>>>>>>>>>>
> >>>>>>>>>>> Signed-off-by: Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx>
> >>>>>>>>>>> ---
> >>>>>>>>>>> drivers/counter/interrupt-cnt.c | 2 ++
> >>>>>>>>>>> 1 file changed, 2 insertions(+)
> >>>>>>>>>>>
> >>>>>>>>>>> diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c
> >>>>>>>>>>> index 8514a87fcbee..b237137b552b 100644
> >>>>>>>>>>> --- a/drivers/counter/interrupt-cnt.c
> >>>>>>>>>>> +++ b/drivers/counter/interrupt-cnt.c
> >>>>>>>>>>> @@ -31,6 +31,8 @@ static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
> >>>>>>>>>>>
> >>>>>>>>>>> atomic_inc(&priv->count);
> >>>>>>>>>>>
> >>>>>>>>>>> + counter_push_event(&priv->counter, COUNTER_EVENT_OVERFLOW, 0);
> >>>>>>>>>>> +
> >>>>>>>>>>> return IRQ_HANDLED;
> >>>>>>>>>>> }
> >>>>>>>>>>>
> >>>>>>>>>>> --
> >>>>>>>>>>> 2.30.2
> >>>>>>>>>>
> >>>>>>>>>> Hi Oleksij,
> >>>>>>>>>>
> >>>>>>>>>> It looks like this is pushing a COUNTER_EVENT_OVERFLOW event every time
> >>>>>>>>>> an interrupt is handled, which I suspect is not what you want to happen.
> >>>>>>>>>> The COUNTER_EVENT_OVERFLOW event indicates a count value overflow event,
> >>>>>>>>>> so you'll need to check for a count value overflow before pushing the
> >>>>>>>>>> event.
> >>>>>>>>>>
> >>>>>>>>>> It would be good idea to implement a ceiling extension as well (you can
> >>>>>>>>>> use the COUNTER_COMP_CEILING() macro) so that users can configure the
> >>>>>>>>>> particular point where the value overflows.
> >>>>>>>>>
> >>>>>>>>> Thank you!
> >>>>>>>>>
> >>>>>>>>> What would be the best and resource effective strategy for periodically
> >>>>>>>>> getting frequency of interrupts/pulses? This is actual information which is
> >>>>>>>>> needed for my use case.
> >>>>>>>>>
> >>>>>>>>> So far, I was pushing every event to the user space, which is working
> >>>>>>>>> but probably not the most resource effective method of doing it.
> >>>>>>>>>
> >>>>>>>>> Regards,
> >>>>>>>>> Oleskij
> >>>>>>>>> --
> >>>>>>>>> Pengutronix e.K. | |
> >>>>>>>>> Steuerwalder Str. 21 | http://www.pengutronix.de/ |
> >>>>>>>>> 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
> >>>>>>>>> Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
> >>>>>>>>
> >>>>>>>> We could introduce a new Counter change-of-state event type which would
> >>>>>>>> trigger whenever the count value changes, but I agree with you that this
> >>>>>>>> is likely not the best way for us to derive the frequency of the
> >>>>>>>> interrupts due to the indirection of handling and parsing the event
> >>>>>>>> data.
> >>>>>>>>
> >>>>>>>> Instead, perhaps introducing a "frequency" or "period" Count extension
> >>>>>>>> would make more sense here. This extension could report the value delta
> >>>>>>>> between counts, or alternatively the time delta from which you can
> >>>>>>>> derive frequency. Regarding implementation, you can store the previous
> >>>>>>>> value in a variable, updating it whenever an interrupt occurs, and
> >>>>>>>> compute the particular delta every time a read is requested by the user.
> >>>>>>>>
> >>>>>>>> David Lechner is implementing something similar for the TI eQEP driver
> >>>>>>>> to expose speed, so I'm CCing them here in case this is of interest to
> >>>>>>>> them.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Based on my experience, I would recommend that counter drivers be as
> >>>>>>> "thin" as possible. They shouldn't try to provide any information that
> >>>>>>> the hardware itself doesn't provide. In other words, the kernel should
> >>>>>>> provide userspace the information needed to calculate the speed/rate
> >>>>>>> but not try to do the actual calculation in the kernel. Inevitably
> >>>>>>> there are nuances for specific use cases that can't all possibly be
> >>>>>>> handled by such an implementation.
> >>>>>>
> >>>>>> I completely agree with this. While interrupts aren't really meant for
> >>>>>> measuring frequency, and this being somewhat of a mis-use of something, it is
> >>>>>> still possible to do and very useful in many cases. That said, while the
> >>>>>> counter framework is AFAIK the best fit for this, the main use-case for this
> >>>>>> driver is measuring wheel speed (and similar "speeds"). For this, the minimum
> >>>>>> amount of information the driver needs to provide user-space with to do
> >>>>>> reliable calculations, is high-resolution time-stamps of GPIO events. A simple
> >>>>>> counter is not suited, because there can be glitches that need to be detected.
> >>>>>> If user-space gets a buffer full of consecutive time-stamps (don't need to be
> >>>>>> all of them, just a sample of n consecutive timestamps), as well as total
> >>>>>> count, all needed calculations, glitch filtering, low-pass filtering, etc...
> >>>>>> can be done in user-space just fine.
> >>>>>>
> >>>>>>> I've tried using gpio interrupts to try to calculate speed/rate in
> >>>>>>> the kernel before and it simply doesn't work reliably. Interrupts
> >>>>>>> get missed and the calculation will be off.
> >>>>>>
> >>>>>> Exactly. Been there, done that.
> >>>>>> For reliable speed calculations of a mechanical system, the properties of the
> >>>>>> mechanical system need to be known, like physical limits of accelerations,
> >>>>>> maximum (or minimum) speed, etc. The minimum set of input data needed by a
> >>>>>> user-space application to do these calculations is total pulse count in
> >>>>>> addition to a buffer of timestamps of n consecutive input events (raising or
> >>>>>> falling edges on GPIO). So IMHO this is what the driver should provide, and
> >>>>>> in the most resource-efficient way possible. This particular driver will be
> >>>>>> used 3 times on the same SoC, with each up to 10-15k pulses per second. That
> >>>>>> is a lot of interrupts for an embedded system, so they better consume as
> >>>>>> little resources as possible. Filling a ring buffer with timestamps should be
> >>>>>> possible, as long as no locking is involved. Locks in IRQ context must be
> >>>>>> avoided at all costs, specially in this case.
> >>>>>>
> >>>>>>> For really slow counts (i.e. 1 count/second), I can see a use for
> >>>>>>> generating an event on each count though. For high rates, I would
> >>>>>>> just read the count every 100ms in usespace and divide the change in
> >>>>>>> the number of counts by the time period to get the rate.
> >>>>>>
> >>>>>> For slow counts, I agree, but for high rates, I don't (see above). There can
> >>>>>> be glitches and false events that can (and must) be effectively filtered out.
> >>>>>> For that user-space needs to know the time of each event during the
> >>>>>> measurement period.
> >>>>>
> >>>>> No sure I understood the problem here. If you keep the driver as is and
> >>>>> in userspace just read out the counter value twice and measure the time
> >>>>> between the reads[1], you can calculate the average frequency of the
> >>>>> event in userspace.
> >>>>>
> >>>>> Isn't that good enough?
> >>>>
> >>>> No, I'm afraid it isn't, for two reasons:
> >>>>
> >>>> 1. These counters are often used in environments, where glitches can and do
> >>>> happen. So sometimes there are fake events. The only way to tell fake from
> >>>> real pulses, is to filter them. Unfortunately you need the timestamps of each
> >>>> event for that.
> >>>>
> >>>> 2. Another reason for having time-stamps is the case when the frequency is low
> >>>> and one still requires fast accurate measurements. In that case timestamps
> >>>> provide a way of accurately measuring period time.
> >>>>
> >>>> Best regards,
> >>>>
> >>>> --
> >>>> David Jander
> >>>> Protonic Holland.
> >>>
> >>> Keeping drivers focused on just exposing the hardware data and
> >>> functionality is likely the best path to choose, so my earlier
> >>> suggestion of a "frequency" extension would better be left for userspace
> >>> to handle.
> >>
> >> I agree.
> >>
> >>> So in order to enable userspace to derive frequency, you need reliable
> >>> timestamps for enough consecutive events to provide an adequate size
> >>> sample of data on which to perform filtering and other such operations.
> >>
> >> Ack.
> >>
> >>> If we add a COUNTER_EVENT_CHANGE_OF_STATE or similar, every count change
> >>> will generate an event with a logged timestamp. Is the problem with this
> >>> solution primarily that the Counter event queue is currently utilizing
> >>> spinlocks for synchronization?
> >>
> >> Yes. Basically, since one can expect a very high amount of IRQs, it seems
> >> paramount to eliminate any source of latency (spinlocks, etc...) from
> >> interrupt context as well as to keep CPU load as low as technically possible.
> >>
> >> If a spinlock is used, and at 10kHz pulses, on a moderately fast embedded SoC,
> >> it is IMHO quite possible to have user-space cause the spinlock to be held for
> >> more than 100 microseconds, thus causing a pulse to be missed. Not to mention
> >> slight jitter introduced to the timestamps that can cause user-space to falsely
> >> filter out events (a software PLL that doesn't correctly lock).
> >>
> >> The ideal ISR in this case would only take a timestamp from a hardware TSC (or
> >> similarly latency-free direct source) and put it into a circular buffer
> >> without using locks, and maybe increase an unsigned long counter value (atomic
> >> operation if MB's are correctly used) and nothing else.
> >> If, for example, such a solution would require user-space access CPU
> >> load (complexity) to increase by a factor of 10 or even more (in order to
> >> avoid locks), this is likely still preferable, since the ISR is executed maybe
> >> 1000+ times more often than user-space accessing the driver.
> >>
> >> Best regards,
> >>
> >> --
> >> David Jander
> >> Protonic Holland.
> >
> > So the counter_push_event() function interacts with two spinlocks:
> > events_list_lock and events_in_lock. The events_list_lock spinlock is
> > necessary because userspace can modify the events_list list via the
> > counter_enable_events() and counter_disable_events() functions. The
> > events_in_lock spinlock is necessary because userspace can modify the
> > events kfifo via the counter_events_queue_size_write() function.
> >
> > A lockless solution for this might be possible if the driver maintains
> > its own circular buffer as you suggest. The driver's IRQ handler can
> > write to this circular buffer without calling the counter_push_event()
> > function, and then flush the buffer to the Counter character device via
> > a userspace write to a "flush_events" sysfs attribute or similar; this
> > eliminates the need for the events_in_lock spinlock. The state of the
> > events_list list can be captured in the driver's events_configure()
> > callback and stored locally in the driver for reference, thus
> > eliminating the need for the events_list_lock; interrupts can be
> > disabled before the driver's local copy of events_list is modified.
> >
> > With only one reader and one writer operating on the driver's buffer,
> > you can use the normal kfifo_in and kfifo_out calls for lockless
> > operations. Perhaps that is a way forward for this problem.
> >
>
> Would it be possible to make it so that userspace can't modify the
> events_list when IRQs are enabled? Then we wouldn't have to add asecond event buffer.
>
> IIRC, the only operations that modify events_list are when another
> list replaces events_list when events are enabled and when
> events_list is cleared when events are disabled. So as long as the
> ordering is right with respect to enabling and disabling IRQs, it
> seems like the spin lock should not be needed.

I think that could work. If IRQs are always disabled before events_list
is modified, then there is never a risk of interacting with an invalid
events_list and thus counter_push_events() won't need spinlocks. When
IRQs are disabled we miss any possible events, but we are missing those
already anyway when the events_list is modified.

William Breathitt Gray

Attachment: signature.asc
Description: PGP signature