1 /*- 2 * Copyright (c) 2011 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Konstantin Belousov under sponsorship from 6 * the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD: head/sys/dev/drm2/drm_gem.c 247835 2013-03-05 09:49:34Z kib $" 30 */ 31 32 #include "opt_vm.h" 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/limits.h> 37 #include <sys/lock.h> 38 #include <sys/mutex.h> 39 #include <sys/conf.h> 40 41 #include <vm/vm.h> 42 #include <vm/vm_page.h> 43 44 #include <drm/drmP.h> 45 46 /* 47 * We make up offsets for buffer objects so we can recognize them at 48 * mmap time. 49 */ 50 51 /* pgoff in mmap is an unsigned long, so we need to make sure that 52 * the faked up offset will fit 53 */ 54 55 #if ULONG_MAX == UINT64_MAX 56 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) 57 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16) 58 #else 59 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1) 60 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16) 61 #endif 62 63 /** 64 * Initialize the GEM device fields 65 */ 66 67 int 68 drm_gem_init(struct drm_device *dev) 69 { 70 struct drm_gem_mm *mm; 71 72 lockinit(&dev->object_name_lock, "objnam", 0, LK_CANRECURSE); 73 idr_init(&dev->object_name_idr); 74 75 mm = kmalloc(sizeof(*mm), M_DRM, M_WAITOK); 76 if (!mm) { 77 DRM_ERROR("out of memory\n"); 78 return -ENOMEM; 79 } 80 81 dev->mm_private = mm; 82 83 if (drm_ht_create(&mm->offset_hash, 12)) { 84 drm_free(mm, M_DRM); 85 return -ENOMEM; 86 } 87 88 mm->idxunr = new_unrhdr(0, DRM_GEM_MAX_IDX, NULL); 89 return 0; 90 } 91 92 void 93 drm_gem_destroy(struct drm_device *dev) 94 { 95 struct drm_gem_mm *mm = dev->mm_private; 96 97 drm_ht_remove(&mm->offset_hash); 98 delete_unrhdr(mm->idxunr); 99 drm_free(mm, M_DRM); 100 dev->mm_private = NULL; 101 } 102 103 /** 104 * Initialize an already allocated GEM object of the specified size with 105 * shmfs backing store. 106 */ 107 int drm_gem_object_init(struct drm_device *dev, 108 struct drm_gem_object *obj, size_t size) 109 { 110 111 KASSERT((size & (PAGE_SIZE - 1)) == 0, 112 ("Bad size %ju", (uintmax_t)size)); 113 114 obj->dev = dev; 115 obj->vm_obj = default_pager_alloc(NULL, size, 116 VM_PROT_READ | VM_PROT_WRITE, 0); 117 118 kref_init(&obj->refcount); 119 atomic_set(&obj->handle_count, 0); 120 obj->size = size; 121 122 return (0); 123 } 124 125 /** 126 * Initialize an already allocated GEM object of the specified size with 127 * no GEM provided backing store. Instead the caller is responsible for 128 * backing the object and handling it. 129 */ 130 int drm_gem_private_object_init(struct drm_device *dev, 131 struct drm_gem_object *obj, size_t size) 132 { 133 134 KASSERT((size & (PAGE_SIZE - 1)) == 0, 135 ("Bad size %ju", (uintmax_t)size)); 136 137 obj->dev = dev; 138 obj->vm_obj = NULL; 139 140 kref_init(&obj->refcount); 141 atomic_set(&obj->handle_count, 0); 142 obj->size = size; 143 144 return (0); 145 } 146 147 148 struct drm_gem_object * 149 drm_gem_object_alloc(struct drm_device *dev, size_t size) 150 { 151 struct drm_gem_object *obj; 152 153 obj = kmalloc(sizeof(*obj), M_DRM, M_WAITOK | M_ZERO); 154 if (drm_gem_object_init(dev, obj, size) != 0) 155 goto free; 156 157 if (dev->driver->gem_init_object != NULL && 158 dev->driver->gem_init_object(obj) != 0) 159 goto dealloc; 160 return (obj); 161 dealloc: 162 vm_object_deallocate(obj->vm_obj); 163 free: 164 drm_free(obj, M_DRM); 165 return (NULL); 166 } 167 168 /** 169 * Called after the last reference to the object has been lost. 170 * Must be called holding struct_ mutex 171 * 172 * Frees the object 173 */ 174 void 175 drm_gem_object_free(struct kref *kref) 176 { 177 struct drm_gem_object *obj = (struct drm_gem_object *) kref; 178 struct drm_device *dev = obj->dev; 179 180 DRM_LOCK_ASSERT(dev); 181 if (dev->driver->gem_free_object != NULL) 182 dev->driver->gem_free_object(obj); 183 } 184 185 static void drm_gem_object_ref_bug(struct kref *list_kref) 186 { 187 panic("BUG"); 188 } 189 190 /** 191 * Called after the last handle to the object has been closed 192 * 193 * Removes any name for the object. Note that this must be 194 * called before drm_gem_object_free or we'll be touching 195 * freed memory 196 */ 197 void drm_gem_object_handle_free(struct drm_gem_object *obj) 198 { 199 struct drm_device *dev = obj->dev; 200 201 /* Remove any name for this object */ 202 lockmgr(&dev->object_name_lock, LK_EXCLUSIVE); 203 if (obj->name) { 204 idr_remove(&dev->object_name_idr, obj->name); 205 obj->name = 0; 206 lockmgr(&dev->object_name_lock, LK_RELEASE); 207 /* 208 * The object name held a reference to this object, drop 209 * that now. 210 * 211 * This cannot be the last reference, since the handle holds one too. 212 */ 213 kref_put(&obj->refcount, drm_gem_object_ref_bug); 214 } else 215 lockmgr(&dev->object_name_lock, LK_RELEASE); 216 217 } 218 219 /** 220 * Removes the mapping from handle to filp for this object. 221 */ 222 int 223 drm_gem_handle_delete(struct drm_file *filp, u32 handle) 224 { 225 struct drm_device *dev; 226 struct drm_gem_object *obj; 227 228 /* This is gross. The idr system doesn't let us try a delete and 229 * return an error code. It just spews if you fail at deleting. 230 * So, we have to grab a lock around finding the object and then 231 * doing the delete on it and dropping the refcount, or the user 232 * could race us to double-decrement the refcount and cause a 233 * use-after-free later. Given the frequency of our handle lookups, 234 * we may want to use ida for number allocation and a hash table 235 * for the pointers, anyway. 236 */ 237 lockmgr(&filp->table_lock, LK_EXCLUSIVE); 238 239 /* Check if we currently have a reference on the object */ 240 obj = idr_find(&filp->object_idr, handle); 241 if (obj == NULL) { 242 lockmgr(&filp->table_lock, LK_RELEASE); 243 return -EINVAL; 244 } 245 dev = obj->dev; 246 247 /* Release reference and decrement refcount. */ 248 idr_remove(&filp->object_idr, handle); 249 lockmgr(&filp->table_lock, LK_RELEASE); 250 251 if (dev->driver->gem_close_object) 252 dev->driver->gem_close_object(obj, filp); 253 drm_gem_object_handle_unreference_unlocked(obj); 254 255 return 0; 256 } 257 258 /** 259 * Create a handle for this object. This adds a handle reference 260 * to the object, which includes a regular reference count. Callers 261 * will likely want to dereference the object afterwards. 262 */ 263 int 264 drm_gem_handle_create(struct drm_file *file_priv, 265 struct drm_gem_object *obj, 266 u32 *handlep) 267 { 268 struct drm_device *dev = obj->dev; 269 int ret; 270 271 /* 272 * Get the user-visible handle using idr. 273 */ 274 again: 275 /* ensure there is space available to allocate a handle */ 276 if (idr_pre_get(&file_priv->object_idr, GFP_KERNEL) == 0) 277 return -ENOMEM; 278 279 /* do the allocation under our spinlock */ 280 lockmgr(&file_priv->table_lock, LK_EXCLUSIVE); 281 ret = idr_get_new_above(&file_priv->object_idr, obj, 1, (int *)handlep); 282 lockmgr(&file_priv->table_lock, LK_RELEASE); 283 if (ret == -EAGAIN) 284 goto again; 285 else if (ret) 286 return ret; 287 288 drm_gem_object_handle_reference(obj); 289 290 if (dev->driver->gem_open_object) { 291 ret = dev->driver->gem_open_object(obj, file_priv); 292 if (ret) { 293 drm_gem_handle_delete(file_priv, *handlep); 294 return ret; 295 } 296 } 297 298 return 0; 299 } 300 301 /** Returns a reference to the object named by the handle. */ 302 struct drm_gem_object * 303 drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, 304 u32 handle) 305 { 306 struct drm_gem_object *obj; 307 308 lockmgr(&filp->table_lock, LK_EXCLUSIVE); 309 310 /* Check if we currently have a reference on the object */ 311 obj = idr_find(&filp->object_idr, handle); 312 if (obj == NULL) { 313 lockmgr(&filp->table_lock, LK_RELEASE); 314 return NULL; 315 } 316 317 drm_gem_object_reference(obj); 318 319 lockmgr(&filp->table_lock, LK_RELEASE); 320 321 return obj; 322 } 323 324 int 325 drm_gem_close_ioctl(struct drm_device *dev, void *data, 326 struct drm_file *file_priv) 327 { 328 struct drm_gem_close *args; 329 330 if (!drm_core_check_feature(dev, DRIVER_GEM)) 331 return (ENODEV); 332 args = data; 333 334 return (drm_gem_handle_delete(file_priv, args->handle)); 335 } 336 337 /** 338 * Create a global name for an object, returning the name. 339 * 340 * Note that the name does not hold a reference; when the object 341 * is freed, the name goes away. 342 */ 343 int 344 drm_gem_flink_ioctl(struct drm_device *dev, void *data, 345 struct drm_file *file_priv) 346 { 347 struct drm_gem_flink *args = data; 348 struct drm_gem_object *obj; 349 int ret; 350 351 if (!drm_core_check_feature(dev, DRIVER_GEM)) 352 return -ENODEV; 353 354 obj = drm_gem_object_lookup(dev, file_priv, args->handle); 355 if (obj == NULL) 356 return -ENOENT; 357 358 again: 359 if (idr_pre_get(&dev->object_name_idr, GFP_KERNEL) == 0) { 360 ret = -ENOMEM; 361 goto err; 362 } 363 364 lockmgr(&dev->object_name_lock, LK_EXCLUSIVE); 365 if (!obj->name) { 366 ret = idr_get_new_above(&dev->object_name_idr, obj, 1, 367 &obj->name); 368 args->name = (uint64_t) obj->name; 369 lockmgr(&dev->object_name_lock, LK_RELEASE); 370 371 if (ret == -EAGAIN) 372 goto again; 373 else if (ret) 374 goto err; 375 376 /* Allocate a reference for the name table. */ 377 drm_gem_object_reference(obj); 378 } else { 379 args->name = (uint64_t) obj->name; 380 lockmgr(&dev->object_name_lock, LK_RELEASE); 381 ret = 0; 382 } 383 384 err: 385 drm_gem_object_unreference_unlocked(obj); 386 return ret; 387 } 388 389 /** 390 * Open an object using the global name, returning a handle and the size. 391 * 392 * This handle (of course) holds a reference to the object, so the object 393 * will not go away until the handle is deleted. 394 */ 395 int 396 drm_gem_open_ioctl(struct drm_device *dev, void *data, 397 struct drm_file *file_priv) 398 { 399 struct drm_gem_open *args = data; 400 struct drm_gem_object *obj; 401 int ret; 402 u32 handle; 403 404 #if 0 405 if (!drm_core_check_feature(dev, DRIVER_GEM)) 406 #endif 407 if (!(dev->driver->driver_features & DRIVER_GEM)) 408 return -ENODEV; 409 410 lockmgr(&dev->object_name_lock, LK_EXCLUSIVE); 411 obj = idr_find(&dev->object_name_idr, (int) args->name); 412 if (obj) 413 drm_gem_object_reference(obj); 414 lockmgr(&dev->object_name_lock, LK_RELEASE); 415 if (!obj) 416 return -ENOENT; 417 418 ret = drm_gem_handle_create(file_priv, obj, &handle); 419 drm_gem_object_unreference_unlocked(obj); 420 if (ret) 421 return ret; 422 423 args->handle = handle; 424 args->size = obj->size; 425 426 return 0; 427 } 428 429 /** 430 * Called at device open time, sets up the structure for handling refcounting 431 * of mm objects. 432 */ 433 void 434 drm_gem_open(struct drm_device *dev, struct drm_file *file_private) 435 { 436 idr_init(&file_private->object_idr); 437 lockinit(&file_private->table_lock, "fptab", 0, LK_CANRECURSE); 438 } 439 440 /** 441 * Called at device close to release the file's 442 * handle references on objects. 443 */ 444 static int 445 drm_gem_object_release_handle(int id, void *ptr, void *data) 446 { 447 struct drm_file *file_priv = data; 448 struct drm_gem_object *obj = ptr; 449 struct drm_device *dev = obj->dev; 450 451 if (dev->driver->gem_close_object) 452 dev->driver->gem_close_object(obj, file_priv); 453 454 drm_gem_object_handle_unreference_unlocked(obj); 455 456 return 0; 457 } 458 459 void 460 drm_gem_object_release(struct drm_gem_object *obj) 461 { 462 463 /* 464 * obj->vm_obj can be NULL for private gem objects. 465 */ 466 vm_object_deallocate(obj->vm_obj); 467 } 468 469 /** 470 * Called at close time when the filp is going away. 471 * 472 * Releases any remaining references on objects by this filp. 473 */ 474 void 475 drm_gem_release(struct drm_device *dev, struct drm_file *file_private) 476 { 477 idr_for_each(&file_private->object_idr, 478 &drm_gem_object_release_handle, file_private); 479 480 idr_remove_all(&file_private->object_idr); 481 idr_destroy(&file_private->object_idr); 482 } 483 484 static struct drm_gem_object * 485 drm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset) 486 { 487 struct drm_gem_object *obj; 488 struct drm_gem_mm *mm; 489 struct drm_hash_item *map_list; 490 491 if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY) 492 return (NULL); 493 offset &= ~DRM_GEM_MAPPING_KEY; 494 mm = dev->mm_private; 495 if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset), 496 &map_list) != 0) { 497 DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n", 498 (uintmax_t)offset); 499 return (NULL); 500 } 501 obj = container_of(map_list, struct drm_gem_object, map_list); 502 return (obj); 503 } 504 505 int 506 drm_gem_create_mmap_offset(struct drm_gem_object *obj) 507 { 508 struct drm_device *dev; 509 struct drm_gem_mm *mm; 510 int ret; 511 512 if (obj->on_map) 513 return (0); 514 dev = obj->dev; 515 mm = dev->mm_private; 516 ret = 0; 517 518 obj->map_list.key = alloc_unr(mm->idxunr); 519 ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list); 520 if (ret != 0) { 521 DRM_ERROR("failed to add to map hash\n"); 522 free_unr(mm->idxunr, obj->map_list.key); 523 return (ret); 524 } 525 obj->on_map = true; 526 return (0); 527 } 528 529 void 530 drm_gem_free_mmap_offset(struct drm_gem_object *obj) 531 { 532 struct drm_hash_item *list; 533 struct drm_gem_mm *mm; 534 535 if (!obj->on_map) 536 return; 537 mm = obj->dev->mm_private; 538 list = &obj->map_list; 539 540 drm_ht_remove_item(&mm->offset_hash, list); 541 free_unr(mm->idxunr, list->key); 542 obj->on_map = false; 543 } 544 545 int 546 drm_gem_mmap_single(struct drm_device *dev, vm_ooffset_t *offset, vm_size_t size, 547 struct vm_object **obj_res, int nprot) 548 { 549 struct drm_gem_object *gem_obj; 550 struct vm_object *vm_obj; 551 552 DRM_LOCK(dev); 553 gem_obj = drm_gem_object_from_offset(dev, *offset); 554 if (gem_obj == NULL) { 555 DRM_UNLOCK(dev); 556 return (ENODEV); 557 } 558 drm_gem_object_reference(gem_obj); 559 DRM_UNLOCK(dev); 560 vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE, 561 dev->driver->gem_pager_ops, size, nprot, 562 DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred); 563 if (vm_obj == NULL) { 564 drm_gem_object_unreference_unlocked(gem_obj); 565 return (EINVAL); 566 } 567 *offset = DRM_GEM_MAPPING_MAPOFF(*offset); 568 *obj_res = vm_obj; 569 return (0); 570 } 571 572 void 573 drm_gem_pager_dtr(void *handle) 574 { 575 struct drm_gem_object *obj; 576 struct drm_device *dev; 577 578 obj = handle; 579 dev = obj->dev; 580 581 DRM_LOCK(dev); 582 drm_gem_free_mmap_offset(obj); 583 drm_gem_object_unreference(obj); 584 DRM_UNLOCK(dev); 585 } 586