diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index bf397b643cc0..e226794ffc1b 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3999,4 +3999,37 @@ int remap_io_mapping(struct vm_area_struct *vma,
 	__T;								\
 })
 
+/**
+ * struct i915_sg_create_state - state object for the sg creation helpers
+ * @st: sg_table being created
+ * @sg: Current scatterlist entry iterated over
+ * @idx: Current page index iterated over
+ * @page_count: Number of pages requested for the sg table to hold at creation
+ * @max_segment: Maximum size in bytes of a single sg entry
+ * @last_pfn: Page frame number of the previously added page
+ */
+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;
+};
+
+struct i915_sg_create_state *i915_sg_create(unsigned int page_count);
+void i915_sg_add_page(struct i915_sg_create_state *state, struct page *page);
+struct sg_table *i915_sg_complete(struct i915_sg_create_state *state);
+void i915_sg_abort(struct i915_sg_create_state *state);
+
+/**
+ * i915_sg_for_each_page - sg creation iterator
+ * @state: state object created by i915_sg_create
+ *
+ * Iterates page_count times over the created state allowing the caller to
+ * call i915_sg_add_page for all the pages it wants to build the list from.
+ */
+#define i915_sg_for_each_page(state) \
+	for( ; (state)->idx < (state)->page_count; )
+
 #endif
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 93b047735e6b..1c1aa3bbde8a 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2216,17 +2216,16 @@ 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)
+/**
+ * i915_sg_create - creates the state object for sg list building
+ * @page_count: number of pages the caller intends to add to the list
+ *
+ * This function creates a state object which is to be used with the
+ * accompanying family of functions used to build the scatter-gather list.
+ *
+ * Returns a pointer to the state structure or ERR_PTR otherwise.
+ */
+struct i915_sg_create_state *i915_sg_create(unsigned int page_count)
 {
 	struct i915_sg_create_state *state;
 	struct sg_table *st;
@@ -2260,8 +2259,17 @@ i915_sg_create(unsigned int page_count)
 	return state;
 }
 
-static void
-i915_sg_add_page(struct i915_sg_create_state *state, struct page *page)
+/**
+ * i915_sg_add_page - adds a page to the sg list being built
+ * @state: state created with i915_sg_create
+ * @page: struct page pointer of the page to add to the list
+ *
+ * Intended to be called under the i915_sg_for_each_page iterator once for each
+ * page which needs to be added to the sg list.
+ * Function manages the internal state which can be read (only!) by the caller
+ * where appropriate.
+ */
+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;
@@ -2282,8 +2290,17 @@ i915_sg_add_page(struct i915_sg_create_state *state, struct page *page)
 	state->idx++;
 }
 
-static struct sg_table *
-i915_sg_complete(struct i915_sg_create_state *state)
+/**
+ * i915_sg_complete - completes the sg list building
+ * @state: state created with i915_sg_create
+ *
+ * When all the pages have been successsfuly added to the sg list, this function
+ * is called to retrieve the fully build sg_table pointer.
+ * State object is not valid after this is called.
+ *
+ * Returns the sg_table pointer ready to be used.
+ */
+struct sg_table *i915_sg_complete(struct i915_sg_create_state *state)
 {
 	struct sg_table *st = state->st;
 
@@ -2295,17 +2312,25 @@ i915_sg_complete(struct i915_sg_create_state *state)
 	return st;
 }
 
-static void
-i915_sg_abort(struct i915_sg_create_state *state)
+/**
+ * i915_sg_abort - aborts the sg list building
+ * @state: state previously created with i915_sg_create
+ *
+ * In cases when something goes wrong with the sg list building, callers need
+ * to call this function to cleanup the objects and state allocated by the
+ * i915_sg_create.
+ * State object must not be accessed after calling this.
+ * Callers are responsible to do the correct thing with regards to individual
+ * page freeing/releasing themselves, but this helper will free the sg table
+ * and scatter gather entries.
+ */
+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)
 {
diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
index e537930c64b5..ced19610d911 100644
--- a/drivers/gpu/drm/i915/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
@@ -397,54 +397,21 @@ struct get_pages_work {
 	struct task_struct *task;
 };
 
-#if IS_ENABLED(CONFIG_SWIOTLB)
-#define swiotlb_active() swiotlb_nr_tbl()
-#else
-#define swiotlb_active() 0
-#endif
-
-static int
-st_set_pages(struct sg_table **st, struct page **pvec, int num_pages)
-{
-	struct scatterlist *sg;
-	int ret, n;
-
-	*st = kmalloc(sizeof(**st), GFP_KERNEL);
-	if (*st == NULL)
-		return -ENOMEM;
-
-	if (swiotlb_active()) {
-		ret = sg_alloc_table(*st, num_pages, GFP_KERNEL);
-		if (ret)
-			goto err;
-
-		for_each_sg((*st)->sgl, sg, num_pages, n)
-			sg_set_page(sg, pvec[n], PAGE_SIZE, 0);
-	} else {
-		ret = sg_alloc_table_from_pages(*st, pvec, num_pages,
-						0, num_pages << PAGE_SHIFT,
-						GFP_KERNEL);
-		if (ret)
-			goto err;
-	}
-
-	return 0;
-
-err:
-	kfree(*st);
-	*st = NULL;
-	return ret;
-}
-
 static int
 __i915_gem_userptr_set_pages(struct drm_i915_gem_object *obj,
 			     struct page **pvec, int num_pages)
 {
+	struct i915_sg_create_state *state;
 	int ret;
 
-	ret = st_set_pages(&obj->pages, pvec, num_pages);
-	if (ret)
-		return ret;
+	state = i915_sg_create(num_pages);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
+
+	i915_sg_for_each_page(state)
+		i915_sg_add_page(state, pvec[state->idx]);
+
+	obj->pages = i915_sg_complete(state);
 
 	ret = i915_gem_gtt_prepare_object(obj);
 	if (ret) {
