[Intel-gfx] [RFC 5/9] dma-fence: Track explicit waiters

Rob Clark robdclark at gmail.com
Sat Feb 18 19:54:20 UTC 2023


On Thu, Feb 16, 2023 at 3:00 AM Tvrtko Ursulin
<tvrtko.ursulin at linux.intel.com> wrote:
>
> From: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
>
> Track how many callers are explicity waiting on a fence to signal and
> allow querying that via new dma_fence_wait_count() API.
>
> This provides infrastructure on top of which generic "waitboost" concepts
> can be implemented by individual drivers. Wait-boosting is any reactive
> activity, such as raising the GPU clocks, which happens while there are
> active external waiters.
>
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> ---
>  drivers/dma-buf/dma-fence.c               | 98 +++++++++++++++++------
>  drivers/gpu/drm/i915/gt/intel_engine_pm.c |  1 -
>  include/linux/dma-fence.h                 | 15 ++++
>  3 files changed, 87 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
> index ea4a1f82c9bf..bdba5a8e21b1 100644
> --- a/drivers/dma-buf/dma-fence.c
> +++ b/drivers/dma-buf/dma-fence.c
> @@ -344,6 +344,25 @@ void __dma_fence_might_wait(void)
>  }
>  #endif
>
> +static void incr_wait_count(struct dma_fence *fence, struct dma_fence_cb *cb)
> +{
> +       lockdep_assert_held(fence->lock);
> +
> +       __set_bit(DMA_FENCE_CB_FLAG_WAITCOUNT_BIT, &cb->flags);
> +       fence->waitcount++;
> +       WARN_ON_ONCE(!fence->waitcount);
> +}
> +
> +static void decr_wait_count(struct dma_fence *fence, struct dma_fence_cb *cb)
> +{
> +       lockdep_assert_held(fence->lock);
> +
> +       if (__test_and_clear_bit(DMA_FENCE_CB_FLAG_WAITCOUNT_BIT, &cb->flags)) {
> +               WARN_ON_ONCE(!fence->waitcount);
> +               fence->waitcount--;
> +       }
> +}
> +
>  void __dma_fence_signal__timestamp(struct dma_fence *fence, ktime_t timestamp)
>  {
>         lockdep_assert_held(fence->lock);
> @@ -363,6 +382,7 @@ __dma_fence_signal__notify(struct dma_fence *fence,
>         lockdep_assert_held(fence->lock);
>
>         list_for_each_entry_safe(cur, tmp, list, node) {
> +               decr_wait_count(fence, cur);
>                 INIT_LIST_HEAD(&cur->node);
>                 cur->func(fence, cur);
>         }
> @@ -629,11 +649,44 @@ void dma_fence_enable_sw_signaling(struct dma_fence *fence)
>         unsigned long flags;
>
>         spin_lock_irqsave(fence->lock, flags);
> +       fence->waitcount++;
> +       WARN_ON_ONCE(!fence->waitcount);
>         __dma_fence_enable_signaling(fence);
>         spin_unlock_irqrestore(fence->lock, flags);
>  }
>  EXPORT_SYMBOL(dma_fence_enable_sw_signaling);
>
> +static int add_callback(struct dma_fence *fence, struct dma_fence_cb *cb,
> +                       dma_fence_func_t func, bool wait)
> +{
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       __dma_fence_cb_init(cb, func);
> +
> +       if (WARN_ON(!fence || !func))
> +               return -EINVAL;
> +
> +       if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
> +               return -ENOENT;
> +
> +       spin_lock_irqsave(fence->lock, flags);
> +
> +       if (wait)
> +               incr_wait_count(fence, cb);
> +
> +       if (__dma_fence_enable_signaling(fence)) {
> +               list_add_tail(&cb->node, &fence->cb_list);
> +       } else {
> +               decr_wait_count(fence, cb);
> +               ret = -ENOENT;
> +       }
> +
> +       spin_unlock_irqrestore(fence->lock, flags);
> +
> +       return ret;
> +}
> +
>  /**
>   * dma_fence_add_callback - add a callback to be called when the fence
>   * is signaled
> @@ -659,31 +712,18 @@ EXPORT_SYMBOL(dma_fence_enable_sw_signaling);
>  int dma_fence_add_callback(struct dma_fence *fence, struct dma_fence_cb *cb,
>                            dma_fence_func_t func)
>  {
> -       unsigned long flags;
> -       int ret = 0;
> -
> -       __dma_fence_cb_init(cb, func);
> -
> -       if (WARN_ON(!fence || !func))
> -               return -EINVAL;
> -
> -       if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
> -               return -ENOENT;
> -
> -       spin_lock_irqsave(fence->lock, flags);
> -
> -       if (__dma_fence_enable_signaling(fence)) {
> -               list_add_tail(&cb->node, &fence->cb_list);
> -       } else {
> -               ret = -ENOENT;
> -       }
> -
> -       spin_unlock_irqrestore(fence->lock, flags);
> -
> -       return ret;
> +       return add_callback(fence, cb, func, false);
>  }
>  EXPORT_SYMBOL(dma_fence_add_callback);
>
> +int dma_fence_add_wait_callback(struct dma_fence *fence,
> +                               struct dma_fence_cb *cb,
> +                               dma_fence_func_t func)
> +{
> +       return add_callback(fence, cb, func, true);
> +}
> +EXPORT_SYMBOL(dma_fence_add_wait_callback);
> +
>  /**
>   * dma_fence_get_status - returns the status upon completion
>   * @fence: the dma_fence to query
> @@ -736,8 +776,10 @@ dma_fence_remove_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
>         spin_lock_irqsave(fence->lock, flags);
>
>         ret = !list_empty(&cb->node);
> -       if (ret)
> +       if (ret) {
> +               decr_wait_count(fence, cb);
>                 list_del_init(&cb->node);
> +       }
>
>         spin_unlock_irqrestore(fence->lock, flags);
>
> @@ -795,6 +837,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
>
>         __dma_fence_cb_init(&cb.base, dma_fence_default_wait_cb);
>         cb.task = current;
> +       incr_wait_count(fence, &cb.base);
>         list_add(&cb.base.node, &fence->cb_list);
>
>         while (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) && ret > 0) {
> @@ -811,8 +854,10 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
>                         ret = -ERESTARTSYS;
>         }
>
> -       if (!list_empty(&cb.base.node))
> +       if (!list_empty(&cb.base.node)) {
> +               decr_wait_count(fence, &cb.base);
>                 list_del(&cb.base.node);
> +       }
>         __set_current_state(TASK_RUNNING);
>
>  out:
> @@ -890,8 +935,8 @@ dma_fence_wait_any_timeout(struct dma_fence **fences, uint32_t count,
>                 struct dma_fence *fence = fences[i];
>
>                 cb[i].task = current;
> -               if (dma_fence_add_callback(fence, &cb[i].base,
> -                                          dma_fence_default_wait_cb)) {
> +               if (dma_fence_add_wait_callback(fence, &cb[i].base,
> +                                               dma_fence_default_wait_cb)) {
>                         /* This fence is already signaled */
>                         if (idx)
>                                 *idx = i;
> @@ -972,6 +1017,7 @@ dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops,
>         fence->context = context;
>         fence->seqno = seqno;
>         fence->flags = 0UL;
> +       fence->waitcount = 0;
>         fence->error = 0;
>
>         trace_dma_fence_init(fence);
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pm.c b/drivers/gpu/drm/i915/gt/intel_engine_pm.c
> index e971b153fda9..2693a0151a6b 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_pm.c
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_pm.c
> @@ -218,7 +218,6 @@ static bool switch_to_kernel_context(struct intel_engine_cs *engine)
>                  * until the background request retirement running every
>                  * second or two).
>                  */
> -               BUILD_BUG_ON(sizeof(rq->duration) > sizeof(rq->submitq));
>                 dma_fence_add_callback(&rq->fence, &rq->duration.cb, duration);
>                 rq->duration.emitted = ktime_get();
>         }
> diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h
> index 35933e0ae62c..2b696f9de276 100644
> --- a/include/linux/dma-fence.h
> +++ b/include/linux/dma-fence.h
> @@ -92,6 +92,7 @@ struct dma_fence {
>         u64 seqno;
>         unsigned long flags;
>         struct kref refcount;
> +       unsigned int waitcount;
>         int error;
>  };
>
> @@ -116,6 +117,11 @@ typedef void (*dma_fence_func_t)(struct dma_fence *fence,
>  struct dma_fence_cb {
>         struct list_head node;
>         dma_fence_func_t func;
> +       unsigned long flags;
> +};
> +
> +enum dma_fence_cb_flag_bits {
> +       DMA_FENCE_CB_FLAG_WAITCOUNT_BIT,
>  };
>
>  /**
> @@ -381,6 +387,9 @@ signed long dma_fence_default_wait(struct dma_fence *fence,
>  int dma_fence_add_callback(struct dma_fence *fence,
>                            struct dma_fence_cb *cb,
>                            dma_fence_func_t func);
> +int dma_fence_add_wait_callback(struct dma_fence *fence,
> +                               struct dma_fence_cb *cb,
> +                               dma_fence_func_t func);
>  bool dma_fence_remove_callback(struct dma_fence *fence,
>                                struct dma_fence_cb *cb);
>  void dma_fence_enable_sw_signaling(struct dma_fence *fence);
> @@ -532,6 +541,11 @@ static inline int dma_fence_get_status_locked(struct dma_fence *fence)
>
>  int dma_fence_get_status(struct dma_fence *fence);
>
> +static inline unsigned int dma_fence_wait_count(struct dma_fence *fence)
> +{
> +       return fence->waitcount;
> +}

One thing I noticed while reviving my fence-deadline series is that
this approach is not propagating through array and chain fences

BR,
-R

> +
>  /**
>   * dma_fence_set_error - flag an error condition on the fence
>   * @fence: the dma_fence
> @@ -634,6 +648,7 @@ static inline void __dma_fence_cb_init(struct dma_fence_cb *cb,
>  {
>         INIT_LIST_HEAD(&cb->node);
>         cb->func = func;
> +       cb->flags = 0;
>  }
>
>  #endif /* __LINUX_DMA_FENCE_H */
> --
> 2.34.1
>


More information about the dri-devel mailing list