Chris Wilson <chris@xxxxxxxxxxxxxxxxxx> writes: > A long time ago, I wrote some selftests for the struct kfence idea. Now > that we have infrastructure in i915/igt for running kselftests, include > some for i915_sw_fence. > > Signed-off-by: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx> Only minor thing that caught my eye was that I would have preferred: err_free_C: type of gotos instead of just err_C. In here tho everything was straightfoward teardown of allocations so it was clear. Reviewed-by: Mika Kuoppala <mika.kuoppala@xxxxxxxxx> > --- > drivers/gpu/drm/i915/Kconfig.debug | 12 + > drivers/gpu/drm/i915/i915_sw_fence.c | 7 +- > .../gpu/drm/i915/selftests/i915_mock_selftests.h | 1 + > drivers/gpu/drm/i915/selftests/i915_sw_fence.c | 576 +++++++++++++++++++++ > 4 files changed, 595 insertions(+), 1 deletion(-) > create mode 100644 drivers/gpu/drm/i915/selftests/i915_sw_fence.c > > diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug > index b00edd3b8800..78c5c049a347 100644 > --- a/drivers/gpu/drm/i915/Kconfig.debug > +++ b/drivers/gpu/drm/i915/Kconfig.debug > @@ -61,6 +61,18 @@ config DRM_I915_SW_FENCE_DEBUG_OBJECTS > > If in doubt, say "N". > > +config DRM_I915_SW_FENCE_CHECK_DAG > + bool "Enable additional driver debugging for detecting dependency cycles" > + depends on DRM_I915 > + default n > + help > + Choose this option to turn on extra driver debugging that may affect > + performance but will catch some internal issues. > + > + Recommended for driver developers only. > + > + If in doubt, say "N". > + > config DRM_I915_SELFTEST > bool "Enable selftests upon driver load" > depends on DRM_I915 > diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c > index a0a690d6627e..474d23c0c0ce 100644 > --- a/drivers/gpu/drm/i915/i915_sw_fence.c > +++ b/drivers/gpu/drm/i915/i915_sw_fence.c > @@ -12,6 +12,7 @@ > #include <linux/reservation.h> > > #include "i915_sw_fence.h" > +#include "i915_selftest.h" > > #define I915_SW_FENCE_FLAG_ALLOC BIT(3) /* after WQ_FLAG_* for safety */ > > @@ -274,7 +275,7 @@ static bool i915_sw_fence_check_if_after(struct i915_sw_fence *fence, > unsigned long flags; > bool err; > > - if (!IS_ENABLED(CONFIG_I915_SW_FENCE_CHECK_DAG)) > + if (!IS_ENABLED(CONFIG_DRM_I915_SW_FENCE_CHECK_DAG)) > return false; > > spin_lock_irqsave(&i915_sw_fence_lock, flags); > @@ -490,3 +491,7 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, > > return ret; > } > + > +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) > +#include "selftests/i915_sw_fence.c" > +#endif > diff --git a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h > index 76c1f149a0a0..fc74687501ba 100644 > --- a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h > +++ b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h > @@ -9,6 +9,7 @@ > * Tests are executed in order by igt/drv_selftest > */ > selftest(sanitycheck, i915_mock_sanitycheck) /* keep first (igt selfcheck) */ > +selftest(fence, i915_sw_fence_mock_selftests) > selftest(scatterlist, scatterlist_mock_selftests) > selftest(syncmap, i915_syncmap_mock_selftests) > selftest(uncore, intel_uncore_mock_selftests) > diff --git a/drivers/gpu/drm/i915/selftests/i915_sw_fence.c b/drivers/gpu/drm/i915/selftests/i915_sw_fence.c > new file mode 100644 > index 000000000000..c552c23eecff > --- /dev/null > +++ b/drivers/gpu/drm/i915/selftests/i915_sw_fence.c > @@ -0,0 +1,576 @@ > +/* > + * Copyright © 2017 Intel Corporation > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS > + * IN THE SOFTWARE. > + * > + */ > + > +#include <linux/completion.h> > +#include <linux/delay.h> > + > +#include "../i915_selftest.h" > + > +static int __i915_sw_fence_call > +fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state) > +{ > + switch (state) { > + case FENCE_COMPLETE: > + break; > + > + case FENCE_FREE: > + /* Leave the fence for the caller to free it after testing */ > + break; > + } > + > + return NOTIFY_DONE; > +} > + > +static struct i915_sw_fence *alloc_fence(void) > +{ > + struct i915_sw_fence *fence; > + > + fence = kmalloc(sizeof(*fence), GFP_KERNEL); > + if (!fence) > + return NULL; > + > + i915_sw_fence_init(fence, fence_notify); > + return fence; > +} > + > +static void free_fence(struct i915_sw_fence *fence) > +{ > + i915_sw_fence_fini(fence); > + kfree(fence); > +} > + > +static int __test_self(struct i915_sw_fence *fence) > +{ > + if (i915_sw_fence_done(fence)) > + return -EINVAL; > + > + i915_sw_fence_commit(fence); > + if (!i915_sw_fence_done(fence)) > + return -EINVAL; > + > + i915_sw_fence_wait(fence); > + if (!i915_sw_fence_done(fence)) > + return -EINVAL; > + > + return 0; > +} > + > +static int test_self(void *arg) > +{ > + struct i915_sw_fence *fence; > + int ret; > + > + /* Test i915_sw_fence signaling and completion testing */ > + fence = alloc_fence(); > + if (!fence) > + return -ENOMEM; > + > + ret = __test_self(fence); > + > + free_fence(fence); > + return ret; > +} > + > +static int test_dag(void *arg) > +{ > + struct i915_sw_fence *A, *B, *C; > + int ret = -EINVAL; > + > + /* Test detection of cycles within the i915_sw_fence graphs */ > + if (!IS_ENABLED(CONFIG_DRM_I915_SW_FENCE_CHECK_DAG)) > + return 0; > + > + A = alloc_fence(); > + if (!A) > + return -ENOMEM; > + > + if (i915_sw_fence_await_sw_fence_gfp(A, A, GFP_KERNEL) != -EINVAL) { > + pr_err("recursive cycle not detected (AA)\n"); > + goto err_A; > + } > + > + B = alloc_fence(); > + if (!B) { > + ret = -ENOMEM; > + goto err_A; > + } > + > + i915_sw_fence_await_sw_fence_gfp(A, B, GFP_KERNEL); > + if (i915_sw_fence_await_sw_fence_gfp(B, A, GFP_KERNEL) != -EINVAL) { > + pr_err("single depth cycle not detected (BAB)\n"); > + goto err_B; > + } > + > + C = alloc_fence(); > + if (i915_sw_fence_await_sw_fence_gfp(B, C, GFP_KERNEL) == -EINVAL) { > + pr_err("invalid cycle detected\n"); > + goto err_C; > + } > + if (i915_sw_fence_await_sw_fence_gfp(C, B, GFP_KERNEL) != -EINVAL) { > + pr_err("single depth cycle not detected (CBC)\n"); > + goto err_C; > + } > + if (i915_sw_fence_await_sw_fence_gfp(C, A, GFP_KERNEL) != -EINVAL) { > + pr_err("cycle not detected (BA, CB, AC)\n"); > + goto err_C; > + } > + if (i915_sw_fence_await_sw_fence_gfp(A, C, GFP_KERNEL) == -EINVAL) { > + pr_err("invalid cycle detected\n"); > + goto err_C; > + } > + > + i915_sw_fence_commit(A); > + i915_sw_fence_commit(B); > + i915_sw_fence_commit(C); > + > + ret = 0; > + if (!i915_sw_fence_done(C)) { > + pr_err("fence C not done\n"); > + ret = -EINVAL; > + } > + if (!i915_sw_fence_done(B)) { > + pr_err("fence B not done\n"); > + ret = -EINVAL; > + } > + if (!i915_sw_fence_done(A)) { > + pr_err("fence A not done\n"); > + ret = -EINVAL; > + } > +err_C: > + free_fence(C); > +err_B: > + free_fence(B); > +err_A: > + free_fence(A); > + return ret; > +} > + > +static int test_AB(void *arg) > +{ > + struct i915_sw_fence *A, *B; > + int ret; > + > + /* Test i915_sw_fence (A) waiting on an event source (B) */ > + A = alloc_fence(); > + if (!A) > + return -ENOMEM; > + B = alloc_fence(); > + if (!B) { > + ret = -ENOMEM; > + goto err_A; > + } > + > + ret = i915_sw_fence_await_sw_fence_gfp(A, B, GFP_KERNEL); > + if (ret < 0) > + goto err_B; > + if (ret == 0) { > + pr_err("Incorrectly reported fence A was complete before await\n"); > + ret = -EINVAL; > + goto err_B; > + } > + > + ret = -EINVAL; > + i915_sw_fence_commit(A); > + if (i915_sw_fence_done(A)) > + goto err_B; > + > + i915_sw_fence_commit(B); > + if (!i915_sw_fence_done(B)) { > + pr_err("Fence B is not done\n"); > + goto err_B; > + } > + > + if (!i915_sw_fence_done(A)) { > + pr_err("Fence A is not done\n"); > + goto err_B; > + } > + > + ret = 0; > +err_B: > + free_fence(B); > +err_A: > + free_fence(A); > + return ret; > +} > + > +static int test_ABC(void *arg) > +{ > + struct i915_sw_fence *A, *B, *C; > + int ret; > + > + /* Test a chain of fences, A waits on B who waits on C */ > + A = alloc_fence(); > + if (!A) > + return -ENOMEM; > + > + B = alloc_fence(); > + if (!B) { > + ret = -ENOMEM; > + goto err_A; > + } > + > + C = alloc_fence(); > + if (!C) { > + ret = -ENOMEM; > + goto err_B; > + } > + > + ret = i915_sw_fence_await_sw_fence_gfp(A, B, GFP_KERNEL); > + if (ret < 0) > + goto err_C; > + if (ret == 0) { > + pr_err("Incorrectly reported fence B was complete before await\n"); > + goto err_C; > + } > + > + ret = i915_sw_fence_await_sw_fence_gfp(B, C, GFP_KERNEL); > + if (ret < 0) > + goto err_C; > + if (ret == 0) { > + pr_err("Incorrectly reported fence C was complete before await\n"); > + goto err_C; > + } > + > + ret = -EINVAL; > + i915_sw_fence_commit(A); > + if (i915_sw_fence_done(A)) { > + pr_err("Fence A completed early\n"); > + goto err_C; > + } > + > + i915_sw_fence_commit(B); > + if (i915_sw_fence_done(B)) { > + pr_err("Fence B completed early\n"); > + goto err_C; > + } > + > + if (i915_sw_fence_done(A)) { > + pr_err("Fence A completed early (after signaling B)\n"); > + goto err_C; > + } > + > + i915_sw_fence_commit(C); > + > + ret = 0; > + if (!i915_sw_fence_done(C)) { > + pr_err("Fence C not done\n"); > + ret = -EINVAL; > + } > + if (!i915_sw_fence_done(B)) { > + pr_err("Fence B not done\n"); > + ret = -EINVAL; > + } > + if (!i915_sw_fence_done(A)) { > + pr_err("Fence A not done\n"); > + ret = -EINVAL; > + } > +err_C: > + free_fence(C); > +err_B: > + free_fence(B); > +err_A: > + free_fence(A); > + return ret; > +} > + > +static int test_AB_C(void *arg) > +{ > + struct i915_sw_fence *A, *B, *C; > + int ret = -EINVAL; > + > + /* Test multiple fences (AB) waiting on a single event (C) */ > + A = alloc_fence(); > + if (!A) > + return -ENOMEM; > + > + B = alloc_fence(); > + if (!B) { > + ret = -ENOMEM; > + goto err_A; > + } > + > + C = alloc_fence(); > + if (!B) { > + ret = -ENOMEM; > + goto err_B; > + } > + > + ret = i915_sw_fence_await_sw_fence_gfp(A, C, GFP_KERNEL); > + if (ret < 0) > + goto err_C; > + if (ret == 0) { > + ret = -EINVAL; > + goto err_C; > + } > + > + ret = i915_sw_fence_await_sw_fence_gfp(B, C, GFP_KERNEL); > + if (ret < 0) > + goto err_C; > + if (ret == 0) { > + ret = -EINVAL; > + goto err_C; > + } > + > + i915_sw_fence_commit(A); > + i915_sw_fence_commit(B); > + > + ret = 0; > + if (i915_sw_fence_done(A)) { > + pr_err("Fence A completed early\n"); > + ret = -EINVAL; > + } > + > + if (i915_sw_fence_done(B)) { > + pr_err("Fence B completed early\n"); > + ret = -EINVAL; > + } > + > + i915_sw_fence_commit(C); > + if (!i915_sw_fence_done(C)) { > + pr_err("Fence C not done\n"); > + ret = -EINVAL; > + } > + > + if (!i915_sw_fence_done(B)) { > + pr_err("Fence B not done\n"); > + ret = -EINVAL; > + } > + > + if (!i915_sw_fence_done(A)) { > + pr_err("Fence A not done\n"); > + ret = -EINVAL; > + } > + > +err_C: > + free_fence(C); > +err_B: > + free_fence(B); > +err_A: > + free_fence(A); > + return ret; > +} > + > +static int test_C_AB(void *arg) > +{ > + struct i915_sw_fence *A, *B, *C; > + int ret; > + > + /* Test multiple event sources (A,B) for a single fence (C) */ > + A = alloc_fence(); > + if (!A) > + return -ENOMEM; > + > + B = alloc_fence(); > + if (!B) { > + ret = -ENOMEM; > + goto err_A; > + } > + > + C = alloc_fence(); > + if (!B) { > + ret = -ENOMEM; > + goto err_B; > + } > + > + ret = i915_sw_fence_await_sw_fence_gfp(C, A, GFP_KERNEL); > + if (ret < 0) > + goto err_C; > + if (ret == 0) { > + ret = -EINVAL; > + goto err_C; > + } > + > + ret = i915_sw_fence_await_sw_fence_gfp(C, B, GFP_KERNEL); > + if (ret < 0) > + goto err_C; > + if (ret == 0) { > + ret = -EINVAL; > + goto err_C; > + } > + > + ret = 0; > + i915_sw_fence_commit(C); > + if (i915_sw_fence_done(C)) > + ret = -EINVAL; > + > + i915_sw_fence_commit(A); > + i915_sw_fence_commit(B); > + > + if (!i915_sw_fence_done(A)) { > + pr_err("Fence A not done\n"); > + ret = -EINVAL; > + } > + > + if (!i915_sw_fence_done(B)) { > + pr_err("Fence B not done\n"); > + ret = -EINVAL; > + } > + > + if (!i915_sw_fence_done(C)) { > + pr_err("Fence C not done\n"); > + ret = -EINVAL; > + } > + > +err_C: > + free_fence(C); > +err_B: > + free_fence(B); > +err_A: > + free_fence(A); > + return ret; > +} > + > +static int test_chain(void *arg) > +{ > + int nfences = 4096; > + struct i915_sw_fence **fences; > + int ret, i; > + > + /* Test a long chain of fences */ > + fences = kmalloc_array(nfences, sizeof(*fences), GFP_KERNEL); > + if (!fences) > + return -ENOMEM; > + > + for (i = 0; i < nfences; i++) { > + fences[i] = alloc_fence(); > + if (!fences[i]) { > + nfences = i; > + ret = -ENOMEM; > + goto err; > + } > + > + if (i > 0) { > + ret = i915_sw_fence_await_sw_fence_gfp(fences[i], > + fences[i - 1], > + GFP_KERNEL); > + if (ret < 0) { > + nfences = i + 1; > + goto err; > + } > + > + i915_sw_fence_commit(fences[i]); > + } > + } > + > + ret = 0; > + for (i = nfences; --i; ) { > + if (i915_sw_fence_done(fences[i])) { > + if (ret == 0) > + pr_err("Fence[%d] completed early\n", i); > + ret = -EINVAL; > + } > + } > + i915_sw_fence_commit(fences[0]); > + for (i = 0; ret == 0 && i < nfences; i++) { > + if (!i915_sw_fence_done(fences[i])) { > + pr_err("Fence[%d] is not done\n", i); > + ret = -EINVAL; > + } > + } > + > +err: > + for (i = 0; i < nfences; i++) > + free_fence(fences[i]); > + kfree(fences); > + return ret; > +} > + > +struct task_ipc { > + struct work_struct work; > + struct completion started; > + struct i915_sw_fence *in, *out; > + int value; > +}; > + > +static void task_ipc(struct work_struct *work) > +{ > + struct task_ipc *ipc = container_of(work, typeof(*ipc), work); > + > + complete(&ipc->started); > + > + i915_sw_fence_wait(ipc->in); > + smp_store_mb(ipc->value, 1); > + i915_sw_fence_commit(ipc->out); > +} > + > +static int test_ipc(void *arg) > +{ > + struct task_ipc ipc; > + int ret = 0; > + > + /* Test use of i915_sw_fence as an interprocess signaling mechanism */ > + ipc.in = alloc_fence(); > + if (!ipc.in) > + return -ENOMEM; > + ipc.out = alloc_fence(); > + if (!ipc.out) { > + ret = -ENOMEM; > + goto err_in; > + } > + > + /* use a completion to avoid chicken-and-egg testing */ > + init_completion(&ipc.started); > + > + ipc.value = 0; > + INIT_WORK(&ipc.work, task_ipc); > + schedule_work(&ipc.work); > + > + wait_for_completion(&ipc.started); > + > + usleep_range(1000, 2000); > + if (READ_ONCE(ipc.value)) { > + pr_err("worker updated value before i915_sw_fence was signaled\n"); > + ret = -EINVAL; > + } > + > + i915_sw_fence_commit(ipc.in); > + i915_sw_fence_wait(ipc.out); > + > + if (!READ_ONCE(ipc.value)) { > + pr_err("worker signaled i915_sw_fence before value was posted\n"); > + ret = -EINVAL; > + } > + > + flush_work(&ipc.work); > + free_fence(ipc.out); > +err_in: > + free_fence(ipc.in); > + return ret; > +} > + > +int i915_sw_fence_mock_selftests(void) > +{ > + static const struct i915_subtest tests[] = { > + SUBTEST(test_self), > + SUBTEST(test_dag), > + SUBTEST(test_AB), > + SUBTEST(test_ABC), > + SUBTEST(test_AB_C), > + SUBTEST(test_C_AB), > + SUBTEST(test_chain), > + SUBTEST(test_ipc), > + }; > + > + return i915_subtests(tests, NULL); > +} > -- > 2.11.0 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/intel-gfx _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx