1
2
3
4
5
6#include <linux/slab.h>
7
8#include <drm/ttm/ttm_bo_driver.h>
9#include <drm/ttm/ttm_placement.h>
10
11#include "i915_ttm_buddy_manager.h"
12
13#include "i915_buddy.h"
14#include "i915_gem.h"
15
16struct i915_ttm_buddy_manager {
17 struct ttm_resource_manager manager;
18 struct i915_buddy_mm mm;
19 struct list_head reserved;
20 struct mutex lock;
21 u64 default_page_size;
22};
23
24static struct i915_ttm_buddy_manager *
25to_buddy_manager(struct ttm_resource_manager *man)
26{
27 return container_of(man, struct i915_ttm_buddy_manager, manager);
28}
29
30static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
31 struct ttm_buffer_object *bo,
32 const struct ttm_place *place,
33 struct ttm_resource **res)
34{
35 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
36 struct i915_ttm_buddy_resource *bman_res;
37 struct i915_buddy_mm *mm = &bman->mm;
38 unsigned long n_pages;
39 unsigned int min_order;
40 u64 min_page_size;
41 u64 size;
42 int err;
43
44 GEM_BUG_ON(place->fpfn || place->lpfn);
45
46 bman_res = kzalloc(sizeof(*bman_res), GFP_KERNEL);
47 if (!bman_res)
48 return -ENOMEM;
49
50 ttm_resource_init(bo, place, &bman_res->base);
51 INIT_LIST_HEAD(&bman_res->blocks);
52 bman_res->mm = mm;
53
54 GEM_BUG_ON(!bman_res->base.num_pages);
55 size = bman_res->base.num_pages << PAGE_SHIFT;
56
57 min_page_size = bman->default_page_size;
58 if (bo->page_alignment)
59 min_page_size = bo->page_alignment << PAGE_SHIFT;
60
61 GEM_BUG_ON(min_page_size < mm->chunk_size);
62 min_order = ilog2(min_page_size) - ilog2(mm->chunk_size);
63 if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
64 size = roundup_pow_of_two(size);
65 min_order = ilog2(size) - ilog2(mm->chunk_size);
66 }
67
68 if (size > mm->size) {
69 err = -E2BIG;
70 goto err_free_res;
71 }
72
73 n_pages = size >> ilog2(mm->chunk_size);
74
75 do {
76 struct i915_buddy_block *block;
77 unsigned int order;
78
79 order = fls(n_pages) - 1;
80 GEM_BUG_ON(order > mm->max_order);
81 GEM_BUG_ON(order < min_order);
82
83 do {
84 mutex_lock(&bman->lock);
85 block = i915_buddy_alloc(mm, order);
86 mutex_unlock(&bman->lock);
87 if (!IS_ERR(block))
88 break;
89
90 if (order-- == min_order) {
91 err = -ENOSPC;
92 goto err_free_blocks;
93 }
94 } while (1);
95
96 n_pages -= BIT(order);
97
98 list_add_tail(&block->link, &bman_res->blocks);
99
100 if (!n_pages)
101 break;
102 } while (1);
103
104 *res = &bman_res->base;
105 return 0;
106
107err_free_blocks:
108 mutex_lock(&bman->lock);
109 i915_buddy_free_list(mm, &bman_res->blocks);
110 mutex_unlock(&bman->lock);
111err_free_res:
112 kfree(bman_res);
113 return err;
114}
115
116static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man,
117 struct ttm_resource *res)
118{
119 struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
120 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
121
122 mutex_lock(&bman->lock);
123 i915_buddy_free_list(&bman->mm, &bman_res->blocks);
124 mutex_unlock(&bman->lock);
125
126 kfree(bman_res);
127}
128
129static const struct ttm_resource_manager_func i915_ttm_buddy_manager_func = {
130 .alloc = i915_ttm_buddy_man_alloc,
131 .free = i915_ttm_buddy_man_free,
132};
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162int i915_ttm_buddy_man_init(struct ttm_device *bdev,
163 unsigned int type, bool use_tt,
164 u64 size, u64 default_page_size,
165 u64 chunk_size)
166{
167 struct ttm_resource_manager *man;
168 struct i915_ttm_buddy_manager *bman;
169 int err;
170
171 bman = kzalloc(sizeof(*bman), GFP_KERNEL);
172 if (!bman)
173 return -ENOMEM;
174
175 err = i915_buddy_init(&bman->mm, size, chunk_size);
176 if (err)
177 goto err_free_bman;
178
179 mutex_init(&bman->lock);
180 INIT_LIST_HEAD(&bman->reserved);
181 GEM_BUG_ON(default_page_size < chunk_size);
182 bman->default_page_size = default_page_size;
183
184 man = &bman->manager;
185 man->use_tt = use_tt;
186 man->func = &i915_ttm_buddy_manager_func;
187 ttm_resource_manager_init(man, bman->mm.size >> PAGE_SHIFT);
188
189 ttm_resource_manager_set_used(man, true);
190 ttm_set_driver_manager(bdev, type, man);
191
192 return 0;
193
194err_free_bman:
195 kfree(bman);
196 return err;
197}
198
199
200
201
202
203
204
205
206
207
208
209int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
210{
211 struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
212 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
213 struct i915_buddy_mm *mm = &bman->mm;
214 int ret;
215
216 ttm_resource_manager_set_used(man, false);
217
218 ret = ttm_resource_manager_evict_all(bdev, man);
219 if (ret)
220 return ret;
221
222 ttm_set_driver_manager(bdev, type, NULL);
223
224 mutex_lock(&bman->lock);
225 i915_buddy_free_list(mm, &bman->reserved);
226 i915_buddy_fini(mm);
227 mutex_unlock(&bman->lock);
228
229 ttm_resource_manager_cleanup(man);
230 kfree(bman);
231
232 return 0;
233}
234
235
236
237
238
239
240
241
242
243
244
245int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man,
246 u64 start, u64 size)
247{
248 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
249 struct i915_buddy_mm *mm = &bman->mm;
250 int ret;
251
252 mutex_lock(&bman->lock);
253 ret = i915_buddy_alloc_range(mm, &bman->reserved, start, size);
254 mutex_unlock(&bman->lock);
255
256 return ret;
257}
258
259