diff options
author | Jason Ekstrand <jason@jlekstrand.net> | 2017-08-25 13:52:20 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2017-08-28 16:20:30 -0400 |
commit | 309a5482fa9eb7bc754bf95a2cd89091b01c33d2 (patch) | |
tree | 0867e51139f138fa4a8824f0df52db357de77d12 | |
parent | afaf59237843bf89823c33143beca6b262dff0ca (diff) |
drm/syncobj: Add a race-free drm_syncobj_fence_get helper (v2)
The atomic exchange operation in drm_syncobj_replace_fence is sufficient
for the case where it races with itself. However, if you have a race
between a replace_fence and dma_fence_get(syncobj->fence), you may end
up with the entire replace_fence happening between the point in time
where the one thread gets the syncobj->fence pointer and when it calls
dma_fence_get() on it. If this happens, then the reference may be
dropped before we get a chance to get a new one. The new helper uses
dma_fence_get_rcu_safe to get rid of the race.
This is also needed because it allows us to do a bit more than just get
a reference in drm_syncobj_fence_get should we wish to do so.
v2:
- RCU isn't that scary
- Call rcu_read_lock/unlock
- Don't rename fence to _fence
- Make the helper static inline
Signed-off-by: Jason Ekstrand <jason@jlekstrand.net>
Acked-by: Christian König <christian.koenig@amd.com> (v1)
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | drivers/gpu/drm/drm_syncobj.c | 2 | ||||
-rw-r--r-- | include/drm/drm_syncobj.h | 12 |
2 files changed, 13 insertions, 1 deletions
diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 0412b0b0a342..eea38d82645c 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c | |||
@@ -105,7 +105,7 @@ int drm_syncobj_find_fence(struct drm_file *file_private, | |||
105 | if (!syncobj) | 105 | if (!syncobj) |
106 | return -ENOENT; | 106 | return -ENOENT; |
107 | 107 | ||
108 | *fence = dma_fence_get(syncobj->fence); | 108 | *fence = drm_syncobj_fence_get(syncobj); |
109 | if (!*fence) { | 109 | if (!*fence) { |
110 | ret = -EINVAL; | 110 | ret = -EINVAL; |
111 | } | 111 | } |
diff --git a/include/drm/drm_syncobj.h b/include/drm/drm_syncobj.h index 7d4ad777132e..ce94d14c5087 100644 --- a/include/drm/drm_syncobj.h +++ b/include/drm/drm_syncobj.h | |||
@@ -77,6 +77,18 @@ drm_syncobj_put(struct drm_syncobj *obj) | |||
77 | kref_put(&obj->refcount, drm_syncobj_free); | 77 | kref_put(&obj->refcount, drm_syncobj_free); |
78 | } | 78 | } |
79 | 79 | ||
80 | static inline struct dma_fence * | ||
81 | drm_syncobj_fence_get(struct drm_syncobj *syncobj) | ||
82 | { | ||
83 | struct dma_fence *fence; | ||
84 | |||
85 | rcu_read_lock(); | ||
86 | fence = dma_fence_get_rcu_safe(&syncobj->fence); | ||
87 | rcu_read_unlock(); | ||
88 | |||
89 | return fence; | ||
90 | } | ||
91 | |||
80 | struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, | 92 | struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, |
81 | u32 handle); | 93 | u32 handle); |
82 | void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, | 94 | void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, |