Re: atomic use is not atomic?

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

 



On Tue, 8 Jun 2021 16:30:16 +0300
Andy Shevchenko <andy.shevchenko@xxxxxxxxx> wrote:

> On Tue, Jun 8, 2021 at 4:22 PM Lars-Peter Clausen <lars@xxxxxxxxxx> wrote:
> > On 6/8/21 3:14 PM, Andy Shevchenko wrote:  
> > > On Tue, Jun 8, 2021 at 3:44 PM Lars-Peter Clausen <lars@xxxxxxxxxx> wrote:  
> > >> On 6/8/21 2:35 PM, Andy Shevchenko wrote:  
> > >>> Hi!
> > >>>
> > >>> Can anybody explain this code [1] in terms of atomicity?
> > >>>
> > >>>     if (!atomic_read(&trig->use_count)) {
> > >>>       atomic_set(&trig->use_count, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
> > >>>       ...
> > >>>     }
> > >>>
> > >>> AFAIU how atomics are supposed to work the above doesn't do anything
> > >>> atomically. I.o.w. use_count may be simple int with the same effect.
> > >>>  
> > >> The operations here do not require atomic access, it is just operating
> > >> on a atomic type. The atomic access is the atomic_dec_and_test() in
> > >> iio_trigger_notify_done().  
> > > So, between atomic_read and atomic_set somebody can call
> > > atomic_dec_and_test(), for example. Is it a problem to actually lose
> > > the value?
> > > Why are atomic types being used here when there is no atomicity guaranteed?  
> >
> > We know that if the value is zero that there are no more active
> > consumers. Then we set the value to the number of consumers, and then
> > launch the consumers, which will decrement the counter once they are done.  
> 
> So, why atomic type?

Intent is to avoid locking in the release path where a whole bunch of drivers will be
calling this async in their interrupt threads (via the irqchip that is used
to implement the trigger demux).  It might well be a bit of premature optimization!

The suspicious bit here is actually the atomic_read()
as (in the ideal world) we shouldn't be able to get here with out it being 0.

There are reasons that 'might' happen if we have a path where masking is tricky
or somewhat unreliable.  However in those cases, either we are 0 in which case
we won't get any more calls of atomic_dec_and_test() until after the value is
reset to a high value, or we aren't in which case we'll skip this interrupt
and wait for the next one.

> 
> I can see various races with this code.
> 
> For example, we have two CPUs calling the same function (because it's
> hardware driven model)

That's a driver bug if it happens.  This should be called from an interrupt
(probably a thread), possibly a software emulated interrupt, but whatever
it should be impossible to call it concurrently.

> 
> Imagine that one goes into counter 0 (just after the last notify done)
> followed by the atomic_set to something non-zero followed by a
> skipping trigger notification. Next time the loop won't be called,
> because nobody will reduce atomic to 0, right?
> Is it a possible scenario?
> 

Multiple entry in here shouldn't happen.

Having said that..

There is a race though which can happen if a driver has been a bit loose
on how it implements things and the hardware has some annoying 'optimizations'.

Imagine a device (A) that will not set a pulsed interrupt for data_ready again
until after a read of some particular register that is part of the data
read out (rather than a separate clear interrupt register).
This can be device A.

Now, if we aren't careful we have two devices running off the trigger.
The first has this read data to clear data_ready status approach, the
second is something slow (device B)

A gets done quickly, our counter goes to 1, but not 0 (as B is taking ages) and
we get the next data sample from A causing another interrupt.
Data_ready is set again but we never re read the data.

There are three solutions to this that various drivers use.
a) just prevent use of data_ready trigger for other devices via a validate_device() callback.
b) Have a try_reenable() callback for the trigger.  This is called after the counter
   goes to 0.  That causes an additional data read back and hence the extra scan of
   data is dropped (came too fast), but we will get the one after.
c) Grab data in the top level interrupt handler and stash it (thus the data_ready clear
   condition always happens). That data is the available to the driver in the trigger
   handler.  (after it's checked it's using it's own trigger).

This is fiddly and I'm fairly sure we have a few drivers that will fall
down this hole.  We've caught a few over the years though I'm too lazy
to find an example now!  Mostly these were found when someone decided
to use a data_ready from one sensor to trigger an entirely different one,
but not the sensor it was actually indicating data was ready on.

Jonathan

p.s. I suspect the number of people who actually use a data ready signal to trigger
     a different sensor is very small.  Mostly the multi trigger consumer is
     probably used with a hrtimer or similar trigger.  I cared about this case
     in my original application (grabbing pressure sensor data in parallel to
     an accelerometer in a sprinter's shoe ;)






[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux