diff options
author | Alex Deucher <alexander.deucher@amd.com> | 2015-04-20 16:55:21 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2015-06-03 21:03:15 -0400 |
commit | d38ceaf99ed015f2a0b9af3499791bd3a3daae21 (patch) | |
tree | c8e237ea218e8ed8a5f64c1654fc01fe5d2239cb /drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c | |
parent | 97b2e202fba05b87d720318a6500a337100dab4d (diff) |
drm/amdgpu: add core driver (v4)
This adds the non-asic specific core driver code.
v2: remove extra kconfig option
v3: implement minor fixes from Fengguang Wu
v4: fix cast in amdgpu_ucode.c
Acked-by: Christian König <christian.koenig@amd.com>
Acked-by: Jammy Zhou <Jammy.Zhou@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c new file mode 100644 index 000000000000..eb20987ce18d --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c | |||
@@ -0,0 +1,419 @@ | |||
1 | /* | ||
2 | * Copyright 2011 Red Hat Inc. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sub license, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
16 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | ||
17 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
18 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||
19 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
20 | * | ||
21 | * The above copyright notice and this permission notice (including the | ||
22 | * next paragraph) shall be included in all copies or substantial portions | ||
23 | * of the Software. | ||
24 | * | ||
25 | */ | ||
26 | /* | ||
27 | * Authors: | ||
28 | * Jerome Glisse <glisse@freedesktop.org> | ||
29 | */ | ||
30 | /* Algorithm: | ||
31 | * | ||
32 | * We store the last allocated bo in "hole", we always try to allocate | ||
33 | * after the last allocated bo. Principle is that in a linear GPU ring | ||
34 | * progression was is after last is the oldest bo we allocated and thus | ||
35 | * the first one that should no longer be in use by the GPU. | ||
36 | * | ||
37 | * If it's not the case we skip over the bo after last to the closest | ||
38 | * done bo if such one exist. If none exist and we are not asked to | ||
39 | * block we report failure to allocate. | ||
40 | * | ||
41 | * If we are asked to block we wait on all the oldest fence of all | ||
42 | * rings. We just wait for any of those fence to complete. | ||
43 | */ | ||
44 | #include <drm/drmP.h> | ||
45 | #include "amdgpu.h" | ||
46 | |||
47 | static void amdgpu_sa_bo_remove_locked(struct amdgpu_sa_bo *sa_bo); | ||
48 | static void amdgpu_sa_bo_try_free(struct amdgpu_sa_manager *sa_manager); | ||
49 | |||
50 | int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, | ||
51 | struct amdgpu_sa_manager *sa_manager, | ||
52 | unsigned size, u32 align, u32 domain) | ||
53 | { | ||
54 | int i, r; | ||
55 | |||
56 | init_waitqueue_head(&sa_manager->wq); | ||
57 | sa_manager->bo = NULL; | ||
58 | sa_manager->size = size; | ||
59 | sa_manager->domain = domain; | ||
60 | sa_manager->align = align; | ||
61 | sa_manager->hole = &sa_manager->olist; | ||
62 | INIT_LIST_HEAD(&sa_manager->olist); | ||
63 | for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | ||
64 | INIT_LIST_HEAD(&sa_manager->flist[i]); | ||
65 | } | ||
66 | |||
67 | r = amdgpu_bo_create(adev, size, align, true, | ||
68 | domain, 0, NULL, &sa_manager->bo); | ||
69 | if (r) { | ||
70 | dev_err(adev->dev, "(%d) failed to allocate bo for manager\n", r); | ||
71 | return r; | ||
72 | } | ||
73 | |||
74 | return r; | ||
75 | } | ||
76 | |||
77 | void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev, | ||
78 | struct amdgpu_sa_manager *sa_manager) | ||
79 | { | ||
80 | struct amdgpu_sa_bo *sa_bo, *tmp; | ||
81 | |||
82 | if (!list_empty(&sa_manager->olist)) { | ||
83 | sa_manager->hole = &sa_manager->olist, | ||
84 | amdgpu_sa_bo_try_free(sa_manager); | ||
85 | if (!list_empty(&sa_manager->olist)) { | ||
86 | dev_err(adev->dev, "sa_manager is not empty, clearing anyway\n"); | ||
87 | } | ||
88 | } | ||
89 | list_for_each_entry_safe(sa_bo, tmp, &sa_manager->olist, olist) { | ||
90 | amdgpu_sa_bo_remove_locked(sa_bo); | ||
91 | } | ||
92 | amdgpu_bo_unref(&sa_manager->bo); | ||
93 | sa_manager->size = 0; | ||
94 | } | ||
95 | |||
96 | int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev, | ||
97 | struct amdgpu_sa_manager *sa_manager) | ||
98 | { | ||
99 | int r; | ||
100 | |||
101 | if (sa_manager->bo == NULL) { | ||
102 | dev_err(adev->dev, "no bo for sa manager\n"); | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | |||
106 | /* map the buffer */ | ||
107 | r = amdgpu_bo_reserve(sa_manager->bo, false); | ||
108 | if (r) { | ||
109 | dev_err(adev->dev, "(%d) failed to reserve manager bo\n", r); | ||
110 | return r; | ||
111 | } | ||
112 | r = amdgpu_bo_pin(sa_manager->bo, sa_manager->domain, &sa_manager->gpu_addr); | ||
113 | if (r) { | ||
114 | amdgpu_bo_unreserve(sa_manager->bo); | ||
115 | dev_err(adev->dev, "(%d) failed to pin manager bo\n", r); | ||
116 | return r; | ||
117 | } | ||
118 | r = amdgpu_bo_kmap(sa_manager->bo, &sa_manager->cpu_ptr); | ||
119 | amdgpu_bo_unreserve(sa_manager->bo); | ||
120 | return r; | ||
121 | } | ||
122 | |||
123 | int amdgpu_sa_bo_manager_suspend(struct amdgpu_device *adev, | ||
124 | struct amdgpu_sa_manager *sa_manager) | ||
125 | { | ||
126 | int r; | ||
127 | |||
128 | if (sa_manager->bo == NULL) { | ||
129 | dev_err(adev->dev, "no bo for sa manager\n"); | ||
130 | return -EINVAL; | ||
131 | } | ||
132 | |||
133 | r = amdgpu_bo_reserve(sa_manager->bo, false); | ||
134 | if (!r) { | ||
135 | amdgpu_bo_kunmap(sa_manager->bo); | ||
136 | amdgpu_bo_unpin(sa_manager->bo); | ||
137 | amdgpu_bo_unreserve(sa_manager->bo); | ||
138 | } | ||
139 | return r; | ||
140 | } | ||
141 | |||
142 | static void amdgpu_sa_bo_remove_locked(struct amdgpu_sa_bo *sa_bo) | ||
143 | { | ||
144 | struct amdgpu_sa_manager *sa_manager = sa_bo->manager; | ||
145 | if (sa_manager->hole == &sa_bo->olist) { | ||
146 | sa_manager->hole = sa_bo->olist.prev; | ||
147 | } | ||
148 | list_del_init(&sa_bo->olist); | ||
149 | list_del_init(&sa_bo->flist); | ||
150 | amdgpu_fence_unref(&sa_bo->fence); | ||
151 | kfree(sa_bo); | ||
152 | } | ||
153 | |||
154 | static void amdgpu_sa_bo_try_free(struct amdgpu_sa_manager *sa_manager) | ||
155 | { | ||
156 | struct amdgpu_sa_bo *sa_bo, *tmp; | ||
157 | |||
158 | if (sa_manager->hole->next == &sa_manager->olist) | ||
159 | return; | ||
160 | |||
161 | sa_bo = list_entry(sa_manager->hole->next, struct amdgpu_sa_bo, olist); | ||
162 | list_for_each_entry_safe_from(sa_bo, tmp, &sa_manager->olist, olist) { | ||
163 | if (sa_bo->fence == NULL || !amdgpu_fence_signaled(sa_bo->fence)) { | ||
164 | return; | ||
165 | } | ||
166 | amdgpu_sa_bo_remove_locked(sa_bo); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | static inline unsigned amdgpu_sa_bo_hole_soffset(struct amdgpu_sa_manager *sa_manager) | ||
171 | { | ||
172 | struct list_head *hole = sa_manager->hole; | ||
173 | |||
174 | if (hole != &sa_manager->olist) { | ||
175 | return list_entry(hole, struct amdgpu_sa_bo, olist)->eoffset; | ||
176 | } | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static inline unsigned amdgpu_sa_bo_hole_eoffset(struct amdgpu_sa_manager *sa_manager) | ||
181 | { | ||
182 | struct list_head *hole = sa_manager->hole; | ||
183 | |||
184 | if (hole->next != &sa_manager->olist) { | ||
185 | return list_entry(hole->next, struct amdgpu_sa_bo, olist)->soffset; | ||
186 | } | ||
187 | return sa_manager->size; | ||
188 | } | ||
189 | |||
190 | static bool amdgpu_sa_bo_try_alloc(struct amdgpu_sa_manager *sa_manager, | ||
191 | struct amdgpu_sa_bo *sa_bo, | ||
192 | unsigned size, unsigned align) | ||
193 | { | ||
194 | unsigned soffset, eoffset, wasted; | ||
195 | |||
196 | soffset = amdgpu_sa_bo_hole_soffset(sa_manager); | ||
197 | eoffset = amdgpu_sa_bo_hole_eoffset(sa_manager); | ||
198 | wasted = (align - (soffset % align)) % align; | ||
199 | |||
200 | if ((eoffset - soffset) >= (size + wasted)) { | ||
201 | soffset += wasted; | ||
202 | |||
203 | sa_bo->manager = sa_manager; | ||
204 | sa_bo->soffset = soffset; | ||
205 | sa_bo->eoffset = soffset + size; | ||
206 | list_add(&sa_bo->olist, sa_manager->hole); | ||
207 | INIT_LIST_HEAD(&sa_bo->flist); | ||
208 | sa_manager->hole = &sa_bo->olist; | ||
209 | return true; | ||
210 | } | ||
211 | return false; | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * amdgpu_sa_event - Check if we can stop waiting | ||
216 | * | ||
217 | * @sa_manager: pointer to the sa_manager | ||
218 | * @size: number of bytes we want to allocate | ||
219 | * @align: alignment we need to match | ||
220 | * | ||
221 | * Check if either there is a fence we can wait for or | ||
222 | * enough free memory to satisfy the allocation directly | ||
223 | */ | ||
224 | static bool amdgpu_sa_event(struct amdgpu_sa_manager *sa_manager, | ||
225 | unsigned size, unsigned align) | ||
226 | { | ||
227 | unsigned soffset, eoffset, wasted; | ||
228 | int i; | ||
229 | |||
230 | for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | ||
231 | if (!list_empty(&sa_manager->flist[i])) { | ||
232 | return true; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | soffset = amdgpu_sa_bo_hole_soffset(sa_manager); | ||
237 | eoffset = amdgpu_sa_bo_hole_eoffset(sa_manager); | ||
238 | wasted = (align - (soffset % align)) % align; | ||
239 | |||
240 | if ((eoffset - soffset) >= (size + wasted)) { | ||
241 | return true; | ||
242 | } | ||
243 | |||
244 | return false; | ||
245 | } | ||
246 | |||
247 | static bool amdgpu_sa_bo_next_hole(struct amdgpu_sa_manager *sa_manager, | ||
248 | struct amdgpu_fence **fences, | ||
249 | unsigned *tries) | ||
250 | { | ||
251 | struct amdgpu_sa_bo *best_bo = NULL; | ||
252 | unsigned i, soffset, best, tmp; | ||
253 | |||
254 | /* if hole points to the end of the buffer */ | ||
255 | if (sa_manager->hole->next == &sa_manager->olist) { | ||
256 | /* try again with its beginning */ | ||
257 | sa_manager->hole = &sa_manager->olist; | ||
258 | return true; | ||
259 | } | ||
260 | |||
261 | soffset = amdgpu_sa_bo_hole_soffset(sa_manager); | ||
262 | /* to handle wrap around we add sa_manager->size */ | ||
263 | best = sa_manager->size * 2; | ||
264 | /* go over all fence list and try to find the closest sa_bo | ||
265 | * of the current last | ||
266 | */ | ||
267 | for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | ||
268 | struct amdgpu_sa_bo *sa_bo; | ||
269 | |||
270 | if (list_empty(&sa_manager->flist[i])) { | ||
271 | continue; | ||
272 | } | ||
273 | |||
274 | sa_bo = list_first_entry(&sa_manager->flist[i], | ||
275 | struct amdgpu_sa_bo, flist); | ||
276 | |||
277 | if (!amdgpu_fence_signaled(sa_bo->fence)) { | ||
278 | fences[i] = sa_bo->fence; | ||
279 | continue; | ||
280 | } | ||
281 | |||
282 | /* limit the number of tries each ring gets */ | ||
283 | if (tries[i] > 2) { | ||
284 | continue; | ||
285 | } | ||
286 | |||
287 | tmp = sa_bo->soffset; | ||
288 | if (tmp < soffset) { | ||
289 | /* wrap around, pretend it's after */ | ||
290 | tmp += sa_manager->size; | ||
291 | } | ||
292 | tmp -= soffset; | ||
293 | if (tmp < best) { | ||
294 | /* this sa bo is the closest one */ | ||
295 | best = tmp; | ||
296 | best_bo = sa_bo; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | if (best_bo) { | ||
301 | ++tries[best_bo->fence->ring->idx]; | ||
302 | sa_manager->hole = best_bo->olist.prev; | ||
303 | |||
304 | /* we knew that this one is signaled, | ||
305 | so it's save to remote it */ | ||
306 | amdgpu_sa_bo_remove_locked(best_bo); | ||
307 | return true; | ||
308 | } | ||
309 | return false; | ||
310 | } | ||
311 | |||
312 | int amdgpu_sa_bo_new(struct amdgpu_device *adev, | ||
313 | struct amdgpu_sa_manager *sa_manager, | ||
314 | struct amdgpu_sa_bo **sa_bo, | ||
315 | unsigned size, unsigned align) | ||
316 | { | ||
317 | struct amdgpu_fence *fences[AMDGPU_MAX_RINGS]; | ||
318 | unsigned tries[AMDGPU_MAX_RINGS]; | ||
319 | int i, r; | ||
320 | |||
321 | BUG_ON(align > sa_manager->align); | ||
322 | BUG_ON(size > sa_manager->size); | ||
323 | |||
324 | *sa_bo = kmalloc(sizeof(struct amdgpu_sa_bo), GFP_KERNEL); | ||
325 | if ((*sa_bo) == NULL) { | ||
326 | return -ENOMEM; | ||
327 | } | ||
328 | (*sa_bo)->manager = sa_manager; | ||
329 | (*sa_bo)->fence = NULL; | ||
330 | INIT_LIST_HEAD(&(*sa_bo)->olist); | ||
331 | INIT_LIST_HEAD(&(*sa_bo)->flist); | ||
332 | |||
333 | spin_lock(&sa_manager->wq.lock); | ||
334 | do { | ||
335 | for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { | ||
336 | fences[i] = NULL; | ||
337 | tries[i] = 0; | ||
338 | } | ||
339 | |||
340 | do { | ||
341 | amdgpu_sa_bo_try_free(sa_manager); | ||
342 | |||
343 | if (amdgpu_sa_bo_try_alloc(sa_manager, *sa_bo, | ||
344 | size, align)) { | ||
345 | spin_unlock(&sa_manager->wq.lock); | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | /* see if we can skip over some allocations */ | ||
350 | } while (amdgpu_sa_bo_next_hole(sa_manager, fences, tries)); | ||
351 | |||
352 | spin_unlock(&sa_manager->wq.lock); | ||
353 | r = amdgpu_fence_wait_any(adev, fences, false); | ||
354 | spin_lock(&sa_manager->wq.lock); | ||
355 | /* if we have nothing to wait for block */ | ||
356 | if (r == -ENOENT) { | ||
357 | r = wait_event_interruptible_locked( | ||
358 | sa_manager->wq, | ||
359 | amdgpu_sa_event(sa_manager, size, align) | ||
360 | ); | ||
361 | } | ||
362 | |||
363 | } while (!r); | ||
364 | |||
365 | spin_unlock(&sa_manager->wq.lock); | ||
366 | kfree(*sa_bo); | ||
367 | *sa_bo = NULL; | ||
368 | return r; | ||
369 | } | ||
370 | |||
371 | void amdgpu_sa_bo_free(struct amdgpu_device *adev, struct amdgpu_sa_bo **sa_bo, | ||
372 | struct amdgpu_fence *fence) | ||
373 | { | ||
374 | struct amdgpu_sa_manager *sa_manager; | ||
375 | |||
376 | if (sa_bo == NULL || *sa_bo == NULL) { | ||
377 | return; | ||
378 | } | ||
379 | |||
380 | sa_manager = (*sa_bo)->manager; | ||
381 | spin_lock(&sa_manager->wq.lock); | ||
382 | if (fence && !amdgpu_fence_signaled(fence)) { | ||
383 | (*sa_bo)->fence = amdgpu_fence_ref(fence); | ||
384 | list_add_tail(&(*sa_bo)->flist, | ||
385 | &sa_manager->flist[fence->ring->idx]); | ||
386 | } else { | ||
387 | amdgpu_sa_bo_remove_locked(*sa_bo); | ||
388 | } | ||
389 | wake_up_all_locked(&sa_manager->wq); | ||
390 | spin_unlock(&sa_manager->wq.lock); | ||
391 | *sa_bo = NULL; | ||
392 | } | ||
393 | |||
394 | #if defined(CONFIG_DEBUG_FS) | ||
395 | void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager, | ||
396 | struct seq_file *m) | ||
397 | { | ||
398 | struct amdgpu_sa_bo *i; | ||
399 | |||
400 | spin_lock(&sa_manager->wq.lock); | ||
401 | list_for_each_entry(i, &sa_manager->olist, olist) { | ||
402 | uint64_t soffset = i->soffset + sa_manager->gpu_addr; | ||
403 | uint64_t eoffset = i->eoffset + sa_manager->gpu_addr; | ||
404 | if (&i->olist == sa_manager->hole) { | ||
405 | seq_printf(m, ">"); | ||
406 | } else { | ||
407 | seq_printf(m, " "); | ||
408 | } | ||
409 | seq_printf(m, "[0x%010llx 0x%010llx] size %8lld", | ||
410 | soffset, eoffset, eoffset - soffset); | ||
411 | if (i->fence) { | ||
412 | seq_printf(m, " protected by 0x%016llx on ring %d", | ||
413 | i->fence->seq, i->fence->ring->idx); | ||
414 | } | ||
415 | seq_printf(m, "\n"); | ||
416 | } | ||
417 | spin_unlock(&sa_manager->wq.lock); | ||
418 | } | ||
419 | #endif | ||