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