xref: /dflybsd-src/sys/dev/drm/ttm/ttm_object.c (revision 450f08dbfd98cded95c51be4079ef10f5adb3241)
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  * $FreeBSD: head/sys/dev/drm2/ttm/ttm_object.c 247835 2013-03-05 09:49:34Z kib $
37  */
38 
39 /**
40  * struct ttm_object_file
41  *
42  * @tdev: Pointer to the ttm_object_device.
43  *
44  * @lock: Lock that protects the ref_list list and the
45  * ref_hash hash tables.
46  *
47  * @ref_list: List of ttm_ref_objects to be destroyed at
48  * file release.
49  *
50  * @ref_hash: Hash tables of ref objects, one per ttm_ref_type,
51  * for fast lookup of ref objects given a base object.
52  */
53 
54 #define pr_fmt(fmt) "[TTM] " fmt
55 
56 #include <dev/drm/drmP.h>
57 #include <dev/drm/drm.h>
58 #include <dev/drm/ttm/ttm_object.h>
59 #include <dev/drm/ttm/ttm_module.h>
60 
61 struct ttm_object_file {
62 	struct ttm_object_device *tdev;
63 	struct lock lock;
64 	struct list_head ref_list;
65 	struct drm_open_hash ref_hash[TTM_REF_NUM];
66 	u_int refcount;
67 };
68 
69 /**
70  * struct ttm_object_device
71  *
72  * @object_lock: lock that protects the object_hash hash table.
73  *
74  * @object_hash: hash table for fast lookup of object global names.
75  *
76  * @object_count: Per device object count.
77  *
78  * This is the per-device data structure needed for ttm object management.
79  */
80 
81 struct ttm_object_device {
82 	struct lock object_lock;
83 	struct drm_open_hash object_hash;
84 	atomic_t object_count;
85 	struct ttm_mem_global *mem_glob;
86 };
87 
88 /**
89  * struct ttm_ref_object
90  *
91  * @hash: Hash entry for the per-file object reference hash.
92  *
93  * @head: List entry for the per-file list of ref-objects.
94  *
95  * @kref: Ref count.
96  *
97  * @obj: Base object this ref object is referencing.
98  *
99  * @ref_type: Type of ref object.
100  *
101  * This is similar to an idr object, but it also has a hash table entry
102  * that allows lookup with a pointer to the referenced object as a key. In
103  * that way, one can easily detect whether a base object is referenced by
104  * a particular ttm_object_file. It also carries a ref count to avoid creating
105  * multiple ref objects if a ttm_object_file references the same base
106  * object more than once.
107  */
108 
109 struct ttm_ref_object {
110 	struct drm_hash_item hash;
111 	struct list_head head;
112 	u_int kref;
113 	enum ttm_ref_type ref_type;
114 	struct ttm_base_object *obj;
115 	struct ttm_object_file *tfile;
116 };
117 
118 MALLOC_DEFINE(M_TTM_OBJ_FILE, "ttm_obj_file", "TTM File Objects");
119 
120 static inline struct ttm_object_file *
121 ttm_object_file_ref(struct ttm_object_file *tfile)
122 {
123 	refcount_acquire(&tfile->refcount);
124 	return tfile;
125 }
126 
127 static void ttm_object_file_destroy(struct ttm_object_file *tfile)
128 {
129 
130 	drm_free(tfile, M_TTM_OBJ_FILE);
131 }
132 
133 
134 static inline void ttm_object_file_unref(struct ttm_object_file **p_tfile)
135 {
136 	struct ttm_object_file *tfile = *p_tfile;
137 
138 	*p_tfile = NULL;
139 	if (refcount_release(&tfile->refcount))
140 		ttm_object_file_destroy(tfile);
141 }
142 
143 
144 int ttm_base_object_init(struct ttm_object_file *tfile,
145 			 struct ttm_base_object *base,
146 			 bool shareable,
147 			 enum ttm_object_type object_type,
148 			 void (*rcount_release) (struct ttm_base_object **),
149 			 void (*ref_obj_release) (struct ttm_base_object *,
150 						  enum ttm_ref_type ref_type))
151 {
152 	struct ttm_object_device *tdev = tfile->tdev;
153 	int ret;
154 
155 	base->shareable = shareable;
156 	base->tfile = ttm_object_file_ref(tfile);
157 	base->refcount_release = rcount_release;
158 	base->ref_obj_release = ref_obj_release;
159 	base->object_type = object_type;
160 	refcount_init(&base->refcount, 1);
161 	lockinit(&tdev->object_lock, "ttmbao", 0, LK_CANRECURSE);
162 	lockmgr(&tdev->object_lock, LK_EXCLUSIVE);
163 	ret = drm_ht_just_insert_please(&tdev->object_hash,
164 					    &base->hash,
165 					    (unsigned long)base, 31, 0, 0);
166 	lockmgr(&tdev->object_lock, LK_RELEASE);
167 	if (unlikely(ret != 0))
168 		goto out_err0;
169 
170 	ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
171 	if (unlikely(ret != 0))
172 		goto out_err1;
173 
174 	ttm_base_object_unref(&base);
175 
176 	return 0;
177 out_err1:
178 	lockmgr(&tdev->object_lock, LK_EXCLUSIVE);
179 	(void)drm_ht_remove_item(&tdev->object_hash, &base->hash);
180 	lockmgr(&tdev->object_lock, LK_RELEASE);
181 out_err0:
182 	return ret;
183 }
184 
185 static void ttm_release_base(struct ttm_base_object *base)
186 {
187 	struct ttm_object_device *tdev = base->tfile->tdev;
188 
189 	(void)drm_ht_remove_item(&tdev->object_hash, &base->hash);
190 	lockmgr(&tdev->object_lock, LK_RELEASE);
191 	/*
192 	 * Note: We don't use synchronize_rcu() here because it's far
193 	 * too slow. It's up to the user to free the object using
194 	 * call_rcu() or ttm_base_object_kfree().
195 	 */
196 
197 	if (base->refcount_release) {
198 		ttm_object_file_unref(&base->tfile);
199 		base->refcount_release(&base);
200 	}
201 	lockmgr(&tdev->object_lock, LK_EXCLUSIVE);
202 }
203 
204 void ttm_base_object_unref(struct ttm_base_object **p_base)
205 {
206 	struct ttm_base_object *base = *p_base;
207 	struct ttm_object_device *tdev = base->tfile->tdev;
208 
209 	*p_base = NULL;
210 
211 	/*
212 	 * Need to take the lock here to avoid racing with
213 	 * users trying to look up the object.
214 	 */
215 
216 	lockmgr(&tdev->object_lock, LK_EXCLUSIVE);
217 	if (refcount_release(&base->refcount))
218 		ttm_release_base(base);
219 	lockmgr(&tdev->object_lock, LK_RELEASE);
220 }
221 
222 struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
223 					       uint32_t key)
224 {
225 	struct ttm_object_device *tdev = tfile->tdev;
226 	struct ttm_base_object *base;
227 	struct drm_hash_item *hash;
228 	int ret;
229 
230 	lockmgr(&tdev->object_lock, LK_EXCLUSIVE);
231 	ret = drm_ht_find_item(&tdev->object_hash, key, &hash);
232 
233 	if (ret == 0) {
234 		base = drm_hash_entry(hash, struct ttm_base_object, hash);
235 		refcount_acquire(&base->refcount);
236 	}
237 	lockmgr(&tdev->object_lock, LK_RELEASE);
238 
239 	if (unlikely(ret != 0))
240 		return NULL;
241 
242 	if (tfile != base->tfile && !base->shareable) {
243 		kprintf("[TTM] Attempted access of non-shareable object %p\n",
244 		    base);
245 		ttm_base_object_unref(&base);
246 		return NULL;
247 	}
248 
249 	return base;
250 }
251 
252 MALLOC_DEFINE(M_TTM_OBJ_REF, "ttm_obj_ref", "TTM Ref Objects");
253 
254 int ttm_ref_object_add(struct ttm_object_file *tfile,
255 		       struct ttm_base_object *base,
256 		       enum ttm_ref_type ref_type, bool *existed)
257 {
258 	struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
259 	struct ttm_ref_object *ref;
260 	struct drm_hash_item *hash;
261 	struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;
262 	int ret = -EINVAL;
263 
264 	if (existed != NULL)
265 		*existed = true;
266 
267 	while (ret == -EINVAL) {
268 		lockmgr(&tfile->lock, LK_EXCLUSIVE);
269 		ret = drm_ht_find_item(ht, base->hash.key, &hash);
270 
271 		if (ret == 0) {
272 			ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
273 			refcount_acquire(&ref->kref);
274 			lockmgr(&tfile->lock, LK_RELEASE);
275 			break;
276 		}
277 
278 		lockmgr(&tfile->lock, LK_RELEASE);
279 		ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref),
280 					   false, false);
281 		if (unlikely(ret != 0))
282 			return ret;
283 		ref = kmalloc(sizeof(*ref), M_TTM_OBJ_REF, M_WAITOK);
284 		if (unlikely(ref == NULL)) {
285 			ttm_mem_global_free(mem_glob, sizeof(*ref));
286 			return -ENOMEM;
287 		}
288 
289 		ref->hash.key = base->hash.key;
290 		ref->obj = base;
291 		ref->tfile = tfile;
292 		ref->ref_type = ref_type;
293 		refcount_init(&ref->kref, 1);
294 
295 		lockmgr(&tfile->lock, LK_EXCLUSIVE);
296 		ret = drm_ht_insert_item(ht, &ref->hash);
297 
298 		if (ret == 0) {
299 			list_add_tail(&ref->head, &tfile->ref_list);
300 			refcount_acquire(&base->refcount);
301 			lockmgr(&tfile->lock, LK_RELEASE);
302 			if (existed != NULL)
303 				*existed = false;
304 			break;
305 		}
306 
307 		lockmgr(&tfile->lock, LK_RELEASE);
308 		KKASSERT(ret == -EINVAL);
309 
310 		ttm_mem_global_free(mem_glob, sizeof(*ref));
311 		drm_free(ref, M_TTM_OBJ_REF);
312 	}
313 
314 	return ret;
315 }
316 
317 static void ttm_ref_object_release(struct ttm_ref_object *ref)
318 {
319 	struct ttm_base_object *base = ref->obj;
320 	struct ttm_object_file *tfile = ref->tfile;
321 	struct drm_open_hash *ht;
322 	struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;
323 
324 	ht = &tfile->ref_hash[ref->ref_type];
325 	(void)drm_ht_remove_item(ht, &ref->hash);
326 	list_del(&ref->head);
327 	lockmgr(&tfile->lock, LK_RELEASE);
328 
329 	if (ref->ref_type != TTM_REF_USAGE && base->ref_obj_release)
330 		base->ref_obj_release(base, ref->ref_type);
331 
332 	ttm_base_object_unref(&ref->obj);
333 	ttm_mem_global_free(mem_glob, sizeof(*ref));
334 	drm_free(ref, M_TTM_OBJ_REF);
335 	lockmgr(&tfile->lock, LK_EXCLUSIVE);
336 }
337 
338 int ttm_ref_object_base_unref(struct ttm_object_file *tfile,
339 			      unsigned long key, enum ttm_ref_type ref_type)
340 {
341 	struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
342 	struct ttm_ref_object *ref;
343 	struct drm_hash_item *hash;
344 	int ret;
345 
346 	lockmgr(&tfile->lock, LK_EXCLUSIVE);
347 	ret = drm_ht_find_item(ht, key, &hash);
348 	if (unlikely(ret != 0)) {
349 		lockmgr(&tfile->lock, LK_RELEASE);
350 		return -EINVAL;
351 	}
352 	ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
353 	if (refcount_release(&ref->kref))
354 		ttm_ref_object_release(ref);
355 	lockmgr(&tfile->lock, LK_RELEASE);
356 	return 0;
357 }
358 
359 void ttm_object_file_release(struct ttm_object_file **p_tfile)
360 {
361 	struct ttm_ref_object *ref;
362 	struct list_head *list;
363 	unsigned int i;
364 	struct ttm_object_file *tfile = *p_tfile;
365 
366 	*p_tfile = NULL;
367 	lockmgr(&tfile->lock, LK_EXCLUSIVE);
368 
369 	/*
370 	 * Since we release the lock within the loop, we have to
371 	 * restart it from the beginning each time.
372 	 */
373 
374 	while (!list_empty(&tfile->ref_list)) {
375 		list = tfile->ref_list.next;
376 		ref = list_entry(list, struct ttm_ref_object, head);
377 		ttm_ref_object_release(ref);
378 	}
379 
380 	for (i = 0; i < TTM_REF_NUM; ++i)
381 		drm_ht_remove(&tfile->ref_hash[i]);
382 
383 	lockmgr(&tfile->lock, LK_RELEASE);
384 	ttm_object_file_unref(&tfile);
385 }
386 
387 struct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev,
388 					     unsigned int hash_order)
389 {
390 	struct ttm_object_file *tfile;
391 	unsigned int i;
392 	unsigned int j = 0;
393 	int ret;
394 
395 	tfile = kmalloc(sizeof(*tfile), M_TTM_OBJ_FILE, M_WAITOK);
396 	lockinit(&tfile->lock, "ttmfo", 0, LK_CANRECURSE);
397 	tfile->tdev = tdev;
398 	refcount_init(&tfile->refcount, 1);
399 	INIT_LIST_HEAD(&tfile->ref_list);
400 
401 	for (i = 0; i < TTM_REF_NUM; ++i) {
402 		ret = drm_ht_create(&tfile->ref_hash[i], hash_order);
403 		if (ret) {
404 			j = i;
405 			goto out_err;
406 		}
407 	}
408 
409 	return tfile;
410 out_err:
411 	for (i = 0; i < j; ++i)
412 		drm_ht_remove(&tfile->ref_hash[i]);
413 
414 	drm_free(tfile, M_TTM_OBJ_FILE);
415 
416 	return NULL;
417 }
418 
419 MALLOC_DEFINE(M_TTM_OBJ_DEV, "ttm_obj_dev", "TTM Device Objects");
420 
421 struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global
422 						 *mem_glob,
423 						 unsigned int hash_order)
424 {
425 	struct ttm_object_device *tdev;
426 	int ret;
427 
428 	tdev = kmalloc(sizeof(*tdev), M_TTM_OBJ_DEV, M_WAITOK);
429 	tdev->mem_glob = mem_glob;
430 	lockinit(&tdev->object_lock, "ttmdo", 0, LK_CANRECURSE);
431 	atomic_set(&tdev->object_count, 0);
432 	ret = drm_ht_create(&tdev->object_hash, hash_order);
433 
434 	if (ret == 0)
435 		return tdev;
436 
437 	drm_free(tdev, M_TTM_OBJ_DEV);
438 	return NULL;
439 }
440 
441 void ttm_object_device_release(struct ttm_object_device **p_tdev)
442 {
443 	struct ttm_object_device *tdev = *p_tdev;
444 
445 	*p_tdev = NULL;
446 
447 	lockmgr(&tdev->object_lock, LK_EXCLUSIVE);
448 	drm_ht_remove(&tdev->object_hash);
449 	lockmgr(&tdev->object_lock, LK_RELEASE);
450 
451 	drm_free(tdev, M_TTM_OBJ_DEV);
452 }
453