]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/gpu/drm/drm_syncobj.c
89441bc78591f61b4069c31b145e76a73d9a7093
[linux.git] / drivers / gpu / drm / drm_syncobj.c
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 #include <linux/sync_file.h>
49
50 #include "drm_internal.h"
51 #include <drm/drm_syncobj.h>
52
53 /**
54  * drm_syncobj_find - lookup and reference a sync object.
55  * @file_private: drm file private pointer
56  * @handle: sync object handle to lookup.
57  *
58  * Returns a reference to the syncobj pointed to by handle or NULL.
59  */
60 struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private,
61                                      u32 handle)
62 {
63         struct drm_syncobj *syncobj;
64
65         spin_lock(&file_private->syncobj_table_lock);
66
67         /* Check if we currently have a reference on the object */
68         syncobj = idr_find(&file_private->syncobj_idr, handle);
69         if (syncobj)
70                 drm_syncobj_get(syncobj);
71
72         spin_unlock(&file_private->syncobj_table_lock);
73
74         return syncobj;
75 }
76 EXPORT_SYMBOL(drm_syncobj_find);
77
78 /**
79  * drm_syncobj_replace_fence - replace fence in a sync object.
80  * @file_private: drm file private pointer.
81  * @syncobj: Sync object to replace fence in
82  * @fence: fence to install in sync file.
83  *
84  * This replaces the fence on a sync object.
85  */
86 void drm_syncobj_replace_fence(struct drm_file *file_private,
87                                struct drm_syncobj *syncobj,
88                                struct dma_fence *fence)
89 {
90         struct dma_fence *old_fence = NULL;
91
92         if (fence)
93                 dma_fence_get(fence);
94         old_fence = xchg(&syncobj->fence, fence);
95
96         dma_fence_put(old_fence);
97 }
98 EXPORT_SYMBOL(drm_syncobj_replace_fence);
99
100 int drm_syncobj_fence_get(struct drm_file *file_private,
101                           u32 handle,
102                           struct dma_fence **fence)
103 {
104         struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
105         int ret = 0;
106
107         if (!syncobj)
108                 return -ENOENT;
109
110         *fence = dma_fence_get(syncobj->fence);
111         if (!*fence) {
112                 ret = -EINVAL;
113         }
114         drm_syncobj_put(syncobj);
115         return ret;
116 }
117 EXPORT_SYMBOL(drm_syncobj_fence_get);
118
119 /**
120  * drm_syncobj_free - free a sync object.
121  * @kref: kref to free.
122  *
123  * Only to be called from kref_put in drm_syncobj_put.
124  */
125 void drm_syncobj_free(struct kref *kref)
126 {
127         struct drm_syncobj *syncobj = container_of(kref,
128                                                    struct drm_syncobj,
129                                                    refcount);
130         dma_fence_put(syncobj->fence);
131         kfree(syncobj);
132 }
133 EXPORT_SYMBOL(drm_syncobj_free);
134
135 static int drm_syncobj_create(struct drm_file *file_private,
136                               u32 *handle)
137 {
138         int ret;
139         struct drm_syncobj *syncobj;
140
141         syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL);
142         if (!syncobj)
143                 return -ENOMEM;
144
145         kref_init(&syncobj->refcount);
146
147         idr_preload(GFP_KERNEL);
148         spin_lock(&file_private->syncobj_table_lock);
149         ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
150         spin_unlock(&file_private->syncobj_table_lock);
151
152         idr_preload_end();
153
154         if (ret < 0) {
155                 drm_syncobj_put(syncobj);
156                 return ret;
157         }
158
159         *handle = ret;
160         return 0;
161 }
162
163 static int drm_syncobj_destroy(struct drm_file *file_private,
164                                u32 handle)
165 {
166         struct drm_syncobj *syncobj;
167
168         spin_lock(&file_private->syncobj_table_lock);
169         syncobj = idr_remove(&file_private->syncobj_idr, handle);
170         spin_unlock(&file_private->syncobj_table_lock);
171
172         if (!syncobj)
173                 return -EINVAL;
174
175         drm_syncobj_put(syncobj);
176         return 0;
177 }
178
179 static int drm_syncobj_file_release(struct inode *inode, struct file *file)
180 {
181         struct drm_syncobj *syncobj = file->private_data;
182
183         drm_syncobj_put(syncobj);
184         return 0;
185 }
186
187 static const struct file_operations drm_syncobj_file_fops = {
188         .release = drm_syncobj_file_release,
189 };
190
191 static int drm_syncobj_alloc_file(struct drm_syncobj *syncobj)
192 {
193         struct file *file = anon_inode_getfile("syncobj_file",
194                                                &drm_syncobj_file_fops,
195                                                syncobj, 0);
196         if (IS_ERR(file))
197                 return PTR_ERR(file);
198
199         drm_syncobj_get(syncobj);
200         if (cmpxchg(&syncobj->file, NULL, file)) {
201                 /* lost the race */
202                 fput(file);
203         }
204
205         return 0;
206 }
207
208 static int drm_syncobj_handle_to_fd(struct drm_file *file_private,
209                                     u32 handle, int *p_fd)
210 {
211         struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle);
212         int ret;
213         int fd;
214
215         if (!syncobj)
216                 return -EINVAL;
217
218         fd = get_unused_fd_flags(O_CLOEXEC);
219         if (fd < 0) {
220                 drm_syncobj_put(syncobj);
221                 return fd;
222         }
223
224         if (!syncobj->file) {
225                 ret = drm_syncobj_alloc_file(syncobj);
226                 if (ret)
227                         goto out_put_fd;
228         }
229         fd_install(fd, syncobj->file);
230         drm_syncobj_put(syncobj);
231         *p_fd = fd;
232         return 0;
233 out_put_fd:
234         put_unused_fd(fd);
235         drm_syncobj_put(syncobj);
236         return ret;
237 }
238
239 static struct drm_syncobj *drm_syncobj_fdget(int fd)
240 {
241         struct file *file = fget(fd);
242
243         if (!file)
244                 return NULL;
245         if (file->f_op != &drm_syncobj_file_fops)
246                 goto err;
247
248         return file->private_data;
249 err:
250         fput(file);
251         return NULL;
252 };
253
254 static int drm_syncobj_fd_to_handle(struct drm_file *file_private,
255                                     int fd, u32 *handle)
256 {
257         struct drm_syncobj *syncobj = drm_syncobj_fdget(fd);
258         int ret;
259
260         if (!syncobj)
261                 return -EINVAL;
262
263         /* take a reference to put in the idr */
264         drm_syncobj_get(syncobj);
265
266         idr_preload(GFP_KERNEL);
267         spin_lock(&file_private->syncobj_table_lock);
268         ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT);
269         spin_unlock(&file_private->syncobj_table_lock);
270         idr_preload_end();
271
272         if (ret < 0) {
273                 fput(syncobj->file);
274                 return ret;
275         }
276         *handle = ret;
277         return 0;
278 }
279
280 int drm_syncobj_import_sync_file_fence(struct drm_file *file_private,
281                                        int fd, int handle)
282 {
283         struct dma_fence *fence = sync_file_get_fence(fd);
284         struct drm_syncobj *syncobj;
285
286         if (!fence)
287                 return -EINVAL;
288
289         syncobj = drm_syncobj_find(file_private, handle);
290         if (!syncobj) {
291                 dma_fence_put(fence);
292                 return -ENOENT;
293         }
294
295         drm_syncobj_replace_fence(file_private, syncobj, fence);
296         dma_fence_put(fence);
297         drm_syncobj_put(syncobj);
298         return 0;
299 }
300
301 int drm_syncobj_export_sync_file(struct drm_file *file_private,
302                                  int handle, int *p_fd)
303 {
304         int ret;
305         struct dma_fence *fence;
306         struct sync_file *sync_file;
307         int fd = get_unused_fd_flags(O_CLOEXEC);
308
309         if (fd < 0)
310                 return fd;
311
312         ret = drm_syncobj_fence_get(file_private, handle, &fence);
313         if (ret)
314                 goto err_put_fd;
315
316         sync_file = sync_file_create(fence);
317
318         dma_fence_put(fence);
319
320         if (!sync_file) {
321                 ret = -EINVAL;
322                 goto err_put_fd;
323         }
324
325         fd_install(fd, sync_file->file);
326
327         *p_fd = fd;
328         return 0;
329 err_put_fd:
330         put_unused_fd(fd);
331         return ret;
332 }
333 /**
334  * drm_syncobj_open - initalizes syncobj file-private structures at devnode open time
335  * @dev: drm_device which is being opened by userspace
336  * @file_private: drm file-private structure to set up
337  *
338  * Called at device open time, sets up the structure for handling refcounting
339  * of sync objects.
340  */
341 void
342 drm_syncobj_open(struct drm_file *file_private)
343 {
344         idr_init(&file_private->syncobj_idr);
345         spin_lock_init(&file_private->syncobj_table_lock);
346 }
347
348 static int
349 drm_syncobj_release_handle(int id, void *ptr, void *data)
350 {
351         struct drm_syncobj *syncobj = ptr;
352
353         drm_syncobj_put(syncobj);
354         return 0;
355 }
356
357 /**
358  * drm_syncobj_release - release file-private sync object resources
359  * @dev: drm_device which is being closed by userspace
360  * @file_private: drm file-private structure to clean up
361  *
362  * Called at close time when the filp is going away.
363  *
364  * Releases any remaining references on objects by this filp.
365  */
366 void
367 drm_syncobj_release(struct drm_file *file_private)
368 {
369         idr_for_each(&file_private->syncobj_idr,
370                      &drm_syncobj_release_handle, file_private);
371         idr_destroy(&file_private->syncobj_idr);
372 }
373
374 int
375 drm_syncobj_create_ioctl(struct drm_device *dev, void *data,
376                          struct drm_file *file_private)
377 {
378         struct drm_syncobj_create *args = data;
379
380         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
381                 return -ENODEV;
382
383         /* no valid flags yet */
384         if (args->flags)
385                 return -EINVAL;
386
387         return drm_syncobj_create(file_private,
388                                   &args->handle);
389 }
390
391 int
392 drm_syncobj_destroy_ioctl(struct drm_device *dev, void *data,
393                           struct drm_file *file_private)
394 {
395         struct drm_syncobj_destroy *args = data;
396
397         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
398                 return -ENODEV;
399
400         /* make sure padding is empty */
401         if (args->pad)
402                 return -EINVAL;
403         return drm_syncobj_destroy(file_private, args->handle);
404 }
405
406 int
407 drm_syncobj_handle_to_fd_ioctl(struct drm_device *dev, void *data,
408                                    struct drm_file *file_private)
409 {
410         struct drm_syncobj_handle *args = data;
411
412         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
413                 return -ENODEV;
414
415         if (args->pad)
416                 return -EINVAL;
417
418         if (args->flags != 0 &&
419             args->flags != DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
420                 return -EINVAL;
421
422         if (args->flags & DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE)
423                 return drm_syncobj_export_sync_file(file_private, args->handle,
424                                                     &args->fd);
425
426         return drm_syncobj_handle_to_fd(file_private, args->handle,
427                                         &args->fd);
428 }
429
430 int
431 drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data,
432                                    struct drm_file *file_private)
433 {
434         struct drm_syncobj_handle *args = data;
435
436         if (!drm_core_check_feature(dev, DRIVER_SYNCOBJ))
437                 return -ENODEV;
438
439         if (args->pad)
440                 return -EINVAL;
441
442         if (args->flags != 0 &&
443             args->flags != DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
444                 return -EINVAL;
445
446         if (args->flags & DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE)
447                 return drm_syncobj_import_sync_file_fence(file_private,
448                                                           args->fd,
449                                                           args->handle);
450
451         return drm_syncobj_fd_to_handle(file_private, args->fd,
452                                         &args->handle);
453 }