@@ -4019,6 +4019,7 @@ struct i915_sg_create_state {
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);
+void i915_sg_add_dma(struct i915_sg_create_state *state, dma_addr_t addr);
struct sg_table *i915_sg_complete(struct i915_sg_create_state *state);
void i915_sg_abort(struct i915_sg_create_state *state);
@@ -2268,6 +2268,8 @@ struct i915_sg_create_state *i915_sg_create(unsigned int page_count)
* 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.
+ *
+ * MUST NOT be mixed with i915_sg_add_dma!
*/
void i915_sg_add_page(struct i915_sg_create_state *state, struct page *page)
{
@@ -2291,6 +2293,42 @@ void i915_sg_add_page(struct i915_sg_create_state *state, struct page *page)
}
/**
+ * i915_sg_add_dma - adds a dma address to the sg list being built
+ * @state: state created with i915_sg_create
+ * @addr: dma address to add
+ *
+ * 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.
+ *
+ * MUST NOT be mixed with i915_sg_add_page!
+ */
+void i915_sg_add_dma(struct i915_sg_create_state *state, dma_addr_t addr)
+{
+ struct scatterlist *sg = state->sg;
+
+ if (!state->idx ||
+ sg->length >= state->max_segment ||
+ addr != state->last_pfn + PAGE_SIZE) {
+ if (state->idx)
+ sg = sg_next(sg);
+ state->st->nents++;
+ sg_set_page(sg, NULL, PAGE_SIZE, 0);
+ sg_dma_address(sg) = addr;
+ sg_dma_len(sg) = PAGE_SIZE;
+ } else {
+ sg->length += PAGE_SIZE;
+ if (IS_ENABLED(CONFIG_NEED_SG_DMA_LENGTH))
+ sg_dma_len(sg) += PAGE_SIZE;
+ }
+
+ state->last_pfn = addr;
+ state->sg = sg;
+ state->idx++;
+}
+
+/**
* i915_sg_complete - completes the sg list building
* @state: state created with i915_sg_create
*
@@ -3559,41 +3559,23 @@ static struct sg_table *
intel_partial_pages(const struct i915_ggtt_view *view,
struct drm_i915_gem_object *obj)
{
- struct sg_table *st;
- struct scatterlist *sg;
+ struct i915_sg_create_state *state;
struct sg_page_iter obj_sg_iter;
- int ret = -ENOMEM;
- st = kmalloc(sizeof(*st), GFP_KERNEL);
- if (!st)
- goto err_st_alloc;
-
- ret = sg_alloc_table(st, view->params.partial.size, GFP_KERNEL);
- if (ret)
- goto err_sg_alloc;
+ state = i915_sg_create(view->params.partial.size);
+ if (IS_ERR(state))
+ return ERR_CAST(state);
- sg = st->sgl;
- st->nents = 0;
for_each_sg_page(obj->pages->sgl, &obj_sg_iter, obj->pages->nents,
- view->params.partial.offset)
+ view->params.partial.offset)
{
- if (st->nents >= view->params.partial.size)
+ if (state->idx >= view->params.partial.size)
break;
- sg_set_page(sg, NULL, PAGE_SIZE, 0);
- sg_dma_address(sg) = sg_page_iter_dma_address(&obj_sg_iter);
- sg_dma_len(sg) = PAGE_SIZE;
-
- sg = sg_next(sg);
- st->nents++;
+ i915_sg_add_dma(state, sg_page_iter_dma_address(&obj_sg_iter));
}
- return st;
-
-err_sg_alloc:
- kfree(st);
-err_st_alloc:
- return ERR_PTR(ret);
+ return i915_sg_complete(state);
}
static int