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