162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 OR MIT */ 262306a36Sopenharmony_ci/************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2009-2022 VMware, Inc., Palo Alto, CA., USA 562306a36Sopenharmony_ci * All Rights Reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 862306a36Sopenharmony_ci * copy of this software and associated documentation files (the 962306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 1062306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 1162306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1262306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1362306a36Sopenharmony_ci * the following conditions: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1662306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 1762306a36Sopenharmony_ci * of the Software. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2062306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2262306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2362306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2462306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2562306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci **************************************************************************/ 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * While no substantial code is shared, the prime code is inspired by 3262306a36Sopenharmony_ci * drm_prime.c, with 3362306a36Sopenharmony_ci * Authors: 3462306a36Sopenharmony_ci * Dave Airlie <airlied@redhat.com> 3562306a36Sopenharmony_ci * Rob Clark <rob.clark@linaro.org> 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci/** @file ttm_ref_object.c 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * Base- and reference object implementation for the various 4062306a36Sopenharmony_ci * ttm objects. Implements reference counting, minimal security checks 4162306a36Sopenharmony_ci * and release on file close. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define pr_fmt(fmt) "[TTM] " fmt 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include "ttm_object.h" 4862306a36Sopenharmony_ci#include "vmwgfx_drv.h" 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include <linux/list.h> 5162306a36Sopenharmony_ci#include <linux/spinlock.h> 5262306a36Sopenharmony_ci#include <linux/slab.h> 5362306a36Sopenharmony_ci#include <linux/atomic.h> 5462306a36Sopenharmony_ci#include <linux/module.h> 5562306a36Sopenharmony_ci#include <linux/hashtable.h> 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciMODULE_IMPORT_NS(DMA_BUF); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define VMW_TTM_OBJECT_REF_HT_ORDER 10 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/** 6262306a36Sopenharmony_ci * struct ttm_object_file 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci * @tdev: Pointer to the ttm_object_device. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * @lock: Lock that protects the ref_list list and the 6762306a36Sopenharmony_ci * ref_hash hash tables. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * @ref_list: List of ttm_ref_objects to be destroyed at 7062306a36Sopenharmony_ci * file release. 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * @ref_hash: Hash tables of ref objects, one per ttm_ref_type, 7362306a36Sopenharmony_ci * for fast lookup of ref objects given a base object. 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * @refcount: reference/usage count 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistruct ttm_object_file { 7862306a36Sopenharmony_ci struct ttm_object_device *tdev; 7962306a36Sopenharmony_ci spinlock_t lock; 8062306a36Sopenharmony_ci struct list_head ref_list; 8162306a36Sopenharmony_ci DECLARE_HASHTABLE(ref_hash, VMW_TTM_OBJECT_REF_HT_ORDER); 8262306a36Sopenharmony_ci struct kref refcount; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * struct ttm_object_device 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci * @object_lock: lock that protects idr. 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * @object_count: Per device object count. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * This is the per-device data structure needed for ttm object management. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistruct ttm_object_device { 9662306a36Sopenharmony_ci spinlock_t object_lock; 9762306a36Sopenharmony_ci atomic_t object_count; 9862306a36Sopenharmony_ci struct dma_buf_ops ops; 9962306a36Sopenharmony_ci void (*dmabuf_release)(struct dma_buf *dma_buf); 10062306a36Sopenharmony_ci struct idr idr; 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* 10462306a36Sopenharmony_ci * struct ttm_ref_object 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * @hash: Hash entry for the per-file object reference hash. 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * @head: List entry for the per-file list of ref-objects. 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * @kref: Ref count. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * @obj: Base object this ref object is referencing. 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * @ref_type: Type of ref object. 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * This is similar to an idr object, but it also has a hash table entry 11762306a36Sopenharmony_ci * that allows lookup with a pointer to the referenced object as a key. In 11862306a36Sopenharmony_ci * that way, one can easily detect whether a base object is referenced by 11962306a36Sopenharmony_ci * a particular ttm_object_file. It also carries a ref count to avoid creating 12062306a36Sopenharmony_ci * multiple ref objects if a ttm_object_file references the same base 12162306a36Sopenharmony_ci * object more than once. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct ttm_ref_object { 12562306a36Sopenharmony_ci struct rcu_head rcu_head; 12662306a36Sopenharmony_ci struct vmwgfx_hash_item hash; 12762306a36Sopenharmony_ci struct list_head head; 12862306a36Sopenharmony_ci struct kref kref; 12962306a36Sopenharmony_ci struct ttm_base_object *obj; 13062306a36Sopenharmony_ci struct ttm_object_file *tfile; 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void ttm_prime_dmabuf_release(struct dma_buf *dma_buf); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic inline struct ttm_object_file * 13662306a36Sopenharmony_cittm_object_file_ref(struct ttm_object_file *tfile) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci kref_get(&tfile->refcount); 13962306a36Sopenharmony_ci return tfile; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int ttm_tfile_find_ref_rcu(struct ttm_object_file *tfile, 14362306a36Sopenharmony_ci uint64_t key, 14462306a36Sopenharmony_ci struct vmwgfx_hash_item **p_hash) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct vmwgfx_hash_item *hash; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci hash_for_each_possible_rcu(tfile->ref_hash, hash, head, key) { 14962306a36Sopenharmony_ci if (hash->key == key) { 15062306a36Sopenharmony_ci *p_hash = hash; 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci return -EINVAL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int ttm_tfile_find_ref(struct ttm_object_file *tfile, 15862306a36Sopenharmony_ci uint64_t key, 15962306a36Sopenharmony_ci struct vmwgfx_hash_item **p_hash) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct vmwgfx_hash_item *hash; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci hash_for_each_possible(tfile->ref_hash, hash, head, key) { 16462306a36Sopenharmony_ci if (hash->key == key) { 16562306a36Sopenharmony_ci *p_hash = hash; 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci return -EINVAL; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void ttm_object_file_destroy(struct kref *kref) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct ttm_object_file *tfile = 17562306a36Sopenharmony_ci container_of(kref, struct ttm_object_file, refcount); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci kfree(tfile); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic inline void ttm_object_file_unref(struct ttm_object_file **p_tfile) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct ttm_object_file *tfile = *p_tfile; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci *p_tfile = NULL; 18662306a36Sopenharmony_ci kref_put(&tfile->refcount, ttm_object_file_destroy); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciint ttm_base_object_init(struct ttm_object_file *tfile, 19162306a36Sopenharmony_ci struct ttm_base_object *base, 19262306a36Sopenharmony_ci bool shareable, 19362306a36Sopenharmony_ci enum ttm_object_type object_type, 19462306a36Sopenharmony_ci void (*refcount_release) (struct ttm_base_object **)) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct ttm_object_device *tdev = tfile->tdev; 19762306a36Sopenharmony_ci int ret; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci base->shareable = shareable; 20062306a36Sopenharmony_ci base->tfile = ttm_object_file_ref(tfile); 20162306a36Sopenharmony_ci base->refcount_release = refcount_release; 20262306a36Sopenharmony_ci base->object_type = object_type; 20362306a36Sopenharmony_ci kref_init(&base->refcount); 20462306a36Sopenharmony_ci idr_preload(GFP_KERNEL); 20562306a36Sopenharmony_ci spin_lock(&tdev->object_lock); 20662306a36Sopenharmony_ci ret = idr_alloc(&tdev->idr, base, 1, 0, GFP_NOWAIT); 20762306a36Sopenharmony_ci spin_unlock(&tdev->object_lock); 20862306a36Sopenharmony_ci idr_preload_end(); 20962306a36Sopenharmony_ci if (ret < 0) 21062306a36Sopenharmony_ci return ret; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci base->handle = ret; 21362306a36Sopenharmony_ci ret = ttm_ref_object_add(tfile, base, NULL, false); 21462306a36Sopenharmony_ci if (unlikely(ret != 0)) 21562306a36Sopenharmony_ci goto out_err1; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ttm_base_object_unref(&base); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ciout_err1: 22162306a36Sopenharmony_ci spin_lock(&tdev->object_lock); 22262306a36Sopenharmony_ci idr_remove(&tdev->idr, base->handle); 22362306a36Sopenharmony_ci spin_unlock(&tdev->object_lock); 22462306a36Sopenharmony_ci return ret; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void ttm_release_base(struct kref *kref) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct ttm_base_object *base = 23062306a36Sopenharmony_ci container_of(kref, struct ttm_base_object, refcount); 23162306a36Sopenharmony_ci struct ttm_object_device *tdev = base->tfile->tdev; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci spin_lock(&tdev->object_lock); 23462306a36Sopenharmony_ci idr_remove(&tdev->idr, base->handle); 23562306a36Sopenharmony_ci spin_unlock(&tdev->object_lock); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * Note: We don't use synchronize_rcu() here because it's far 23962306a36Sopenharmony_ci * too slow. It's up to the user to free the object using 24062306a36Sopenharmony_ci * call_rcu() or ttm_base_object_kfree(). 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci ttm_object_file_unref(&base->tfile); 24462306a36Sopenharmony_ci if (base->refcount_release) 24562306a36Sopenharmony_ci base->refcount_release(&base); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_civoid ttm_base_object_unref(struct ttm_base_object **p_base) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct ttm_base_object *base = *p_base; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci *p_base = NULL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci kref_put(&base->refcount, ttm_release_base); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistruct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile, 25862306a36Sopenharmony_ci uint64_t key) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct ttm_base_object *base = NULL; 26162306a36Sopenharmony_ci struct vmwgfx_hash_item *hash; 26262306a36Sopenharmony_ci int ret; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci spin_lock(&tfile->lock); 26562306a36Sopenharmony_ci ret = ttm_tfile_find_ref(tfile, key, &hash); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (likely(ret == 0)) { 26862306a36Sopenharmony_ci base = hlist_entry(hash, struct ttm_ref_object, hash)->obj; 26962306a36Sopenharmony_ci if (!kref_get_unless_zero(&base->refcount)) 27062306a36Sopenharmony_ci base = NULL; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci spin_unlock(&tfile->lock); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return base; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistruct ttm_base_object * 27962306a36Sopenharmony_cittm_base_object_lookup_for_ref(struct ttm_object_device *tdev, uint64_t key) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct ttm_base_object *base; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci rcu_read_lock(); 28462306a36Sopenharmony_ci base = idr_find(&tdev->idr, key); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (base && !kref_get_unless_zero(&base->refcount)) 28762306a36Sopenharmony_ci base = NULL; 28862306a36Sopenharmony_ci rcu_read_unlock(); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return base; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciint ttm_ref_object_add(struct ttm_object_file *tfile, 29462306a36Sopenharmony_ci struct ttm_base_object *base, 29562306a36Sopenharmony_ci bool *existed, 29662306a36Sopenharmony_ci bool require_existed) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct ttm_ref_object *ref; 29962306a36Sopenharmony_ci struct vmwgfx_hash_item *hash; 30062306a36Sopenharmony_ci int ret = -EINVAL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (base->tfile != tfile && !base->shareable) 30362306a36Sopenharmony_ci return -EPERM; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (existed != NULL) 30662306a36Sopenharmony_ci *existed = true; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci while (ret == -EINVAL) { 30962306a36Sopenharmony_ci rcu_read_lock(); 31062306a36Sopenharmony_ci ret = ttm_tfile_find_ref_rcu(tfile, base->handle, &hash); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (ret == 0) { 31362306a36Sopenharmony_ci ref = hlist_entry(hash, struct ttm_ref_object, hash); 31462306a36Sopenharmony_ci if (kref_get_unless_zero(&ref->kref)) { 31562306a36Sopenharmony_ci rcu_read_unlock(); 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci rcu_read_unlock(); 32162306a36Sopenharmony_ci if (require_existed) 32262306a36Sopenharmony_ci return -EPERM; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ref = kmalloc(sizeof(*ref), GFP_KERNEL); 32562306a36Sopenharmony_ci if (unlikely(ref == NULL)) { 32662306a36Sopenharmony_ci return -ENOMEM; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci ref->hash.key = base->handle; 33062306a36Sopenharmony_ci ref->obj = base; 33162306a36Sopenharmony_ci ref->tfile = tfile; 33262306a36Sopenharmony_ci kref_init(&ref->kref); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci spin_lock(&tfile->lock); 33562306a36Sopenharmony_ci hash_add_rcu(tfile->ref_hash, &ref->hash.head, ref->hash.key); 33662306a36Sopenharmony_ci ret = 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci list_add_tail(&ref->head, &tfile->ref_list); 33962306a36Sopenharmony_ci kref_get(&base->refcount); 34062306a36Sopenharmony_ci spin_unlock(&tfile->lock); 34162306a36Sopenharmony_ci if (existed != NULL) 34262306a36Sopenharmony_ci *existed = false; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return ret; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void __releases(tfile->lock) __acquires(tfile->lock) 34962306a36Sopenharmony_cittm_ref_object_release(struct kref *kref) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct ttm_ref_object *ref = 35262306a36Sopenharmony_ci container_of(kref, struct ttm_ref_object, kref); 35362306a36Sopenharmony_ci struct ttm_object_file *tfile = ref->tfile; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci hash_del_rcu(&ref->hash.head); 35662306a36Sopenharmony_ci list_del(&ref->head); 35762306a36Sopenharmony_ci spin_unlock(&tfile->lock); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ttm_base_object_unref(&ref->obj); 36062306a36Sopenharmony_ci kfree_rcu(ref, rcu_head); 36162306a36Sopenharmony_ci spin_lock(&tfile->lock); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ciint ttm_ref_object_base_unref(struct ttm_object_file *tfile, 36562306a36Sopenharmony_ci unsigned long key) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct ttm_ref_object *ref; 36862306a36Sopenharmony_ci struct vmwgfx_hash_item *hash; 36962306a36Sopenharmony_ci int ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci spin_lock(&tfile->lock); 37262306a36Sopenharmony_ci ret = ttm_tfile_find_ref(tfile, key, &hash); 37362306a36Sopenharmony_ci if (unlikely(ret != 0)) { 37462306a36Sopenharmony_ci spin_unlock(&tfile->lock); 37562306a36Sopenharmony_ci return -EINVAL; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci ref = hlist_entry(hash, struct ttm_ref_object, hash); 37862306a36Sopenharmony_ci kref_put(&ref->kref, ttm_ref_object_release); 37962306a36Sopenharmony_ci spin_unlock(&tfile->lock); 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_civoid ttm_object_file_release(struct ttm_object_file **p_tfile) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct ttm_ref_object *ref; 38662306a36Sopenharmony_ci struct list_head *list; 38762306a36Sopenharmony_ci struct ttm_object_file *tfile = *p_tfile; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci *p_tfile = NULL; 39062306a36Sopenharmony_ci spin_lock(&tfile->lock); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* 39362306a36Sopenharmony_ci * Since we release the lock within the loop, we have to 39462306a36Sopenharmony_ci * restart it from the beginning each time. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci while (!list_empty(&tfile->ref_list)) { 39862306a36Sopenharmony_ci list = tfile->ref_list.next; 39962306a36Sopenharmony_ci ref = list_entry(list, struct ttm_ref_object, head); 40062306a36Sopenharmony_ci ttm_ref_object_release(&ref->kref); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci spin_unlock(&tfile->lock); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ttm_object_file_unref(&tfile); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistruct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct ttm_object_file *tfile = kmalloc(sizeof(*tfile), GFP_KERNEL); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (unlikely(tfile == NULL)) 41362306a36Sopenharmony_ci return NULL; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci spin_lock_init(&tfile->lock); 41662306a36Sopenharmony_ci tfile->tdev = tdev; 41762306a36Sopenharmony_ci kref_init(&tfile->refcount); 41862306a36Sopenharmony_ci INIT_LIST_HEAD(&tfile->ref_list); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci hash_init(tfile->ref_hash); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return tfile; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistruct ttm_object_device * 42662306a36Sopenharmony_cittm_object_device_init(const struct dma_buf_ops *ops) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct ttm_object_device *tdev = kmalloc(sizeof(*tdev), GFP_KERNEL); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (unlikely(tdev == NULL)) 43162306a36Sopenharmony_ci return NULL; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci spin_lock_init(&tdev->object_lock); 43462306a36Sopenharmony_ci atomic_set(&tdev->object_count, 0); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * Our base is at VMWGFX_NUM_MOB + 1 because we want to create 43862306a36Sopenharmony_ci * a seperate namespace for GEM handles (which are 43962306a36Sopenharmony_ci * 1..VMWGFX_NUM_MOB) and the surface handles. Some ioctl's 44062306a36Sopenharmony_ci * can take either handle as an argument so we want to 44162306a36Sopenharmony_ci * easily be able to tell whether the handle refers to a 44262306a36Sopenharmony_ci * GEM buffer or a surface. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci idr_init_base(&tdev->idr, VMWGFX_NUM_MOB + 1); 44562306a36Sopenharmony_ci tdev->ops = *ops; 44662306a36Sopenharmony_ci tdev->dmabuf_release = tdev->ops.release; 44762306a36Sopenharmony_ci tdev->ops.release = ttm_prime_dmabuf_release; 44862306a36Sopenharmony_ci return tdev; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_civoid ttm_object_device_release(struct ttm_object_device **p_tdev) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct ttm_object_device *tdev = *p_tdev; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci *p_tdev = NULL; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci WARN_ON_ONCE(!idr_is_empty(&tdev->idr)); 45862306a36Sopenharmony_ci idr_destroy(&tdev->idr); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci kfree(tdev); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/** 46462306a36Sopenharmony_ci * get_dma_buf_unless_doomed - get a dma_buf reference if possible. 46562306a36Sopenharmony_ci * 46662306a36Sopenharmony_ci * @dmabuf: Non-refcounted pointer to a struct dma-buf. 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * Obtain a file reference from a lookup structure that doesn't refcount 46962306a36Sopenharmony_ci * the file, but synchronizes with its release method to make sure it has 47062306a36Sopenharmony_ci * not been freed yet. See for example kref_get_unless_zero documentation. 47162306a36Sopenharmony_ci * Returns true if refcounting succeeds, false otherwise. 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * Nobody really wants this as a public API yet, so let it mature here 47462306a36Sopenharmony_ci * for some time... 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_cistatic bool __must_check get_dma_buf_unless_doomed(struct dma_buf *dmabuf) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci return atomic_long_inc_not_zero(&dmabuf->file->f_count) != 0L; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/** 48262306a36Sopenharmony_ci * ttm_prime_refcount_release - refcount release method for a prime object. 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * @p_base: Pointer to ttm_base_object pointer. 48562306a36Sopenharmony_ci * 48662306a36Sopenharmony_ci * This is a wrapper that calls the refcount_release founction of the 48762306a36Sopenharmony_ci * underlying object. At the same time it cleans up the prime object. 48862306a36Sopenharmony_ci * This function is called when all references to the base object we 48962306a36Sopenharmony_ci * derive from are gone. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_cistatic void ttm_prime_refcount_release(struct ttm_base_object **p_base) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct ttm_base_object *base = *p_base; 49462306a36Sopenharmony_ci struct ttm_prime_object *prime; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci *p_base = NULL; 49762306a36Sopenharmony_ci prime = container_of(base, struct ttm_prime_object, base); 49862306a36Sopenharmony_ci BUG_ON(prime->dma_buf != NULL); 49962306a36Sopenharmony_ci mutex_destroy(&prime->mutex); 50062306a36Sopenharmony_ci if (prime->refcount_release) 50162306a36Sopenharmony_ci prime->refcount_release(&base); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/** 50562306a36Sopenharmony_ci * ttm_prime_dmabuf_release - Release method for the dma-bufs we export 50662306a36Sopenharmony_ci * 50762306a36Sopenharmony_ci * @dma_buf: 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * This function first calls the dma_buf release method the driver 51062306a36Sopenharmony_ci * provides. Then it cleans up our dma_buf pointer used for lookup, 51162306a36Sopenharmony_ci * and finally releases the reference the dma_buf has on our base 51262306a36Sopenharmony_ci * object. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_cistatic void ttm_prime_dmabuf_release(struct dma_buf *dma_buf) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct ttm_prime_object *prime = 51762306a36Sopenharmony_ci (struct ttm_prime_object *) dma_buf->priv; 51862306a36Sopenharmony_ci struct ttm_base_object *base = &prime->base; 51962306a36Sopenharmony_ci struct ttm_object_device *tdev = base->tfile->tdev; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (tdev->dmabuf_release) 52262306a36Sopenharmony_ci tdev->dmabuf_release(dma_buf); 52362306a36Sopenharmony_ci mutex_lock(&prime->mutex); 52462306a36Sopenharmony_ci if (prime->dma_buf == dma_buf) 52562306a36Sopenharmony_ci prime->dma_buf = NULL; 52662306a36Sopenharmony_ci mutex_unlock(&prime->mutex); 52762306a36Sopenharmony_ci ttm_base_object_unref(&base); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci/** 53162306a36Sopenharmony_ci * ttm_prime_fd_to_handle - Get a base object handle from a prime fd 53262306a36Sopenharmony_ci * 53362306a36Sopenharmony_ci * @tfile: A struct ttm_object_file identifying the caller. 53462306a36Sopenharmony_ci * @fd: The prime / dmabuf fd. 53562306a36Sopenharmony_ci * @handle: The returned handle. 53662306a36Sopenharmony_ci * 53762306a36Sopenharmony_ci * This function returns a handle to an object that previously exported 53862306a36Sopenharmony_ci * a dma-buf. Note that we don't handle imports yet, because we simply 53962306a36Sopenharmony_ci * have no consumers of that implementation. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ciint ttm_prime_fd_to_handle(struct ttm_object_file *tfile, 54262306a36Sopenharmony_ci int fd, u32 *handle) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct ttm_object_device *tdev = tfile->tdev; 54562306a36Sopenharmony_ci struct dma_buf *dma_buf; 54662306a36Sopenharmony_ci struct ttm_prime_object *prime; 54762306a36Sopenharmony_ci struct ttm_base_object *base; 54862306a36Sopenharmony_ci int ret; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci dma_buf = dma_buf_get(fd); 55162306a36Sopenharmony_ci if (IS_ERR(dma_buf)) 55262306a36Sopenharmony_ci return PTR_ERR(dma_buf); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (dma_buf->ops != &tdev->ops) 55562306a36Sopenharmony_ci return -ENOSYS; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci prime = (struct ttm_prime_object *) dma_buf->priv; 55862306a36Sopenharmony_ci base = &prime->base; 55962306a36Sopenharmony_ci *handle = base->handle; 56062306a36Sopenharmony_ci ret = ttm_ref_object_add(tfile, base, NULL, false); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci dma_buf_put(dma_buf); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return ret; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci/** 56862306a36Sopenharmony_ci * ttm_prime_handle_to_fd - Return a dma_buf fd from a ttm prime object 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * @tfile: Struct ttm_object_file identifying the caller. 57162306a36Sopenharmony_ci * @handle: Handle to the object we're exporting from. 57262306a36Sopenharmony_ci * @flags: flags for dma-buf creation. We just pass them on. 57362306a36Sopenharmony_ci * @prime_fd: The returned file descriptor. 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ciint ttm_prime_handle_to_fd(struct ttm_object_file *tfile, 57762306a36Sopenharmony_ci uint32_t handle, uint32_t flags, 57862306a36Sopenharmony_ci int *prime_fd) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct ttm_object_device *tdev = tfile->tdev; 58162306a36Sopenharmony_ci struct ttm_base_object *base; 58262306a36Sopenharmony_ci struct dma_buf *dma_buf; 58362306a36Sopenharmony_ci struct ttm_prime_object *prime; 58462306a36Sopenharmony_ci int ret; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci base = ttm_base_object_lookup(tfile, handle); 58762306a36Sopenharmony_ci if (unlikely(base == NULL || 58862306a36Sopenharmony_ci base->object_type != ttm_prime_type)) { 58962306a36Sopenharmony_ci ret = -ENOENT; 59062306a36Sopenharmony_ci goto out_unref; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci prime = container_of(base, struct ttm_prime_object, base); 59462306a36Sopenharmony_ci if (unlikely(!base->shareable)) { 59562306a36Sopenharmony_ci ret = -EPERM; 59662306a36Sopenharmony_ci goto out_unref; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci ret = mutex_lock_interruptible(&prime->mutex); 60062306a36Sopenharmony_ci if (unlikely(ret != 0)) { 60162306a36Sopenharmony_ci ret = -ERESTARTSYS; 60262306a36Sopenharmony_ci goto out_unref; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci dma_buf = prime->dma_buf; 60662306a36Sopenharmony_ci if (!dma_buf || !get_dma_buf_unless_doomed(dma_buf)) { 60762306a36Sopenharmony_ci DEFINE_DMA_BUF_EXPORT_INFO(exp_info); 60862306a36Sopenharmony_ci exp_info.ops = &tdev->ops; 60962306a36Sopenharmony_ci exp_info.size = prime->size; 61062306a36Sopenharmony_ci exp_info.flags = flags; 61162306a36Sopenharmony_ci exp_info.priv = prime; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * Need to create a new dma_buf 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci dma_buf = dma_buf_export(&exp_info); 61862306a36Sopenharmony_ci if (IS_ERR(dma_buf)) { 61962306a36Sopenharmony_ci ret = PTR_ERR(dma_buf); 62062306a36Sopenharmony_ci mutex_unlock(&prime->mutex); 62162306a36Sopenharmony_ci goto out_unref; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* 62562306a36Sopenharmony_ci * dma_buf has taken the base object reference 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci base = NULL; 62862306a36Sopenharmony_ci prime->dma_buf = dma_buf; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci mutex_unlock(&prime->mutex); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ret = dma_buf_fd(dma_buf, flags); 63362306a36Sopenharmony_ci if (ret >= 0) { 63462306a36Sopenharmony_ci *prime_fd = ret; 63562306a36Sopenharmony_ci ret = 0; 63662306a36Sopenharmony_ci } else 63762306a36Sopenharmony_ci dma_buf_put(dma_buf); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciout_unref: 64062306a36Sopenharmony_ci if (base) 64162306a36Sopenharmony_ci ttm_base_object_unref(&base); 64262306a36Sopenharmony_ci return ret; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/** 64662306a36Sopenharmony_ci * ttm_prime_object_init - Initialize a ttm_prime_object 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * @tfile: struct ttm_object_file identifying the caller 64962306a36Sopenharmony_ci * @size: The size of the dma_bufs we export. 65062306a36Sopenharmony_ci * @prime: The object to be initialized. 65162306a36Sopenharmony_ci * @shareable: See ttm_base_object_init 65262306a36Sopenharmony_ci * @type: See ttm_base_object_init 65362306a36Sopenharmony_ci * @refcount_release: See ttm_base_object_init 65462306a36Sopenharmony_ci * 65562306a36Sopenharmony_ci * Initializes an object which is compatible with the drm_prime model 65662306a36Sopenharmony_ci * for data sharing between processes and devices. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ciint ttm_prime_object_init(struct ttm_object_file *tfile, size_t size, 65962306a36Sopenharmony_ci struct ttm_prime_object *prime, bool shareable, 66062306a36Sopenharmony_ci enum ttm_object_type type, 66162306a36Sopenharmony_ci void (*refcount_release) (struct ttm_base_object **)) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci mutex_init(&prime->mutex); 66462306a36Sopenharmony_ci prime->size = PAGE_ALIGN(size); 66562306a36Sopenharmony_ci prime->real_type = type; 66662306a36Sopenharmony_ci prime->dma_buf = NULL; 66762306a36Sopenharmony_ci prime->refcount_release = refcount_release; 66862306a36Sopenharmony_ci return ttm_base_object_init(tfile, &prime->base, shareable, 66962306a36Sopenharmony_ci ttm_prime_type, 67062306a36Sopenharmony_ci ttm_prime_refcount_release); 67162306a36Sopenharmony_ci} 672