[PATCH 31/35] drm/i915/userptr: Add a flag to populate the userptr on creation

Chris Wilson chris at chris-wilson.co.uk
Sat Dec 2 18:35:08 UTC 2017


Acquiring the backing struct pages for the userptr range is not free;
the first client for userptr would insist on frequently creating userptr
objects ahead of time and not use them. For that first client, deferring
the cost of populating the userptr (calling get_user_pages()) to the
actual execbuf was a substantial improvement. However, not all clients
are the same, and most would like to validate that the userptr is valid
and backed by struct pages upon creation, so offer a
I915_USERPTR_POPULATE flag to do just that.

Note that big difference between I915_USERPTR_POPULATE and the deferred
scheme is that POPULATE is guaranteed to be synchronous, the result is
known before the ioctl returns (and the handle exposed). However, due to
system memory pressure, the object may be paged out before use,
requiring them to be paged back in on execbuf (as may always happen).

Testcase: igt/gem_userptr_blits/populate
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
Cc: MichaƂ Winiarski <michal.winiarski at intel.com>
Cc: Jason Ekstrand <jason at jlekstrand.net>
---
 drivers/gpu/drm/i915/i915_gem_userptr.c | 63 ++++++++++++++++++++++++++++++++-
 include/uapi/drm/i915_drm.h             |  1 +
 2 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
index 841ae4caf768..d3d776f8cf00 100644
--- a/drivers/gpu/drm/i915/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
@@ -748,6 +748,64 @@ probe_range(struct mm_struct *mm, unsigned long addr, unsigned long len)
 	return ret;
 }
 
+static int populate(struct drm_i915_gem_object *obj)
+{
+	const unsigned long num_pages = obj->base.size >> PAGE_SHIFT;
+	const unsigned long addr = obj->userptr.ptr;
+	struct sg_table *pages;
+	struct page **pvec;
+	int pinned;
+	int ret = 0;
+
+	pvec = kvmalloc_array(num_pages, sizeof(struct page *), GFP_KERNEL);
+	if (!pvec)
+		return -ENOMEM;
+
+	pinned = __get_user_pages_fast(addr, num_pages,
+				       !obj->userptr.read_only,
+				       pvec);
+	if (pinned < 0) {
+		ret = pinned;
+		goto err;
+	}
+
+	if (pinned < num_pages) {
+		unsigned int flags;
+
+		flags = 0;
+		if (!obj->userptr.read_only)
+			flags |= FOLL_WRITE;
+
+		down_read(&current->mm->mmap_sem);
+		do {
+			ret = get_user_pages(addr + pinned * PAGE_SIZE,
+					     num_pages - pinned,
+					     flags,
+					     pvec + pinned, NULL);
+			if (ret < 0)
+				break;
+
+			pinned += ret;
+		} while (pinned < num_pages);
+		up_read(&current->mm->mmap_sem);
+		if (ret < 0)
+			goto err;
+	}
+
+	mutex_lock(&obj->mm.lock);
+	pages = __i915_gem_userptr_alloc_pages(obj, pvec, num_pages);
+	mutex_unlock(&obj->mm.lock);
+	if (!IS_ERR(pages))
+		pinned = 0;
+	else
+		ret = PTR_ERR(pages);
+
+err:
+	release_pages(pvec, pinned);
+	kvfree(pvec);
+	return ret;
+}
+
 /**
  * Creates a new mm object that wraps some normal memory from the process
  * context - user memory.
@@ -801,6 +859,7 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file
 
 	if (args->flags & ~(I915_USERPTR_READ_ONLY |
 			    I915_USERPTR_PROBE |
+			    I915_USERPTR_POPULATE |
 			    I915_USERPTR_UNSYNCHRONIZED))
 		return -EINVAL;
 
@@ -818,7 +877,7 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file
 		return -ENODEV;
 	}
 
-	if (args->flags & I915_USERPTR_PROBE) {
+	if (args->flags & (I915_USERPTR_PROBE | I915_USERPTR_POPULATE)) {
 		/*
 		 * Check that the range pointed to represents real struct
 		 * pages and not iomappings (at this moment in time!)
@@ -848,6 +907,8 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file
 	ret = i915_gem_userptr_init__mm_struct(obj);
 	if (ret == 0)
 		ret = i915_gem_userptr_init__mmu_notifier(obj, args->flags);
+	if (ret == 0 && args->flags & I915_USERPTR_POPULATE)
+		ret = populate(obj);
 	if (ret == 0)
 		ret = drm_gem_handle_create(file, &obj->base, &handle);
 
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index 7381342602ab..f077681fc637 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -1431,6 +1431,7 @@ struct drm_i915_gem_userptr {
 	__u32 flags;
 #define I915_USERPTR_READ_ONLY		0x1
 #define I915_USERPTR_PROBE		0x2
+#define I915_USERPTR_POPULATE		0x4
 #define I915_USERPTR_UNSYNCHRONIZED	0x80000000
 	/**
 	 * Returned handle for the object.
-- 
2.15.1



More information about the Intel-gfx-trybot mailing list