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