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