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