xref: /linux-6.15/drivers/gpu/drm/drm_syncobj.c (revision 7eaf1198)
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 }
454