aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_syncobj.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2017-04-03 23:26:24 -0400
committerDave Airlie <airlied@redhat.com>2017-06-13 22:10:22 -0400
commite9083420bbacce27e43d418064d0d2dfb4b37aaa (patch)
treea539c575bae132fa2cc992ee53da4c04909e7fe1 /drivers/gpu/drm/drm_syncobj.c
parentc9f0726ff360cf75aaafd326e439e9234630aee9 (diff)
drm: introduce sync objects (v4)
Sync objects are new toplevel drm object, that contain a pointer to a fence. This fence can be updated via command submission ioctls via drivers. There is also a generic wait obj API modelled on the vulkan wait API (with code modelled on some amdgpu code). These objects can be converted to an opaque fd that can be passes between processes. v2: rename reference/unreference to put/get (Chris) fix leaked reference (David Zhou) drop mutex in favour of cmpxchg (Chris) v3: cleanups from danvet, rebase on drm_fops rename check fd_flags is 0 in ioctls. v4: export find/free, change replace fence to take a syncobj. In order to support lookup first, replace later semantics which seem in the end to be cleaner. Reviewed-by: Sean Paul <seanpaul@chromium.org> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/drm_syncobj.c')
-rw-r--r--drivers/gpu/drm/drm_syncobj.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c
new file mode 100644
index 000000000000..7144825d45f2
--- /dev/null
+++ b/drivers/gpu/drm/drm_syncobj.c
@@ -0,0 +1,382 @@
1/*
2 * Copyright 2017 Red Hat
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 *
25 */
26
27/**
28 * DOC: Overview
29 *
30 * DRM synchronisation objects (syncobj) are a persistent objects,
31 * that contain an optional fence. The fence can be updated with a new
32 * fence, or be NULL.
33 *
34 * syncobj's can be export to fd's and back, these fd's are opaque and
35 * have no other use case, except passing the syncobj between processes.
36 *
37 * Their primary use-case is to implement Vulkan fences and semaphores.
38 *
39 * syncobj have a kref reference count, but also have an optional file.
40 * The file is only created once the syncobj is exported.
41 * The file takes a reference on the kref.
42 */
43
44#include <drm/drmP.h>
45#include <linux/file.h>
46#include <linux/fs.h>
47#include <linux/anon_inodes.h>
48
49#include "drm_internal.h"
50#include <drm/drm_syncobj.h>
51
52/**
53 * drm_syncobj_find - lookup and reference a sync object.
54 * @file_private: drm file private pointer
55 * @handle: sync object handle to lookup.
56 *
57 * Returns a reference to the syncobj pointed to by handle or NULL.
58 */
59struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
60 u32 handle)
61{
62 struct drm_syncobj *syncobj;
63
64 spin_lock(&file_private->syncobj_table_lock);
65
66 /* Check if we currently have a reference on the object */
67 syncobj = idr_find(&file_private->syncobj_idr, handle);
68 if (syncobj)
69 drm_syncobj_get(syncobj);
70
71 spin_unlock(&file_private->syncobj_table_lock);
72
73 return syncobj;
74}
75EXPORT_SYMBOL(drm_syncobj_find);
76
77/**
78 * drm_syncobj_replace_fence - replace fence in a sync object.
79 * @file_private: drm file private pointer.
80 * @syncobj: Sync object to replace fence in
81 * @fence: fence to install in sync file.
82 *
83 * This replaces the fence on a sync object.
84 */
85void drm_syncobj_replace_fence(struct drm_file *file_private,
86 struct drm_syncobj *syncobj,
87 struct dma_fence *fence)
88{
89 struct dma_fence *old_fence = NULL;
90
91 if (fence)
92 dma_fence_get(fence);
93 old_fence = xchg(&syncobj->fence, fence);
94
95 dma_fence_put(old_fence);
96}
97EXPORT_SYMBOL(drm_syncobj_replace_fence);
98
99int drm_syncobj_fence_get(struct drm_file *file_private,
100 u32 handle,
101 struct dma_fence **fence)
102{
103 struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
104 int ret = 0;
105
106 if (!syncobj)
107 return -ENOENT;
108
109 *fence = dma_fence_get(syncobj->fence);
110 if (!*fence) {
111 ret = -EINVAL;
112 }
113 drm_syncobj_put(syncobj);
114 return ret;
115}
116EXPORT_SYMBOL(drm_syncobj_fence_get);
117
118/**
119 * drm_syncobj_free - free a sync object.
120 * @kref: kref to free.
121 *
122 * Only to be called from kref_put in drm_syncobj_put.
123 */
124void drm_syncobj_free(struct kref *kref)
125{
126 struct drm_syncobj *syncobj = container_of(kref,
127 struct drm_syncobj,
128 refcount);
129 dma_fence_put(syncobj->fence);
130 kfree(syncobj);
131}
132EXPORT_SYMBOL(drm_syncobj_free);
133
134static int drm_syncobj_create(struct drm_file *file_private,
135 u32 *handle)
136{
137 int ret;
138 struct drm_syncobj *syncobj;
139
140 syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);
141 if (!syncobj)
142 return -ENOMEM;
143
144 kref_init(&syncobj->refcount);
145
146 idr_preload(GFP_KERNEL);
147 spin_lock(&file_private->syncobj_table_lock);
148 ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
149 spin_unlock(&file_private->syncobj_table_lock);
150
151 idr_preload_end();
152
153 if (ret < 0) {
154 drm_syncobj_put(syncobj);
155 return ret;
156 }
157
158 *handle = ret;
159 return 0;
160}
161
162static int drm_syncobj_destroy(struct drm_file *file_private,
163 u32 handle)
164{
165 struct drm_syncobj *syncobj;
166
167 spin_lock(&file_private->syncobj_table_lock);
168 syncobj = idr_remove(&file_private->syncobj_idr, handle);
169 spin_unlock(&file_private->syncobj_table_lock);
170
171 if (!syncobj)
172 return -EINVAL;
173
174 drm_syncobj_put(syncobj);
175 return 0;
176}
177
178static int drm_syncobj_file_release(struct inode *inode, struct file *file)
179{
180 struct drm_syncobj *syncobj = file->private_data;
181
182 drm_syncobj_put(syncobj);
183 return 0;
184}
185
186static const struct file_operations drm_syncobj_file_fops = {
187 .release = drm_syncobj_file_release,
188};
189
190static int drm_syncobj_alloc_file(struct drm_syncobj *syncobj)
191{
192 struct file *file = anon_inode_getfile("syncobj_file",
193 &drm_syncobj_file_fops,
194 syncobj, 0);
195 if (IS_ERR(file))
196 return PTR_ERR(file);
197
198 drm_syncobj_get(syncobj);
199 if (cmpxchg(&syncobj->file, NULL, file)) {
200 /* lost the race */
201 fput(file);
202 }
203
204 return 0;
205}
206
207static int drm_syncobj_handle_to_fd(struct drm_file *file_private,
208 u32 handle, int *p_fd)
209{
210 struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
211 int ret;
212 int fd;
213
214 if (!syncobj)
215 return -EINVAL;
216
217 fd = get_unused_fd_flags(O_CLOEXEC);
218 if (fd < 0) {
219 drm_syncobj_put(syncobj);
220 return fd;
221 }
222
223 if (!syncobj->file) {
224 ret = drm_syncobj_alloc_file(syncobj);
225 if (ret)
226 goto out_put_fd;
227 }
228 fd_install(fd, syncobj->file);
229 drm_syncobj_put(syncobj);
230 *p_fd = fd;
231 return 0;
232out_put_fd:
233 put_unused_fd(fd);
234 drm_syncobj_put(syncobj);
235 return ret;
236}
237
238static struct drm_syncobj *drm_syncobj_fdget(int fd)
239{
240 struct file *file = fget(fd);
241
242 if (!file)
243 return NULL;
244 if (file->f_op != &drm_syncobj_file_fops)
245 goto err;
246
247 return file->private_data;
248err:
249 fput(file);
250 return NULL;
251};
252
253static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
254 int fd, u32 *handle)
255{
256 struct drm_syncobj *syncobj = drm_syncobj_fdget(fd);
257 int ret;
258
259 if (!syncobj)
260 return -EINVAL;
261
262 /* take a reference to put in the idr */
263 drm_syncobj_get(syncobj);
264
265 idr_preload(GFP_KERNEL);
266 spin_lock(&file_private->syncobj_table_lock);
267 ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
268 spin_unlock(&file_private->syncobj_table_lock);
269 idr_preload_end();
270
271 if (ret < 0) {
272 fput(syncobj->file);
273 return ret;
274 }
275 *handle = ret;
276 return 0;
277}
278
279/**
280 * drm_syncobj_open - initalizes syncobj file-private structures at devnode open time
281 * @dev: drm_device which is being opened by userspace
282 * @file_private: drm file-private structure to set up
283 *
284 * Called at device open time, sets up the structure for handling refcounting
285 * of sync objects.
286 */
287void
288drm_syncobj_open(struct drm_file *file_private)
289{
290 idr_init(&file_private->syncobj_idr);
291 spin_lock_init(&file_private->syncobj_table_lock);
292}
293
294static int
295drm_syncobj_release_handle(int id, void *ptr, void *data)
296{
297 struct drm_syncobj *syncobj = ptr;
298
299 drm_syncobj_put(syncobj);
300 return 0;
301}
302
303/**
304 * drm_syncobj_release - release file-private sync object resources
305 * @dev: drm_device which is being closed by userspace
306 * @file_private: drm file-private structure to clean up
307 *
308 * Called at close time when the filp is going away.
309 *
310 * Releases any remaining references on objects by this filp.
311 */
312void
313drm_syncobj_release(struct drm_file *file_private)
314{
315 idr_for_each(&file_private->syncobj_idr,
316 &drm_syncobj_release_handle, file_private);
317 idr_destroy(&file_private->syncobj_idr);
318}
319
320int
321drm_syncobj_create_ioctl(struct drm_device *dev, void *data,
322 struct drm_file *file_private)
323{
324 struct drm_syncobj_create *args = data;
325
326 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
327 return -ENODEV;
328
329 /* no valid flags yet */
330 if (args->flags)
331 return -EINVAL;
332
333 return drm_syncobj_create(file_private,
334 &args->handle);
335}
336
337int
338drm_syncobj_destroy_ioctl(struct drm_device *dev, void *data,
339 struct drm_file *file_private)
340{
341 struct drm_syncobj_destroy *args = data;
342
343 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
344 return -ENODEV;
345
346 /* make sure padding is empty */
347 if (args->pad)
348 return -EINVAL;
349 return drm_syncobj_destroy(file_private, args->handle);
350}
351
352int
353drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
354 struct drm_file *file_private)
355{
356 struct drm_syncobj_handle *args = data;
357
358 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
359 return -ENODEV;
360
361 if (args->pad || args->flags)
362 return -EINVAL;
363
364 return drm_syncobj_handle_to_fd(file_private, args->handle,
365 &args->fd);
366}
367
368int
369drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
370 struct drm_file *file_private)
371{
372 struct drm_syncobj_handle *args = data;
373
374 if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
375 return -ENODEV;
376
377 if (args->pad || args->flags)
378 return -EINVAL;
379
380 return drm_syncobj_fd_to_handle(file_private, args->fd,
381 &args->handle);
382}