(Cc: Alice, Boris, Christian, Dave, Gary)

On Mon Jun 15, 2026 at 11:26 AM CEST, Tvrtko Ursulin wrote:
> @@ -176,9 +160,25 @@ nouveau_cli_work(struct work_struct *w)
>       struct nouveau_cli_work *work, *wtmp;
>       mutex_lock(&cli->lock);
>       list_for_each_entry_safe(work, wtmp, &cli->worker, head) {
> -             if (!work->fence || nouveau_cli_work_ready(work->fence)) {
> +             struct dma_fence *fence = work->fence;
> +
> +             if (!fence || dma_fence_is_signaled(fence)) {
> +                     if (fence) {
> +                             unsigned long flags;
> +
> +                             /*
> +                              * Because fence can still be in the process of
> +                              * processing the callback list, and the
> +                              * callback references the work we are about to
> +                              * free, we need to sync with the callback
> +                              * processing before freeing the work.
> +                              */
> +                             dma_fence_lock_irqsave(fence, flags);
> +                             dma_fence_unlock_irqrestore(fence, flags);

This does not ensure that there are no more dma_fence_ops callbacks into the
driver. For instance, a concurrent dma_fence_is_signaled() might read the
signaled flag as false before we do this lock dance and then still enter
ops->signaled().

Luckily, for this specific code here in nouveau it should be enough to ensure
that all dma_fence_cb callbacks are finished, which this hack does achieve.

The reason I call it hack is that it fundamentally relies on dma_fence
implementation details and not a specified API contract.

The closest thing to an API contract for this is the RCU protection that was
recently added. So, the proper approach would be to run this work with
queue_rcu_work() and then call flush_rcu_work().

In this context it is worth noting that in this case it is even more
complicated. Since the work struct is shared per CLI the single RCU barrier in
flush_rcu_work() is not enough, i.e. there is no per fence callback grace period
guarantee.

To me this still seems to be a bit of a footgun (although we could probably
improve the nouveau code to make it a bit simpler).

That said, the patch does make things a bit clearer, so I picked it up --
thanks!

Thanks,
Danilo

> +                     }
>                       list_del(&work->head);
>                       work->func(work);
> +                     dma_fence_put(fence);
>               }
>       }
>       mutex_unlock(&cli->lock);
> -- 
> 2.54.0

Reply via email to