]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dma-buf: detach fence ops on signal v3
authorChristian König <christian.koenig@amd.com>
Wed, 8 Oct 2025 16:12:46 +0000 (18:12 +0200)
committerChristian König <christian.koenig@amd.com>
Mon, 23 Feb 2026 15:14:02 +0000 (16:14 +0100)
When neither a release nor a wait backend ops is specified it is possible
to let the dma_fence live on independently of the module who issued it.

This makes it possible to unload drivers and only wait for all their
fences to signal.

v2: fix typo in comment
v3: fix sparse rcu warnings

Signed-off-by: Christian König <christian.koenig@amd.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
Reviewed-by: Philipp Stanner <phasta@kernel.org>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Link: https://lore.kernel.org/r/20260219160822.1529-3-christian.koenig@amd.com
drivers/dma-buf/dma-fence.c
include/linux/dma-fence.h

index 076e6e6c75bef40fab3f53ba702b312a983a8eb0..3279d82ffa987b3dad5e9d37462da001762b1efd 100644 (file)
@@ -362,6 +362,7 @@ void __dma_fence_might_wait(void)
 void dma_fence_signal_timestamp_locked(struct dma_fence *fence,
                                      ktime_t timestamp)
 {
+       const struct dma_fence_ops *ops;
        struct dma_fence_cb *cur, *tmp;
        struct list_head cb_list;
 
@@ -371,6 +372,15 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence,
                                      &fence->flags)))
                return;
 
+       /*
+        * When neither a release nor a wait operation is specified set the ops
+        * pointer to NULL to allow the fence structure to become independent
+        * from who originally issued it.
+        */
+       ops = rcu_dereference_protected(fence->ops, true);
+       if (!ops->release && !ops->wait)
+               RCU_INIT_POINTER(fence->ops, NULL);
+
        /* Stash the cb_list before replacing it with the timestamp */
        list_replace(&fence->cb_list, &cb_list);
 
@@ -537,7 +547,7 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)
        rcu_read_lock();
        ops = rcu_dereference(fence->ops);
        trace_dma_fence_wait_start(fence);
-       if (ops->wait) {
+       if (ops && ops->wait) {
                /*
                 * Implementing the wait ops is deprecated and not supported for
                 * issuers of fences who need their lifetime to be independent
@@ -603,7 +613,7 @@ void dma_fence_release(struct kref *kref)
        }
 
        ops = rcu_dereference(fence->ops);
-       if (ops->release)
+       if (ops && ops->release)
                ops->release(fence);
        else
                dma_fence_free(fence);
@@ -639,7 +649,7 @@ static bool __dma_fence_enable_signaling(struct dma_fence *fence)
 
        rcu_read_lock();
        ops = rcu_dereference(fence->ops);
-       if (!was_set && ops->enable_signaling) {
+       if (!was_set && ops && ops->enable_signaling) {
                trace_dma_fence_enable_signal(fence);
 
                if (!ops->enable_signaling(fence)) {
@@ -1025,7 +1035,7 @@ void dma_fence_set_deadline(struct dma_fence *fence, ktime_t deadline)
 
        rcu_read_lock();
        ops = rcu_dereference(fence->ops);
-       if (ops->set_deadline && !dma_fence_is_signaled(fence))
+       if (ops && ops->set_deadline && !dma_fence_is_signaled(fence))
                ops->set_deadline(fence, deadline);
        rcu_read_unlock();
 }
index fa3cfe3e98ac1e0b9d457a7c9de0aa8fd8a4504a..9ff2c4a09cdcd918362f9d4609b53f55d9088bbe 100644 (file)
@@ -472,7 +472,7 @@ dma_fence_is_signaled_locked(struct dma_fence *fence)
 
        rcu_read_lock();
        ops = rcu_dereference(fence->ops);
-       if (ops->signaled && ops->signaled(fence)) {
+       if (ops && ops->signaled && ops->signaled(fence)) {
                rcu_read_unlock();
                dma_fence_signal_locked(fence);
                return true;
@@ -508,7 +508,7 @@ dma_fence_is_signaled(struct dma_fence *fence)
 
        rcu_read_lock();
        ops = rcu_dereference(fence->ops);
-       if (ops->signaled && ops->signaled(fence)) {
+       if (ops && ops->signaled && ops->signaled(fence)) {
                rcu_read_unlock();
                dma_fence_signal(fence);
                return true;