--- a/sys/dev/drm/include/linux/dma-fence.h +++ b/sys/dev/drm/include/linux/dma-fence.h @@ -97,9 +97,10 @@ static inline struct dma_fence * dma_fence_get_rcu(struct dma_fence *fence) { - if (fence) - kref_get(&fence->refcount); - return fence; + /* DF-4510: must not resurrect a fence whose release already fired. */ + if (fence && kref_get_unless_zero(&fence->refcount)) + return fence; + return NULL; } static inline void @@ -192,10 +193,23 @@ struct dma_fence *fence; if (dfp == NULL) return NULL; - fence = *dfp; - if (fence) - kref_get(&fence->refcount); - return fence; + /* + * DF-4510: DragonFly's rcu_read_lock() is only preempt_disable(), + * so the load of *dfp can race the last dma_fence_put() of the + * pointed fence (e.g. drm_syncobj_replace_fence dropping the old + * fence outside its lock). With the deferred kfree_rcu free in + * dma_fence_release()/dma_fence_free() the object stays mapped, + * and kref_get_unless_zero + retry yields upstream semantics: + * either a live reference or the pointer changed / went NULL. + */ + do { + fence = rcu_dereference(*dfp); + if (!fence) + return NULL; + if (kref_get_unless_zero(&fence->refcount)) + return fence; + cpu_relax(); + } while (1); } void dma_fence_free(struct dma_fence *fence); --- a/sys/dev/drm/linux_fence.c +++ b/sys/dev/drm/linux_fence.c @@ -47,7 +47,12 @@ if (fence->ops && fence->ops->release) fence->ops->release(fence); else - kfree(fence); + /* DF-4510: lockless readers (drm_syncobj_fence_get and the + * dma_fence_get_rcu family) may still sit between their + * pointer load and their kref_get; defer the free through + * the port's RCU delay-line (>= 1s) instead of freeing + * synchronously. */ + kfree_rcu(fence, rcu); } long @@ -339,5 +344,7 @@ void dma_fence_free(struct dma_fence *fence) { - kfree(fence); + /* DF-4510: same deferred free for drivers using dma_fence_free as + * their ops->release. */ + kfree_rcu(fence, rcu); }