1 /*	$NetBSD: radeon_sa.c,v 1.4 2021/12/18 23:45:43 riastradh Exp $	*/
2 
3 /*
4  * Copyright 2011 Red Hat Inc.
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sub license, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * The above copyright notice and this permission notice (including the
24  * next paragraph) shall be included in all copies or substantial portions
25  * of the Software.
26  *
27  */
28 /*
29  * Authors:
30  *    Jerome Glisse <glisse@freedesktop.org>
31  */
32 /* Algorithm:
33  *
34  * We store the last allocated bo in "hole", we always try to allocate
35  * after the last allocated bo. Principle is that in a linear GPU ring
36  * progression was is after last is the oldest bo we allocated and thus
37  * the first one that should no longer be in use by the GPU.
38  *
39  * If it's not the case we skip over the bo after last to the closest
40  * done bo if such one exist. If none exist and we are not asked to
41  * block we report failure to allocate.
42  *
43  * If we are asked to block we wait on all the oldest fence of all
44  * rings. We just wait for any of those fence to complete.
45  */
46 
47 #include <sys/cdefs.h>
48 __KERNEL_RCSID(0, "$NetBSD: radeon_sa.c,v 1.4 2021/12/18 23:45:43 riastradh Exp $");
49 
50 #include "radeon.h"
51 
52 static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo);
53 static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager);
54 
radeon_sa_bo_manager_init(struct radeon_device * rdev,struct radeon_sa_manager * sa_manager,unsigned size,u32 align,u32 domain,u32 flags)55 int radeon_sa_bo_manager_init(struct radeon_device *rdev,
56 			      struct radeon_sa_manager *sa_manager,
57 			      unsigned size, u32 align, u32 domain, u32 flags)
58 {
59 	int i, r;
60 
61 #ifdef __NetBSD__
62 	spin_lock_init(&sa_manager->wq_lock);
63 	DRM_INIT_WAITQUEUE(&sa_manager->wq, "radsabom");
64 #else
65 	init_waitqueue_head(&sa_manager->wq);
66 #endif
67 	sa_manager->bo = NULL;
68 	sa_manager->size = size;
69 	sa_manager->domain = domain;
70 	sa_manager->align = align;
71 	sa_manager->hole = &sa_manager->olist;
72 	INIT_LIST_HEAD(&sa_manager->olist);
73 	for (i = 0; i < RADEON_NUM_RINGS; ++i) {
74 		INIT_LIST_HEAD(&sa_manager->flist[i]);
75 	}
76 
77 	r = radeon_bo_create(rdev, size, align, true,
78 			     domain, flags, NULL, NULL, &sa_manager->bo);
79 	if (r) {
80 		dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r);
81 		return r;
82 	}
83 
84 	return r;
85 }
86 
radeon_sa_bo_manager_fini(struct radeon_device * rdev,struct radeon_sa_manager * sa_manager)87 void radeon_sa_bo_manager_fini(struct radeon_device *rdev,
88 			       struct radeon_sa_manager *sa_manager)
89 {
90 	struct radeon_sa_bo *sa_bo, *tmp;
91 
92 	if (!list_empty(&sa_manager->olist)) {
93 		sa_manager->hole = &sa_manager->olist,
94 		radeon_sa_bo_try_free(sa_manager);
95 		if (!list_empty(&sa_manager->olist)) {
96 			dev_err(rdev->dev, "sa_manager is not empty, clearing anyway\n");
97 		}
98 	}
99 	list_for_each_entry_safe(sa_bo, tmp, &sa_manager->olist, olist) {
100 		radeon_sa_bo_remove_locked(sa_bo);
101 	}
102 	radeon_bo_unref(&sa_manager->bo);
103 	sa_manager->size = 0;
104 #ifdef __NetBSD__
105 	DRM_DESTROY_WAITQUEUE(&sa_manager->wq);
106 	spin_lock_destroy(&sa_manager->wq_lock);
107 #endif
108 }
109 
radeon_sa_bo_manager_start(struct radeon_device * rdev,struct radeon_sa_manager * sa_manager)110 int radeon_sa_bo_manager_start(struct radeon_device *rdev,
111 			       struct radeon_sa_manager *sa_manager)
112 {
113 	int r;
114 
115 	if (sa_manager->bo == NULL) {
116 		dev_err(rdev->dev, "no bo for sa manager\n");
117 		return -EINVAL;
118 	}
119 
120 	/* map the buffer */
121 	r = radeon_bo_reserve(sa_manager->bo, false);
122 	if (r) {
123 		dev_err(rdev->dev, "(%d) failed to reserve manager bo\n", r);
124 		return r;
125 	}
126 	r = radeon_bo_pin(sa_manager->bo, sa_manager->domain, &sa_manager->gpu_addr);
127 	if (r) {
128 		radeon_bo_unreserve(sa_manager->bo);
129 		dev_err(rdev->dev, "(%d) failed to pin manager bo\n", r);
130 		return r;
131 	}
132 	r = radeon_bo_kmap(sa_manager->bo, &sa_manager->cpu_ptr);
133 	radeon_bo_unreserve(sa_manager->bo);
134 	return r;
135 }
136 
radeon_sa_bo_manager_suspend(struct radeon_device * rdev,struct radeon_sa_manager * sa_manager)137 int radeon_sa_bo_manager_suspend(struct radeon_device *rdev,
138 				 struct radeon_sa_manager *sa_manager)
139 {
140 	int r;
141 
142 	if (sa_manager->bo == NULL) {
143 		dev_err(rdev->dev, "no bo for sa manager\n");
144 		return -EINVAL;
145 	}
146 
147 	r = radeon_bo_reserve(sa_manager->bo, false);
148 	if (!r) {
149 		radeon_bo_kunmap(sa_manager->bo);
150 		radeon_bo_unpin(sa_manager->bo);
151 		radeon_bo_unreserve(sa_manager->bo);
152 	}
153 	return r;
154 }
155 
radeon_sa_bo_remove_locked(struct radeon_sa_bo * sa_bo)156 static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo)
157 {
158 	struct radeon_sa_manager *sa_manager = sa_bo->manager;
159 	if (sa_manager->hole == &sa_bo->olist) {
160 		sa_manager->hole = sa_bo->olist.prev;
161 	}
162 	list_del_init(&sa_bo->olist);
163 	list_del_init(&sa_bo->flist);
164 	radeon_fence_unref(&sa_bo->fence);
165 	kfree(sa_bo);
166 }
167 
radeon_sa_bo_try_free(struct radeon_sa_manager * sa_manager)168 static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager)
169 {
170 	struct radeon_sa_bo *sa_bo, *tmp;
171 
172 	if (sa_manager->hole->next == &sa_manager->olist)
173 		return;
174 
175 	sa_bo = list_entry(sa_manager->hole->next, struct radeon_sa_bo, olist);
176 	list_for_each_entry_safe_from(sa_bo, tmp, &sa_manager->olist, olist) {
177 		if (sa_bo->fence == NULL || !radeon_fence_signaled(sa_bo->fence)) {
178 			return;
179 		}
180 		radeon_sa_bo_remove_locked(sa_bo);
181 	}
182 }
183 
radeon_sa_bo_hole_soffset(struct radeon_sa_manager * sa_manager)184 static inline unsigned radeon_sa_bo_hole_soffset(struct radeon_sa_manager *sa_manager)
185 {
186 	struct list_head *hole = sa_manager->hole;
187 
188 	if (hole != &sa_manager->olist) {
189 		return list_entry(hole, struct radeon_sa_bo, olist)->eoffset;
190 	}
191 	return 0;
192 }
193 
radeon_sa_bo_hole_eoffset(struct radeon_sa_manager * sa_manager)194 static inline unsigned radeon_sa_bo_hole_eoffset(struct radeon_sa_manager *sa_manager)
195 {
196 	struct list_head *hole = sa_manager->hole;
197 
198 	if (hole->next != &sa_manager->olist) {
199 		return list_entry(hole->next, struct radeon_sa_bo, olist)->soffset;
200 	}
201 	return sa_manager->size;
202 }
203 
radeon_sa_bo_try_alloc(struct radeon_sa_manager * sa_manager,struct radeon_sa_bo * sa_bo,unsigned size,unsigned align)204 static bool radeon_sa_bo_try_alloc(struct radeon_sa_manager *sa_manager,
205 				   struct radeon_sa_bo *sa_bo,
206 				   unsigned size, unsigned align)
207 {
208 	unsigned soffset, eoffset, wasted;
209 
210 	soffset = radeon_sa_bo_hole_soffset(sa_manager);
211 	eoffset = radeon_sa_bo_hole_eoffset(sa_manager);
212 	wasted = (align - (soffset % align)) % align;
213 
214 	if ((eoffset - soffset) >= (size + wasted)) {
215 		soffset += wasted;
216 
217 		sa_bo->manager = sa_manager;
218 		sa_bo->soffset = soffset;
219 		sa_bo->eoffset = soffset + size;
220 		list_add(&sa_bo->olist, sa_manager->hole);
221 		INIT_LIST_HEAD(&sa_bo->flist);
222 		sa_manager->hole = &sa_bo->olist;
223 		return true;
224 	}
225 	return false;
226 }
227 
228 /**
229  * radeon_sa_event - Check if we can stop waiting
230  *
231  * @sa_manager: pointer to the sa_manager
232  * @size: number of bytes we want to allocate
233  * @align: alignment we need to match
234  *
235  * Check if either there is a fence we can wait for or
236  * enough free memory to satisfy the allocation directly
237  */
radeon_sa_event(struct radeon_sa_manager * sa_manager,unsigned size,unsigned align)238 static bool radeon_sa_event(struct radeon_sa_manager *sa_manager,
239 			    unsigned size, unsigned align)
240 {
241 	unsigned soffset, eoffset, wasted;
242 	int i;
243 
244 	for (i = 0; i < RADEON_NUM_RINGS; ++i) {
245 		if (!list_empty(&sa_manager->flist[i])) {
246 			return true;
247 		}
248 	}
249 
250 	soffset = radeon_sa_bo_hole_soffset(sa_manager);
251 	eoffset = radeon_sa_bo_hole_eoffset(sa_manager);
252 	wasted = (align - (soffset % align)) % align;
253 
254 	if ((eoffset - soffset) >= (size + wasted)) {
255 		return true;
256 	}
257 
258 	return false;
259 }
260 
radeon_sa_bo_next_hole(struct radeon_sa_manager * sa_manager,struct radeon_fence ** fences,unsigned * tries)261 static bool radeon_sa_bo_next_hole(struct radeon_sa_manager *sa_manager,
262 				   struct radeon_fence **fences,
263 				   unsigned *tries)
264 {
265 	struct radeon_sa_bo *best_bo = NULL;
266 	unsigned i, soffset, best, tmp;
267 
268 	/* if hole points to the end of the buffer */
269 	if (sa_manager->hole->next == &sa_manager->olist) {
270 		/* try again with its beginning */
271 		sa_manager->hole = &sa_manager->olist;
272 		return true;
273 	}
274 
275 	soffset = radeon_sa_bo_hole_soffset(sa_manager);
276 	/* to handle wrap around we add sa_manager->size */
277 	best = sa_manager->size * 2;
278 	/* go over all fence list and try to find the closest sa_bo
279 	 * of the current last
280 	 */
281 	for (i = 0; i < RADEON_NUM_RINGS; ++i) {
282 		struct radeon_sa_bo *sa_bo;
283 
284 		if (list_empty(&sa_manager->flist[i])) {
285 			continue;
286 		}
287 
288 		sa_bo = list_first_entry(&sa_manager->flist[i],
289 					 struct radeon_sa_bo, flist);
290 
291 		if (!radeon_fence_signaled(sa_bo->fence)) {
292 			fences[i] = sa_bo->fence;
293 			continue;
294 		}
295 
296 		/* limit the number of tries each ring gets */
297 		if (tries[i] > 2) {
298 			continue;
299 		}
300 
301 		tmp = sa_bo->soffset;
302 		if (tmp < soffset) {
303 			/* wrap around, pretend it's after */
304 			tmp += sa_manager->size;
305 		}
306 		tmp -= soffset;
307 		if (tmp < best) {
308 			/* this sa bo is the closest one */
309 			best = tmp;
310 			best_bo = sa_bo;
311 		}
312 	}
313 
314 	if (best_bo) {
315 		++tries[best_bo->fence->ring];
316 		sa_manager->hole = best_bo->olist.prev;
317 
318 		/* we knew that this one is signaled,
319 		   so it's save to remote it */
320 		radeon_sa_bo_remove_locked(best_bo);
321 		return true;
322 	}
323 	return false;
324 }
325 
radeon_sa_bo_new(struct radeon_device * rdev,struct radeon_sa_manager * sa_manager,struct radeon_sa_bo ** sa_bo,unsigned size,unsigned align)326 int radeon_sa_bo_new(struct radeon_device *rdev,
327 		     struct radeon_sa_manager *sa_manager,
328 		     struct radeon_sa_bo **sa_bo,
329 		     unsigned size, unsigned align)
330 {
331 	struct radeon_fence *fences[RADEON_NUM_RINGS];
332 	unsigned tries[RADEON_NUM_RINGS];
333 	int i, r;
334 
335 	BUG_ON(align > sa_manager->align);
336 	BUG_ON(size > sa_manager->size);
337 
338 	*sa_bo = kmalloc(sizeof(struct radeon_sa_bo), GFP_KERNEL);
339 	if ((*sa_bo) == NULL) {
340 		return -ENOMEM;
341 	}
342 	(*sa_bo)->manager = sa_manager;
343 	(*sa_bo)->fence = NULL;
344 	INIT_LIST_HEAD(&(*sa_bo)->olist);
345 	INIT_LIST_HEAD(&(*sa_bo)->flist);
346 
347 #ifdef __NetBSD__
348 	spin_lock(&sa_manager->wq_lock);
349 #else
350 	spin_lock(&sa_manager->wq.lock);
351 #endif
352 	do {
353 		for (i = 0; i < RADEON_NUM_RINGS; ++i) {
354 			fences[i] = NULL;
355 			tries[i] = 0;
356 		}
357 
358 		do {
359 			radeon_sa_bo_try_free(sa_manager);
360 
361 			if (radeon_sa_bo_try_alloc(sa_manager, *sa_bo,
362 						   size, align)) {
363 #ifdef __NetBSD__
364 				spin_unlock(&sa_manager->wq_lock);
365 #else
366 				spin_unlock(&sa_manager->wq.lock);
367 #endif
368 				return 0;
369 			}
370 
371 			/* see if we can skip over some allocations */
372 		} while (radeon_sa_bo_next_hole(sa_manager, fences, tries));
373 
374 		for (i = 0; i < RADEON_NUM_RINGS; ++i)
375 			radeon_fence_ref(fences[i]);
376 
377 #ifdef __NetBSD__
378 		spin_unlock(&sa_manager->wq_lock);
379 		r = radeon_fence_wait_any(rdev, fences, false);
380 		for (i = 0; i < RADEON_NUM_RINGS; ++i)
381 			radeon_fence_unref(&fences[i]);
382 		spin_lock(&sa_manager->wq_lock);
383 		/* if we have nothing to wait for block */
384 		if (r == -ENOENT)
385 			DRM_SPIN_WAIT_UNTIL(r, &sa_manager->wq,
386 			    &sa_manager->wq_lock,
387 			    radeon_sa_event(sa_manager, size, align));
388 #else
389 		spin_unlock(&sa_manager->wq.lock);
390 		r = radeon_fence_wait_any(rdev, fences, false);
391 		for (i = 0; i < RADEON_NUM_RINGS; ++i)
392 			radeon_fence_unref(&fences[i]);
393 		spin_lock(&sa_manager->wq.lock);
394 		/* if we have nothing to wait for block */
395 		if (r == -ENOENT) {
396 			r = wait_event_interruptible_locked(
397 				sa_manager->wq,
398 				radeon_sa_event(sa_manager, size, align)
399 			);
400 		}
401 #endif
402 
403 	} while (!r);
404 
405 #ifdef __NetBSD__
406 	spin_unlock(&sa_manager->wq_lock);
407 #else
408 	spin_unlock(&sa_manager->wq.lock);
409 #endif
410 	kfree(*sa_bo);
411 	*sa_bo = NULL;
412 	return r;
413 }
414 
radeon_sa_bo_free(struct radeon_device * rdev,struct radeon_sa_bo ** sa_bo,struct radeon_fence * fence)415 void radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo **sa_bo,
416 		       struct radeon_fence *fence)
417 {
418 	struct radeon_sa_manager *sa_manager;
419 
420 	if (sa_bo == NULL || *sa_bo == NULL) {
421 		return;
422 	}
423 
424 	sa_manager = (*sa_bo)->manager;
425 #ifdef __NetBSD__
426 	spin_lock(&sa_manager->wq_lock);
427 #else
428 	spin_lock(&sa_manager->wq.lock);
429 #endif
430 	if (fence && !radeon_fence_signaled(fence)) {
431 		(*sa_bo)->fence = radeon_fence_ref(fence);
432 		list_add_tail(&(*sa_bo)->flist,
433 			      &sa_manager->flist[fence->ring]);
434 	} else {
435 		radeon_sa_bo_remove_locked(*sa_bo);
436 	}
437 #ifdef __NetBSD__
438 	DRM_SPIN_WAKEUP_ALL(&sa_manager->wq, &sa_manager->wq_lock);
439 	spin_unlock(&sa_manager->wq_lock);
440 #else
441 	wake_up_all_locked(&sa_manager->wq);
442 	spin_unlock(&sa_manager->wq.lock);
443 #endif
444 	*sa_bo = NULL;
445 }
446 
447 #if defined(CONFIG_DEBUG_FS)
radeon_sa_bo_dump_debug_info(struct radeon_sa_manager * sa_manager,struct seq_file * m)448 void radeon_sa_bo_dump_debug_info(struct radeon_sa_manager *sa_manager,
449 				  struct seq_file *m)
450 {
451 	struct radeon_sa_bo *i;
452 
453 	spin_lock(&sa_manager->wq.lock);
454 	list_for_each_entry(i, &sa_manager->olist, olist) {
455 		uint64_t soffset = i->soffset + sa_manager->gpu_addr;
456 		uint64_t eoffset = i->eoffset + sa_manager->gpu_addr;
457 		if (&i->olist == sa_manager->hole) {
458 			seq_printf(m, ">");
459 		} else {
460 			seq_printf(m, " ");
461 		}
462 		seq_printf(m, "[0x%010llx 0x%010llx] size %8lld",
463 			   soffset, eoffset, eoffset - soffset);
464 		if (i->fence) {
465 			seq_printf(m, " protected by 0x%016llx on ring %d",
466 				   i->fence->seq, i->fence->ring);
467 		}
468 		seq_printf(m, "\n");
469 	}
470 	spin_unlock(&sa_manager->wq.lock);
471 }
472 #endif
473