drm: Import drm2+i915 work from FreeBSD
[dragonfly.git] / sys / dev / drm2 / drm_sman.c
1 /**************************************************************************
2  *
3  * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., 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 SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
18  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20  * USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  *
27  **************************************************************************/
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD: src/sys/dev/drm2/drm_sman.c,v 1.1 2012/05/22 11:07:44 kib Exp $");
31
32 /*
33  * Simple memory manager interface that keeps track on allocate regions on a
34  * per "owner" basis. All regions associated with an "owner" can be released
35  * with a simple call. Typically if the "owner" exists. The owner is any
36  * "unsigned long" identifier. Can typically be a pointer to a file private
37  * struct or a context identifier.
38  *
39  * Authors:
40  * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
41  */
42
43 #include <dev/drm2/drmP.h>
44 #include <dev/drm2/drm_sman.h>
45
46 struct drm_owner_item {
47         struct drm_hash_item owner_hash;
48         struct list_head sman_list;
49         struct list_head mem_blocks;
50 };
51
52 void drm_sman_takedown(struct drm_sman * sman)
53 {
54         drm_ht_remove(&sman->user_hash_tab);
55         drm_ht_remove(&sman->owner_hash_tab);
56         if (sman->mm)
57                 drm_free(sman->mm, sman->num_managers * sizeof(*sman->mm),
58                     DRM_MEM_MM);
59 }
60
61 int
62 drm_sman_init(struct drm_sman * sman, unsigned int num_managers,
63               unsigned int user_order, unsigned int owner_order)
64 {
65         int ret = 0;
66
67         sman->mm = (struct drm_sman_mm *) drm_calloc(num_managers,
68             sizeof(*sman->mm), DRM_MEM_MM);
69         if (!sman->mm) {
70                 ret = -ENOMEM;
71                 goto out;
72         }
73         sman->num_managers = num_managers;
74         INIT_LIST_HEAD(&sman->owner_items);
75         ret = drm_ht_create(&sman->owner_hash_tab, owner_order);
76         if (ret)
77                 goto out1;
78         ret = drm_ht_create(&sman->user_hash_tab, user_order);
79         if (!ret)
80                 goto out;
81
82         drm_ht_remove(&sman->owner_hash_tab);
83 out1:
84         drm_free(sman->mm, num_managers * sizeof(*sman->mm), DRM_MEM_MM);
85 out:
86         return ret;
87 }
88
89 static void *drm_sman_mm_allocate(void *private, unsigned long size,
90                                   unsigned alignment)
91 {
92         struct drm_mm *mm = (struct drm_mm *) private;
93         struct drm_mm_node *tmp;
94
95         tmp = drm_mm_search_free(mm, size, alignment, 1);
96         if (!tmp) {
97                 return NULL;
98         }
99         /* This could be non-atomic, but we are called from a locked path */
100         tmp = drm_mm_get_block_atomic(tmp, size, alignment);
101         return tmp;
102 }
103
104 static void drm_sman_mm_free(void *private, void *ref)
105 {
106         struct drm_mm_node *node = (struct drm_mm_node *) ref;
107
108         drm_mm_put_block(node);
109 }
110
111 static void drm_sman_mm_destroy(void *private)
112 {
113         struct drm_mm *mm = (struct drm_mm *) private;
114         drm_mm_takedown(mm);
115         drm_free(mm, sizeof(*mm), DRM_MEM_MM);
116 }
117
118 static unsigned long drm_sman_mm_offset(void *private, void *ref)
119 {
120         struct drm_mm_node *node = (struct drm_mm_node *) ref;
121         return node->start;
122 }
123
124 int
125 drm_sman_set_range(struct drm_sman * sman, unsigned int manager,
126                    unsigned long start, unsigned long size)
127 {
128         struct drm_sman_mm *sman_mm;
129         struct drm_mm *mm;
130         int ret;
131
132         KASSERT(manager < sman->num_managers, ("Invalid manager"));
133
134         sman_mm = &sman->mm[manager];
135         mm = malloc(sizeof(*mm), DRM_MEM_MM, M_NOWAIT | M_ZERO);
136         if (!mm) {
137                 return -ENOMEM;
138         }
139         sman_mm->private = mm;
140         ret = drm_mm_init(mm, start, size);
141
142         if (ret) {
143                 drm_free(mm, sizeof(*mm), DRM_MEM_MM);
144                 return ret;
145         }
146
147         sman_mm->allocate = drm_sman_mm_allocate;
148         sman_mm->free = drm_sman_mm_free;
149         sman_mm->destroy = drm_sman_mm_destroy;
150         sman_mm->offset = drm_sman_mm_offset;
151
152         return 0;
153 }
154
155 int
156 drm_sman_set_manager(struct drm_sman * sman, unsigned int manager,
157                      struct drm_sman_mm * allocator)
158 {
159         KASSERT(manager < sman->num_managers, ("Invalid manager"));
160         sman->mm[manager] = *allocator;
161
162         return 0;
163 }
164
165 static struct drm_owner_item *drm_sman_get_owner_item(struct drm_sman * sman,
166                                                  unsigned long owner)
167 {
168         int ret;
169         struct drm_hash_item *owner_hash_item;
170         struct drm_owner_item *owner_item;
171
172         ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item);
173         if (!ret) {
174                 return drm_hash_entry(owner_hash_item, struct drm_owner_item,
175                                       owner_hash);
176         }
177
178         owner_item = malloc(sizeof(*owner_item), DRM_MEM_MM, M_NOWAIT | M_ZERO);
179         if (!owner_item)
180                 goto out;
181
182         INIT_LIST_HEAD(&owner_item->mem_blocks);
183         owner_item->owner_hash.key = owner;
184         DRM_DEBUG("owner_item = %p, mem_blocks = %p\n", owner_item, &owner_item->mem_blocks);
185         if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash))
186                 goto out1;
187
188         list_add_tail(&owner_item->sman_list, &sman->owner_items);
189         return owner_item;
190
191 out1:
192         drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM);
193 out:
194         return NULL;
195 }
196
197 struct drm_memblock_item *drm_sman_alloc(struct drm_sman *sman, unsigned int manager,
198                                     unsigned long size, unsigned alignment,
199                                     unsigned long owner)
200 {
201         void *tmp;
202         struct drm_sman_mm *sman_mm;
203         struct drm_owner_item *owner_item;
204         struct drm_memblock_item *memblock;
205
206         KASSERT(manager < sman->num_managers, ("Invalid manager"));
207
208         sman_mm = &sman->mm[manager];
209         tmp = sman_mm->allocate(sman_mm->private, size, alignment);
210         if (!tmp) {
211                 return NULL;
212         }
213
214         memblock = malloc(sizeof(*memblock), DRM_MEM_MM, M_NOWAIT | M_ZERO);
215         DRM_DEBUG("allocated mem_block %p\n", memblock);
216         if (!memblock)
217                 goto out;
218
219         memblock->mm_info = tmp;
220         memblock->mm = sman_mm;
221         memblock->sman = sman;
222         INIT_LIST_HEAD(&memblock->owner_list);
223
224         if (drm_ht_just_insert_please
225             (&sman->user_hash_tab, &memblock->user_hash,
226              (unsigned long)memblock, 32, 0, 0))
227                 goto out1;
228
229         owner_item = drm_sman_get_owner_item(sman, owner);
230         if (!owner_item)
231                 goto out2;
232
233         DRM_DEBUG("owner_item = %p, mem_blocks = %p\n", owner_item, &owner_item->mem_blocks);
234         DRM_DEBUG("owner_list.prev = %p, mem_blocks.prev = %p\n", memblock->owner_list.prev, owner_item->mem_blocks.prev);
235         DRM_DEBUG("owner_list.next = %p, mem_blocks.next = %p\n", memblock->owner_list.next, owner_item->mem_blocks.next);
236         list_add_tail(&memblock->owner_list, &owner_item->mem_blocks);
237
238         DRM_DEBUG("Complete\n");
239         return memblock;
240
241 out2:
242         drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash);
243 out1:
244         drm_free(memblock, sizeof(*memblock), DRM_MEM_MM);
245 out:
246         sman_mm->free(sman_mm->private, tmp);
247
248         return NULL;
249 }
250
251 static void drm_sman_free(struct drm_memblock_item *item)
252 {
253         struct drm_sman *sman = item->sman;
254
255         list_del(&item->owner_list);
256         drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash);
257         item->mm->free(item->mm->private, item->mm_info);
258         drm_free(item, sizeof(*item), DRM_MEM_MM);
259 }
260
261 int drm_sman_free_key(struct drm_sman *sman, unsigned int key)
262 {
263         struct drm_hash_item *hash_item;
264         struct drm_memblock_item *memblock_item;
265
266         if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item))
267                 return -EINVAL;
268
269         memblock_item = drm_hash_entry(hash_item, struct drm_memblock_item,
270                                        user_hash);
271         drm_sman_free(memblock_item);
272         return 0;
273 }
274
275 static void drm_sman_remove_owner(struct drm_sman *sman,
276                                   struct drm_owner_item *owner_item)
277 {
278         list_del(&owner_item->sman_list);
279         drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash);
280         drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM);
281 }
282
283 int drm_sman_owner_clean(struct drm_sman *sman, unsigned long owner)
284 {
285
286         struct drm_hash_item *hash_item;
287         struct drm_owner_item *owner_item;
288
289         if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
290                 return -1;
291         }
292
293         owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
294         DRM_DEBUG("cleaning owner_item %p\n", owner_item);
295         if (owner_item->mem_blocks.next == &owner_item->mem_blocks) {
296                 drm_sman_remove_owner(sman, owner_item);
297                 return -1;
298         }
299
300         return 0;
301 }
302
303 static void drm_sman_do_owner_cleanup(struct drm_sman *sman,
304                                       struct drm_owner_item *owner_item)
305 {
306         struct drm_memblock_item *entry, *next;
307
308         list_for_each_entry_safe(entry, next, &owner_item->mem_blocks,
309                                  owner_list) {
310                 DRM_DEBUG("freeing mem_block %p\n", entry);
311                 drm_sman_free(entry);
312         }
313         drm_sman_remove_owner(sman, owner_item);
314 }
315
316 void drm_sman_owner_cleanup(struct drm_sman *sman, unsigned long owner)
317 {
318
319         struct drm_hash_item *hash_item;
320         struct drm_owner_item *owner_item;
321
322         if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
323
324                 return;
325         }
326
327         owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
328         drm_sman_do_owner_cleanup(sman, owner_item);
329 }
330
331 void drm_sman_cleanup(struct drm_sman *sman)
332 {
333         struct drm_owner_item *entry, *next;
334         unsigned int i;
335         struct drm_sman_mm *sman_mm;
336
337         DRM_DEBUG("sman = %p, owner_items = %p\n",
338             sman, &sman->owner_items);
339         list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) {
340                 DRM_DEBUG("cleaning owner_item = %p\n", entry);
341                 drm_sman_do_owner_cleanup(sman, entry);
342         }
343         if (sman->mm) {
344                 for (i = 0; i < sman->num_managers; ++i) {
345                         sman_mm = &sman->mm[i];
346                         if (sman_mm->private) {
347                                 sman_mm->destroy(sman_mm->private);
348                                 sman_mm->private = NULL;
349                         }
350                 }
351         }
352 }