[RFC 1/7] drm/i915: Extract sg creation into a helper

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Tvrtko Ursulin <tvrtko.ursulin@xxxxxxxxx>

In order to reuse the same logic in several places in the driver,
extract the logic which adds pages to the sg list and does the
potential coalescing, into separate functions.

Code wanting to build the sg table needs to do the following:

1. Call i915_sg_create to create the state object for a given
   maximum number of pages.
2. Iterate using i915_sg_for_each_page
3. Call i915_sg_add_page to add all the pages in order
4. On success call i915_sg_complete, or on failure i915_sg_abort

In this patch only i915_gem_object_get_pages_gtt gets converted
to use the new functions.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@xxxxxxxxx>
---
 drivers/gpu/drm/i915/i915_gem.c | 153 ++++++++++++++++++++++++++++------------
 1 file changed, 109 insertions(+), 44 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index fdd496e6c081..93b047735e6b 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2216,18 +2216,104 @@ static unsigned long swiotlb_max_size(void)
 #endif
 }
 
+struct i915_sg_create_state {
+	struct sg_table *st;
+	struct scatterlist *sg;
+	unsigned int idx;
+	unsigned int page_count;
+	unsigned long max_segment;
+	unsigned long last_pfn;
+};
+
+static struct i915_sg_create_state *
+i915_sg_create(unsigned int page_count)
+{
+	struct i915_sg_create_state *state;
+	struct sg_table *st;
+
+	state = kmalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return ERR_PTR(-ENOMEM);
+
+	st = kmalloc(sizeof(*st), GFP_KERNEL);
+	if (!st) {
+		kfree(state);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	if (sg_alloc_table(st, page_count, GFP_KERNEL)) {
+		kfree(st);
+		kfree(state);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	st->nents = 0;
+	state->st = st;
+	state->sg = st->sgl;
+	state->page_count = page_count;
+	state->idx = 0;
+	state->max_segment = swiotlb_max_size();
+	if (!state->max_segment)
+		state->max_segment = page_count * PAGE_SIZE;
+	state->last_pfn = 0;
+
+	return state;
+}
+
+static void
+i915_sg_add_page(struct i915_sg_create_state *state, struct page *page)
+{
+	unsigned long pfn = page_to_pfn(page);
+	struct scatterlist *sg = state->sg;
+
+	if (!state->idx ||
+	    sg->length >= state->max_segment ||
+	    pfn != state->last_pfn + 1) {
+		if (state->idx)
+			sg = sg_next(sg);
+		state->st->nents++;
+		sg_set_page(sg, page, PAGE_SIZE, 0);
+	} else {
+		sg->length += PAGE_SIZE;
+	}
+
+	state->last_pfn = pfn;
+	state->sg = sg;
+	state->idx++;
+}
+
+static struct sg_table *
+i915_sg_complete(struct i915_sg_create_state *state)
+{
+	struct sg_table *st = state->st;
+
+	if (state->sg) /* fewer sg entries written than max allocated */
+		sg_mark_end(state->sg);
+
+	kfree(state);
+
+	return st;
+}
+
+static void
+i915_sg_abort(struct i915_sg_create_state *state)
+{
+	sg_free_table(state->st);
+	kfree(state->st);
+	kfree(state);
+}
+
+#define i915_sg_for_each_page(state) \
+	for( ; (state)->idx < (state)->page_count; )
+
 static int
 i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
 {
 	struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
-	int page_count, i;
 	struct address_space *mapping;
-	struct sg_table *st;
-	struct scatterlist *sg;
+	struct i915_sg_create_state *state;
 	struct sgt_iter sgt_iter;
 	struct page *page;
-	unsigned long last_pfn = 0;	/* suppress gcc warning */
-	unsigned long max_segment;
 	int ret;
 	gfp_t gfp;
 
@@ -2238,19 +2324,9 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
 	BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
 	BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
 
-	max_segment = swiotlb_max_size();
-	if (!max_segment)
-		max_segment = obj->base.size;
-
-	st = kmalloc(sizeof(*st), GFP_KERNEL);
-	if (st == NULL)
-		return -ENOMEM;
-
-	page_count = obj->base.size / PAGE_SIZE;
-	if (sg_alloc_table(st, page_count, GFP_KERNEL)) {
-		kfree(st);
-		return -ENOMEM;
-	}
+	state = i915_sg_create(obj->base.size / PAGE_SIZE);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
 
 	/* Get the list of pages out of our struct file.  They'll be pinned
 	 * at this point until we release them.
@@ -2260,47 +2336,37 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
 	mapping = obj->base.filp->f_mapping;
 	gfp = mapping_gfp_constraint(mapping, ~(__GFP_IO | __GFP_RECLAIM));
 	gfp |= __GFP_NORETRY | __GFP_NOWARN;
-	sg = st->sgl;
-	st->nents = 0;
-	for (i = 0; i < page_count; i++) {
-		page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+	i915_sg_for_each_page(state) {
+		page = shmem_read_mapping_page_gfp(mapping, state->idx, gfp);
 		if (IS_ERR(page)) {
 			i915_gem_shrink(dev_priv,
-					page_count,
+					state->page_count,
 					I915_SHRINK_BOUND |
 					I915_SHRINK_UNBOUND |
 					I915_SHRINK_PURGEABLE);
-			page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+			page = shmem_read_mapping_page_gfp(mapping, state->idx,
+							   gfp);
 		}
 		if (IS_ERR(page)) {
 			/* We've tried hard to allocate the memory by reaping
 			 * our own buffer, now let the real VM do its job and
 			 * go down in flames if truly OOM.
 			 */
-			page = shmem_read_mapping_page(mapping, i);
+			page = shmem_read_mapping_page(mapping, state->idx);
 			if (IS_ERR(page)) {
 				ret = PTR_ERR(page);
 				goto err_pages;
 			}
 		}
-		if (!i ||
-		    sg->length >= max_segment ||
-		    page_to_pfn(page) != last_pfn + 1) {
-			if (i)
-				sg = sg_next(sg);
-			st->nents++;
-			sg_set_page(sg, page, PAGE_SIZE, 0);
-		} else {
-			sg->length += PAGE_SIZE;
-		}
-		last_pfn = page_to_pfn(page);
+
+		i915_sg_add_page(state, page);
 
 		/* Check that the i965g/gm workaround works. */
-		WARN_ON((gfp & __GFP_DMA32) && (last_pfn >= 0x00100000UL));
+		WARN_ON((gfp & __GFP_DMA32) &&
+			(state->last_pfn >= 0x00100000UL));
 	}
-	if (sg) /* loop terminated early; short sg table */
-		sg_mark_end(sg);
-	obj->pages = st;
+
+	obj->pages = i915_sg_complete(state);
 
 	ret = i915_gem_gtt_prepare_object(obj);
 	if (ret)
@@ -2316,11 +2382,10 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
 	return 0;
 
 err_pages:
-	sg_mark_end(sg);
-	for_each_sgt_page(page, sgt_iter, st)
+	sg_mark_end(state->sg);
+	for_each_sgt_page(page, sgt_iter, state->st)
 		put_page(page);
-	sg_free_table(st);
-	kfree(st);
+	i915_sg_abort(state);
 
 	/* shmemfs first checks if there is enough memory to allocate the page
 	 * and reports ENOSPC should there be insufficient, along with the usual
-- 
2.7.4

_______________________________________________
Intel-gfx mailing list
Intel-gfx@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/intel-gfx




[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux