1247835Skib/************************************************************************** 2247835Skib * 3247835Skib * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA 4247835Skib * All Rights Reserved. 5247835Skib * 6247835Skib * Permission is hereby granted, free of charge, to any person obtaining a 7247835Skib * copy of this software and associated documentation files (the 8247835Skib * "Software"), to deal in the Software without restriction, including 9247835Skib * without limitation the rights to use, copy, modify, merge, publish, 10247835Skib * distribute, sub license, and/or sell copies of the Software, and to 11247835Skib * permit persons to whom the Software is furnished to do so, subject to 12247835Skib * the following conditions: 13247835Skib * 14247835Skib * The above copyright notice and this permission notice (including the 15247835Skib * next paragraph) shall be included in all copies or substantial portions 16247835Skib * of the Software. 17247835Skib * 18247835Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19247835Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20247835Skib * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21247835Skib * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 22247835Skib * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23247835Skib * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24247835Skib * USE OR OTHER DEALINGS IN THE SOFTWARE. 25247835Skib * 26247835Skib **************************************************************************/ 27247835Skib/* 28247835Skib * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> 29247835Skib */ 30247835Skib/** @file ttm_ref_object.c 31247835Skib * 32247835Skib * Base- and reference object implementation for the various 33247835Skib * ttm objects. Implements reference counting, minimal security checks 34247835Skib * and release on file close. 35247835Skib */ 36247835Skib 37247835Skib 38247835Skib#include <sys/cdefs.h> 39247835Skib__FBSDID("$FreeBSD$"); 40247835Skib 41247835Skib/** 42247835Skib * struct ttm_object_file 43247835Skib * 44247835Skib * @tdev: Pointer to the ttm_object_device. 45247835Skib * 46247835Skib * @lock: Lock that protects the ref_list list and the 47247835Skib * ref_hash hash tables. 48247835Skib * 49247835Skib * @ref_list: List of ttm_ref_objects to be destroyed at 50247835Skib * file release. 51247835Skib * 52247835Skib * @ref_hash: Hash tables of ref objects, one per ttm_ref_type, 53247835Skib * for fast lookup of ref objects given a base object. 54247835Skib */ 55247835Skib 56247835Skib#define pr_fmt(fmt) "[TTM] " fmt 57247835Skib 58247835Skib#include <dev/drm2/drmP.h> 59247835Skib#include <dev/drm2/drm.h> 60247835Skib#include <sys/rwlock.h> 61247835Skib#include <dev/drm2/ttm/ttm_object.h> 62247835Skib#include <dev/drm2/ttm/ttm_module.h> 63247835Skib 64247835Skibstruct ttm_object_file { 65247835Skib struct ttm_object_device *tdev; 66247835Skib struct rwlock lock; 67247835Skib struct list_head ref_list; 68247835Skib struct drm_open_hash ref_hash[TTM_REF_NUM]; 69247835Skib u_int refcount; 70247835Skib}; 71247835Skib 72247835Skib/** 73247835Skib * struct ttm_object_device 74247835Skib * 75247835Skib * @object_lock: lock that protects the object_hash hash table. 76247835Skib * 77247835Skib * @object_hash: hash table for fast lookup of object global names. 78247835Skib * 79247835Skib * @object_count: Per device object count. 80247835Skib * 81247835Skib * This is the per-device data structure needed for ttm object management. 82247835Skib */ 83247835Skib 84247835Skibstruct ttm_object_device { 85247835Skib struct rwlock object_lock; 86247835Skib struct drm_open_hash object_hash; 87247835Skib atomic_t object_count; 88247835Skib struct ttm_mem_global *mem_glob; 89247835Skib}; 90247835Skib 91247835Skib/** 92247835Skib * struct ttm_ref_object 93247835Skib * 94247835Skib * @hash: Hash entry for the per-file object reference hash. 95247835Skib * 96247835Skib * @head: List entry for the per-file list of ref-objects. 97247835Skib * 98247835Skib * @kref: Ref count. 99247835Skib * 100247835Skib * @obj: Base object this ref object is referencing. 101247835Skib * 102247835Skib * @ref_type: Type of ref object. 103247835Skib * 104247835Skib * This is similar to an idr object, but it also has a hash table entry 105247835Skib * that allows lookup with a pointer to the referenced object as a key. In 106247835Skib * that way, one can easily detect whether a base object is referenced by 107247835Skib * a particular ttm_object_file. It also carries a ref count to avoid creating 108247835Skib * multiple ref objects if a ttm_object_file references the same base 109247835Skib * object more than once. 110247835Skib */ 111247835Skib 112247835Skibstruct ttm_ref_object { 113247835Skib struct drm_hash_item hash; 114247835Skib struct list_head head; 115247835Skib u_int kref; 116247835Skib enum ttm_ref_type ref_type; 117247835Skib struct ttm_base_object *obj; 118247835Skib struct ttm_object_file *tfile; 119247835Skib}; 120247835Skib 121247835SkibMALLOC_DEFINE(M_TTM_OBJ_FILE, "ttm_obj_file", "TTM File Objects"); 122247835Skib 123247835Skibstatic inline struct ttm_object_file * 124247835Skibttm_object_file_ref(struct ttm_object_file *tfile) 125247835Skib{ 126247835Skib refcount_acquire(&tfile->refcount); 127247835Skib return tfile; 128247835Skib} 129247835Skib 130247835Skibstatic void ttm_object_file_destroy(struct ttm_object_file *tfile) 131247835Skib{ 132247835Skib 133247835Skib free(tfile, M_TTM_OBJ_FILE); 134247835Skib} 135247835Skib 136247835Skib 137247835Skibstatic inline void ttm_object_file_unref(struct ttm_object_file **p_tfile) 138247835Skib{ 139247835Skib struct ttm_object_file *tfile = *p_tfile; 140247835Skib 141247835Skib *p_tfile = NULL; 142247835Skib if (refcount_release(&tfile->refcount)) 143247835Skib ttm_object_file_destroy(tfile); 144247835Skib} 145247835Skib 146247835Skib 147247835Skibint ttm_base_object_init(struct ttm_object_file *tfile, 148247835Skib struct ttm_base_object *base, 149247835Skib bool shareable, 150247835Skib enum ttm_object_type object_type, 151247835Skib void (*rcount_release) (struct ttm_base_object **), 152247835Skib void (*ref_obj_release) (struct ttm_base_object *, 153247835Skib enum ttm_ref_type ref_type)) 154247835Skib{ 155247835Skib struct ttm_object_device *tdev = tfile->tdev; 156247835Skib int ret; 157247835Skib 158247835Skib base->shareable = shareable; 159247835Skib base->tfile = ttm_object_file_ref(tfile); 160247835Skib base->refcount_release = rcount_release; 161247835Skib base->ref_obj_release = ref_obj_release; 162247835Skib base->object_type = object_type; 163247835Skib refcount_init(&base->refcount, 1); 164247835Skib rw_init(&tdev->object_lock, "ttmbao"); 165247835Skib rw_wlock(&tdev->object_lock); 166247835Skib ret = drm_ht_just_insert_please(&tdev->object_hash, 167247835Skib &base->hash, 168247835Skib (unsigned long)base, 31, 0, 0); 169247835Skib rw_wunlock(&tdev->object_lock); 170247835Skib if (unlikely(ret != 0)) 171247835Skib goto out_err0; 172247835Skib 173247835Skib ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL); 174247835Skib if (unlikely(ret != 0)) 175247835Skib goto out_err1; 176247835Skib 177247835Skib ttm_base_object_unref(&base); 178247835Skib 179247835Skib return 0; 180247835Skibout_err1: 181247835Skib rw_wlock(&tdev->object_lock); 182247835Skib (void)drm_ht_remove_item(&tdev->object_hash, &base->hash); 183247835Skib rw_wunlock(&tdev->object_lock); 184247835Skibout_err0: 185247835Skib return ret; 186247835Skib} 187247835Skib 188247835Skibstatic void ttm_release_base(struct ttm_base_object *base) 189247835Skib{ 190247835Skib struct ttm_object_device *tdev = base->tfile->tdev; 191247835Skib 192247835Skib (void)drm_ht_remove_item(&tdev->object_hash, &base->hash); 193247835Skib rw_wunlock(&tdev->object_lock); 194247835Skib /* 195247835Skib * Note: We don't use synchronize_rcu() here because it's far 196247835Skib * too slow. It's up to the user to free the object using 197247835Skib * call_rcu() or ttm_base_object_kfree(). 198247835Skib */ 199247835Skib 200247835Skib if (base->refcount_release) { 201247835Skib ttm_object_file_unref(&base->tfile); 202247835Skib base->refcount_release(&base); 203247835Skib } 204247835Skib rw_wlock(&tdev->object_lock); 205247835Skib} 206247835Skib 207247835Skibvoid ttm_base_object_unref(struct ttm_base_object **p_base) 208247835Skib{ 209247835Skib struct ttm_base_object *base = *p_base; 210247835Skib struct ttm_object_device *tdev = base->tfile->tdev; 211247835Skib 212247835Skib *p_base = NULL; 213247835Skib 214247835Skib /* 215247835Skib * Need to take the lock here to avoid racing with 216247835Skib * users trying to look up the object. 217247835Skib */ 218247835Skib 219247835Skib rw_wlock(&tdev->object_lock); 220247835Skib if (refcount_release(&base->refcount)) 221247835Skib ttm_release_base(base); 222247835Skib rw_wunlock(&tdev->object_lock); 223247835Skib} 224247835Skib 225247835Skibstruct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile, 226247835Skib uint32_t key) 227247835Skib{ 228247835Skib struct ttm_object_device *tdev = tfile->tdev; 229247835Skib struct ttm_base_object *base; 230247835Skib struct drm_hash_item *hash; 231247835Skib int ret; 232247835Skib 233247835Skib rw_rlock(&tdev->object_lock); 234247835Skib ret = drm_ht_find_item(&tdev->object_hash, key, &hash); 235247835Skib 236247835Skib if (ret == 0) { 237247835Skib base = drm_hash_entry(hash, struct ttm_base_object, hash); 238247835Skib refcount_acquire(&base->refcount); 239247835Skib } 240247835Skib rw_runlock(&tdev->object_lock); 241247835Skib 242247835Skib if (unlikely(ret != 0)) 243247835Skib return NULL; 244247835Skib 245247835Skib if (tfile != base->tfile && !base->shareable) { 246247835Skib printf("[TTM] Attempted access of non-shareable object %p\n", 247247835Skib base); 248247835Skib ttm_base_object_unref(&base); 249247835Skib return NULL; 250247835Skib } 251247835Skib 252247835Skib return base; 253247835Skib} 254247835Skib 255247835SkibMALLOC_DEFINE(M_TTM_OBJ_REF, "ttm_obj_ref", "TTM Ref Objects"); 256247835Skib 257247835Skibint ttm_ref_object_add(struct ttm_object_file *tfile, 258247835Skib struct ttm_base_object *base, 259247835Skib enum ttm_ref_type ref_type, bool *existed) 260247835Skib{ 261247835Skib struct drm_open_hash *ht = &tfile->ref_hash[ref_type]; 262247835Skib struct ttm_ref_object *ref; 263247835Skib struct drm_hash_item *hash; 264247835Skib struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob; 265247835Skib int ret = -EINVAL; 266247835Skib 267247835Skib if (existed != NULL) 268247835Skib *existed = true; 269247835Skib 270247835Skib while (ret == -EINVAL) { 271247835Skib rw_rlock(&tfile->lock); 272247835Skib ret = drm_ht_find_item(ht, base->hash.key, &hash); 273247835Skib 274247835Skib if (ret == 0) { 275247835Skib ref = drm_hash_entry(hash, struct ttm_ref_object, hash); 276247835Skib refcount_acquire(&ref->kref); 277247835Skib rw_runlock(&tfile->lock); 278247835Skib break; 279247835Skib } 280247835Skib 281247835Skib rw_runlock(&tfile->lock); 282247835Skib ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref), 283247835Skib false, false); 284247835Skib if (unlikely(ret != 0)) 285247835Skib return ret; 286247835Skib ref = malloc(sizeof(*ref), M_TTM_OBJ_REF, M_WAITOK); 287247835Skib if (unlikely(ref == NULL)) { 288247835Skib ttm_mem_global_free(mem_glob, sizeof(*ref)); 289247835Skib return -ENOMEM; 290247835Skib } 291247835Skib 292247835Skib ref->hash.key = base->hash.key; 293247835Skib ref->obj = base; 294247835Skib ref->tfile = tfile; 295247835Skib ref->ref_type = ref_type; 296247835Skib refcount_init(&ref->kref, 1); 297247835Skib 298247835Skib rw_wlock(&tfile->lock); 299247835Skib ret = drm_ht_insert_item(ht, &ref->hash); 300247835Skib 301247835Skib if (ret == 0) { 302247835Skib list_add_tail(&ref->head, &tfile->ref_list); 303247835Skib refcount_acquire(&base->refcount); 304247835Skib rw_wunlock(&tfile->lock); 305247835Skib if (existed != NULL) 306247835Skib *existed = false; 307247835Skib break; 308247835Skib } 309247835Skib 310247835Skib rw_wunlock(&tfile->lock); 311247835Skib MPASS(ret == -EINVAL); 312247835Skib 313247835Skib ttm_mem_global_free(mem_glob, sizeof(*ref)); 314247835Skib free(ref, M_TTM_OBJ_REF); 315247835Skib } 316247835Skib 317247835Skib return ret; 318247835Skib} 319247835Skib 320247835Skibstatic void ttm_ref_object_release(struct ttm_ref_object *ref) 321247835Skib{ 322247835Skib struct ttm_base_object *base = ref->obj; 323247835Skib struct ttm_object_file *tfile = ref->tfile; 324247835Skib struct drm_open_hash *ht; 325247835Skib struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob; 326247835Skib 327247835Skib ht = &tfile->ref_hash[ref->ref_type]; 328247835Skib (void)drm_ht_remove_item(ht, &ref->hash); 329247835Skib list_del(&ref->head); 330247835Skib rw_wunlock(&tfile->lock); 331247835Skib 332247835Skib if (ref->ref_type != TTM_REF_USAGE && base->ref_obj_release) 333247835Skib base->ref_obj_release(base, ref->ref_type); 334247835Skib 335247835Skib ttm_base_object_unref(&ref->obj); 336247835Skib ttm_mem_global_free(mem_glob, sizeof(*ref)); 337247835Skib free(ref, M_TTM_OBJ_REF); 338247835Skib rw_wlock(&tfile->lock); 339247835Skib} 340247835Skib 341247835Skibint ttm_ref_object_base_unref(struct ttm_object_file *tfile, 342247835Skib unsigned long key, enum ttm_ref_type ref_type) 343247835Skib{ 344247835Skib struct drm_open_hash *ht = &tfile->ref_hash[ref_type]; 345247835Skib struct ttm_ref_object *ref; 346247835Skib struct drm_hash_item *hash; 347247835Skib int ret; 348247835Skib 349247835Skib rw_wlock(&tfile->lock); 350247835Skib ret = drm_ht_find_item(ht, key, &hash); 351247835Skib if (unlikely(ret != 0)) { 352247835Skib rw_wunlock(&tfile->lock); 353247835Skib return -EINVAL; 354247835Skib } 355247835Skib ref = drm_hash_entry(hash, struct ttm_ref_object, hash); 356247835Skib if (refcount_release(&ref->kref)) 357247835Skib ttm_ref_object_release(ref); 358247835Skib rw_wunlock(&tfile->lock); 359247835Skib return 0; 360247835Skib} 361247835Skib 362247835Skibvoid ttm_object_file_release(struct ttm_object_file **p_tfile) 363247835Skib{ 364247835Skib struct ttm_ref_object *ref; 365247835Skib struct list_head *list; 366247835Skib unsigned int i; 367247835Skib struct ttm_object_file *tfile = *p_tfile; 368247835Skib 369247835Skib *p_tfile = NULL; 370247835Skib rw_wlock(&tfile->lock); 371247835Skib 372247835Skib /* 373247835Skib * Since we release the lock within the loop, we have to 374247835Skib * restart it from the beginning each time. 375247835Skib */ 376247835Skib 377247835Skib while (!list_empty(&tfile->ref_list)) { 378247835Skib list = tfile->ref_list.next; 379247835Skib ref = list_entry(list, struct ttm_ref_object, head); 380247835Skib ttm_ref_object_release(ref); 381247835Skib } 382247835Skib 383247835Skib for (i = 0; i < TTM_REF_NUM; ++i) 384247835Skib drm_ht_remove(&tfile->ref_hash[i]); 385247835Skib 386247835Skib rw_wunlock(&tfile->lock); 387247835Skib ttm_object_file_unref(&tfile); 388247835Skib} 389247835Skib 390247835Skibstruct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev, 391247835Skib unsigned int hash_order) 392247835Skib{ 393247835Skib struct ttm_object_file *tfile; 394247835Skib unsigned int i; 395247835Skib unsigned int j = 0; 396247835Skib int ret; 397247835Skib 398247835Skib tfile = malloc(sizeof(*tfile), M_TTM_OBJ_FILE, M_WAITOK); 399247835Skib rw_init(&tfile->lock, "ttmfo"); 400247835Skib tfile->tdev = tdev; 401247835Skib refcount_init(&tfile->refcount, 1); 402247835Skib INIT_LIST_HEAD(&tfile->ref_list); 403247835Skib 404247835Skib for (i = 0; i < TTM_REF_NUM; ++i) { 405247835Skib ret = drm_ht_create(&tfile->ref_hash[i], hash_order); 406247835Skib if (ret) { 407247835Skib j = i; 408247835Skib goto out_err; 409247835Skib } 410247835Skib } 411247835Skib 412247835Skib return tfile; 413247835Skibout_err: 414247835Skib for (i = 0; i < j; ++i) 415247835Skib drm_ht_remove(&tfile->ref_hash[i]); 416247835Skib 417247835Skib free(tfile, M_TTM_OBJ_FILE); 418247835Skib 419247835Skib return NULL; 420247835Skib} 421247835Skib 422247835SkibMALLOC_DEFINE(M_TTM_OBJ_DEV, "ttm_obj_dev", "TTM Device Objects"); 423247835Skib 424247835Skibstruct ttm_object_device *ttm_object_device_init(struct ttm_mem_global 425247835Skib *mem_glob, 426247835Skib unsigned int hash_order) 427247835Skib{ 428247835Skib struct ttm_object_device *tdev; 429247835Skib int ret; 430247835Skib 431247835Skib tdev = malloc(sizeof(*tdev), M_TTM_OBJ_DEV, M_WAITOK); 432247835Skib tdev->mem_glob = mem_glob; 433247835Skib rw_init(&tdev->object_lock, "ttmdo"); 434247835Skib atomic_set(&tdev->object_count, 0); 435247835Skib ret = drm_ht_create(&tdev->object_hash, hash_order); 436247835Skib 437247835Skib if (ret == 0) 438247835Skib return tdev; 439247835Skib 440247835Skib free(tdev, M_TTM_OBJ_DEV); 441247835Skib return NULL; 442247835Skib} 443247835Skib 444247835Skibvoid ttm_object_device_release(struct ttm_object_device **p_tdev) 445247835Skib{ 446247835Skib struct ttm_object_device *tdev = *p_tdev; 447247835Skib 448247835Skib *p_tdev = NULL; 449247835Skib 450247835Skib rw_wlock(&tdev->object_lock); 451247835Skib drm_ht_remove(&tdev->object_hash); 452247835Skib rw_wunlock(&tdev->object_lock); 453247835Skib 454247835Skib free(tdev, M_TTM_OBJ_DEV); 455247835Skib} 456