dma-buf: Introduce fence_get_rcu_safe()
authorChris Wilson <chris@chris-wilson.co.uk>
Mon, 29 Aug 2016 07:08:29 +0000 (08:08 +0100)
committerSumit Semwal <sumit.semwal@linaro.org>
Wed, 12 Oct 2016 14:18:33 +0000 (19:48 +0530)
This variant of fence_get_rcu() takes an RCU protected pointer to a
fence and carefully returns a reference to the fence ensuring that it is
not reallocated as it does. This is required when mixing fences and
SLAB_DESTROY_BY_RCU - although it serves a more pedagogical function atm

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: linux-media@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: linaro-mm-sig@lists.linaro.org
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
Link: http://patchwork.freedesktop.org/patch/msgid/20160829070834.22296-6-chris@chris-wilson.co.uk
include/linux/fence.h

index 0d763053f97a2665b4571aa5d4710f362a900751..c9c5ba98c30262a9152157b9b96b492fc36a3c9e 100644 (file)
@@ -182,6 +182,16 @@ void fence_init(struct fence *fence, const struct fence_ops *ops,
 void fence_release(struct kref *kref);
 void fence_free(struct fence *fence);
 
+/**
+ * fence_put - decreases refcount of the fence
+ * @fence:     [in]    fence to reduce refcount of
+ */
+static inline void fence_put(struct fence *fence)
+{
+       if (fence)
+               kref_put(&fence->refcount, fence_release);
+}
+
 /**
  * fence_get - increases refcount of the fence
  * @fence:     [in]    fence to increase refcount of
@@ -210,13 +220,49 @@ static inline struct fence *fence_get_rcu(struct fence *fence)
 }
 
 /**
- * fence_put - decreases refcount of the fence
- * @fence:     [in]    fence to reduce refcount of
+ * fence_get_rcu_safe  - acquire a reference to an RCU tracked fence
+ * @fence:     [in]    pointer to fence to increase refcount of
+ *
+ * Function returns NULL if no refcount could be obtained, or the fence.
+ * This function handles acquiring a reference to a fence that may be
+ * reallocated within the RCU grace period (such as with SLAB_DESTROY_BY_RCU),
+ * so long as the caller is using RCU on the pointer to the fence.
+ *
+ * An alternative mechanism is to employ a seqlock to protect a bunch of
+ * fences, such as used by struct reservation_object. When using a seqlock,
+ * the seqlock must be taken before and checked after a reference to the
+ * fence is acquired (as shown here).
+ *
+ * The caller is required to hold the RCU read lock.
  */
-static inline void fence_put(struct fence *fence)
+static inline struct fence *fence_get_rcu_safe(struct fence * __rcu *fencep)
 {
-       if (fence)
-               kref_put(&fence->refcount, fence_release);
+       do {
+               struct fence *fence;
+
+               fence = rcu_dereference(*fencep);
+               if (!fence || !fence_get_rcu(fence))
+                       return NULL;
+
+               /* The atomic_inc_not_zero() inside fence_get_rcu()
+                * provides a full memory barrier upon success (such as now).
+                * This is paired with the write barrier from assigning
+                * to the __rcu protected fence pointer so that if that
+                * pointer still matches the current fence, we know we
+                * have successfully acquire a reference to it. If it no
+                * longer matches, we are holding a reference to some other
+                * reallocated pointer. This is possible if the allocator
+                * is using a freelist like SLAB_DESTROY_BY_RCU where the
+                * fence remains valid for the RCU grace period, but it
+                * may be reallocated. When using such allocators, we are
+                * responsible for ensuring the reference we get is to
+                * the right fence, as below.
+                */
+               if (fence == rcu_access_pointer(*fencep))
+                       return rcu_pointer_handoff(fence);
+
+               fence_put(fence);
+       } while (1);
 }
 
 int fence_signal(struct fence *fence);