18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2011 Red Hat Inc.
38c2ecf20Sopenharmony_ci * All Rights Reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
68c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the
78c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
88c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
98c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
108c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
118c2ecf20Sopenharmony_ci * the following conditions:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
148c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
158c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
168c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
178c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
188c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
198c2ecf20Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the
228c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
238c2ecf20Sopenharmony_ci * of the Software.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * Authors:
288c2ecf20Sopenharmony_ci *    Jerome Glisse <glisse@freedesktop.org>
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci/* Algorithm:
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * We store the last allocated bo in "hole", we always try to allocate
338c2ecf20Sopenharmony_ci * after the last allocated bo. Principle is that in a linear GPU ring
348c2ecf20Sopenharmony_ci * progression was is after last is the oldest bo we allocated and thus
358c2ecf20Sopenharmony_ci * the first one that should no longer be in use by the GPU.
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * If it's not the case we skip over the bo after last to the closest
388c2ecf20Sopenharmony_ci * done bo if such one exist. If none exist and we are not asked to
398c2ecf20Sopenharmony_ci * block we report failure to allocate.
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * If we are asked to block we wait on all the oldest fence of all
428c2ecf20Sopenharmony_ci * rings. We just wait for any of those fence to complete.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include "radeon.h"
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo);
488c2ecf20Sopenharmony_cistatic void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ciint radeon_sa_bo_manager_init(struct radeon_device *rdev,
518c2ecf20Sopenharmony_ci			      struct radeon_sa_manager *sa_manager,
528c2ecf20Sopenharmony_ci			      unsigned size, u32 align, u32 domain, u32 flags)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int i, r;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	init_waitqueue_head(&sa_manager->wq);
578c2ecf20Sopenharmony_ci	sa_manager->bo = NULL;
588c2ecf20Sopenharmony_ci	sa_manager->size = size;
598c2ecf20Sopenharmony_ci	sa_manager->domain = domain;
608c2ecf20Sopenharmony_ci	sa_manager->align = align;
618c2ecf20Sopenharmony_ci	sa_manager->hole = &sa_manager->olist;
628c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&sa_manager->olist);
638c2ecf20Sopenharmony_ci	for (i = 0; i < RADEON_NUM_RINGS; ++i) {
648c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&sa_manager->flist[i]);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	r = radeon_bo_create(rdev, size, align, true,
688c2ecf20Sopenharmony_ci			     domain, flags, NULL, NULL, &sa_manager->bo);
698c2ecf20Sopenharmony_ci	if (r) {
708c2ecf20Sopenharmony_ci		dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r);
718c2ecf20Sopenharmony_ci		return r;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return r;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_civoid radeon_sa_bo_manager_fini(struct radeon_device *rdev,
788c2ecf20Sopenharmony_ci			       struct radeon_sa_manager *sa_manager)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct radeon_sa_bo *sa_bo, *tmp;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (!list_empty(&sa_manager->olist)) {
838c2ecf20Sopenharmony_ci		sa_manager->hole = &sa_manager->olist,
848c2ecf20Sopenharmony_ci		radeon_sa_bo_try_free(sa_manager);
858c2ecf20Sopenharmony_ci		if (!list_empty(&sa_manager->olist)) {
868c2ecf20Sopenharmony_ci			dev_err(rdev->dev, "sa_manager is not empty, clearing anyway\n");
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	list_for_each_entry_safe(sa_bo, tmp, &sa_manager->olist, olist) {
908c2ecf20Sopenharmony_ci		radeon_sa_bo_remove_locked(sa_bo);
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	radeon_bo_unref(&sa_manager->bo);
938c2ecf20Sopenharmony_ci	sa_manager->size = 0;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ciint radeon_sa_bo_manager_start(struct radeon_device *rdev,
978c2ecf20Sopenharmony_ci			       struct radeon_sa_manager *sa_manager)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	int r;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (sa_manager->bo == NULL) {
1028c2ecf20Sopenharmony_ci		dev_err(rdev->dev, "no bo for sa manager\n");
1038c2ecf20Sopenharmony_ci		return -EINVAL;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* map the buffer */
1078c2ecf20Sopenharmony_ci	r = radeon_bo_reserve(sa_manager->bo, false);
1088c2ecf20Sopenharmony_ci	if (r) {
1098c2ecf20Sopenharmony_ci		dev_err(rdev->dev, "(%d) failed to reserve manager bo\n", r);
1108c2ecf20Sopenharmony_ci		return r;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci	r = radeon_bo_pin(sa_manager->bo, sa_manager->domain, &sa_manager->gpu_addr);
1138c2ecf20Sopenharmony_ci	if (r) {
1148c2ecf20Sopenharmony_ci		radeon_bo_unreserve(sa_manager->bo);
1158c2ecf20Sopenharmony_ci		dev_err(rdev->dev, "(%d) failed to pin manager bo\n", r);
1168c2ecf20Sopenharmony_ci		return r;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	r = radeon_bo_kmap(sa_manager->bo, &sa_manager->cpu_ptr);
1198c2ecf20Sopenharmony_ci	radeon_bo_unreserve(sa_manager->bo);
1208c2ecf20Sopenharmony_ci	return r;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ciint radeon_sa_bo_manager_suspend(struct radeon_device *rdev,
1248c2ecf20Sopenharmony_ci				 struct radeon_sa_manager *sa_manager)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	int r;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (sa_manager->bo == NULL) {
1298c2ecf20Sopenharmony_ci		dev_err(rdev->dev, "no bo for sa manager\n");
1308c2ecf20Sopenharmony_ci		return -EINVAL;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	r = radeon_bo_reserve(sa_manager->bo, false);
1348c2ecf20Sopenharmony_ci	if (!r) {
1358c2ecf20Sopenharmony_ci		radeon_bo_kunmap(sa_manager->bo);
1368c2ecf20Sopenharmony_ci		radeon_bo_unpin(sa_manager->bo);
1378c2ecf20Sopenharmony_ci		radeon_bo_unreserve(sa_manager->bo);
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci	return r;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct radeon_sa_manager *sa_manager = sa_bo->manager;
1458c2ecf20Sopenharmony_ci	if (sa_manager->hole == &sa_bo->olist) {
1468c2ecf20Sopenharmony_ci		sa_manager->hole = sa_bo->olist.prev;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci	list_del_init(&sa_bo->olist);
1498c2ecf20Sopenharmony_ci	list_del_init(&sa_bo->flist);
1508c2ecf20Sopenharmony_ci	radeon_fence_unref(&sa_bo->fence);
1518c2ecf20Sopenharmony_ci	kfree(sa_bo);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct radeon_sa_bo *sa_bo, *tmp;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (sa_manager->hole->next == &sa_manager->olist)
1598c2ecf20Sopenharmony_ci		return;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	sa_bo = list_entry(sa_manager->hole->next, struct radeon_sa_bo, olist);
1628c2ecf20Sopenharmony_ci	list_for_each_entry_safe_from(sa_bo, tmp, &sa_manager->olist, olist) {
1638c2ecf20Sopenharmony_ci		if (sa_bo->fence == NULL || !radeon_fence_signaled(sa_bo->fence)) {
1648c2ecf20Sopenharmony_ci			return;
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci		radeon_sa_bo_remove_locked(sa_bo);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic inline unsigned radeon_sa_bo_hole_soffset(struct radeon_sa_manager *sa_manager)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct list_head *hole = sa_manager->hole;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (hole != &sa_manager->olist) {
1758c2ecf20Sopenharmony_ci		return list_entry(hole, struct radeon_sa_bo, olist)->eoffset;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci	return 0;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic inline unsigned radeon_sa_bo_hole_eoffset(struct radeon_sa_manager *sa_manager)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct list_head *hole = sa_manager->hole;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (hole->next != &sa_manager->olist) {
1858c2ecf20Sopenharmony_ci		return list_entry(hole->next, struct radeon_sa_bo, olist)->soffset;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci	return sa_manager->size;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic bool radeon_sa_bo_try_alloc(struct radeon_sa_manager *sa_manager,
1918c2ecf20Sopenharmony_ci				   struct radeon_sa_bo *sa_bo,
1928c2ecf20Sopenharmony_ci				   unsigned size, unsigned align)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	unsigned soffset, eoffset, wasted;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	soffset = radeon_sa_bo_hole_soffset(sa_manager);
1978c2ecf20Sopenharmony_ci	eoffset = radeon_sa_bo_hole_eoffset(sa_manager);
1988c2ecf20Sopenharmony_ci	wasted = (align - (soffset % align)) % align;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if ((eoffset - soffset) >= (size + wasted)) {
2018c2ecf20Sopenharmony_ci		soffset += wasted;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		sa_bo->manager = sa_manager;
2048c2ecf20Sopenharmony_ci		sa_bo->soffset = soffset;
2058c2ecf20Sopenharmony_ci		sa_bo->eoffset = soffset + size;
2068c2ecf20Sopenharmony_ci		list_add(&sa_bo->olist, sa_manager->hole);
2078c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&sa_bo->flist);
2088c2ecf20Sopenharmony_ci		sa_manager->hole = &sa_bo->olist;
2098c2ecf20Sopenharmony_ci		return true;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	return false;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/**
2158c2ecf20Sopenharmony_ci * radeon_sa_event - Check if we can stop waiting
2168c2ecf20Sopenharmony_ci *
2178c2ecf20Sopenharmony_ci * @sa_manager: pointer to the sa_manager
2188c2ecf20Sopenharmony_ci * @size: number of bytes we want to allocate
2198c2ecf20Sopenharmony_ci * @align: alignment we need to match
2208c2ecf20Sopenharmony_ci *
2218c2ecf20Sopenharmony_ci * Check if either there is a fence we can wait for or
2228c2ecf20Sopenharmony_ci * enough free memory to satisfy the allocation directly
2238c2ecf20Sopenharmony_ci */
2248c2ecf20Sopenharmony_cistatic bool radeon_sa_event(struct radeon_sa_manager *sa_manager,
2258c2ecf20Sopenharmony_ci			    unsigned size, unsigned align)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	unsigned soffset, eoffset, wasted;
2288c2ecf20Sopenharmony_ci	int i;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	for (i = 0; i < RADEON_NUM_RINGS; ++i) {
2318c2ecf20Sopenharmony_ci		if (!list_empty(&sa_manager->flist[i])) {
2328c2ecf20Sopenharmony_ci			return true;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	soffset = radeon_sa_bo_hole_soffset(sa_manager);
2378c2ecf20Sopenharmony_ci	eoffset = radeon_sa_bo_hole_eoffset(sa_manager);
2388c2ecf20Sopenharmony_ci	wasted = (align - (soffset % align)) % align;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if ((eoffset - soffset) >= (size + wasted)) {
2418c2ecf20Sopenharmony_ci		return true;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return false;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic bool radeon_sa_bo_next_hole(struct radeon_sa_manager *sa_manager,
2488c2ecf20Sopenharmony_ci				   struct radeon_fence **fences,
2498c2ecf20Sopenharmony_ci				   unsigned *tries)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct radeon_sa_bo *best_bo = NULL;
2528c2ecf20Sopenharmony_ci	unsigned i, soffset, best, tmp;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* if hole points to the end of the buffer */
2558c2ecf20Sopenharmony_ci	if (sa_manager->hole->next == &sa_manager->olist) {
2568c2ecf20Sopenharmony_ci		/* try again with its beginning */
2578c2ecf20Sopenharmony_ci		sa_manager->hole = &sa_manager->olist;
2588c2ecf20Sopenharmony_ci		return true;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	soffset = radeon_sa_bo_hole_soffset(sa_manager);
2628c2ecf20Sopenharmony_ci	/* to handle wrap around we add sa_manager->size */
2638c2ecf20Sopenharmony_ci	best = sa_manager->size * 2;
2648c2ecf20Sopenharmony_ci	/* go over all fence list and try to find the closest sa_bo
2658c2ecf20Sopenharmony_ci	 * of the current last
2668c2ecf20Sopenharmony_ci	 */
2678c2ecf20Sopenharmony_ci	for (i = 0; i < RADEON_NUM_RINGS; ++i) {
2688c2ecf20Sopenharmony_ci		struct radeon_sa_bo *sa_bo;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		if (list_empty(&sa_manager->flist[i])) {
2718c2ecf20Sopenharmony_ci			continue;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		sa_bo = list_first_entry(&sa_manager->flist[i],
2758c2ecf20Sopenharmony_ci					 struct radeon_sa_bo, flist);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		if (!radeon_fence_signaled(sa_bo->fence)) {
2788c2ecf20Sopenharmony_ci			fences[i] = sa_bo->fence;
2798c2ecf20Sopenharmony_ci			continue;
2808c2ecf20Sopenharmony_ci		}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci		/* limit the number of tries each ring gets */
2838c2ecf20Sopenharmony_ci		if (tries[i] > 2) {
2848c2ecf20Sopenharmony_ci			continue;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		tmp = sa_bo->soffset;
2888c2ecf20Sopenharmony_ci		if (tmp < soffset) {
2898c2ecf20Sopenharmony_ci			/* wrap around, pretend it's after */
2908c2ecf20Sopenharmony_ci			tmp += sa_manager->size;
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci		tmp -= soffset;
2938c2ecf20Sopenharmony_ci		if (tmp < best) {
2948c2ecf20Sopenharmony_ci			/* this sa bo is the closest one */
2958c2ecf20Sopenharmony_ci			best = tmp;
2968c2ecf20Sopenharmony_ci			best_bo = sa_bo;
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (best_bo) {
3018c2ecf20Sopenharmony_ci		++tries[best_bo->fence->ring];
3028c2ecf20Sopenharmony_ci		sa_manager->hole = best_bo->olist.prev;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		/* we knew that this one is signaled,
3058c2ecf20Sopenharmony_ci		   so it's save to remote it */
3068c2ecf20Sopenharmony_ci		radeon_sa_bo_remove_locked(best_bo);
3078c2ecf20Sopenharmony_ci		return true;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci	return false;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ciint radeon_sa_bo_new(struct radeon_device *rdev,
3138c2ecf20Sopenharmony_ci		     struct radeon_sa_manager *sa_manager,
3148c2ecf20Sopenharmony_ci		     struct radeon_sa_bo **sa_bo,
3158c2ecf20Sopenharmony_ci		     unsigned size, unsigned align)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct radeon_fence *fences[RADEON_NUM_RINGS];
3188c2ecf20Sopenharmony_ci	unsigned tries[RADEON_NUM_RINGS];
3198c2ecf20Sopenharmony_ci	int i, r;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	BUG_ON(align > sa_manager->align);
3228c2ecf20Sopenharmony_ci	BUG_ON(size > sa_manager->size);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	*sa_bo = kmalloc(sizeof(struct radeon_sa_bo), GFP_KERNEL);
3258c2ecf20Sopenharmony_ci	if ((*sa_bo) == NULL) {
3268c2ecf20Sopenharmony_ci		return -ENOMEM;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci	(*sa_bo)->manager = sa_manager;
3298c2ecf20Sopenharmony_ci	(*sa_bo)->fence = NULL;
3308c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&(*sa_bo)->olist);
3318c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&(*sa_bo)->flist);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	spin_lock(&sa_manager->wq.lock);
3348c2ecf20Sopenharmony_ci	do {
3358c2ecf20Sopenharmony_ci		for (i = 0; i < RADEON_NUM_RINGS; ++i) {
3368c2ecf20Sopenharmony_ci			fences[i] = NULL;
3378c2ecf20Sopenharmony_ci			tries[i] = 0;
3388c2ecf20Sopenharmony_ci		}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		do {
3418c2ecf20Sopenharmony_ci			radeon_sa_bo_try_free(sa_manager);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci			if (radeon_sa_bo_try_alloc(sa_manager, *sa_bo,
3448c2ecf20Sopenharmony_ci						   size, align)) {
3458c2ecf20Sopenharmony_ci				spin_unlock(&sa_manager->wq.lock);
3468c2ecf20Sopenharmony_ci				return 0;
3478c2ecf20Sopenharmony_ci			}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci			/* see if we can skip over some allocations */
3508c2ecf20Sopenharmony_ci		} while (radeon_sa_bo_next_hole(sa_manager, fences, tries));
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		for (i = 0; i < RADEON_NUM_RINGS; ++i)
3538c2ecf20Sopenharmony_ci			radeon_fence_ref(fences[i]);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		spin_unlock(&sa_manager->wq.lock);
3568c2ecf20Sopenharmony_ci		r = radeon_fence_wait_any(rdev, fences, false);
3578c2ecf20Sopenharmony_ci		for (i = 0; i < RADEON_NUM_RINGS; ++i)
3588c2ecf20Sopenharmony_ci			radeon_fence_unref(&fences[i]);
3598c2ecf20Sopenharmony_ci		spin_lock(&sa_manager->wq.lock);
3608c2ecf20Sopenharmony_ci		/* if we have nothing to wait for block */
3618c2ecf20Sopenharmony_ci		if (r == -ENOENT) {
3628c2ecf20Sopenharmony_ci			r = wait_event_interruptible_locked(
3638c2ecf20Sopenharmony_ci				sa_manager->wq,
3648c2ecf20Sopenharmony_ci				radeon_sa_event(sa_manager, size, align)
3658c2ecf20Sopenharmony_ci			);
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	} while (!r);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	spin_unlock(&sa_manager->wq.lock);
3718c2ecf20Sopenharmony_ci	kfree(*sa_bo);
3728c2ecf20Sopenharmony_ci	*sa_bo = NULL;
3738c2ecf20Sopenharmony_ci	return r;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_civoid radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo **sa_bo,
3778c2ecf20Sopenharmony_ci		       struct radeon_fence *fence)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct radeon_sa_manager *sa_manager;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (sa_bo == NULL || *sa_bo == NULL) {
3828c2ecf20Sopenharmony_ci		return;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	sa_manager = (*sa_bo)->manager;
3868c2ecf20Sopenharmony_ci	spin_lock(&sa_manager->wq.lock);
3878c2ecf20Sopenharmony_ci	if (fence && !radeon_fence_signaled(fence)) {
3888c2ecf20Sopenharmony_ci		(*sa_bo)->fence = radeon_fence_ref(fence);
3898c2ecf20Sopenharmony_ci		list_add_tail(&(*sa_bo)->flist,
3908c2ecf20Sopenharmony_ci			      &sa_manager->flist[fence->ring]);
3918c2ecf20Sopenharmony_ci	} else {
3928c2ecf20Sopenharmony_ci		radeon_sa_bo_remove_locked(*sa_bo);
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	wake_up_all_locked(&sa_manager->wq);
3958c2ecf20Sopenharmony_ci	spin_unlock(&sa_manager->wq.lock);
3968c2ecf20Sopenharmony_ci	*sa_bo = NULL;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
4008c2ecf20Sopenharmony_civoid radeon_sa_bo_dump_debug_info(struct radeon_sa_manager *sa_manager,
4018c2ecf20Sopenharmony_ci				  struct seq_file *m)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct radeon_sa_bo *i;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	spin_lock(&sa_manager->wq.lock);
4068c2ecf20Sopenharmony_ci	list_for_each_entry(i, &sa_manager->olist, olist) {
4078c2ecf20Sopenharmony_ci		uint64_t soffset = i->soffset + sa_manager->gpu_addr;
4088c2ecf20Sopenharmony_ci		uint64_t eoffset = i->eoffset + sa_manager->gpu_addr;
4098c2ecf20Sopenharmony_ci		if (&i->olist == sa_manager->hole) {
4108c2ecf20Sopenharmony_ci			seq_printf(m, ">");
4118c2ecf20Sopenharmony_ci		} else {
4128c2ecf20Sopenharmony_ci			seq_printf(m, " ");
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci		seq_printf(m, "[0x%010llx 0x%010llx] size %8lld",
4158c2ecf20Sopenharmony_ci			   soffset, eoffset, eoffset - soffset);
4168c2ecf20Sopenharmony_ci		if (i->fence) {
4178c2ecf20Sopenharmony_ci			seq_printf(m, " protected by 0x%016llx on ring %d",
4188c2ecf20Sopenharmony_ci				   i->fence->seq, i->fence->ring);
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci		seq_printf(m, "\n");
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci	spin_unlock(&sa_manager->wq.lock);
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci#endif
425