[PATCH 1/6] drm/i915: Extract sg creation into a helper

Tvrtko Ursulin tursulin at ursulin.net
Wed Oct 19 13:53:25 UTC 2016


From: Tvrtko Ursulin <tvrtko.ursulin at intel.com>

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.

v2: Rebase.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
---
 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 8ed8e24025ac..195c99600afc 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2214,18 +2214,104 @@ static unsigned int 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 int 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 = rounddown(UINT_MAX, 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 int max_segment;
 	int ret;
 	gfp_t gfp;
 
@@ -2236,19 +2322,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 = rounddown(UINT_MAX, PAGE_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.
@@ -2258,47 +2334,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)
@@ -2314,11 +2380,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



More information about the Intel-gfx-trybot mailing list