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