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