[PATCH] dma-buf: fix dma_fence_array_signaled

Christian König ckoenig.leichtzumerken at gmail.com
Tue Nov 12 12:16:01 UTC 2024


Am 08.11.24 um 16:52 schrieb Tvrtko Ursulin:
> 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.

Oh, good point. Yeah num_pending can indeed underflow when signaling is 
enabled. I've fixed the check accordingly.

> 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.

I've added the summary from Boris as comment.

Thanks,
Christian.

>
> 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