aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/radeon/radeon_sa.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-24 15:42:54 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-24 15:42:54 -0400
commitf2fde3a65e88330017b816faf2ef75f141d21375 (patch)
tree57152ab5756e7ed1c58742e7e16f13a45ff11f21 /drivers/gpu/drm/radeon/radeon_sa.c
parent28f3d717618156c0dcd2f497d791b578a7931d87 (diff)
parent8c914028f5ddaa417b7d0f4b7fdc24caceaa8043 (diff)
Merge branch 'drm-core-next' of git://people.freedesktop.org/~airlied/linux
Pull main drm updates from Dave Airlie: "This is the main merge window request for the drm. It's big, but jam packed will lots of features and of course 0 regressions. (okay maybe there'll be one). Highlights: - new KMS drivers for server GPU chipsets: ast, mgag200 and cirrus (qemu only). These drivers use the generic modesetting drivers. - initial prime/dma-buf support for i915, nouveau, radeon, udl and exynos - switcheroo audio support: so GPUs with HDMI can turn off the sound driver without crashing stuff. - There are some patches drifting outside drivers/gpu into x86 and EFI for better handling of multiple video adapters in Apple Macs, they've got correct acks except one trivial fixup. - Core: edid parser has better DMT and reduced blanking support, crtc properties, plane properties, - Drivers: exynos: add 2D core accel support, prime support, hdmi features intel: more Haswell support, initial Valleyview support, more hdmi infoframe fixes, update MAINTAINERS for Daniel, lots of cleanups and fixes radeon: more HDMI audio support, improved GPU lockup recovery support, remove nested mutexes, less memory copying on PCIE, fix bus master enable race (kexec), improved fence handling gma500: cleanups, 1080p support, acpi fixes nouveau: better nva3 memory reclocking, kepler accel (needs external firmware rip), async buffer moves on nv84+ hw. I've some more dma-buf patches that rely on the dma-buf merge for vmap stuff, and I've a few fixes building up, but I'd decided I'd better get rid of the main pull sooner rather than later, so the audio guys are also unblocked." Fix up trivial conflict due to some duplicated changes in drivers/gpu/drm/i915/intel_ringbuffer.c * 'drm-core-next' of git://people.freedesktop.org/~airlied/linux: (605 commits) drm/nouveau/nvd9: Fix GPIO initialisation sequence. drm/nouveau: Unregister switcheroo client on exit drm/nouveau: Check dsm on switcheroo unregister drm/nouveau: fix a minor annoyance in an output string drm/nouveau: turn a BUG into a WARN drm/nv50: decode PGRAPH DATA_ERROR = 0x24 drm/nouveau/disp: fix dithering not being enabled on some eDP macbooks drm/nvd9/copy: initialise copy engine, seems to work like nvc0 drm/nvc0/ttm: use copy engines for async buffer moves drm/nva3/ttm: use copy engine for async buffer moves drm/nv98/ttm: add in a (disabled) crypto engine buffer copy method drm/nv84/ttm: use crypto engine for async buffer copies drm/nouveau/ttm: untangle code to support accelerated buffer moves drm/nouveau/fbcon: use fence for sync, rather than notifier drm/nv98/crypt: non-stub implementation of the engine hooks drm/nouveau/fifo: turn all fifo modules into engine modules drm/nv50/graph: remove ability to do interrupt-driven context switching drm/nv50: remove manual context unload on context destruction drm/nv50: remove execution engine context saves on suspend drm/nv50/fifo: use hardware channel kickoff functionality ...
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_sa.c')
-rw-r--r--drivers/gpu/drm/radeon/radeon_sa.c325
1 files changed, 261 insertions, 64 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c
index 4cce47e7dc0d..32059b745728 100644
--- a/drivers/gpu/drm/radeon/radeon_sa.c
+++ b/drivers/gpu/drm/radeon/radeon_sa.c
@@ -27,23 +27,45 @@
27 * Authors: 27 * Authors:
28 * Jerome Glisse <glisse@freedesktop.org> 28 * Jerome Glisse <glisse@freedesktop.org>
29 */ 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 */
30#include "drmP.h" 44#include "drmP.h"
31#include "drm.h" 45#include "drm.h"
32#include "radeon.h" 46#include "radeon.h"
33 47
48static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo);
49static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager);
50
34int radeon_sa_bo_manager_init(struct radeon_device *rdev, 51int radeon_sa_bo_manager_init(struct radeon_device *rdev,
35 struct radeon_sa_manager *sa_manager, 52 struct radeon_sa_manager *sa_manager,
36 unsigned size, u32 domain) 53 unsigned size, u32 domain)
37{ 54{
38 int r; 55 int i, r;
39 56
57 spin_lock_init(&sa_manager->lock);
40 sa_manager->bo = NULL; 58 sa_manager->bo = NULL;
41 sa_manager->size = size; 59 sa_manager->size = size;
42 sa_manager->domain = domain; 60 sa_manager->domain = domain;
43 INIT_LIST_HEAD(&sa_manager->sa_bo); 61 sa_manager->hole = &sa_manager->olist;
62 INIT_LIST_HEAD(&sa_manager->olist);
63 for (i = 0; i < RADEON_NUM_RINGS; ++i) {
64 INIT_LIST_HEAD(&sa_manager->flist[i]);
65 }
44 66
45 r = radeon_bo_create(rdev, size, RADEON_GPU_PAGE_SIZE, true, 67 r = radeon_bo_create(rdev, size, RADEON_GPU_PAGE_SIZE, true,
46 RADEON_GEM_DOMAIN_CPU, &sa_manager->bo); 68 RADEON_GEM_DOMAIN_CPU, NULL, &sa_manager->bo);
47 if (r) { 69 if (r) {
48 dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r); 70 dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r);
49 return r; 71 return r;
@@ -57,11 +79,15 @@ void radeon_sa_bo_manager_fini(struct radeon_device *rdev,
57{ 79{
58 struct radeon_sa_bo *sa_bo, *tmp; 80 struct radeon_sa_bo *sa_bo, *tmp;
59 81
60 if (!list_empty(&sa_manager->sa_bo)) { 82 if (!list_empty(&sa_manager->olist)) {
61 dev_err(rdev->dev, "sa_manager is not empty, clearing anyway\n"); 83 sa_manager->hole = &sa_manager->olist,
84 radeon_sa_bo_try_free(sa_manager);
85 if (!list_empty(&sa_manager->olist)) {
86 dev_err(rdev->dev, "sa_manager is not empty, clearing anyway\n");
87 }
62 } 88 }
63 list_for_each_entry_safe(sa_bo, tmp, &sa_manager->sa_bo, list) { 89 list_for_each_entry_safe(sa_bo, tmp, &sa_manager->olist, olist) {
64 list_del_init(&sa_bo->list); 90 radeon_sa_bo_remove_locked(sa_bo);
65 } 91 }
66 radeon_bo_unref(&sa_manager->bo); 92 radeon_bo_unref(&sa_manager->bo);
67 sa_manager->size = 0; 93 sa_manager->size = 0;
@@ -113,77 +139,248 @@ int radeon_sa_bo_manager_suspend(struct radeon_device *rdev,
113 return r; 139 return r;
114} 140}
115 141
116/* 142static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo)
117 * Principe is simple, we keep a list of sub allocation in offset 143{
118 * order (first entry has offset == 0, last entry has the highest 144 struct radeon_sa_manager *sa_manager = sa_bo->manager;
119 * offset). 145 if (sa_manager->hole == &sa_bo->olist) {
120 * 146 sa_manager->hole = sa_bo->olist.prev;
121 * When allocating new object we first check if there is room at 147 }
122 * the end total_size - (last_object_offset + last_object_size) >= 148 list_del_init(&sa_bo->olist);
123 * alloc_size. If so we allocate new object there. 149 list_del_init(&sa_bo->flist);
124 * 150 radeon_fence_unref(&sa_bo->fence);
125 * When there is not enough room at the end, we start waiting for 151 kfree(sa_bo);
126 * each sub object until we reach object_offset+object_size >= 152}
127 * alloc_size, this object then become the sub object we return. 153
128 * 154static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager)
129 * Alignment can't be bigger than page size 155{
130 */ 156 struct radeon_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 radeon_sa_bo, olist);
162 list_for_each_entry_safe_from(sa_bo, tmp, &sa_manager->olist, olist) {
163 if (sa_bo->fence == NULL || !radeon_fence_signaled(sa_bo->fence)) {
164 return;
165 }
166 radeon_sa_bo_remove_locked(sa_bo);
167 }
168}
169
170static inline unsigned radeon_sa_bo_hole_soffset(struct radeon_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 radeon_sa_bo, olist)->eoffset;
176 }
177 return 0;
178}
179
180static inline unsigned radeon_sa_bo_hole_eoffset(struct radeon_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 radeon_sa_bo, olist)->soffset;
186 }
187 return sa_manager->size;
188}
189
190static bool radeon_sa_bo_try_alloc(struct radeon_sa_manager *sa_manager,
191 struct radeon_sa_bo *sa_bo,
192 unsigned size, unsigned align)
193{
194 unsigned soffset, eoffset, wasted;
195
196 soffset = radeon_sa_bo_hole_soffset(sa_manager);
197 eoffset = radeon_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
214static bool radeon_sa_bo_next_hole(struct radeon_sa_manager *sa_manager,
215 struct radeon_fence **fences,
216 unsigned *tries)
217{
218 struct radeon_sa_bo *best_bo = NULL;
219 unsigned i, soffset, best, tmp;
220
221 /* if hole points to the end of the buffer */
222 if (sa_manager->hole->next == &sa_manager->olist) {
223 /* try again with its beginning */
224 sa_manager->hole = &sa_manager->olist;
225 return true;
226 }
227
228 soffset = radeon_sa_bo_hole_soffset(sa_manager);
229 /* to handle wrap around we add sa_manager->size */
230 best = sa_manager->size * 2;
231 /* go over all fence list and try to find the closest sa_bo
232 * of the current last
233 */
234 for (i = 0; i < RADEON_NUM_RINGS; ++i) {
235 struct radeon_sa_bo *sa_bo;
236
237 if (list_empty(&sa_manager->flist[i])) {
238 continue;
239 }
240
241 sa_bo = list_first_entry(&sa_manager->flist[i],
242 struct radeon_sa_bo, flist);
243
244 if (!radeon_fence_signaled(sa_bo->fence)) {
245 fences[i] = sa_bo->fence;
246 continue;
247 }
248
249 /* limit the number of tries each ring gets */
250 if (tries[i] > 2) {
251 continue;
252 }
253
254 tmp = sa_bo->soffset;
255 if (tmp < soffset) {
256 /* wrap around, pretend it's after */
257 tmp += sa_manager->size;
258 }
259 tmp -= soffset;
260 if (tmp < best) {
261 /* this sa bo is the closest one */
262 best = tmp;
263 best_bo = sa_bo;
264 }
265 }
266
267 if (best_bo) {
268 ++tries[best_bo->fence->ring];
269 sa_manager->hole = best_bo->olist.prev;
270
271 /* we knew that this one is signaled,
272 so it's save to remote it */
273 radeon_sa_bo_remove_locked(best_bo);
274 return true;
275 }
276 return false;
277}
278
131int radeon_sa_bo_new(struct radeon_device *rdev, 279int radeon_sa_bo_new(struct radeon_device *rdev,
132 struct radeon_sa_manager *sa_manager, 280 struct radeon_sa_manager *sa_manager,
133 struct radeon_sa_bo *sa_bo, 281 struct radeon_sa_bo **sa_bo,
134 unsigned size, unsigned align) 282 unsigned size, unsigned align, bool block)
135{ 283{
136 struct radeon_sa_bo *tmp; 284 struct radeon_fence *fences[RADEON_NUM_RINGS];
137 struct list_head *head; 285 unsigned tries[RADEON_NUM_RINGS];
138 unsigned offset = 0, wasted = 0; 286 int i, r = -ENOMEM;
139 287
140 BUG_ON(align > RADEON_GPU_PAGE_SIZE); 288 BUG_ON(align > RADEON_GPU_PAGE_SIZE);
141 BUG_ON(size > sa_manager->size); 289 BUG_ON(size > sa_manager->size);
142 290
143 /* no one ? */ 291 *sa_bo = kmalloc(sizeof(struct radeon_sa_bo), GFP_KERNEL);
144 head = sa_manager->sa_bo.prev; 292 if ((*sa_bo) == NULL) {
145 if (list_empty(&sa_manager->sa_bo)) { 293 return -ENOMEM;
146 goto out;
147 } 294 }
295 (*sa_bo)->manager = sa_manager;
296 (*sa_bo)->fence = NULL;
297 INIT_LIST_HEAD(&(*sa_bo)->olist);
298 INIT_LIST_HEAD(&(*sa_bo)->flist);
148 299
149 /* look for a hole big enough */ 300 spin_lock(&sa_manager->lock);
150 offset = 0; 301 do {
151 list_for_each_entry(tmp, &sa_manager->sa_bo, list) { 302 for (i = 0; i < RADEON_NUM_RINGS; ++i) {
152 /* room before this object ? */ 303 fences[i] = NULL;
153 if ((tmp->offset - offset) >= size) { 304 tries[i] = 0;
154 head = tmp->list.prev;
155 goto out;
156 } 305 }
157 offset = tmp->offset + tmp->size; 306
158 wasted = offset % align; 307 do {
159 if (wasted) { 308 radeon_sa_bo_try_free(sa_manager);
160 wasted = align - wasted; 309
310 if (radeon_sa_bo_try_alloc(sa_manager, *sa_bo,
311 size, align)) {
312 spin_unlock(&sa_manager->lock);
313 return 0;
314 }
315
316 /* see if we can skip over some allocations */
317 } while (radeon_sa_bo_next_hole(sa_manager, fences, tries));
318
319 if (block) {
320 spin_unlock(&sa_manager->lock);
321 r = radeon_fence_wait_any(rdev, fences, false);
322 spin_lock(&sa_manager->lock);
323 if (r) {
324 /* if we have nothing to wait for we
325 are practically out of memory */
326 if (r == -ENOENT) {
327 r = -ENOMEM;
328 }
329 goto out_err;
330 }
161 } 331 }
162 offset += wasted; 332 } while (block);
163 } 333
164 /* room at the end ? */ 334out_err:
165 head = sa_manager->sa_bo.prev; 335 spin_unlock(&sa_manager->lock);
166 tmp = list_entry(head, struct radeon_sa_bo, list); 336 kfree(*sa_bo);
167 offset = tmp->offset + tmp->size; 337 *sa_bo = NULL;
168 wasted = offset % align; 338 return r;
169 if (wasted) { 339}
170 wasted = align - wasted; 340
171 } 341void radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo **sa_bo,
172 offset += wasted; 342 struct radeon_fence *fence)
173 if ((sa_manager->size - offset) < size) { 343{
174 /* failed to find somethings big enough */ 344 struct radeon_sa_manager *sa_manager;
175 return -ENOMEM; 345
346 if (sa_bo == NULL || *sa_bo == NULL) {
347 return;
176 } 348 }
177 349
178out: 350 sa_manager = (*sa_bo)->manager;
179 sa_bo->manager = sa_manager; 351 spin_lock(&sa_manager->lock);
180 sa_bo->offset = offset; 352 if (fence && fence->seq && fence->seq < RADEON_FENCE_NOTEMITED_SEQ) {
181 sa_bo->size = size; 353 (*sa_bo)->fence = radeon_fence_ref(fence);
182 list_add(&sa_bo->list, head); 354 list_add_tail(&(*sa_bo)->flist,
183 return 0; 355 &sa_manager->flist[fence->ring]);
356 } else {
357 radeon_sa_bo_remove_locked(*sa_bo);
358 }
359 spin_unlock(&sa_manager->lock);
360 *sa_bo = NULL;
184} 361}
185 362
186void radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo *sa_bo) 363#if defined(CONFIG_DEBUG_FS)
364void radeon_sa_bo_dump_debug_info(struct radeon_sa_manager *sa_manager,
365 struct seq_file *m)
187{ 366{
188 list_del_init(&sa_bo->list); 367 struct radeon_sa_bo *i;
368
369 spin_lock(&sa_manager->lock);
370 list_for_each_entry(i, &sa_manager->olist, olist) {
371 if (&i->olist == sa_manager->hole) {
372 seq_printf(m, ">");
373 } else {
374 seq_printf(m, " ");
375 }
376 seq_printf(m, "[0x%08x 0x%08x] size %8d",
377 i->soffset, i->eoffset, i->eoffset - i->soffset);
378 if (i->fence) {
379 seq_printf(m, " protected by 0x%016llx on ring %d",
380 i->fence->seq, i->fence->ring);
381 }
382 seq_printf(m, "\n");
383 }
384 spin_unlock(&sa_manager->lock);
189} 385}
386#endif