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 <dev/drm/drmP.h> 45 #include <dev/drm/drm.h> 46 #include <dev/drm/drm_sarea.h> 47 48 /* 49 * We make up offsets for buffer objects so we can recognize them at 50 * mmap time. 51 */ 52 53 /* pgoff in mmap is an unsigned long, so we need to make sure that 54 * the faked up offset will fit 55 */ 56 57 #if ULONG_MAX == UINT64_MAX 58 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) 59 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16) 60 #else 61 #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFUL >> PAGE_SHIFT) + 1) 62 #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFUL >> PAGE_SHIFT) * 16) 63 #endif 64 65 int 66 drm_gem_init(struct drm_device *dev) 67 { 68 struct drm_gem_mm *mm; 69 70 drm_gem_names_init(&dev->object_names); 71 mm = kmalloc(sizeof(*mm), DRM_MEM_DRIVER, M_WAITOK); 72 dev->mm_private = mm; 73 if (drm_ht_create(&mm->offset_hash, 19) != 0) { 74 drm_free(mm, DRM_MEM_DRIVER); 75 return (ENOMEM); 76 } 77 mm->idxunr = new_unrhdr(0, DRM_GEM_MAX_IDX, NULL); 78 return (0); 79 } 80 81 void 82 drm_gem_destroy(struct drm_device *dev) 83 { 84 struct drm_gem_mm *mm; 85 86 mm = dev->mm_private; 87 dev->mm_private = NULL; 88 drm_ht_remove(&mm->offset_hash); 89 delete_unrhdr(mm->idxunr); 90 drm_free(mm, DRM_MEM_DRIVER); 91 drm_gem_names_fini(&dev->object_names); 92 } 93 94 int 95 drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, 96 size_t size) 97 { 98 99 KASSERT((size & (PAGE_SIZE - 1)) == 0, 100 ("Bad size %ju", (uintmax_t)size)); 101 102 obj->dev = dev; 103 obj->vm_obj = default_pager_alloc(NULL, size, 104 VM_PROT_READ | VM_PROT_WRITE, 0); 105 106 obj->refcount = 1; 107 obj->handle_count = 0; 108 obj->size = size; 109 110 return (0); 111 } 112 113 int 114 drm_gem_private_object_init(struct drm_device *dev, struct drm_gem_object *obj, 115 size_t size) 116 { 117 118 KASSERT((size & (PAGE_SIZE - 1)) == 0, 119 ("Bad size %ju", (uintmax_t)size)); 120 121 obj->dev = dev; 122 obj->vm_obj = NULL; 123 124 obj->refcount = 1; 125 atomic_store_rel_int(&obj->handle_count, 0); 126 obj->size = size; 127 128 return (0); 129 } 130 131 132 struct drm_gem_object * 133 drm_gem_object_alloc(struct drm_device *dev, size_t size) 134 { 135 struct drm_gem_object *obj; 136 137 obj = kmalloc(sizeof(*obj), DRM_MEM_DRIVER, M_WAITOK | M_ZERO); 138 if (drm_gem_object_init(dev, obj, size) != 0) 139 goto free; 140 141 if (dev->driver->gem_init_object != NULL && 142 dev->driver->gem_init_object(obj) != 0) 143 goto dealloc; 144 return (obj); 145 dealloc: 146 vm_object_deallocate(obj->vm_obj); 147 free: 148 drm_free(obj, DRM_MEM_DRIVER); 149 return (NULL); 150 } 151 152 void 153 drm_gem_object_free(struct drm_gem_object *obj) 154 { 155 struct drm_device *dev; 156 157 dev = obj->dev; 158 DRM_LOCK_ASSERT(dev); 159 if (dev->driver->gem_free_object != NULL) 160 dev->driver->gem_free_object(obj); 161 } 162 163 void 164 drm_gem_object_reference(struct drm_gem_object *obj) 165 { 166 167 KASSERT(obj->refcount > 0, ("Dangling obj %p", obj)); 168 refcount_acquire(&obj->refcount); 169 } 170 171 void 172 drm_gem_object_unreference(struct drm_gem_object *obj) 173 { 174 175 if (obj == NULL) 176 return; 177 if (refcount_release(&obj->refcount)) 178 drm_gem_object_free(obj); 179 } 180 181 void 182 drm_gem_object_unreference_unlocked(struct drm_gem_object *obj) 183 { 184 struct drm_device *dev; 185 186 if (obj == NULL) 187 return; 188 dev = obj->dev; 189 DRM_LOCK(dev); 190 drm_gem_object_unreference(obj); 191 DRM_UNLOCK(dev); 192 } 193 194 void 195 drm_gem_object_handle_reference(struct drm_gem_object *obj) 196 { 197 198 drm_gem_object_reference(obj); 199 atomic_add_rel_int(&obj->handle_count, 1); 200 } 201 202 void 203 drm_gem_object_handle_free(struct drm_gem_object *obj) 204 { 205 struct drm_device *dev; 206 struct drm_gem_object *obj1; 207 208 dev = obj->dev; 209 if (obj->name != 0) { 210 obj1 = drm_gem_names_remove(&dev->object_names, obj->name); 211 obj->name = 0; 212 drm_gem_object_unreference(obj1); 213 } 214 } 215 216 void 217 drm_gem_object_handle_unreference(struct drm_gem_object *obj) 218 { 219 220 if (obj == NULL || 221 atomic_load_acq_int(&obj->handle_count) == 0) 222 return; 223 224 if (atomic_fetchadd_int(&obj->handle_count, -1) == 1) 225 drm_gem_object_handle_free(obj); 226 drm_gem_object_unreference(obj); 227 } 228 229 void 230 drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) 231 { 232 233 if (obj == NULL || 234 atomic_load_acq_int(&obj->handle_count) == 0) 235 return; 236 237 if (atomic_fetchadd_int(&obj->handle_count, -1) == 1) 238 drm_gem_object_handle_free(obj); 239 drm_gem_object_unreference_unlocked(obj); 240 } 241 242 int 243 drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj, 244 uint32_t *handle) 245 { 246 struct drm_device *dev = obj->dev; 247 int ret; 248 249 ret = drm_gem_name_create(&file_priv->object_names, obj, handle); 250 if (ret != 0) 251 return (ret); 252 drm_gem_object_handle_reference(obj); 253 254 if (dev->driver->gem_open_object) { 255 ret = dev->driver->gem_open_object(obj, file_priv); 256 if (ret) { 257 drm_gem_handle_delete(file_priv, *handle); 258 return ret; 259 } 260 } 261 262 return (0); 263 } 264 265 int 266 drm_gem_handle_delete(struct drm_file *file_priv, uint32_t handle) 267 { 268 struct drm_device *dev; 269 struct drm_gem_object *obj; 270 271 obj = drm_gem_names_remove(&file_priv->object_names, handle); 272 if (obj == NULL) 273 return (EINVAL); 274 275 dev = obj->dev; 276 if (dev->driver->gem_close_object) 277 dev->driver->gem_close_object(obj, file_priv); 278 drm_gem_object_handle_unreference_unlocked(obj); 279 280 return (0); 281 } 282 283 void 284 drm_gem_object_release(struct drm_gem_object *obj) 285 { 286 287 /* 288 * obj->vm_obj can be NULL for private gem objects. 289 */ 290 vm_object_deallocate(obj->vm_obj); 291 } 292 293 int 294 drm_gem_open_ioctl(struct drm_device *dev, void *data, 295 struct drm_file *file_priv) 296 { 297 struct drm_gem_open *args; 298 struct drm_gem_object *obj; 299 int ret; 300 uint32_t handle; 301 302 if (!drm_core_check_feature(dev, DRIVER_GEM)) 303 return (ENODEV); 304 args = data; 305 306 obj = drm_gem_name_ref(&dev->object_names, args->name, 307 (void (*)(void *))drm_gem_object_reference); 308 if (obj == NULL) 309 return (ENOENT); 310 handle = 0; 311 ret = drm_gem_handle_create(file_priv, obj, &handle); 312 drm_gem_object_unreference_unlocked(obj); 313 if (ret != 0) 314 return (ret); 315 316 args->handle = handle; 317 args->size = obj->size; 318 319 return (0); 320 } 321 322 void 323 drm_gem_open(struct drm_device *dev, struct drm_file *file_priv) 324 { 325 326 drm_gem_names_init(&file_priv->object_names); 327 } 328 329 static int 330 drm_gem_object_release_handle(uint32_t name, void *ptr, void *arg) 331 { 332 struct drm_file *file_priv; 333 struct drm_gem_object *obj; 334 struct drm_device *dev; 335 336 file_priv = arg; 337 obj = ptr; 338 dev = obj->dev; 339 340 if (dev->driver->gem_close_object) 341 dev->driver->gem_close_object(obj, file_priv); 342 343 drm_gem_object_handle_unreference(obj); 344 return (0); 345 } 346 347 void 348 drm_gem_release(struct drm_device *dev, struct drm_file *file_priv) 349 { 350 351 drm_gem_names_foreach(&file_priv->object_names, 352 drm_gem_object_release_handle, file_priv); 353 drm_gem_names_fini(&file_priv->object_names); 354 } 355 356 int 357 drm_gem_close_ioctl(struct drm_device *dev, void *data, 358 struct drm_file *file_priv) 359 { 360 struct drm_gem_close *args; 361 362 if (!drm_core_check_feature(dev, DRIVER_GEM)) 363 return (ENODEV); 364 args = data; 365 366 return (drm_gem_handle_delete(file_priv, args->handle)); 367 } 368 369 int 370 drm_gem_flink_ioctl(struct drm_device *dev, void *data, 371 struct drm_file *file_priv) 372 { 373 struct drm_gem_flink *args; 374 struct drm_gem_object *obj; 375 int error; 376 377 if (!drm_core_check_feature(dev, DRIVER_GEM)) 378 return (ENODEV); 379 args = data; 380 381 obj = drm_gem_name_ref(&file_priv->object_names, args->handle, 382 (void (*)(void *))drm_gem_object_reference); 383 if (obj == NULL) 384 return (ENOENT); 385 error = drm_gem_name_create(&dev->object_names, obj, &obj->name); 386 if (error != 0) { 387 if (error == EALREADY) 388 error = 0; 389 drm_gem_object_unreference_unlocked(obj); 390 } 391 if (error == 0) 392 args->name = obj->name; 393 return (error); 394 } 395 396 struct drm_gem_object * 397 drm_gem_object_lookup(struct drm_device *dev, struct drm_file *file_priv, 398 uint32_t handle) 399 { 400 struct drm_gem_object *obj; 401 402 obj = drm_gem_name_ref(&file_priv->object_names, handle, 403 (void (*)(void *))drm_gem_object_reference); 404 return (obj); 405 } 406 407 static struct drm_gem_object * 408 drm_gem_object_from_offset(struct drm_device *dev, vm_ooffset_t offset) 409 { 410 struct drm_gem_object *obj; 411 struct drm_gem_mm *mm; 412 struct drm_hash_item *map_list; 413 414 if ((offset & DRM_GEM_MAPPING_MASK) != DRM_GEM_MAPPING_KEY) 415 return (NULL); 416 offset &= ~DRM_GEM_MAPPING_KEY; 417 mm = dev->mm_private; 418 if (drm_ht_find_item(&mm->offset_hash, DRM_GEM_MAPPING_IDX(offset), 419 &map_list) != 0) { 420 DRM_DEBUG("drm_gem_object_from_offset: offset 0x%jx obj not found\n", 421 (uintmax_t)offset); 422 return (NULL); 423 } 424 obj = container_of(map_list, struct drm_gem_object, map_list); 425 return (obj); 426 } 427 428 int 429 drm_gem_create_mmap_offset(struct drm_gem_object *obj) 430 { 431 struct drm_device *dev; 432 struct drm_gem_mm *mm; 433 int ret; 434 435 if (obj->on_map) 436 return (0); 437 dev = obj->dev; 438 mm = dev->mm_private; 439 ret = 0; 440 441 obj->map_list.key = alloc_unr(mm->idxunr); 442 ret = drm_ht_insert_item(&mm->offset_hash, &obj->map_list); 443 if (ret != 0) { 444 DRM_ERROR("failed to add to map hash\n"); 445 free_unr(mm->idxunr, obj->map_list.key); 446 return (ret); 447 } 448 obj->on_map = true; 449 return (0); 450 } 451 452 void 453 drm_gem_free_mmap_offset(struct drm_gem_object *obj) 454 { 455 struct drm_hash_item *list; 456 struct drm_gem_mm *mm; 457 458 if (!obj->on_map) 459 return; 460 mm = obj->dev->mm_private; 461 list = &obj->map_list; 462 463 drm_ht_remove_item(&mm->offset_hash, list); 464 free_unr(mm->idxunr, list->key); 465 obj->on_map = false; 466 } 467 468 int 469 drm_gem_mmap_single(struct drm_device *dev, vm_ooffset_t *offset, vm_size_t size, 470 struct vm_object **obj_res, int nprot) 471 { 472 struct drm_gem_object *gem_obj; 473 struct vm_object *vm_obj; 474 475 DRM_LOCK(dev); 476 gem_obj = drm_gem_object_from_offset(dev, *offset); 477 if (gem_obj == NULL) { 478 DRM_UNLOCK(dev); 479 return (ENODEV); 480 } 481 drm_gem_object_reference(gem_obj); 482 DRM_UNLOCK(dev); 483 vm_obj = cdev_pager_allocate(gem_obj, OBJT_MGTDEVICE, 484 dev->driver->gem_pager_ops, size, nprot, 485 DRM_GEM_MAPPING_MAPOFF(*offset), curthread->td_ucred); 486 if (vm_obj == NULL) { 487 drm_gem_object_unreference_unlocked(gem_obj); 488 return (EINVAL); 489 } 490 *offset = DRM_GEM_MAPPING_MAPOFF(*offset); 491 *obj_res = vm_obj; 492 return (0); 493 } 494 495 void 496 drm_gem_pager_dtr(void *handle) 497 { 498 struct drm_gem_object *obj; 499 struct drm_device *dev; 500 501 obj = handle; 502 dev = obj->dev; 503 504 DRM_LOCK(dev); 505 drm_gem_free_mmap_offset(obj); 506 drm_gem_object_unreference(obj); 507 DRM_UNLOCK(dev); 508 } 509