Modernize the open coded test framework by using kunit.
Add a num_online_cpus() check to test_race_signal_callback() as the default kunit.py runs the VM with a single CPU and this test depends on two truly parallel kthreads. Skip it instead of hanging.
Signed-off-by: Jason Gunthorpe jgg@nvidia.com --- drivers/dma-buf/Makefile | 2 +- drivers/dma-buf/selftests.h | 1 - drivers/dma-buf/st-dma-fence.c | 200 ++++++++++++++------------------- 3 files changed, 88 insertions(+), 115 deletions(-)
diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index 2e7a1453e2fe04..37c94562e677ca 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -9,13 +9,13 @@ obj-$(CONFIG_UDMABUF) += udmabuf.o
dmabuf_selftests-y := \ selftest.o \ - st-dma-fence.o \ st-dma-fence-chain.o \ st-dma-fence-unwrap.o
obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
dmabuf_kunit-y := \ + st-dma-fence.o \ st-dma-resv.o
obj-$(CONFIG_DMABUF_KUNIT_TEST) += dmabuf_kunit.o diff --git a/drivers/dma-buf/selftests.h b/drivers/dma-buf/selftests.h index 2fdaca6b3e92e2..0a348a5cbbebc7 100644 --- a/drivers/dma-buf/selftests.h +++ b/drivers/dma-buf/selftests.h @@ -10,6 +10,5 @@ * Tests are executed in order by igt/dmabuf_selftest */ selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */ -selftest(dma_fence, dma_fence) selftest(dma_fence_chain, dma_fence_chain) selftest(dma_fence_unwrap, dma_fence_unwrap) diff --git a/drivers/dma-buf/st-dma-fence.c b/drivers/dma-buf/st-dma-fence.c index 0d9d524d79b6d9..4992722296968d 100644 --- a/drivers/dma-buf/st-dma-fence.c +++ b/drivers/dma-buf/st-dma-fence.c @@ -4,6 +4,7 @@ * Copyright © 2019 Intel Corporation */
+#include <kunit/test.h> #include <linux/delay.h> #include <linux/dma-fence.h> #include <linux/kernel.h> @@ -12,8 +13,6 @@ #include <linux/slab.h> #include <linux/spinlock.h>
-#include "selftest.h" - static const char *mock_name(struct dma_fence *f) { return "mock"; @@ -36,62 +35,55 @@ static struct dma_fence *mock_fence(void) return f; }
-static int sanitycheck(void *arg) +static void test_sanitycheck(struct kunit *test) { struct dma_fence *f;
f = mock_fence(); - if (!f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_signal(f); dma_fence_put(f); - - return 0; }
-static int test_signaling(void *arg) +static void test_signaling(struct kunit *test) { struct dma_fence *f; - int err = -EINVAL;
f = mock_fence(); - if (!f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
if (dma_fence_is_signaled(f)) { - pr_err("Fence unexpectedly signaled on creation\n"); + KUNIT_FAIL(test, "Fence unexpectedly signaled on creation"); goto err_free; }
if (dma_fence_check_and_signal(f)) { - pr_err("Fence reported being already signaled\n"); + KUNIT_FAIL(test, "Fence reported being already signaled"); goto err_free; }
if (!dma_fence_is_signaled(f)) { - pr_err("Fence not reporting signaled\n"); + KUNIT_FAIL(test, "Fence not reporting signaled"); goto err_free; }
if (!dma_fence_test_signaled_flag(f)) { - pr_err("Fence reported not being already signaled\n"); + KUNIT_FAIL(test, "Fence reported not being already signaled"); goto err_free; }
if (rcu_dereference_protected(f->ops, true)) { - pr_err("Fence ops not cleared on signal\n"); + KUNIT_FAIL(test, "Fence ops not cleared on signal"); goto err_free; }
- err = 0; err_free: dma_fence_put(f); - return err; }
struct simple_cb { @@ -104,215 +96,187 @@ static void simple_callback(struct dma_fence *f, struct dma_fence_cb *cb) smp_store_mb(container_of(cb, struct simple_cb, cb)->seen, true); }
-static int test_add_callback(void *arg) +static void test_add_callback(struct kunit *test) { struct simple_cb cb = {}; struct dma_fence *f; - int err = -EINVAL;
f = mock_fence(); - if (!f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, f);
if (dma_fence_add_callback(f, &cb.cb, simple_callback)) { - pr_err("Failed to add callback, fence already signaled!\n"); + KUNIT_FAIL(test, "Failed to add callback, fence already signaled!"); goto err_free; }
dma_fence_signal(f); if (!cb.seen) { - pr_err("Callback failed!\n"); + KUNIT_FAIL(test, "Callback failed!"); goto err_free; }
- err = 0; err_free: dma_fence_put(f); - return err; }
-static int test_late_add_callback(void *arg) +static void test_late_add_callback(struct kunit *test) { struct simple_cb cb = {}; struct dma_fence *f; - int err = -EINVAL;
f = mock_fence(); - if (!f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_signal(f);
if (!dma_fence_add_callback(f, &cb.cb, simple_callback)) { - pr_err("Added callback, but fence was already signaled!\n"); + KUNIT_FAIL(test, "Added callback, but fence was already signaled!"); goto err_free; }
dma_fence_signal(f); if (cb.seen) { - pr_err("Callback called after failed attachment !\n"); + KUNIT_FAIL(test, "Callback called after failed attachment!"); goto err_free; }
- err = 0; err_free: dma_fence_put(f); - return err; }
-static int test_rm_callback(void *arg) +static void test_rm_callback(struct kunit *test) { struct simple_cb cb = {}; struct dma_fence *f; - int err = -EINVAL;
f = mock_fence(); - if (!f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, f);
if (dma_fence_add_callback(f, &cb.cb, simple_callback)) { - pr_err("Failed to add callback, fence already signaled!\n"); + KUNIT_FAIL(test, "Failed to add callback, fence already signaled!"); goto err_free; }
if (!dma_fence_remove_callback(f, &cb.cb)) { - pr_err("Failed to remove callback!\n"); + KUNIT_FAIL(test, "Failed to remove callback!"); goto err_free; }
dma_fence_signal(f); if (cb.seen) { - pr_err("Callback still signaled after removal!\n"); + KUNIT_FAIL(test, "Callback still signaled after removal!"); goto err_free; }
- err = 0; err_free: dma_fence_put(f); - return err; }
-static int test_late_rm_callback(void *arg) +static void test_late_rm_callback(struct kunit *test) { struct simple_cb cb = {}; struct dma_fence *f; - int err = -EINVAL;
f = mock_fence(); - if (!f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, f);
if (dma_fence_add_callback(f, &cb.cb, simple_callback)) { - pr_err("Failed to add callback, fence already signaled!\n"); + KUNIT_FAIL(test, "Failed to add callback, fence already signaled!"); goto err_free; }
dma_fence_signal(f); if (!cb.seen) { - pr_err("Callback failed!\n"); + KUNIT_FAIL(test, "Callback failed!"); goto err_free; }
if (dma_fence_remove_callback(f, &cb.cb)) { - pr_err("Callback removal succeed after being executed!\n"); + KUNIT_FAIL(test, "Callback removal succeeded after being executed!"); goto err_free; }
- err = 0; err_free: dma_fence_put(f); - return err; }
-static int test_status(void *arg) +static void test_status(struct kunit *test) { struct dma_fence *f; - int err = -EINVAL;
f = mock_fence(); - if (!f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
if (dma_fence_get_status(f)) { - pr_err("Fence unexpectedly has signaled status on creation\n"); + KUNIT_FAIL(test, "Fence unexpectedly has signaled status on creation"); goto err_free; }
dma_fence_signal(f); if (!dma_fence_get_status(f)) { - pr_err("Fence not reporting signaled status\n"); + KUNIT_FAIL(test, "Fence not reporting signaled status"); goto err_free; }
- err = 0; err_free: dma_fence_put(f); - return err; }
-static int test_error(void *arg) +static void test_error(struct kunit *test) { struct dma_fence *f; - int err = -EINVAL;
f = mock_fence(); - if (!f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_set_error(f, -EIO);
if (dma_fence_get_status(f)) { - pr_err("Fence unexpectedly has error status before signal\n"); + KUNIT_FAIL(test, "Fence unexpectedly has error status before signal"); goto err_free; }
dma_fence_signal(f); if (dma_fence_get_status(f) != -EIO) { - pr_err("Fence not reporting error status, got %d\n", - dma_fence_get_status(f)); + KUNIT_FAIL(test, "Fence not reporting error status, got %d", + dma_fence_get_status(f)); goto err_free; }
- err = 0; err_free: dma_fence_put(f); - return err; }
-static int test_wait(void *arg) +static void test_wait(struct kunit *test) { struct dma_fence *f; - int err = -EINVAL;
f = mock_fence(); - if (!f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
if (dma_fence_wait_timeout(f, false, 0) != 0) { - pr_err("Wait reported complete before being signaled\n"); + KUNIT_FAIL(test, "Wait reported complete before being signaled"); goto err_free; }
dma_fence_signal(f);
if (dma_fence_wait_timeout(f, false, 0) != 1) { - pr_err("Wait reported incomplete after being signaled\n"); + KUNIT_FAIL(test, "Wait reported incomplete after being signaled"); goto err_free; }
- err = 0; err_free: dma_fence_signal(f); dma_fence_put(f); - return err; }
struct wait_timer { @@ -327,21 +291,19 @@ static void wait_timer(struct timer_list *timer) dma_fence_signal(wt->f); }
-static int test_wait_timeout(void *arg) +static void test_wait_timeout(struct kunit *test) { struct wait_timer wt; - int err = -EINVAL;
timer_setup_on_stack(&wt.timer, wait_timer, 0);
wt.f = mock_fence(); - if (!wt.f) - return -ENOMEM; + KUNIT_ASSERT_NOT_NULL(test, wt.f);
dma_fence_enable_sw_signaling(wt.f);
if (dma_fence_wait_timeout(wt.f, false, 1) != 0) { - pr_err("Wait reported complete before being signaled\n"); + KUNIT_FAIL(test, "Wait reported complete before being signaled"); goto err_free; }
@@ -349,42 +311,38 @@ static int test_wait_timeout(void *arg)
if (dma_fence_wait_timeout(wt.f, false, HZ) == 0) { if (timer_pending(&wt.timer)) { - pr_notice("Timer did not fire within one HZ!\n"); - err = 0; /* not our fault! */ + kunit_mark_skipped( + test, "Timer did not fire within on HZ!\n"); } else { - pr_err("Wait reported incomplete after timeout\n"); + KUNIT_FAIL(test, + "Wait reported incomplete after timeout"); } goto err_free; }
- err = 0; err_free: timer_delete_sync(&wt.timer); timer_destroy_on_stack(&wt.timer); dma_fence_signal(wt.f); dma_fence_put(wt.f); - return err; }
-static int test_stub(void *arg) +static void test_stub(struct kunit *test) { struct dma_fence *f[64]; - int err = -EINVAL; int i;
for (i = 0; i < ARRAY_SIZE(f); i++) { f[i] = dma_fence_get_stub(); if (!dma_fence_is_signaled(f[i])) { - pr_err("Obtained unsignaled stub fence!\n"); + KUNIT_FAIL(test, "Obtained unsignaled stub fence!"); goto err; } }
- err = 0; err: while (i--) dma_fence_put(f[i]); - return err; }
/* Now off to the races! */ @@ -473,12 +431,19 @@ static int thread_signal_callback(void *arg) return err; }
-static int race_signal_callback(void *arg) +static void test_race_signal_callback(struct kunit *test) { struct dma_fence __rcu *f[2] = {}; int ret = 0; int pass;
+ /* + * thread_signal_callback() spins under RCU and it cannot make forward + * progress unless the threads are truly running concurrently. + */ + if (num_online_cpus() < 2) + kunit_skip(test, "requires at least 2 CPUs"); + for (pass = 0; !ret && pass <= 1; pass++) { struct race_thread t[2]; int i; @@ -490,10 +455,10 @@ static int race_signal_callback(void *arg) t[i].task = kthread_run(thread_signal_callback, &t[i], "dma-fence:%d", i); if (IS_ERR(t[i].task)) { - ret = PTR_ERR(t[i].task); + KUNIT_FAIL(test, "Failed to create kthread"); while (--i >= 0) kthread_stop_put(t[i].task); - return ret; + return; } get_task_struct(t[i].task); } @@ -509,26 +474,35 @@ static int race_signal_callback(void *arg) } }
- return ret; + KUNIT_EXPECT_EQ(test, ret, 0); }
-int dma_fence(void) +static int dma_fence_suite_init(struct kunit_suite *suite) { - static const struct subtest tests[] = { - SUBTEST(sanitycheck), - SUBTEST(test_signaling), - SUBTEST(test_add_callback), - SUBTEST(test_late_add_callback), - SUBTEST(test_rm_callback), - SUBTEST(test_late_rm_callback), - SUBTEST(test_status), - SUBTEST(test_error), - SUBTEST(test_wait), - SUBTEST(test_wait_timeout), - SUBTEST(test_stub), - SUBTEST(race_signal_callback), - }; - pr_info("sizeof(dma_fence)=%zu\n", sizeof(struct dma_fence)); - return subtests(tests, NULL); + return 0; } + +static struct kunit_case dma_fence_cases[] = { + KUNIT_CASE(test_sanitycheck), + KUNIT_CASE(test_signaling), + KUNIT_CASE(test_add_callback), + KUNIT_CASE(test_late_add_callback), + KUNIT_CASE(test_rm_callback), + KUNIT_CASE(test_late_rm_callback), + KUNIT_CASE(test_status), + KUNIT_CASE(test_error), + KUNIT_CASE(test_wait), + KUNIT_CASE(test_wait_timeout), + KUNIT_CASE(test_stub), + KUNIT_CASE(test_race_signal_callback), + {} +}; + +static struct kunit_suite dma_fence_test_suite = { + .name = "dma-buf-fence", + .suite_init = dma_fence_suite_init, + .test_cases = dma_fence_cases, +}; + +kunit_test_suite(dma_fence_test_suite);