[PATCH 54/55] drm/i915: Add asynchronous waits on a value to change from non-zero

Chris Wilson chris at chris-wilson.co.uk
Tue Nov 20 09:30:54 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..bba1f1bef863 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 *fence, void *data);
+	void *data;
+};
+
+static int i915_sw_fence_wake_value(wait_queue_entry_t *wq,
+				    unsigned int 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.1



More information about the Intel-gfx-trybot mailing list