1 /************************************************************************** 2 * 3 * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24 * USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 /* 28 * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> 29 */ 30 /** @file ttm_ref_object.c 31 * 32 * Base- and reference object implementation for the various 33 * ttm objects. Implements reference counting, minimal security checks 34 * and release on file close. 35 */ 36 37 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 /** 42 * struct ttm_object_file 43 * 44 * @tdev: Pointer to the ttm_object_device. 45 * 46 * @lock: Lock that protects the ref_list list and the 47 * ref_hash hash tables. 48 * 49 * @ref_list: List of ttm_ref_objects to be destroyed at 50 * file release. 51 * 52 * @ref_hash: Hash tables of ref objects, one per ttm_ref_type, 53 * for fast lookup of ref objects given a base object. 54 */ 55 56 #define pr_fmt(fmt) "[TTM] " fmt 57 58 #include <dev/drm2/drmP.h> 59 #include <dev/drm2/drm.h> 60 #include <sys/rwlock.h> 61 #include <dev/drm2/ttm/ttm_object.h> 62 #include <dev/drm2/ttm/ttm_module.h> 63 64 struct ttm_object_file { 65 struct ttm_object_device *tdev; 66 struct rwlock lock; 67 struct list_head ref_list; 68 struct drm_open_hash ref_hash[TTM_REF_NUM]; 69 u_int refcount; 70 }; 71 72 /** 73 * struct ttm_object_device 74 * 75 * @object_lock: lock that protects the object_hash hash table. 76 * 77 * @object_hash: hash table for fast lookup of object global names. 78 * 79 * @object_count: Per device object count. 80 * 81 * This is the per-device data structure needed for ttm object management. 82 */ 83 84 struct ttm_object_device { 85 struct rwlock object_lock; 86 struct drm_open_hash object_hash; 87 atomic_t object_count; 88 struct ttm_mem_global *mem_glob; 89 }; 90 91 /** 92 * struct ttm_ref_object 93 * 94 * @hash: Hash entry for the per-file object reference hash. 95 * 96 * @head: List entry for the per-file list of ref-objects. 97 * 98 * @kref: Ref count. 99 * 100 * @obj: Base object this ref object is referencing. 101 * 102 * @ref_type: Type of ref object. 103 * 104 * This is similar to an idr object, but it also has a hash table entry 105 * that allows lookup with a pointer to the referenced object as a key. In 106 * that way, one can easily detect whether a base object is referenced by 107 * a particular ttm_object_file. It also carries a ref count to avoid creating 108 * multiple ref objects if a ttm_object_file references the same base 109 * object more than once. 110 */ 111 112 struct ttm_ref_object { 113 struct drm_hash_item hash; 114 struct list_head head; 115 u_int kref; 116 enum ttm_ref_type ref_type; 117 struct ttm_base_object *obj; 118 struct ttm_object_file *tfile; 119 }; 120 121 MALLOC_DEFINE(M_TTM_OBJ_FILE, "ttm_obj_file", "TTM File Objects"); 122 123 static inline struct ttm_object_file * 124 ttm_object_file_ref(struct ttm_object_file *tfile) 125 { 126 refcount_acquire(&tfile->refcount); 127 return tfile; 128 } 129 130 static void ttm_object_file_destroy(struct ttm_object_file *tfile) 131 { 132 133 free(tfile, M_TTM_OBJ_FILE); 134 } 135 136 137 static inline void ttm_object_file_unref(struct ttm_object_file **p_tfile) 138 { 139 struct ttm_object_file *tfile = *p_tfile; 140 141 *p_tfile = NULL; 142 if (refcount_release(&tfile->refcount)) 143 ttm_object_file_destroy(tfile); 144 } 145 146 147 int ttm_base_object_init(struct ttm_object_file *tfile, 148 struct ttm_base_object *base, 149 bool shareable, 150 enum ttm_object_type object_type, 151 void (*rcount_release) (struct ttm_base_object **), 152 void (*ref_obj_release) (struct ttm_base_object *, 153 enum ttm_ref_type ref_type)) 154 { 155 struct ttm_object_device *tdev = tfile->tdev; 156 int ret; 157 158 base->shareable = shareable; 159 base->tfile = ttm_object_file_ref(tfile); 160 base->refcount_release = rcount_release; 161 base->ref_obj_release = ref_obj_release; 162 base->object_type = object_type; 163 refcount_init(&base->refcount, 1); 164 rw_init(&tdev->object_lock, "ttmbao"); 165 rw_wlock(&tdev->object_lock); 166 ret = drm_ht_just_insert_please(&tdev->object_hash, 167 &base->hash, 168 (unsigned long)base, 31, 0, 0); 169 rw_wunlock(&tdev->object_lock); 170 if (unlikely(ret != 0)) 171 goto out_err0; 172 173 ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL); 174 if (unlikely(ret != 0)) 175 goto out_err1; 176 177 ttm_base_object_unref(&base); 178 179 return 0; 180 out_err1: 181 rw_wlock(&tdev->object_lock); 182 (void)drm_ht_remove_item(&tdev->object_hash, &base->hash); 183 rw_wunlock(&tdev->object_lock); 184 out_err0: 185 return ret; 186 } 187 188 static void ttm_release_base(struct ttm_base_object *base) 189 { 190 struct ttm_object_device *tdev = base->tfile->tdev; 191 192 (void)drm_ht_remove_item(&tdev->object_hash, &base->hash); 193 rw_wunlock(&tdev->object_lock); 194 /* 195 * Note: We don't use synchronize_rcu() here because it's far 196 * too slow. It's up to the user to free the object using 197 * call_rcu() or ttm_base_object_kfree(). 198 */ 199 200 if (base->refcount_release) { 201 ttm_object_file_unref(&base->tfile); 202 base->refcount_release(&base); 203 } 204 rw_wlock(&tdev->object_lock); 205 } 206 207 void ttm_base_object_unref(struct ttm_base_object **p_base) 208 { 209 struct ttm_base_object *base = *p_base; 210 struct ttm_object_device *tdev = base->tfile->tdev; 211 212 *p_base = NULL; 213 214 /* 215 * Need to take the lock here to avoid racing with 216 * users trying to look up the object. 217 */ 218 219 rw_wlock(&tdev->object_lock); 220 if (refcount_release(&base->refcount)) 221 ttm_release_base(base); 222 rw_wunlock(&tdev->object_lock); 223 } 224 225 struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile, 226 uint32_t key) 227 { 228 struct ttm_object_device *tdev = tfile->tdev; 229 struct ttm_base_object *base; 230 struct drm_hash_item *hash; 231 int ret; 232 233 rw_rlock(&tdev->object_lock); 234 ret = drm_ht_find_item(&tdev->object_hash, key, &hash); 235 236 if (ret == 0) { 237 base = drm_hash_entry(hash, struct ttm_base_object, hash); 238 refcount_acquire(&base->refcount); 239 } 240 rw_runlock(&tdev->object_lock); 241 242 if (unlikely(ret != 0)) 243 return NULL; 244 245 if (tfile != base->tfile && !base->shareable) { 246 printf("[TTM] Attempted access of non-shareable object %p\n", 247 base); 248 ttm_base_object_unref(&base); 249 return NULL; 250 } 251 252 return base; 253 } 254 255 MALLOC_DEFINE(M_TTM_OBJ_REF, "ttm_obj_ref", "TTM Ref Objects"); 256 257 int ttm_ref_object_add(struct ttm_object_file *tfile, 258 struct ttm_base_object *base, 259 enum ttm_ref_type ref_type, bool *existed) 260 { 261 struct drm_open_hash *ht = &tfile->ref_hash[ref_type]; 262 struct ttm_ref_object *ref; 263 struct drm_hash_item *hash; 264 struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob; 265 int ret = -EINVAL; 266 267 if (existed != NULL) 268 *existed = true; 269 270 while (ret == -EINVAL) { 271 rw_rlock(&tfile->lock); 272 ret = drm_ht_find_item(ht, base->hash.key, &hash); 273 274 if (ret == 0) { 275 ref = drm_hash_entry(hash, struct ttm_ref_object, hash); 276 refcount_acquire(&ref->kref); 277 rw_runlock(&tfile->lock); 278 break; 279 } 280 281 rw_runlock(&tfile->lock); 282 ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref), 283 false, false); 284 if (unlikely(ret != 0)) 285 return ret; 286 ref = malloc(sizeof(*ref), M_TTM_OBJ_REF, M_WAITOK); 287 if (unlikely(ref == NULL)) { 288 ttm_mem_global_free(mem_glob, sizeof(*ref)); 289 return -ENOMEM; 290 } 291 292 ref->hash.key = base->hash.key; 293 ref->obj = base; 294 ref->tfile = tfile; 295 ref->ref_type = ref_type; 296 refcount_init(&ref->kref, 1); 297 298 rw_wlock(&tfile->lock); 299 ret = drm_ht_insert_item(ht, &ref->hash); 300 301 if (ret == 0) { 302 list_add_tail(&ref->head, &tfile->ref_list); 303 refcount_acquire(&base->refcount); 304 rw_wunlock(&tfile->lock); 305 if (existed != NULL) 306 *existed = false; 307 break; 308 } 309 310 rw_wunlock(&tfile->lock); 311 MPASS(ret == -EINVAL); 312 313 ttm_mem_global_free(mem_glob, sizeof(*ref)); 314 free(ref, M_TTM_OBJ_REF); 315 } 316 317 return ret; 318 } 319 320 static void ttm_ref_object_release(struct ttm_ref_object *ref) 321 { 322 struct ttm_base_object *base = ref->obj; 323 struct ttm_object_file *tfile = ref->tfile; 324 struct drm_open_hash *ht; 325 struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob; 326 327 ht = &tfile->ref_hash[ref->ref_type]; 328 (void)drm_ht_remove_item(ht, &ref->hash); 329 list_del(&ref->head); 330 rw_wunlock(&tfile->lock); 331 332 if (ref->ref_type != TTM_REF_USAGE && base->ref_obj_release) 333 base->ref_obj_release(base, ref->ref_type); 334 335 ttm_base_object_unref(&ref->obj); 336 ttm_mem_global_free(mem_glob, sizeof(*ref)); 337 free(ref, M_TTM_OBJ_REF); 338 rw_wlock(&tfile->lock); 339 } 340 341 int ttm_ref_object_base_unref(struct ttm_object_file *tfile, 342 unsigned long key, enum ttm_ref_type ref_type) 343 { 344 struct drm_open_hash *ht = &tfile->ref_hash[ref_type]; 345 struct ttm_ref_object *ref; 346 struct drm_hash_item *hash; 347 int ret; 348 349 rw_wlock(&tfile->lock); 350 ret = drm_ht_find_item(ht, key, &hash); 351 if (unlikely(ret != 0)) { 352 rw_wunlock(&tfile->lock); 353 return -EINVAL; 354 } 355 ref = drm_hash_entry(hash, struct ttm_ref_object, hash); 356 if (refcount_release(&ref->kref)) 357 ttm_ref_object_release(ref); 358 rw_wunlock(&tfile->lock); 359 return 0; 360 } 361 362 void ttm_object_file_release(struct ttm_object_file **p_tfile) 363 { 364 struct ttm_ref_object *ref; 365 struct list_head *list; 366 unsigned int i; 367 struct ttm_object_file *tfile = *p_tfile; 368 369 *p_tfile = NULL; 370 rw_wlock(&tfile->lock); 371 372 /* 373 * Since we release the lock within the loop, we have to 374 * restart it from the beginning each time. 375 */ 376 377 while (!list_empty(&tfile->ref_list)) { 378 list = tfile->ref_list.next; 379 ref = list_entry(list, struct ttm_ref_object, head); 380 ttm_ref_object_release(ref); 381 } 382 383 for (i = 0; i < TTM_REF_NUM; ++i) 384 drm_ht_remove(&tfile->ref_hash[i]); 385 386 rw_wunlock(&tfile->lock); 387 ttm_object_file_unref(&tfile); 388 } 389 390 struct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev, 391 unsigned int hash_order) 392 { 393 struct ttm_object_file *tfile; 394 unsigned int i; 395 unsigned int j = 0; 396 int ret; 397 398 tfile = malloc(sizeof(*tfile), M_TTM_OBJ_FILE, M_WAITOK); 399 rw_init(&tfile->lock, "ttmfo"); 400 tfile->tdev = tdev; 401 refcount_init(&tfile->refcount, 1); 402 INIT_LIST_HEAD(&tfile->ref_list); 403 404 for (i = 0; i < TTM_REF_NUM; ++i) { 405 ret = drm_ht_create(&tfile->ref_hash[i], hash_order); 406 if (ret) { 407 j = i; 408 goto out_err; 409 } 410 } 411 412 return tfile; 413 out_err: 414 for (i = 0; i < j; ++i) 415 drm_ht_remove(&tfile->ref_hash[i]); 416 417 free(tfile, M_TTM_OBJ_FILE); 418 419 return NULL; 420 } 421 422 MALLOC_DEFINE(M_TTM_OBJ_DEV, "ttm_obj_dev", "TTM Device Objects"); 423 424 struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global 425 *mem_glob, 426 unsigned int hash_order) 427 { 428 struct ttm_object_device *tdev; 429 int ret; 430 431 tdev = malloc(sizeof(*tdev), M_TTM_OBJ_DEV, M_WAITOK); 432 tdev->mem_glob = mem_glob; 433 rw_init(&tdev->object_lock, "ttmdo"); 434 atomic_set(&tdev->object_count, 0); 435 ret = drm_ht_create(&tdev->object_hash, hash_order); 436 437 if (ret == 0) 438 return tdev; 439 440 free(tdev, M_TTM_OBJ_DEV); 441 return NULL; 442 } 443 444 void ttm_object_device_release(struct ttm_object_device **p_tdev) 445 { 446 struct ttm_object_device *tdev = *p_tdev; 447 448 *p_tdev = NULL; 449 450 rw_wlock(&tdev->object_lock); 451 drm_ht_remove(&tdev->object_hash); 452 rw_wunlock(&tdev->object_lock); 453 454 free(tdev, M_TTM_OBJ_DEV); 455 } 456