[PATCH 01/28] dma-buf: add dma_resv_for_each_fence_unlocked v7

Christian König ckoenig.leichtzumerken at gmail.com
Mon Oct 4 12:59:46 UTC 2021


Am 04.10.21 um 12:50 schrieb Tvrtko Ursulin:
>
> On 04/10/2021 11:44, Christian König wrote:
>> Am 04.10.21 um 12:34 schrieb Tvrtko Ursulin:
>>>
>>> On 04/10/2021 10:53, Christian König wrote:
>>>> Am 04.10.21 um 11:29 schrieb Tvrtko Ursulin:
>>>>>
>>>>> On 01/10/2021 11:05, Christian König wrote:
>>>>>> Abstract the complexity of iterating over all the fences
>>>>>> in a dma_resv object.
>>>>>>
>>>>>> The new loop handles the whole RCU and retry dance and
>>>>>> returns only fences where we can be sure we grabbed the
>>>>>> right one.
>>>>>>
>>>>>> v2: fix accessing the shared fences while they might be freed,
>>>>>>      improve kerneldoc, rename _cursor to _iter, add
>>>>>>      dma_resv_iter_is_exclusive, add dma_resv_iter_begin/end
>>>>>>
>>>>>> v3: restructor the code, move rcu_read_lock()/unlock() into the
>>>>>>      iterator, add dma_resv_iter_is_restarted()
>>>>>>
>>>>>> v4: fix NULL deref when no explicit fence exists, drop superflous
>>>>>>      rcu_read_lock()/unlock() calls.
>>>>>>
>>>>>> v5: fix typos in the documentation
>>>>>>
>>>>>> v6: fix coding error when excl fence is NULL
>>>>>>
>>>>>> v7: one more logic fix
>>>>>>
>>>>>> Signed-off-by: Christian König <christian.koenig at amd.com>
>>>>>> ---
>>>>>>   drivers/dma-buf/dma-resv.c | 100 
>>>>>> +++++++++++++++++++++++++++++++++++++
>>>>>>   include/linux/dma-resv.h   |  95 
>>>>>> +++++++++++++++++++++++++++++++++++
>>>>>>   2 files changed, 195 insertions(+)
>>>>>>
>>>>>> diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c
>>>>>> index 84fbe60629e3..3cbcf66a137e 100644
>>>>>> --- a/drivers/dma-buf/dma-resv.c
>>>>>> +++ b/drivers/dma-buf/dma-resv.c
>>>>>> @@ -323,6 +323,106 @@ void dma_resv_add_excl_fence(struct 
>>>>>> dma_resv *obj, struct dma_fence *fence)
>>>>>>   }
>>>>>>   EXPORT_SYMBOL(dma_resv_add_excl_fence);
>>>>>>   +/**
>>>>>> + * dma_resv_iter_restart_unlocked - restart the unlocked iterator
>>>>>> + * @cursor: The dma_resv_iter object to restart
>>>>>> + *
>>>>>> + * Restart the unlocked iteration by initializing the cursor 
>>>>>> object.
>>>>>> + */
>>>>>> +static void dma_resv_iter_restart_unlocked(struct dma_resv_iter 
>>>>>> *cursor)
>>>>>> +{
>>>>>> +    cursor->seq = read_seqcount_begin(&cursor->obj->seq);
>>>>>> +    cursor->index = -1;
>>>>>> +    if (cursor->all_fences)
>>>>>> +        cursor->fences = dma_resv_shared_list(cursor->obj);
>>>>>> +    else
>>>>>> +        cursor->fences = NULL;
>>>>>> +    cursor->is_restarted = true;
>>>>>> +}
>>>>>> +
>>>>>> +/**
>>>>>> + * dma_resv_iter_walk_unlocked - walk over fences in a dma_resv obj
>>>>>> + * @cursor: cursor to record the current position
>>>>>> + *
>>>>>> + * Return all the fences in the dma_resv object which are not 
>>>>>> yet signaled.
>>>>>> + * The returned fence has an extra local reference so will stay 
>>>>>> alive.
>>>>>> + * If a concurrent modify is detected the whole iteration is 
>>>>>> started over again.
>>>>>> + */
>>>>>> +static void dma_resv_iter_walk_unlocked(struct dma_resv_iter 
>>>>>> *cursor)
>>>>>> +{
>>>>>> +    struct dma_resv *obj = cursor->obj;
>>>>>> +
>>>>>> +    do {
>>>>>> +        /* Drop the reference from the previous round */
>>>>>> +        dma_fence_put(cursor->fence);
>>>>>> +
>>>>>> +        if (cursor->index == -1) {
>>>>>> +            cursor->fence = dma_resv_excl_fence(obj);
>>>>>> +            cursor->index++;
>>>>>> +            if (!cursor->fence)
>>>>>> +                continue;
>>>>>> +
>>>>>> +        } else if (!cursor->fences ||
>>>>>> +               cursor->index >= cursor->fences->shared_count) {
>>>>>> +            cursor->fence = NULL;
>>>>>> +            break;
>>>>>> +
>>>>>> +        } else {
>>>>>> +            struct dma_resv_list *fences = cursor->fences;
>>>>>> +            unsigned int idx = cursor->index++;
>>>>>> +
>>>>>> +            cursor->fence = rcu_dereference(fences->shared[idx]);
>>>>>> +        }
>>>>>> +        cursor->fence = dma_fence_get_rcu(cursor->fence);
>>>>>
>>>>> Worth having an assert dma_fence_get_rcu does not fail here? Not 
>>>>> sure that I have seen debug build only asserts though on the DRM 
>>>>> core side.
>>>>
>>>> That won't work. It's perfectly valid for dma_fence_get_rcu() to 
>>>> return NULL when we are racing here. Keep in mind that we don't 
>>>> hold any locks.
>>>
>>> Ah yes.. No need to change anything then, sorry for the confusion. I 
>>> did not find any holes, the rest was just about how to maybe make 
>>> the flow more obvious. Let me know if you want r-b now or later.
>>
>> Now would be good. I've tried to make that more cleaner, but this 
>> only lead to repeating the code more often.
>
> Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin at intel.com>

Thanks, but what about the rest?

The selftests in this version still have some bugs which I already 
fixed, but I think we could push most of the set.

Christian.

>
> Regards,
>
> Tvrtko
>
>>
>> Regards,
>> Christian.
>>
>>>
>>> Regards,
>>>
>>> Tvrtko
>>>
>>>> What we could do is to return NULL and repeat with a new sequence 
>>>> immediately though.
>>>>
>>>>>
>>>>> On the bike shedding front, would it be clearer if the continue 
>>>>> condition on signaled fences was standalone, using the continue 
>>>>> statement? I'd also possibly re-arrange the three if-else blocks 
>>>>> so that the end of iteration is not sandwiched between blocks 
>>>>> handling exclusive and shared, and flow tweaked a bit, like:
>>>>>
>>>>>   struct dma_fence *fence = cursor->fence;
>>>>>   int index = cursor->index;
>>>>>
>>>>>   dma_fence_put(fence);
>>>>>   fence = NULL;
>>>>>
>>>>> next:
>>>>>   if (index == -1) {
>>>>>     /* Try picking the exclusive fence. */
>>>>>     index++;
>>>>>     fence = dma_resv_excl_fence(obj);
>>>>>     if (!fence)
>>>>>         goto next;
>>>>>   } else if (cursor->fences && index < 
>>>>> cursor->fences->shared_count) {
>>>>>       /* Try picking next shared fence. */
>>>>>     struct dma_resv_list *fences = cursor->fences;
>>>>>
>>>>>     fence = rcu_dereference(fences->shared[index++]);
>>>>>   }
>>>>>
>>>>>   if (fence) {
>>>>>       if (dma_fence_is_signaled(fence))
>>>>>         goto next; /* Skip signaled. */
>>>>>
>>>>>     fence = dma_fence_get_rcu(fence);
>>>>>     WARN_ON(!fence);
>>>>> }
>>>>>
>>>>>   cursor->fence = fence;
>>>>>   cursor->index = index;
>>>>>
>>>>> (I started with a loop here but ended with goto based flow since 
>>>>> it ended up more succinct.)
>>>>>
>>>>> At least if I don't have a handling flaw in there it looks like 
>>>>> easier to follow flow to me. Plus picking a not signaled fence 
>>>>> works without a reference FWIW.
>>>>
>>>> I strongly don't think that this will work correctly. You need to 
>>>> grab a reference first when you want to call 
>>>> dma_fence_is_signaled(), that's why I used the testbit approach 
>>>> initially.
>>>>
>>>>> How does it look to you?
>>>>
>>>> Mhm, let me try to reorder the loop once more.
>>>>
>>>> Thanks,
>>>> Christian.
>>>>
>>>>>
>>>>> Regards,
>>>>>
>>>>> Tvrtko
>>>>>
>>>>>> +        if (!cursor->fence || 
>>>>>> !dma_fence_is_signaled(cursor->fence))
>>>>>> +            break;
>>>>>> +    } while (true);
>>>>>> +}
>>>>>> +
>>>>>> +/**
>>>>>> + * dma_resv_iter_first_unlocked - first fence in an unlocked 
>>>>>> dma_resv obj.
>>>>>> + * @cursor: the cursor with the current position
>>>>>> + *
>>>>>> + * Returns the first fence from an unlocked dma_resv obj.
>>>>>> + */
>>>>>> +struct dma_fence *dma_resv_iter_first_unlocked(struct 
>>>>>> dma_resv_iter *cursor)
>>>>>> +{
>>>>>> +    rcu_read_lock();
>>>>>> +    do {
>>>>>> +        dma_resv_iter_restart_unlocked(cursor);
>>>>>> +        dma_resv_iter_walk_unlocked(cursor);
>>>>>> +    } while (read_seqcount_retry(&cursor->obj->seq, cursor->seq));
>>>>>> +    rcu_read_unlock();
>>>>>> +
>>>>>> +    return cursor->fence;
>>>>>> +}
>>>>>> +EXPORT_SYMBOL(dma_resv_iter_first_unlocked);
>>>>>> +
>>>>>> +/**
>>>>>> + * dma_resv_iter_next_unlocked - next fence in an unlocked 
>>>>>> dma_resv obj.
>>>>>> + * @cursor: the cursor with the current position
>>>>>> + *
>>>>>> + * Returns the next fence from an unlocked dma_resv obj.
>>>>>> + */
>>>>>> +struct dma_fence *dma_resv_iter_next_unlocked(struct 
>>>>>> dma_resv_iter *cursor)
>>>>>> +{
>>>>>> +    bool restart;
>>>>>> +
>>>>>> +    rcu_read_lock();
>>>>>> +    cursor->is_restarted = false;
>>>>>> +    restart = read_seqcount_retry(&cursor->obj->seq, cursor->seq);
>>>>>> +    do {
>>>>>> +        if (restart)
>>>>>> +            dma_resv_iter_restart_unlocked(cursor);
>>>>>> +        dma_resv_iter_walk_unlocked(cursor);
>>>>>> +        restart = true;
>>>>>> +    } while (read_seqcount_retry(&cursor->obj->seq, cursor->seq));
>>>>>> +    rcu_read_unlock();
>>>>>> +
>>>>>> +    return cursor->fence;
>>>>>> +}
>>>>>> +EXPORT_SYMBOL(dma_resv_iter_next_unlocked);
>>>>>> +
>>>>>>   /**
>>>>>>    * dma_resv_copy_fences - Copy all fences from src to dst.
>>>>>>    * @dst: the destination reservation object
>>>>>> diff --git a/include/linux/dma-resv.h b/include/linux/dma-resv.h
>>>>>> index 9100dd3dc21f..5d7d28cb9008 100644
>>>>>> --- a/include/linux/dma-resv.h
>>>>>> +++ b/include/linux/dma-resv.h
>>>>>> @@ -149,6 +149,101 @@ struct dma_resv {
>>>>>>       struct dma_resv_list __rcu *fence;
>>>>>>   };
>>>>>>   +/**
>>>>>> + * struct dma_resv_iter - current position into the dma_resv fences
>>>>>> + *
>>>>>> + * Don't touch this directly in the driver, use the accessor 
>>>>>> function instead.
>>>>>> + */
>>>>>> +struct dma_resv_iter {
>>>>>> +    /** @obj: The dma_resv object we iterate over */
>>>>>> +    struct dma_resv *obj;
>>>>>> +
>>>>>> +    /** @all_fences: If all fences should be returned */
>>>>>> +    bool all_fences;
>>>>>> +
>>>>>> +    /** @fence: the currently handled fence */
>>>>>> +    struct dma_fence *fence;
>>>>>> +
>>>>>> +    /** @seq: sequence number to check for modifications */
>>>>>> +    unsigned int seq;
>>>>>> +
>>>>>> +    /** @index: index into the shared fences */
>>>>>> +    unsigned int index;
>>>>>> +
>>>>>> +    /** @fences: the shared fences */
>>>>>> +    struct dma_resv_list *fences;
>>>>>> +
>>>>>> +    /** @is_restarted: true if this is the first returned fence */
>>>>>> +    bool is_restarted;
>>>>>> +};
>>>>>> +
>>>>>> +struct dma_fence *dma_resv_iter_first_unlocked(struct 
>>>>>> dma_resv_iter *cursor);
>>>>>> +struct dma_fence *dma_resv_iter_next_unlocked(struct 
>>>>>> dma_resv_iter *cursor);
>>>>>> +
>>>>>> +/**
>>>>>> + * dma_resv_iter_begin - initialize a dma_resv_iter object
>>>>>> + * @cursor: The dma_resv_iter object to initialize
>>>>>> + * @obj: The dma_resv object which we want to iterate over
>>>>>> + * @all_fences: If all fences should be returned or just the 
>>>>>> exclusive one
>>>>>> + */
>>>>>> +static inline void dma_resv_iter_begin(struct dma_resv_iter 
>>>>>> *cursor,
>>>>>> +                       struct dma_resv *obj,
>>>>>> +                       bool all_fences)
>>>>>> +{
>>>>>> +    cursor->obj = obj;
>>>>>> +    cursor->all_fences = all_fences;
>>>>>> +    cursor->fence = NULL;
>>>>>> +}
>>>>>> +
>>>>>> +/**
>>>>>> + * dma_resv_iter_end - cleanup a dma_resv_iter object
>>>>>> + * @cursor: the dma_resv_iter object which should be cleaned up
>>>>>> + *
>>>>>> + * Make sure that the reference to the fence in the cursor is 
>>>>>> properly
>>>>>> + * dropped.
>>>>>> + */
>>>>>> +static inline void dma_resv_iter_end(struct dma_resv_iter *cursor)
>>>>>> +{
>>>>>> +    dma_fence_put(cursor->fence);
>>>>>> +}
>>>>>> +
>>>>>> +/**
>>>>>> + * dma_resv_iter_is_exclusive - test if the current fence is the 
>>>>>> exclusive one
>>>>>> + * @cursor: the cursor of the current position
>>>>>> + *
>>>>>> + * Returns true if the currently returned fence is the exclusive 
>>>>>> one.
>>>>>> + */
>>>>>> +static inline bool dma_resv_iter_is_exclusive(struct 
>>>>>> dma_resv_iter *cursor)
>>>>>> +{
>>>>>> +    return cursor->index == -1;
>>>>>> +}
>>>>>> +
>>>>>> +/**
>>>>>> + * dma_resv_iter_is_restarted - test if this is the first fence 
>>>>>> after a restart
>>>>>> + * @cursor: the cursor with the current position
>>>>>> + *
>>>>>> + * Return true if this is the first fence in an iteration after 
>>>>>> a restart.
>>>>>> + */
>>>>>> +static inline bool dma_resv_iter_is_restarted(struct 
>>>>>> dma_resv_iter *cursor)
>>>>>> +{
>>>>>> +    return cursor->is_restarted;
>>>>>> +}
>>>>>> +
>>>>>> +/**
>>>>>> + * dma_resv_for_each_fence_unlocked - unlocked fence iterator
>>>>>> + * @cursor: a struct dma_resv_iter pointer
>>>>>> + * @fence: the current fence
>>>>>> + *
>>>>>> + * Iterate over the fences in a struct dma_resv object without 
>>>>>> holding the
>>>>>> + * &dma_resv.lock and using RCU instead. The cursor needs to be 
>>>>>> initialized
>>>>>> + * with dma_resv_iter_begin() and cleaned up with 
>>>>>> dma_resv_iter_end(). Inside
>>>>>> + * the iterator a reference to the dma_fence is held and the RCU 
>>>>>> lock dropped.
>>>>>> + * When the dma_resv is modified the iteration starts over again.
>>>>>> + */
>>>>>> +#define dma_resv_for_each_fence_unlocked(cursor, 
>>>>>> fence)            \
>>>>>> +    for (fence = dma_resv_iter_first_unlocked(cursor);        \
>>>>>> +         fence; fence = dma_resv_iter_next_unlocked(cursor))
>>>>>> +
>>>>>>   #define dma_resv_held(obj) lockdep_is_held(&(obj)->lock.base)
>>>>>>   #define dma_resv_assert_held(obj) 
>>>>>> lockdep_assert_held(&(obj)->lock.base)
>>>>>>
>>>>
>>



More information about the dri-devel mailing list