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