[PATCH] drm/radeon: improve sa allocator for multi ring load balancing

j.glisse at gmail.com j.glisse at gmail.com
Thu May 3 17:44:42 PDT 2012


From: Jerome Glisse <jglisse at redhat.com>

This add a per ring allocation management and load balance the
chunk of the temp buffer between each ring. A ring that often
fail to find a hole or worse have to wait for previous fence
will have more chance to grow over other ring. This ring is
properly CPU starve in a sense. Of course with no real user of
other ring this functionalities is hard to test but code seems
to work well (tested with 2 or 3 rings with main ring being either
first, in the middle or last).

Note it could be refine to decrease all ring before or after.
Right now it only change size of adjacent ring.

Signed-off-by: Jerome Glisse <jglisse at redhat.com>
---
 drivers/gpu/drm/radeon/radeon.h        |   30 ++-
 drivers/gpu/drm/radeon/radeon_gart.c   |    2 +-
 drivers/gpu/drm/radeon/radeon_object.h |    2 +-
 drivers/gpu/drm/radeon/radeon_ring.c   |    2 +-
 drivers/gpu/drm/radeon/radeon_sa.c     |  409 ++++++++++++++++++++++----------
 5 files changed, 306 insertions(+), 139 deletions(-)

diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index dd006ab..28bc338 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -113,10 +113,10 @@ extern int radeon_lockup_timeout;
 
 /* internal ring indices */
 /* r1xx+ has gfx CP ring */
-#define RADEON_RING_TYPE_GFX_INDEX  0
+#define RADEON_RING_TYPE_GFX_INDEX  1
 
 /* cayman has 2 compute CP rings */
-#define CAYMAN_RING_TYPE_CP1_INDEX 1
+#define CAYMAN_RING_TYPE_CP1_INDEX 0
 #define CAYMAN_RING_TYPE_CP2_INDEX 2
 
 /* hardcode those limit for now */
@@ -409,15 +409,26 @@ struct radeon_bo_list {
 
 struct radeon_sa_bo;
 
-struct radeon_sa_manager {
-	spinlock_t		lock;
-	struct radeon_bo	*bo;
+struct radeon_ra_manager {
 	struct list_head	sa_bo;
-	unsigned		size;
+	unsigned		soffset;
+	unsigned		eoffset;
+	unsigned		foffset;
+	unsigned		loffset;
+	unsigned		starve;
 	struct radeon_sa_bo	*last;
-	uint64_t		gpu_addr;
-	void			*cpu_ptr;
-	uint32_t		domain;
+};
+
+struct radeon_sa_manager {
+	spinlock_t			lock;
+	struct radeon_bo		*bo;
+	unsigned			size;
+	unsigned			nrings;
+	uint64_t			gpu_addr;
+	void				*cpu_ptr;
+	uint32_t			domain;
+	unsigned long			update_timeout;
+	struct radeon_ra_manager	rings[RADEON_NUM_RINGS];
 };
 
 /* sub-allocation buffer */
@@ -427,6 +438,7 @@ struct radeon_sa_bo {
 	unsigned			soffset;
 	unsigned			eoffset;
 	unsigned			size;
+	unsigned			ring;
 	struct radeon_fence		*fence;
 	bool				free;
 };
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
index cab012f..973c31b 100644
--- a/drivers/gpu/drm/radeon/radeon_gart.c
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
@@ -291,7 +291,7 @@ int radeon_vm_manager_init(struct radeon_device *rdev)
 	/* mark first vm as always in use, it's the system one */
 	r = radeon_sa_bo_manager_init(rdev, &rdev->vm_manager.sa_manager,
 				      rdev->vm_manager.max_pfn * 8,
-				      RADEON_GEM_DOMAIN_VRAM);
+				      RADEON_GEM_DOMAIN_VRAM, 1);
 	if (r) {
 		dev_err(rdev->dev, "failed to allocate vm bo (%dKB)\n",
 			(rdev->vm_manager.max_pfn * 8) >> 10);
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index 35e54da..0e7c099 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -148,7 +148,7 @@ extern struct radeon_bo_va *radeon_bo_va(struct radeon_bo *rbo,
  */
 extern int radeon_sa_bo_manager_init(struct radeon_device *rdev,
 				     struct radeon_sa_manager *sa_manager,
-				     unsigned size, u32 domain);
+				     unsigned size, u32 domain, unsigned nrings);
 extern void radeon_sa_bo_manager_fini(struct radeon_device *rdev,
 				      struct radeon_sa_manager *sa_manager);
 extern int radeon_sa_bo_manager_start(struct radeon_device *rdev,
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index b279a61..ec0aab38 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -151,7 +151,7 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
 
 	r = radeon_sa_bo_manager_init(rdev, &rdev->ring_tmp_bo,
 				      RADEON_IB_POOL_SIZE*64*1024,
-				      RADEON_GEM_DOMAIN_GTT);
+				      RADEON_GEM_DOMAIN_GTT, 3);
 	if (r) {
 		return r;
 	}
diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c
index 2cbf5ba..5869616 100644
--- a/drivers/gpu/drm/radeon/radeon_sa.c
+++ b/drivers/gpu/drm/radeon/radeon_sa.c
@@ -48,110 +48,60 @@
 #include "drm.h"
 #include "radeon.h"
 
-static bool radeon_sa_manager_try_free(struct radeon_device *rdev,
-				       struct radeon_sa_manager *sa_manager,
-				       struct radeon_sa_bo *oldest);
+/* update every 500ms */
+#define SA_UPDATE_TIMEOUT		(HZ / 2)
+#define SA_UPDATE_STARVE_DEC		10
+#define SA_UPDATE_SIZE			(128 << 10)
 
-int radeon_sa_bo_manager_init(struct radeon_device *rdev,
-			      struct radeon_sa_manager *sa_manager,
-			      unsigned size, u32 domain)
-{
-	int r;
 
-	spin_lock_init(&sa_manager->lock);
-	sa_manager->bo = NULL;
-	sa_manager->size = size;
-	sa_manager->domain = domain;
-	sa_manager->last = NULL;
-	INIT_LIST_HEAD(&sa_manager->sa_bo);
-
-	r = radeon_bo_create(rdev, size, RADEON_GPU_PAGE_SIZE, true,
-			     RADEON_GEM_DOMAIN_CPU, &sa_manager->bo);
-	if (r) {
-		dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r);
-		return r;
-	}
+static bool radeon_ra_manager_try_free(struct radeon_device *rdev,
+				       struct radeon_ra_manager *ra_manager,
+				       struct radeon_sa_bo *oldest);
 
-	return r;
+void radeon_ra_bo_manager_init(struct radeon_device *rdev,
+			       struct radeon_ra_manager *ra_manager,
+			       unsigned soffset, unsigned eoffset)
+{
+	ra_manager->soffset = soffset;
+	ra_manager->eoffset = eoffset;
+	/* set first and last so that other ring can grow */
+	ra_manager->foffset = ra_manager->eoffset;
+	ra_manager->loffset = ra_manager->soffset;
+	ra_manager->last = NULL;
+	INIT_LIST_HEAD(&ra_manager->sa_bo);
 }
 
-void radeon_sa_bo_manager_fini(struct radeon_device *rdev,
-			       struct radeon_sa_manager *sa_manager)
+void radeon_ra_bo_manager_fini(struct radeon_device *rdev,
+			       struct radeon_ra_manager *ra_manager)
 {
 	struct radeon_sa_bo *sa_bo, *tmp;
 
-	if (!list_empty(&sa_manager->sa_bo)) {
+	if (!list_empty(&ra_manager->sa_bo)) {
 		struct radeon_sa_bo *oldest;
 
 		/* try to free them */
-		oldest =  list_entry(sa_manager->sa_bo.next, struct radeon_sa_bo, list);
-		radeon_sa_manager_try_free(rdev, sa_manager, oldest);
+		oldest =  list_entry(ra_manager->sa_bo.next, struct radeon_sa_bo, list);
+		radeon_ra_manager_try_free(rdev, ra_manager, oldest);
 
-		if (!list_empty(&sa_manager->sa_bo)) {
+		if (!list_empty(&ra_manager->sa_bo)) {
 			/* something went wrong */
-			dev_err(rdev->dev, "sa_manager is not empty, clearing anyway\n");
+			dev_err(rdev->dev, "ra_manager is not empty, clearing anyway\n");
 		}
 	}
-	list_for_each_entry_safe(sa_bo, tmp, &sa_manager->sa_bo, list) {
+	list_for_each_entry_safe(sa_bo, tmp, &ra_manager->sa_bo, list) {
 		list_del_init(&sa_bo->list);
 	}
-	if (sa_manager->bo) {
-		radeon_bo_unref(&sa_manager->bo);
-	}
-	sa_manager->size = 0;
-}
-
-int radeon_sa_bo_manager_start(struct radeon_device *rdev,
-			       struct radeon_sa_manager *sa_manager)
-{
-	int r;
-
-	if (sa_manager->bo == NULL) {
-		dev_err(rdev->dev, "no bo for sa manager\n");
-		return -EINVAL;
-	}
-
-	/* map the buffer */
-	r = radeon_bo_reserve(sa_manager->bo, false);
-	if (r) {
-		dev_err(rdev->dev, "(%d) failed to reserve manager bo\n", r);
-		return r;
-	}
-	r = radeon_bo_pin(sa_manager->bo, sa_manager->domain, &sa_manager->gpu_addr);
-	if (r) {
-		radeon_bo_unreserve(sa_manager->bo);
-		dev_err(rdev->dev, "(%d) failed to pin manager bo\n", r);
-		return r;
-	}
-	r = radeon_bo_kmap(sa_manager->bo, &sa_manager->cpu_ptr);
-	radeon_bo_unreserve(sa_manager->bo);
-	return r;
-}
-
-int radeon_sa_bo_manager_suspend(struct radeon_device *rdev,
-				 struct radeon_sa_manager *sa_manager)
-{
-	int r;
-
-	if (sa_manager->bo == NULL) {
-		dev_err(rdev->dev, "no bo for sa manager\n");
-		return -EINVAL;
-	}
-
-	r = radeon_bo_reserve(sa_manager->bo, false);
-	if (!r) {
-		radeon_bo_kunmap(sa_manager->bo);
-		radeon_bo_unpin(sa_manager->bo);
-		radeon_bo_unreserve(sa_manager->bo);
-	}
-	return r;
 }
 
-static void radeon_sa_bo_free_locked(struct radeon_device *rdev, struct radeon_sa_bo *sa_bo)
+static void radeon_ra_bo_free_locked(struct radeon_device *rdev, struct radeon_sa_bo *sa_bo)
 {
-	struct radeon_sa_manager *sa_manager = sa_bo->manager;
+	struct radeon_ra_manager *ra_manager;
 	struct list_head *prev;
 
+	if (sa_bo == NULL) {
+		return;
+	}
+	ra_manager = &sa_bo->manager->rings[sa_bo->ring];
 	if (sa_bo->fence) {
 		if (!radeon_fence_signaled(sa_bo->fence)) {
 			return;
@@ -160,19 +110,27 @@ static void radeon_sa_bo_free_locked(struct radeon_device *rdev, struct radeon_s
 	}
 	prev = sa_bo->list.prev;
 	list_del_init(&sa_bo->list);
-	if (list_empty(&sa_manager->sa_bo)) {
+	if (list_empty(&ra_manager->sa_bo)) {
 		/* this bo was alone in the list */
-		sa_manager->last = NULL;
-	} else if (sa_manager->last == sa_bo) {
-		if (prev == &sa_manager->sa_bo) {
+		ra_manager->last = NULL;
+		ra_manager->foffset = ra_manager->eoffset;
+		ra_manager->loffset = ra_manager->soffset;
+	} else if (ra_manager->last == sa_bo) {
+		struct radeon_sa_bo *tmp;
+
+		if (prev == &ra_manager->sa_bo) {
 			/* sa_bo is begining of list, the new last became
 			 * the last of the list
 			 */
-			sa_manager->last = list_entry(sa_manager->sa_bo.prev, struct radeon_sa_bo, list);
+			ra_manager->last = list_entry(ra_manager->sa_bo.prev, struct radeon_sa_bo, list);
 		} else {
 			/* prev became the new last */
-			sa_manager->last = list_entry(prev, struct radeon_sa_bo, list);
+			ra_manager->last = list_entry(prev, struct radeon_sa_bo, list);
 		}
+		tmp = list_entry(ra_manager->sa_bo.prev, struct radeon_sa_bo, list);
+		ra_manager->loffset = tmp->eoffset;
+		tmp = list_entry(ra_manager->sa_bo.next, struct radeon_sa_bo, list);
+		ra_manager->foffset = tmp->soffset;
 	}
 	/* in case try free already free the sa_bo but radeon_sa_bo_free
 	 * wasn't yet call, the free bool protect us from freeing to
@@ -183,8 +141,8 @@ static void radeon_sa_bo_free_locked(struct radeon_device *rdev, struct radeon_s
 	}
 }
 
-static bool radeon_sa_manager_try_free(struct radeon_device *rdev,
-				       struct radeon_sa_manager *sa_manager,
+static bool radeon_ra_manager_try_free(struct radeon_device *rdev,
+				       struct radeon_ra_manager *ra_manager,
 				       struct radeon_sa_bo *oldest)
 {
 	struct radeon_sa_bo *tmp, *sa_bo;
@@ -203,9 +161,9 @@ static bool radeon_sa_manager_try_free(struct radeon_device *rdev,
 	 * as possible
 	 */
 	sa_bo = oldest;
-	list_for_each_entry_safe_continue(sa_bo, tmp, &sa_manager->sa_bo, list) {
+	list_for_each_entry_safe_continue(sa_bo, tmp, &ra_manager->sa_bo, list) {
 		if (sa_bo->fence == NULL || !sa_bo->fence->emitted) {
-			radeon_sa_bo_free_locked(rdev, oldest);
+			radeon_ra_bo_free_locked(rdev, oldest);
 			return true;
 		}
 		if (ring != sa_bo->fence->ring) {
@@ -213,13 +171,13 @@ static bool radeon_sa_manager_try_free(struct radeon_device *rdev,
 			radeon_fence_poll(rdev, ring);
 		}
 		if (rdev->fence_drv[ring].last_seq < sa_bo->fence->seq) {
-			radeon_sa_bo_free_locked(rdev, oldest);
+			radeon_ra_bo_free_locked(rdev, oldest);
 			return true;
 		}
-		radeon_sa_bo_free_locked(rdev, sa_bo);
+		radeon_ra_bo_free_locked(rdev, sa_bo);
 		free_count++;
 	}
-	radeon_sa_bo_free_locked(rdev, oldest);
+	radeon_ra_bo_free_locked(rdev, oldest);
 	return true;
 }
 
@@ -239,19 +197,26 @@ static bool radeon_sa_manager_try_free(struct radeon_device *rdev,
  * -ERESTARTSYS restart ioctl
  * -EDEADLK when fence wait report GPU lockup
  */
-int radeon_sa_bo_new(struct radeon_device *rdev,
+int radeon_ra_bo_new(struct radeon_device *rdev,
 		     struct radeon_sa_bo **tmp,
 		     struct radeon_sa_manager *sa_manager,
 		     unsigned size, unsigned align,
-		     bool block, struct radeon_fence *fence)
+		     bool block, struct radeon_fence *fence,
+		     unsigned ring)
 {
 	struct radeon_sa_bo *sa_bo, *next, *oldest;
 	unsigned offset, wasted, hole_offset, hole_size;
 	bool try_begining = false, add_begining = false;
 	int r = -ENOMEM;
+	struct radeon_ra_manager *ra_manager = &sa_manager->rings[ring];
 
 	BUG_ON(align > RADEON_GPU_PAGE_SIZE);
-	BUG_ON(size > sa_manager->size);
+	BUG_ON(align > SA_UPDATE_SIZE);
+	if (size > (ra_manager->eoffset - ra_manager->soffset)) {
+		dev_warn(rdev->dev, "ring tmp bo too small %d for %d\n",
+			 ra_manager->eoffset - ra_manager->soffset, size);
+		return -ENOMEM;
+	}
 
 	*tmp = NULL;
 	sa_bo = kmalloc(sizeof(struct radeon_sa_bo), GFP_KERNEL);
@@ -263,26 +228,26 @@ int radeon_sa_bo_new(struct radeon_device *rdev,
 	sa_bo->free = false;
 	sa_bo->soffset = 0;
 	sa_bo->eoffset = 0;
+	sa_bo->ring = ring;
 	sa_bo->size = 0;
 	INIT_LIST_HEAD(&sa_bo->list);
 
-	spin_lock(&sa_manager->lock);
 retry:
-	if (sa_manager->last == NULL) {
-		offset = 0;
+	if (ra_manager->last == NULL) {
+		offset = ra_manager->soffset;
 		add_begining = true;
 		goto out;
 	}
 
-	hole_offset = sa_manager->last->eoffset;
+	hole_offset = ra_manager->last->eoffset;
 	wasted = (align - (hole_offset % align)) % align;
-	if (sa_manager->last->list.next == &sa_manager->sa_bo) {
+	if (ra_manager->last->list.next == &ra_manager->sa_bo) {
 		/* no sa bo after that one */
-		hole_size = sa_manager->size - hole_offset;
+		hole_size = ra_manager->eoffset - hole_offset;
 		try_begining = true;
-		oldest = list_entry(sa_manager->sa_bo.next, struct radeon_sa_bo, list);
+		oldest = list_entry(ra_manager->sa_bo.next, struct radeon_sa_bo, list);
 	} else {
-		next = list_entry(sa_manager->last->list.next, struct radeon_sa_bo, list);
+		next = list_entry(ra_manager->last->list.next, struct radeon_sa_bo, list);
 		hole_size = next->soffset - hole_offset;
 		oldest = next;
 	}
@@ -293,15 +258,16 @@ retry:
 		/* last was at end of list, so if we wrap over we might find
 		 * room at the begining of the list
 		 */
-		offset = 0;
-		hole_size = oldest->soffset;
+		offset = ra_manager->soffset;
+		hole_size = oldest->soffset - ra_manager->soffset;
 		if (size <= hole_size) {
 			add_begining = true;
 			goto out;
 		}
 	}
+	ra_manager->starve++;
 	/* try to be optimist and free the oldest one */
-	if (radeon_sa_manager_try_free(rdev, sa_manager, oldest)) {
+	if (radeon_ra_manager_try_free(rdev, ra_manager, oldest)) {
 		goto retry;
 	}
 
@@ -315,7 +281,6 @@ retry:
 		if (oldest->fence) {
 			fence = radeon_fence_ref(oldest->fence);
 		}
-		spin_unlock(&sa_manager->lock);
 
 		if (fence == NULL) {
 			/* this should never happen */
@@ -328,11 +293,11 @@ retry:
 		if (r) {
 			goto out_err;
 		}
+		/* really bad to wait try to force grow */
+		ra_manager->starve += SA_UPDATE_STARVE_DEC;
 
-		spin_lock(&sa_manager->lock);
 		goto retry;
 	}
-	spin_unlock(&sa_manager->lock);
 
 out_err:
 	kfree(sa_bo);
@@ -341,21 +306,216 @@ out_err:
 out:
 	*tmp = sa_bo;
 	if (add_begining) {
-		list_add(&sa_bo->list, &sa_manager->sa_bo);
+		list_add(&sa_bo->list, &ra_manager->sa_bo);
 	} else {
-		list_add(&sa_bo->list, &sa_manager->last->list);
+		list_add(&sa_bo->list, &ra_manager->last->list);
 	}
-	sa_manager->last = sa_bo;
+	ra_manager->last = sa_bo;
 	if (fence) {
 		sa_bo->fence = radeon_fence_ref(fence);
 	}
 	sa_bo->soffset = offset;
 	sa_bo->eoffset = offset + size;
 	sa_bo->size = size;
-	spin_unlock(&sa_manager->lock);
+	/* update first and last offset */
+	sa_bo = list_entry(ra_manager->sa_bo.prev, struct radeon_sa_bo, list);
+	ra_manager->loffset = sa_bo->eoffset;
+	sa_bo = list_entry(ra_manager->sa_bo.next, struct radeon_sa_bo, list);
+	ra_manager->foffset = sa_bo->soffset;
 	return 0;
 }
 
+#if defined(CONFIG_DEBUG_FS)
+void radeon_ra_bo_dump_debug_info(struct radeon_ra_manager *ra_manager,
+				  struct seq_file *m)
+{
+	struct radeon_sa_bo *i;
+	char c;
+
+	list_for_each_entry(i, &ra_manager->sa_bo, list) {
+		c = (ra_manager->last == i) ? 'L' : '.';
+		seq_printf(m, "[0x%08x 0x%08x]/[0x%08x 0x%08x] size %d [%p] (%c)\n",
+			   i->soffset, i->eoffset,
+			   ra_manager->soffset, ra_manager->eoffset,
+			   i->size, i, c);
+	}
+}
+#endif
+
+int radeon_sa_bo_manager_init(struct radeon_device *rdev,
+			      struct radeon_sa_manager *sa_manager,
+			      unsigned size, u32 domain, unsigned nrings)
+{
+	unsigned i, chunk_size, offset;
+	int r;
+
+	spin_lock_init(&sa_manager->lock);
+	sa_manager->bo = NULL;
+	sa_manager->size = size;
+	sa_manager->domain = domain;
+	sa_manager->nrings = nrings;
+	sa_manager->update_timeout = jiffies;
+	chunk_size = size / nrings;
+	if (chunk_size < SA_UPDATE_SIZE) {
+		dev_err(rdev->dev, "ring scratch too small %d for %d rings\n",
+			size, nrings);
+		return -EINVAL;
+	}
+
+	r = radeon_bo_create(rdev, size, RADEON_GPU_PAGE_SIZE, true,
+			     RADEON_GEM_DOMAIN_CPU, &sa_manager->bo);
+	if (r) {
+		dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r);
+		return r;
+	}
+
+	for (i = 0, offset = 0; i < sa_manager->nrings; i++, offset+=chunk_size) {
+		radeon_ra_bo_manager_init(rdev, &sa_manager->rings[i],
+					  offset, offset + chunk_size);
+	}
+
+	return 0;
+}
+
+void radeon_sa_bo_manager_fini(struct radeon_device *rdev,
+			       struct radeon_sa_manager *sa_manager)
+{
+	unsigned i;
+
+	for (i = 0; i < sa_manager->nrings; i++) {
+		radeon_ra_bo_manager_fini(rdev, &sa_manager->rings[i]);
+	}
+	if (sa_manager->bo) {
+		radeon_bo_unref(&sa_manager->bo);
+	}
+	sa_manager->size = 0;
+}
+
+int radeon_sa_bo_manager_start(struct radeon_device *rdev,
+			       struct radeon_sa_manager *sa_manager)
+{
+	int r;
+
+	if (sa_manager->bo == NULL) {
+		dev_err(rdev->dev, "no bo for sa manager\n");
+		return -EINVAL;
+	}
+
+	/* map the buffer */
+	r = radeon_bo_reserve(sa_manager->bo, false);
+	if (r) {
+		dev_err(rdev->dev, "(%d) failed to reserve manager bo\n", r);
+		return r;
+	}
+	r = radeon_bo_pin(sa_manager->bo, sa_manager->domain, &sa_manager->gpu_addr);
+	if (r) {
+		radeon_bo_unreserve(sa_manager->bo);
+		dev_err(rdev->dev, "(%d) failed to pin manager bo\n", r);
+		return r;
+	}
+	r = radeon_bo_kmap(sa_manager->bo, &sa_manager->cpu_ptr);
+	radeon_bo_unreserve(sa_manager->bo);
+	return r;
+}
+
+int radeon_sa_bo_manager_suspend(struct radeon_device *rdev,
+				 struct radeon_sa_manager *sa_manager)
+{
+	int r;
+
+	if (sa_manager->bo == NULL) {
+		dev_err(rdev->dev, "no bo for sa manager\n");
+		return -EINVAL;
+	}
+
+	r = radeon_bo_reserve(sa_manager->bo, false);
+	if (!r) {
+		radeon_bo_kunmap(sa_manager->bo);
+		radeon_bo_unpin(sa_manager->bo);
+		radeon_bo_unreserve(sa_manager->bo);
+	}
+	return r;
+}
+
+static void radeon_sa_manager_update(struct radeon_device *rdev,
+				     struct radeon_sa_manager *sa_manager)
+{
+	unsigned i, max_starve, max_starve_idx;
+
+	/* update the starve counter */
+	for (i = 0, max_starve = 0, max_starve_idx = 0; i < sa_manager->nrings; i++) {
+		if (sa_manager->rings[i].starve >= SA_UPDATE_STARVE_DEC) {
+			sa_manager->rings[i].starve -= SA_UPDATE_STARVE_DEC;
+		} else {
+			sa_manager->rings[i].starve = 0;
+		}
+		if (sa_manager->rings[i].starve > max_starve) {
+			max_starve = sa_manager->rings[i].starve;
+			max_starve_idx = i;
+		}
+	}
+	/* try to grow the segment that most needs it */
+	if (max_starve) {
+		unsigned size, hole_size;
+
+		i = max_starve_idx;
+		if (i) {
+			size = sa_manager->rings[i - 1].eoffset - sa_manager->rings[i - 1].soffset;
+			hole_size = sa_manager->rings[i - 1].eoffset - sa_manager->rings[i - 1].loffset;
+			if (hole_size >= SA_UPDATE_SIZE && size >= (SA_UPDATE_SIZE * 2)) {
+				if (sa_manager->rings[i - 1].foffset == sa_manager->rings[i - 1].eoffset) {
+					sa_manager->rings[i - 1].foffset -= SA_UPDATE_SIZE;
+				}
+				sa_manager->rings[i - 1].eoffset -= SA_UPDATE_SIZE;
+				sa_manager->rings[i].soffset -= SA_UPDATE_SIZE;
+				return;
+			}
+		}
+		if (i < (sa_manager->nrings - 1)) {
+			size = sa_manager->rings[i + 1].eoffset - sa_manager->rings[i + 1].soffset;
+			hole_size = sa_manager->rings[i + 1].foffset - sa_manager->rings[i + 1].soffset;
+			if (hole_size >= SA_UPDATE_SIZE && size >= (SA_UPDATE_SIZE * 2)) {
+				if (sa_manager->rings[i + 1].loffset == sa_manager->rings[i + 1].soffset) {
+					sa_manager->rings[i + 1].loffset += SA_UPDATE_SIZE;
+				}
+				sa_manager->rings[i + 1].soffset += SA_UPDATE_SIZE;
+				sa_manager->rings[i].eoffset += SA_UPDATE_SIZE;
+				return;
+			}
+		}
+	}
+}
+
+int radeon_sa_bo_new(struct radeon_device *rdev,
+		     struct radeon_sa_bo **tmp,
+		     struct radeon_sa_manager *sa_manager,
+		     unsigned size, unsigned align,
+		     bool block, struct radeon_fence *fence)
+{
+	unsigned ring = 0;
+	int r;
+
+	if (fence) {
+		if (fence->ring >= sa_manager->nrings) {
+			dev_warn(rdev->dev, "trying to allocate tmp bo on uninitialized ring %d\n",
+				 fence->ring);
+			return -EINVAL;
+		}
+		ring = fence->ring;
+	}
+
+	spin_lock(&sa_manager->lock);
+
+	if (time_after(jiffies, sa_manager->update_timeout)) {
+		sa_manager->update_timeout = jiffies + SA_UPDATE_TIMEOUT;
+		radeon_sa_manager_update(rdev, sa_manager);
+	}
+
+	r = radeon_ra_bo_new(rdev, tmp, sa_manager, size, align, block, fence, ring);
+	spin_unlock(&sa_manager->lock);
+	return r;
+}
+
 void radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo **tmp)
 {
 	struct radeon_sa_bo *sa_bo;
@@ -378,7 +538,7 @@ void radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo **tmp)
 	if (sa_bo->fence && !sa_bo->fence->emitted) {
 		radeon_fence_unref(&sa_bo->fence);
 	}
-	radeon_sa_bo_free_locked(rdev, sa_bo);
+	radeon_ra_bo_free_locked(rdev, sa_bo);
 
 out:
 	spin_unlock(&sa_manager->lock);
@@ -388,18 +548,13 @@ out:
 void radeon_sa_bo_dump_debug_info(struct radeon_sa_manager *sa_manager,
 				  struct seq_file *m)
 {
-	struct radeon_sa_bo *i;
+	unsigned i, size;
 
 	spin_lock(&sa_manager->lock);
-	seq_printf(m, "last [%p]\n", sa_manager->last);
-	if (sa_manager->last) {
-		i = sa_manager->last;
-		seq_printf(m, "[0x%08x 0x%08x]/0x%08x size %d [%p] LAST\n", i->soffset,
-			   i->eoffset, sa_manager->size, i->size, i);
-	}
-	list_for_each_entry(i, &sa_manager->sa_bo, list) {
-		seq_printf(m, "[0x%08x 0x%08x]/0x%08x size %d [%p]\n", i->soffset,
-			   i->eoffset, sa_manager->size, i->size, i);
+	for (i = 0; i < sa_manager->nrings; i++) {
+		size = sa_manager->rings[i].eoffset - sa_manager->rings[i].soffset;
+		seq_printf(m, "--- ring %d [%db %dkb] ---\n", i, size, size >> 10);
+		radeon_ra_bo_dump_debug_info(&sa_manager->rings[i], m);
 	}
 	spin_unlock(&sa_manager->lock);
 }
-- 
1.7.7.6



More information about the dri-devel mailing list