[PATCH v8 21/22] drm/i915/vm_bind: Properly build persistent map sg table

Niranjana Vishwanathapura niranjana.vishwanathapura at intel.com
Wed Dec 14 04:58:34 UTC 2022


On Mon, Dec 12, 2022 at 06:17:01PM +0000, Matthew Auld wrote:
>On 29/11/2022 07:26, Niranjana Vishwanathapura wrote:
>>Properly build the sg table for persistent mapping which can
>>be partial map of the underlying object. Ensure the sg pages
>>are properly set for page backed regions. The dump capture
>>support requires this for page backed regions.
>>
>>Signed-off-by: Niranjana Vishwanathapura <niranjana.vishwanathapura at intel.com>
>>---
>>  drivers/gpu/drm/i915/i915_vma.c | 120 +++++++++++++++++++++++++++++++-
>>  1 file changed, 119 insertions(+), 1 deletion(-)
>>
>>diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
>>index 1b9033865768..68a9ac77b4f2 100644
>>--- a/drivers/gpu/drm/i915/i915_vma.c
>>+++ b/drivers/gpu/drm/i915/i915_vma.c
>>@@ -1298,6 +1298,120 @@ intel_partial_pages(const struct i915_gtt_view *view,
>>  	return ERR_PTR(ret);
>>  }
>>+static unsigned int
>>+intel_copy_dma_sg(struct sg_table *src_st, struct sg_table *dst_st,
>>+		  u64 offset, u64 length, bool dry_run)
>>+{
>>+	struct scatterlist *dst_sg, *src_sg;
>>+	unsigned int i, len, nents = 0;
>>+
>>+	dst_sg = dst_st->sgl;
>>+	for_each_sgtable_dma_sg(src_st, src_sg, i) {
>>+		if (sg_dma_len(src_sg) <= offset) {
>>+			offset -= sg_dma_len(src_sg);
>>+			continue;
>>+		}
>>+
>>+		nents++;
>>+		len = min(sg_dma_len(src_sg) - offset, length);
>>+		if (!dry_run) {
>>+			sg_dma_address(dst_sg) = sg_dma_address(src_sg) + offset;
>>+			sg_dma_len(dst_sg) = len;
>>+			dst_sg = sg_next(dst_sg);
>>+		}
>>+
>>+		length -= len;
>>+		offset = 0;
>>+		if (!length)
>>+			break;
>>+	}
>>+	WARN_ON_ONCE(length);
>>+
>>+	return nents;
>>+}
>>+
>>+static unsigned int
>>+intel_copy_sg(struct sg_table *src_st, struct sg_table *dst_st,
>>+	      u64 offset, u64 length, bool dry_run)
>>+{
>>+	struct scatterlist *dst_sg, *src_sg;
>>+	unsigned int i, len, nents = 0;
>>+
>>+	dst_sg = dst_st->sgl;
>>+	for_each_sgtable_sg(src_st, src_sg, i) {
>>+		if (src_sg->length <= offset) {
>>+			offset -= src_sg->length;
>>+			continue;
>>+		}
>>+
>>+		nents++;
>>+		len = min(src_sg->length - offset, length);
>>+		if (!dry_run) {
>>+			unsigned long pfn;
>>+
>>+			pfn = page_to_pfn(sg_page(src_sg)) + offset / PAGE_SIZE;
>>+			sg_set_page(dst_sg, pfn_to_page(pfn), len, 0);
>>+			dst_sg = sg_next(dst_sg);
>>+		}
>>+
>>+		length -= len;
>>+		offset = 0;
>>+		if (!length)
>>+			break;
>>+	}
>>+	WARN_ON_ONCE(length);
>>+
>>+	return nents;
>>+}
>>+
>>+static noinline struct sg_table *
>>+intel_persistent_partial_pages(const struct i915_gtt_view *view,
>>+			       struct drm_i915_gem_object *obj)
>>+{
>>+	u64 offset = view->partial.offset << PAGE_SHIFT;
>>+	struct sg_table *st, *obj_st = obj->mm.pages;
>>+	u64 length = view->partial.size << PAGE_SHIFT;
>>+	struct scatterlist *sg;
>>+	unsigned int nents;
>>+	int ret = -ENOMEM;
>>+
>>+	st = kmalloc(sizeof(*st), GFP_KERNEL);
>>+	if (!st)
>>+		goto err_st_alloc;
>>+
>>+	/* Get required sg_table size */
>>+	nents = intel_copy_dma_sg(obj_st, st, offset, length, true);
>>+	if (i915_gem_object_has_struct_page(obj)) {
>>+		unsigned int pg_nents;
>>+
>>+		pg_nents = intel_copy_sg(obj_st, st, offset, length, true);
>>+		if (nents < pg_nents)
>>+			nents = pg_nents;
>>+	}
>>+
>>+	ret = sg_alloc_table(st, nents, GFP_KERNEL);
>>+	if (ret)
>>+		goto err_sg_alloc;
>>+
>>+	/* Build sg_table for specified <offset, length> section */
>>+	intel_copy_dma_sg(obj_st, st, offset, length, false);
>>+	if (i915_gem_object_has_struct_page(obj))
>>+		intel_copy_sg(obj_st, st, offset, length, false);
>>+
>>+	/* Mark last sg */
>>+	sg = st->sgl;
>>+	while (sg_next(sg))
>>+		sg = sg_next(sg);
>>+	sg_mark_end(sg);
>
>Do we need this bit? The nents is exactly orig_nents, and 
>sg_alloc_table will already mark the end for you.
>

Ok, looks like we don't need it as sg_alloc_table() sets it.
While looking at sg_alloc_table(), looks like it is possible for it
to return with -ENOMEM with partial built table, but we are not
cleaning it anywhere. Something to consider later may be.
https://elixir.bootlin.com/linux/latest/source/lib/scatterlist.c#L330

>Is it not possible to re-use remap_contiguous_pages() somehow? Also do 
>we need the dry_run bit if we use sg_trim()? Maybe something like:
>
>dst = sg_alloc_table(partial.size);
>
>remap_contigious_pages_sg(dst, src);
>i915_sg_trim(dst);
>
>dst->nents = 0;
>sg = remap_contigious_pages_dma_sg(dst, src);
>

But then those remap_contiguous_pages[_dma]_sg would look just like
out intel_copy[_dma]_sg().
And the problem I have with i915_sg_trim() is that it uses _sg iterator
only and not _dma_sg iterator. I think at least in theory, it is possible
to have more number of dma_sg elements than the sg (page) elements. Right?
That is why I am doing a dry run of both above and getting the max of both.

Niranjana

>>+
>>+	return st;
>>+
>>+err_sg_alloc:
>>+	kfree(st);
>>+err_st_alloc:
>>+	return ERR_PTR(ret);
>>+}
>>+
>>  static int
>>  __i915_vma_get_pages(struct i915_vma *vma)
>>  {
>>@@ -1330,7 +1444,11 @@ __i915_vma_get_pages(struct i915_vma *vma)
>>  		break;
>>  	case I915_GTT_VIEW_PARTIAL:
>>-		pages = intel_partial_pages(&vma->gtt_view, vma->obj);
>>+		if (i915_vma_is_persistent(vma))
>>+			pages = intel_persistent_partial_pages(&vma->gtt_view,
>>+							       vma->obj);
>>+		else
>>+			pages = intel_partial_pages(&vma->gtt_view, vma->obj);
>>  		break;
>>  	}


More information about the dri-devel mailing list