[PATCH] dma-buf: fix dma_fence_array_signaled

Tvrtko Ursulin tursulin at ursulin.net
Fri Nov 8 15:52:02 UTC 2024


On 08/11/2024 14:18, Christian König wrote:
> Am 08.11.24 um 14:01 schrieb Tvrtko Ursulin:
>>
>> On 08/11/2024 09:42, Christian König wrote:
>>> The function silently assumed that signaling was already enabled for the
>>> dma_fence_array. This meant that without enabling signaling first we 
>>> would
>>> never see forward progress.
>>>
>>> Fix that by falling back to testing each individual fence when signaling
>>> isn't enabled yet.
>>>
>>> Signed-off-by: Christian König <christian.koenig at amd.com>
>>> ---
>>>   drivers/dma-buf/dma-fence-array.c | 14 +++++++++++++-
>>>   1 file changed, 13 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/dma-buf/dma-fence-array.c 
>>> b/drivers/dma-buf/dma-fence-array.c
>>> index 46ac42bcfac0..01203796827a 100644
>>> --- a/drivers/dma-buf/dma-fence-array.c
>>> +++ b/drivers/dma-buf/dma-fence-array.c
>>> @@ -103,10 +103,22 @@ static bool 
>>> dma_fence_array_enable_signaling(struct dma_fence *fence)
>>>   static bool dma_fence_array_signaled(struct dma_fence *fence)
>>>   {
>>>       struct dma_fence_array *array = to_dma_fence_array(fence);
>>> +    unsigned int i, num_pending;
>>>   -    if (atomic_read(&array->num_pending) > 0)
>>> +    num_pending = atomic_read(&array->num_pending);
>>> +    if (test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, 
>>> &array->base.flags)) {
>>> +        if (!num_pending)
>>> +            goto signal;
>>>           return false;
>>> +    }
>>> +
>>> +    for (i = 0; i < array->num_fences; ++i) {
>>> +        if (dma_fence_is_signaled(array->fences[i]) && !--num_pending)
>>> +            goto signal;
>>> +    }
>>> +    return false;
>>
>> Sampling num_pending (and decrementing) and test_bit from an unlocked 
>> path makes one need to think if there are consequences, false 
>> negatives, positives or something. Would it be fine to simplify like 
>> the below?
> 
> Yeah I've played around with those ideas as well but came to the 
> conclusion that neither of them are correct.
> 
>>
>> static bool dma_fence_array_signaled(struct dma_fence *fence)
>> {
>>     struct dma_fence_array *array = to_dma_fence_array(fence);
>>     unsigned int i;
>>
>>     if (atomic_read(&array->num_pending)) {
>>         for (i = 0; i < array->num_fences; i++) {
>>             if (!dma_fence_is_signaled(array->fences[i]))
>>                 return false;
> 
> That's not correct. num_pending is not necessary equal to the number of 
> fences in the array.
> 
> E.g. we have cases where num_pending is just 1 so that the 
> dma_fence_array signals when *any* fence in it signals.

I forgot about that mode.

>> }
>>     }
>>
>>     dma_fence_array_clear_pending_error(array);
>>     return true;
>> }
>>
>> Or if the optimisation to not walk the array when signalling is 
>> already enabled is deemed important, perhaps a less thinking inducing 
>> way would be this:
> ...
>> Decrementing locally cached num_pending in the loop I think does not 
>> bring anything since when signalling is not enabled it will be stuck 
>> at num_fences. So the loop walks the whole array versus bail on first 
>> unsignalled, so latter even more efficient.
> 
> That is not for optimization but for correctness.
> 
> What the patch basically does is the following:
> 1. Grab the current value of num_pending.
> 
> 2. Test if num_pending was potentially already modified because 
> signaling is already enabled, if yes just test it and return the result.
> 
> 3. If it wasn't modified go over the fences and see if we already have 
> at least num_pending signaled.
> 
> I should probably add a code comment explaining that.

It would be good yes.

DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT can appear any time, even after the 
check, hence I am not sure the absence of the bit can be used to 
guarantee num_pending is stable. But I think this one is safe, since the 
loop will in any case find the desired number of signalled fences even 
if num_pending is stale (too high).

Also, can num_pending underflow in signal-on-any mode and if it can what 
can happen in unsigned int num_pending and the below loop. Potentially 
just one false negative with the following query returning signalled 
from the top level dma-fence code.

In summary I think patch works. I am just unsure if the above race can 
silently happen and cause one extra round trip through the query. If it 
can it still works, but definitely needs a big fat comment to explain it.

Regards,

Tvrtko

> 
>> In which case, should dma-fence-chain also be aligned to have the fast 
>> path bail out?
> 
> Good point need to double check that code as well.
> 
> Thanks,
> Christian.
> 
>>
>> Regards,
>>
>> Tvrtko
>>
>>>   +signal:
>>>       dma_fence_array_clear_pending_error(array);
>>>       return true;
>>>   }
> 


More information about the dri-devel mailing list