[PATCH 21/21] drm/i915: Use reservation_object to coordinate userptr get_pages()
Chris Wilson
chris at chris-wilson.co.uk
Fri May 31 22:09:28 UTC 2019
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
.../gpu/drm/i915/gem/i915_gem_execbuffer.c | 3 -
drivers/gpu/drm/i915/gem/i915_gem_userptr.c | 104 +++++++++++++++---
2 files changed, 91 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index db34593aaefc..c7eec146d5be 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -1781,9 +1781,6 @@ static noinline int eb_relocate_slow(struct i915_execbuffer *eb)
goto out;
}
- /* A frequent cause for EAGAIN are currently unavailable client pages */
- flush_workqueue(eb->i915->mm.userptr_wq);
-
err = i915_mutex_lock_interruptible(dev);
if (err) {
mutex_lock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
index cfa990edb351..6fee84628086 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
@@ -18,6 +18,8 @@
#include "i915_trace.h"
#include "intel_drv.h"
+static DEFINE_SPINLOCK(fence_lock);
+
struct i915_mm_struct {
struct mm_struct *mm;
struct drm_i915_private *i915;
@@ -420,11 +422,39 @@ i915_gem_userptr_release__mm_struct(struct drm_i915_gem_object *obj)
}
struct get_pages_work {
+ struct dma_fence dma; /* Must be first for dma_fence_free() */
+ struct i915_sw_fence wait;
struct work_struct work;
struct drm_i915_gem_object *obj;
struct task_struct *task;
};
+static const char *get_pages_work_driver_name(struct dma_fence *fence)
+{
+ return DRIVER_NAME;
+}
+
+static const char *get_pages_work_timeline_name(struct dma_fence *fence)
+{
+ return "allocation";
+}
+
+static void get_pages_work_release(struct dma_fence *fence)
+{
+ struct get_pages_work *w = container_of(fence, typeof(*w), dma);
+
+ i915_sw_fence_fini(&w->wait);
+
+ BUILD_BUG_ON(offsetof(typeof(*w), dma));
+ dma_fence_free(&w->dma);
+}
+
+static const struct dma_fence_ops get_pages_work_ops = {
+ .get_driver_name = get_pages_work_driver_name,
+ .get_timeline_name = get_pages_work_timeline_name,
+ .release = get_pages_work_release,
+};
+
static struct sg_table *
__i915_gem_userptr_alloc_pages(struct drm_i915_gem_object *obj,
struct page **pvec, int num_pages)
@@ -477,11 +507,17 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
struct page **pvec;
int pinned, ret;
- ret = -ENOMEM;
- pinned = 0;
+ if (!work->dma.error) {
+ pvec = kvmalloc_array(npages, sizeof(struct page *),
+ GFP_KERNEL);
+ ret = -ENOMEM;
+ } else {
+ pvec = NULL;
+ ret = work->dma.error;
+ }
- pvec = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
- if (pvec != NULL) {
+ pinned = 0;
+ if (pvec) {
struct mm_struct *mm = obj->userptr.mm->mm;
unsigned int flags = 0;
@@ -532,15 +568,40 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
i915_gem_object_put(obj);
put_task_struct(work->task);
- kfree(work);
+
+ dma_fence_signal(&work->dma);
+ dma_fence_put(&work->dma);
+}
+
+static int __i915_sw_fence_call
+get_pages_work_notify(struct i915_sw_fence *fence,
+ enum i915_sw_fence_notify state)
+{
+ struct get_pages_work *w = container_of(fence, typeof(*w), wait);
+
+ switch (state) {
+ case FENCE_COMPLETE:
+ if (fence->error)
+ dma_fence_set_error(&w->dma, fence->error);
+ queue_work(to_i915(w->obj->base.dev)->mm.userptr_wq, &w->work);
+ break;
+
+ case FENCE_FREE:
+ dma_fence_put(&w->dma);
+ break;
+ }
+
+ return NOTIFY_DONE;
}
static struct sg_table *
__i915_gem_userptr_get_pages_schedule(struct drm_i915_gem_object *obj)
{
struct get_pages_work *work;
+ int err;
- /* Spawn a worker so that we can acquire the
+ /*
+ * Spawn a worker so that we can acquire the
* user pages without holding our mutex. Access
* to the user pages requires mmap_sem, and we have
* a strict lock ordering of mmap_sem, struct_mutex -
@@ -565,15 +626,36 @@ __i915_gem_userptr_get_pages_schedule(struct drm_i915_gem_object *obj)
obj->userptr.work = &work->work;
+ dma_fence_init(&work->dma,
+ &get_pages_work_ops,
+ &fence_lock,
+ to_i915(obj->base.dev)->mm.unordered_timeline,
+ 0);
+ i915_sw_fence_init(&work->wait, get_pages_work_notify);
+
work->obj = i915_gem_object_get(obj);
work->task = current;
get_task_struct(work->task);
INIT_WORK(&work->work, __i915_gem_userptr_get_pages_worker);
- queue_work(to_i915(obj->base.dev)->mm.userptr_wq, &work->work);
- return ERR_PTR(-EAGAIN);
+ i915_gem_object_lock(obj);
+ GEM_BUG_ON(!reservation_object_test_signaled_rcu(obj->resv, true));
+ err = i915_sw_fence_await_reservation(&work->wait,
+ obj->resv, NULL,
+ true, I915_FENCE_TIMEOUT,
+ I915_FENCE_GFP);
+ if (err == 0)
+ reservation_object_add_excl_fence(obj->resv, &work->dma);
+ else
+ dma_fence_set_error(&work->dma, err);
+ i915_gem_object_unlock(obj);
+
+ dma_fence_get(&work->dma);
+ i915_sw_fence_commit(&work->wait);
+
+ return ERR_PTR(err);
}
static int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
@@ -582,7 +664,6 @@ static int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
struct mm_struct *mm = obj->userptr.mm->mm;
struct page **pvec;
struct sg_table *pages;
- bool active;
int pinned;
/* If userspace should engineer that these pages are replaced in
@@ -625,18 +706,15 @@ static int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
pvec);
}
- active = false;
if (pinned < 0) {
pages = ERR_PTR(pinned);
pinned = 0;
} else if (pinned < num_pages) {
pages = __i915_gem_userptr_get_pages_schedule(obj);
- active = pages == ERR_PTR(-EAGAIN);
} else {
pages = __i915_gem_userptr_alloc_pages(obj, pvec, num_pages);
- active = !IS_ERR(pages);
}
- if (active)
+ if (!IS_ERR(pages))
__i915_gem_userptr_set_active(obj, true);
if (IS_ERR(pages))
--
2.20.1
More information about the Intel-gfx-trybot
mailing list