[PATCH 50/53] drm/i915: Add asynchronous waits on a value to change from non-zero
Chris Wilson
chris at chris-wilson.co.uk
Tue Sep 18 10:51:02 UTC 2018
In order for us to be able to wait on a request being executed, we want
to listen to its execute wait queue and act on global_seqno being
assigned, the same task as we require for i915_request_wait. To tie this
into a future await, we want to hook this us to our i915_sw_fence, for
which we just require hooking in a custom wait_queue_entry into the
execute wait_queue. i915_sw_fence_await_value() does precisely this,
being signaled on transition of the target value (e.g. rq->global_seqno)
being assigned (to non-zero).
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
drivers/gpu/drm/i915/i915_sw_fence.c | 75 ++++++++++++++++++
drivers/gpu/drm/i915/i915_sw_fence.h | 7 ++
.../gpu/drm/i915/selftests/i915_sw_fence.c | 79 +++++++++++++++++++
3 files changed, 161 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c
index b68883fe73c5..e9afe9f3bd9d 100644
--- a/drivers/gpu/drm/i915/i915_sw_fence.c
+++ b/drivers/gpu/drm/i915/i915_sw_fence.c
@@ -573,6 +573,81 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence,
return ret;
}
+struct wait_value {
+ wait_queue_entry_t base;
+ unsigned int *value;
+ void (*hook)(struct i915_sw_fence *, void *);
+ void *data;
+};
+
+static int i915_sw_fence_wake_value(wait_queue_entry_t *wq,
+ unsigned mode, int flags, void *key)
+{
+ struct wait_value *wv = container_of(wq, typeof(*wv), base);
+
+ if (!READ_ONCE(*wv->value))
+ return 0;
+
+ if (wv->hook)
+ wv->hook(wv->base.private, wv->data);
+
+ return i915_sw_fence_wake(wq, mode, flags, key);
+}
+
+int i915_sw_fence_await_value(struct i915_sw_fence *fence,
+ wait_queue_head_t *wait,
+ unsigned int *value,
+ void (*hook)(struct i915_sw_fence *, void *),
+ void *data,
+ gfp_t gfp)
+{
+ struct wait_value *wv;
+ unsigned long flags;
+ int pending;
+
+ if (READ_ONCE(*value))
+ goto out;
+
+ wv = kmalloc(sizeof(*wv), gfp);
+ if (unlikely(!wv)) {
+ if (!gfpflags_allow_blocking(gfp))
+ return -ENOMEM;
+
+ wait_event(*wait, READ_ONCE(*value));
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&wv->base.entry);
+ wv->base.flags = I915_SW_FENCE_FLAG_ALLOC;
+ wv->base.func = i915_sw_fence_wake_value;
+ wv->base.private = fence;
+ wv->value = value;
+
+ wv->hook = hook;
+ wv->data = data;
+
+ i915_sw_fence_await(fence);
+
+ spin_lock_irqsave(&wait->lock, flags);
+ if (!READ_ONCE(*value)) {
+ __add_wait_queue_entry_tail(wait, &wv->base);
+ pending = 1;
+ } else {
+ if (hook)
+ hook(fence, data);
+ i915_sw_fence_wake(&wv->base, 0, 0, NULL);
+ pending = 0;
+ }
+ spin_unlock_irqrestore(&wait->lock, flags);
+
+ return pending;
+
+out:
+ if (hook)
+ hook(fence, data);
+ return 0;
+}
+
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftests/lib_sw_fence.c"
#include "selftests/i915_sw_fence.c"
diff --git a/drivers/gpu/drm/i915/i915_sw_fence.h b/drivers/gpu/drm/i915/i915_sw_fence.h
index 914a734d49bc..2604e2bcf20a 100644
--- a/drivers/gpu/drm/i915/i915_sw_fence.h
+++ b/drivers/gpu/drm/i915/i915_sw_fence.h
@@ -69,6 +69,13 @@ int i915_sw_fence_await_sw_fence_gfp(struct i915_sw_fence *fence,
struct i915_sw_fence *after,
gfp_t gfp);
+int i915_sw_fence_await_value(struct i915_sw_fence *fence,
+ wait_queue_head_t *wait,
+ unsigned int *value,
+ void (*hook)(struct i915_sw_fence *, void *data),
+ void *data,
+ gfp_t gfp);
+
struct i915_sw_dma_fence_cb {
struct dma_fence_cb base;
struct i915_sw_fence *fence;
diff --git a/drivers/gpu/drm/i915/selftests/i915_sw_fence.c b/drivers/gpu/drm/i915/selftests/i915_sw_fence.c
index cdbc8f134e5e..020c08b50e0b 100644
--- a/drivers/gpu/drm/i915/selftests/i915_sw_fence.c
+++ b/drivers/gpu/drm/i915/selftests/i915_sw_fence.c
@@ -566,6 +566,84 @@ static int test_ipc(void *arg)
return ret;
}
+static void value_hook(struct i915_sw_fence *fence,
+ void *data)
+{
+ ++*(unsigned int *)data;
+}
+
+static int test_value(void *arg)
+{
+ struct i915_sw_fence *fence;
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+ unsigned int value = 0;
+ unsigned int called = 0;
+ int err;
+
+ fence = alloc_fence();
+ if (!fence)
+ return -ENOMEM;
+
+ value = 1;
+ err = i915_sw_fence_await_value(fence, &wq, &value,
+ value_hook, &called,
+ GFP_KERNEL);
+ if (err < 0)
+ goto err;
+ if (err) {
+ pr_err("Fence await marked as pending, but value is already set\n");
+ err = -EINVAL;
+ goto err;
+ }
+ if (called != 1) {
+ pr_err("Expected completion callback: called=%d (expected 1)\n",
+ called);
+ err = -EINVAL;
+ goto err;
+ }
+
+ called = 0;
+ value = 0;
+ err = i915_sw_fence_await_value(fence, &wq, &value,
+ value_hook, &called,
+ GFP_KERNEL);
+ if (err < 0)
+ goto err;
+ if (!err) {
+ pr_err("Expected pending await, reported completed instead\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ WRITE_ONCE(value, 1);
+ wake_up_all(&wq);
+ if (called != 1) {
+ pr_err("Expected completion callback: called=%d (expected 1)\n",
+ called);
+ err = -EINVAL;
+ goto err;
+ }
+
+ if (i915_sw_fence_done(fence)) {
+ pr_err("Fence signaled before being committed\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ i915_sw_fence_commit(fence);
+
+ if (!i915_sw_fence_done(fence)) {
+ pr_err("Fence not signaled after being completed\n");
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = 0;
+err:
+ free_fence(fence);
+ return err;
+}
+
static int test_timer(void *arg)
{
unsigned long target, delay;
@@ -742,6 +820,7 @@ int i915_sw_fence_mock_selftests(void)
SUBTEST(test_C_AB),
SUBTEST(test_chain),
SUBTEST(test_ipc),
+ SUBTEST(test_value),
SUBTEST(test_timer),
SUBTEST(test_dma_fence),
};
--
2.19.0
More information about the Intel-gfx-trybot
mailing list