From patchwork Mon Aug 19 09:59:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Chris Wilson X-Patchwork-Id: 11100601 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5209C14DB for ; Mon, 19 Aug 2019 09:59:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 40C082864F for ; Mon, 19 Aug 2019 09:59:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3505B28668; Mon, 19 Aug 2019 09:59:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 9B0592864F for ; Mon, 19 Aug 2019 09:59:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B98D36E0F3; Mon, 19 Aug 2019 09:59:45 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from fireflyinternet.com (mail.fireflyinternet.com [109.228.58.192]) by gabe.freedesktop.org (Postfix) with ESMTPS id 3339E6E0F4; Mon, 19 Aug 2019 09:59:42 +0000 (UTC) X-Default-Received-SPF: pass (skip=forwardok (res=PASS)) x-ip-name=78.156.65.138; Received: from haswell.alporthouse.com (unverified [78.156.65.138]) by fireflyinternet.com (Firefly Internet (M1)) with ESMTP id 18186722-1500050 for multiple; Mon, 19 Aug 2019 10:59:29 +0100 From: Chris Wilson To: dri-devel@lists.freedesktop.org Subject: [PATCH 1/3] dma-buf: Introduce selftesting framework Date: Mon, 19 Aug 2019 10:59:26 +0100 Message-Id: <20190819095928.32091-1-chris@chris-wilson.co.uk> X-Mailer: git-send-email 2.23.0.rc1 MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Tomi Sarvela , Daniel Vetter , intel-gfx@lists.freedesktop.org Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP In light of recent review slip ups, the absence of a suite of tests for dma-buf became apparent. Given the current plethora of testing frameworks, opt for one already in use by Intel's CI and so allow easy hook up into igt. We introduce a new module that when loaded will execute the list of selftests and their subtest. The names of the selftests are put into the modinfo as parameters so that igt can identify each, and run them independently, principally for ease of error reporting. Signed-off-by: Chris Wilson Cc: Daniel Vetter Cc: Tomi Sarvela --- drivers/dma-buf/Kconfig | 5 ++ drivers/dma-buf/Makefile | 3 + drivers/dma-buf/selftest.c | 167 ++++++++++++++++++++++++++++++++++++ drivers/dma-buf/selftest.h | 30 +++++++ drivers/dma-buf/selftests.h | 12 +++ 5 files changed, 217 insertions(+) create mode 100644 drivers/dma-buf/selftest.c create mode 100644 drivers/dma-buf/selftest.h create mode 100644 drivers/dma-buf/selftests.h diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index b6a9c2f1bc41..a23b6752d11a 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -39,4 +39,9 @@ config UDMABUF A driver to let userspace turn memfd regions into dma-bufs. Qemu can use this to create host dmabufs for guest framebuffers. +config DMABUF_SELFTESTS + tristate "Selftests for the dma-buf interfaces" + default n + depends on DMA_SHARED_BUFFER + endmenu diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index dcfb01e7c6f4..b5ae122a9349 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -4,3 +4,6 @@ obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ obj-$(CONFIG_SYNC_FILE) += sync_file.o obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o obj-$(CONFIG_UDMABUF) += udmabuf.o + +dmabuf_selftests-y := selftest.o +obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o diff --git a/drivers/dma-buf/selftest.c b/drivers/dma-buf/selftest.c new file mode 100644 index 000000000000..c60b6944b4bd --- /dev/null +++ b/drivers/dma-buf/selftest.c @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: MIT */ + +/* + * Copyright © 2019 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include "selftest.h" + +enum { +#define selftest(n, func) __idx_##n, +#include "selftests.h" +#undef selftest +}; + +#define selftest(n, f) [__idx_##n] = { .name = #n, .func = f }, +static struct selftest { + bool enabled; + const char *name; + int (*func)(void); +} selftests[] = { +#include "selftests.h" +}; +#undef selftest + +/* Embed the line number into the parameter name so that we can order tests */ +#define param(n) __PASTE(igt__, __PASTE(__PASTE(__LINE__, __), n)) +#define selftest_0(n, func, id) \ +module_param_named(id, selftests[__idx_##n].enabled, bool, 0400); +#define selftest(n, func) selftest_0(n, func, param(n)) +#include "selftests.h" +#undef selftest + +int __sanitycheck__(void) +{ + pr_debug("Hello World!\n"); + return 0; +} + +static char *__st_filter; + +static bool apply_subtest_filter(const char *caller, const char *name) +{ + char *filter, *sep, *tok; + bool result = true; + + filter = kstrdup(__st_filter, GFP_KERNEL); + for (sep = filter; (tok = strsep(&sep, ","));) { + bool allow = true; + char *sl; + + if (*tok == '!') { + allow = false; + tok++; + } + + if (*tok == '\0') + continue; + + sl = strchr(tok, '/'); + if (sl) { + *sl++ = '\0'; + if (strcmp(tok, caller)) { + if (allow) + result = false; + continue; + } + tok = sl; + } + + if (strcmp(tok, name)) { + if (allow) + result = false; + continue; + } + + result = allow; + break; + } + kfree(filter); + + return result; +} + +int +__subtests(const char *caller, const struct subtest *st, int count, void *data) +{ + int err; + + for (; count--; st++) { + cond_resched(); + if (signal_pending(current)) + return -EINTR; + + if (!apply_subtest_filter(caller, st->name)) + continue; + + pr_info("dma-buf: Running %s/%s\n", caller, st->name); + + err = st->func(data); + if (err && err != -EINTR) { + pr_err("dma-buf/%s: %s failed with error %d\n", + caller, st->name, err); + return err; + } + } + + return 0; +} + +static void set_default_test_all(struct selftest *st, unsigned long count) +{ + unsigned long i; + + for (i = 0; i < count; i++) + if (st[i].enabled) + return; + + for (i = 0; i < count; i++) + st[i].enabled = true; +} + +static int run_selftests(struct selftest *st, unsigned long count) +{ + int err = 0; + + set_default_test_all(st, count); + + /* Tests are listed in natural order in selftests.h */ + for (; count--; st++) { + if (!st->enabled) + continue; + + pr_info("dma-buf: Running %s\n", st->name); + err = st->func(); + if (err) + break; + } + + if (WARN(err > 0 || err == -ENOTTY, + "%s returned %d, conflicting with selftest's magic values!\n", + st->name, err)) + err = -1; + + return err; +} + +static int __init st_init(void) +{ + return run_selftests(selftests, ARRAY_SIZE(selftests)); +} + +static void __exit st_exit(void) +{ +} + +module_param_named(st_filter, __st_filter, charp, 0400); +module_init(st_init); +module_exit(st_exit); + +MODULE_DESCRIPTION("Self-test harness for dma-buf"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/dma-buf/selftest.h b/drivers/dma-buf/selftest.h new file mode 100644 index 000000000000..45793aff6142 --- /dev/null +++ b/drivers/dma-buf/selftest.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +/* + * Copyright © 2019 Intel Corporation + */ + +#ifndef __SELFTEST_H__ +#define __SELFTEST_H__ + +#include + +#define selftest(name, func) int func(void); +#include "selftests.h" +#undef selftest + +struct subtest { + int (*func)(void *data); + const char *name; +}; + +int __subtests(const char *caller, + const struct subtest *st, + int count, + void *data); +#define subtests(T, data) \ + __subtests(__func__, T, ARRAY_SIZE(T), data) + +#define SUBTEST(x) { x, #x } + +#endif /* __SELFTEST_H__ */ diff --git a/drivers/dma-buf/selftests.h b/drivers/dma-buf/selftests.h new file mode 100644 index 000000000000..44b44390d23a --- /dev/null +++ b/drivers/dma-buf/selftests.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: MIT */ +/* List each unit test as selftest(name, function) + * + * The name is used as both an enum and expanded as subtest__name to create + * a module parameter. It must be unique and legal for a C identifier. + * + * The function should be of type int function(void). It may be conditionally + * compiled using #if IS_ENABLED(DRM_I915_SELFTEST). + * + * Tests are executed in order by igt/dmabuf_selftest + */ +selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */ From patchwork Mon Aug 19 09:59:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Chris Wilson X-Patchwork-Id: 11100593 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5697714DB for ; Mon, 19 Aug 2019 09:59:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 43ACA28671 for ; Mon, 19 Aug 2019 09:59:41 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3277D2864F; Mon, 19 Aug 2019 09:59:41 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 703272864F for ; Mon, 19 Aug 2019 09:59:37 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 822356E0E9; Mon, 19 Aug 2019 09:59:36 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from fireflyinternet.com (mail.fireflyinternet.com [109.228.58.192]) by gabe.freedesktop.org (Postfix) with ESMTPS id 3EF516E0E9; Mon, 19 Aug 2019 09:59:35 +0000 (UTC) X-Default-Received-SPF: pass (skip=forwardok (res=PASS)) x-ip-name=78.156.65.138; Received: from haswell.alporthouse.com (unverified [78.156.65.138]) by fireflyinternet.com (Firefly Internet (M1)) with ESMTP id 18186723-1500050 for multiple; Mon, 19 Aug 2019 10:59:29 +0100 From: Chris Wilson To: dri-devel@lists.freedesktop.org Subject: [PATCH 2/3] dma-buf: Add selftests for dma-fence Date: Mon, 19 Aug 2019 10:59:27 +0100 Message-Id: <20190819095928.32091-2-chris@chris-wilson.co.uk> X-Mailer: git-send-email 2.23.0.rc1 In-Reply-To: <20190819095928.32091-1-chris@chris-wilson.co.uk> References: <20190819095928.32091-1-chris@chris-wilson.co.uk> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Daniel Vetter , intel-gfx@lists.freedesktop.org Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP Exercise the dma-fence API exported to drivers. Signed-off-by: Chris Wilson Cc: Daniel Vetter --- drivers/dma-buf/Makefile | 5 +- drivers/dma-buf/selftests.h | 1 + drivers/dma-buf/st-dma-fence.c | 571 +++++++++++++++++++++++++++++++++ 3 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 drivers/dma-buf/st-dma-fence.c diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index b5ae122a9349..03479da06422 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -5,5 +5,8 @@ obj-$(CONFIG_SYNC_FILE) += sync_file.o obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o obj-$(CONFIG_UDMABUF) += udmabuf.o -dmabuf_selftests-y := selftest.o +dmabuf_selftests-y := \ + selftest.o \ + st-dma-fence.o + obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o diff --git a/drivers/dma-buf/selftests.h b/drivers/dma-buf/selftests.h index 44b44390d23a..5320386f02e5 100644 --- a/drivers/dma-buf/selftests.h +++ b/drivers/dma-buf/selftests.h @@ -10,3 +10,4 @@ * Tests are executed in order by igt/dmabuf_selftest */ selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */ +selftest(dma_fence, dma_fence) diff --git a/drivers/dma-buf/st-dma-fence.c b/drivers/dma-buf/st-dma-fence.c new file mode 100644 index 000000000000..a365dc7440e5 --- /dev/null +++ b/drivers/dma-buf/st-dma-fence.c @@ -0,0 +1,571 @@ +/* SPDX-License-Identifier: MIT */ + +/* + * Copyright © 2019 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "selftest.h" + +static struct kmem_cache *slab_fences; + +static struct mock_fence { + struct dma_fence base; + struct spinlock lock; +} *to_mock_fence(struct dma_fence *f) { + return container_of(f, struct mock_fence, base); +} + +static const char *mock_name(struct dma_fence *f) +{ + return "mock"; +} + +static void mock_fence_release(struct dma_fence *f) +{ + kmem_cache_free(slab_fences, to_mock_fence(f)); +} + +struct wait_cb { + struct dma_fence_cb cb; + struct task_struct *task; +}; + +static void mock_wakeup(struct dma_fence *f, struct dma_fence_cb *cb) +{ + wake_up_process(container_of(cb, struct wait_cb, cb)->task); +} + +static long mock_wait(struct dma_fence *f, bool intr, long timeout) +{ + const int state = intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE; + struct wait_cb cb = { .task = current }; + + if (dma_fence_add_callback(f, &cb.cb, mock_wakeup)) + return timeout; + + while (timeout) { + set_current_state(state); + + if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags)) + break; + + if (signal_pending_state(state, current)) + break; + + timeout = schedule_timeout(timeout); + } + __set_current_state(TASK_RUNNING); + + if (!dma_fence_remove_callback(f, &cb.cb)) + return timeout; + + if (signal_pending_state(state, current)) + return -ERESTARTSYS; + + return -ETIME; +} + +static const struct dma_fence_ops mock_ops = { + .get_driver_name = mock_name, + .get_timeline_name = mock_name, + .wait = mock_wait, + .release = mock_fence_release, +}; + +static struct dma_fence *mock_fence(void) +{ + struct mock_fence *f; + + f = kmem_cache_alloc(slab_fences, GFP_KERNEL); + if (!f) + return NULL; + + spin_lock_init(&f->lock); + dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0); + + return &f->base; +} + +static int sanitycheck(void *arg) +{ + struct dma_fence *f; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + dma_fence_signal(f); + dma_fence_put(f); + + return 0; +} + +static int test_signaling(void *arg) +{ + struct dma_fence *f; + int err = -EINVAL; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + if (dma_fence_is_signaled(f)) { + pr_err("Fence unexpectedly signaled on creation\n"); + goto err_free; + } + + if (dma_fence_signal(f)) { + pr_err("Fence reported being already signaled\n"); + goto err_free; + } + + if (!dma_fence_is_signaled(f)) { + pr_err("Fence not reporting signaled\n"); + goto err_free; + } + + if (!dma_fence_signal(f)) { + pr_err("Fence reported not being already signaled\n"); + goto err_free; + } + + err = 0; +err_free: + dma_fence_put(f); + return err; +} + +struct simple_cb { + struct dma_fence_cb cb; + bool seen; +}; + +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) +{ + struct simple_cb cb = {}; + struct dma_fence *f; + int err = -EINVAL; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + if (dma_fence_add_callback(f, &cb.cb, simple_callback)) { + pr_err("Failed to add callback, fence already signaled!\n"); + goto err_free; + } + + dma_fence_signal(f); + if (!cb.seen) { + pr_err("Callback failed!\n"); + goto err_free; + } + + err = 0; +err_free: + dma_fence_put(f); + return err; +} + +static int test_late_add_callback(void *arg) +{ + struct simple_cb cb = {}; + struct dma_fence *f; + int err = -EINVAL; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + dma_fence_signal(f); + + if (!dma_fence_add_callback(f, &cb.cb, simple_callback)) { + pr_err("Added callback, but fence was already signaled!\n"); + goto err_free; + } + + dma_fence_signal(f); + if (cb.seen) { + pr_err("Callback called after failed attachment !\n"); + goto err_free; + } + + err = 0; +err_free: + dma_fence_put(f); + return err; +} + +static int test_rm_callback(void *arg) +{ + struct simple_cb cb = {}; + struct dma_fence *f; + int err = -EINVAL; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + if (dma_fence_add_callback(f, &cb.cb, simple_callback)) { + pr_err("Failed to add callback, fence already signaled!\n"); + goto err_free; + } + + if (!dma_fence_remove_callback(f, &cb.cb)) { + pr_err("Failed to remove callback!\n"); + goto err_free; + } + + dma_fence_signal(f); + if (cb.seen) { + pr_err("Callback still signaled after removal!\n"); + goto err_free; + } + + err = 0; +err_free: + dma_fence_put(f); + return err; +} + +static int test_late_rm_callback(void *arg) +{ + struct simple_cb cb = {}; + struct dma_fence *f; + int err = -EINVAL; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + if (dma_fence_add_callback(f, &cb.cb, simple_callback)) { + pr_err("Failed to add callback, fence already signaled!\n"); + goto err_free; + } + + dma_fence_signal(f); + if (!cb.seen) { + pr_err("Callback failed!\n"); + goto err_free; + } + + if (dma_fence_remove_callback(f, &cb.cb)) { + pr_err("Callback removal succeed after being executed!\n"); + goto err_free; + } + + err = 0; +err_free: + dma_fence_put(f); + return err; +} + +static int test_status(void *arg) +{ + struct dma_fence *f; + int err = -EINVAL; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + if (dma_fence_get_status(f)) { + pr_err("Fence unexpectedly has signaled status on creation\n"); + goto err_free; + } + + dma_fence_signal(f); + if (!dma_fence_get_status(f)) { + pr_err("Fence not reporting signaled status\n"); + goto err_free; + } + + err = 0; +err_free: + dma_fence_put(f); + return err; +} + +static int test_error(void *arg) +{ + struct dma_fence *f; + int err = -EINVAL; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + dma_fence_set_error(f, -EIO); + + if (dma_fence_get_status(f)) { + pr_err("Fence unexpectedly has error status before signal\n"); + 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)); + goto err_free; + } + + err = 0; +err_free: + dma_fence_put(f); + return err; +} + +static int test_wait(void *arg) +{ + struct dma_fence *f; + int err = -EINVAL; + + f = mock_fence(); + if (!f) + return -ENOMEM; + + if (dma_fence_wait_timeout(f, false, 0) != -ETIME) { + pr_err("Wait reported complete before being signaled\n"); + goto err_free; + } + + dma_fence_signal(f); + + if (dma_fence_wait_timeout(f, false, 0) != 0) { + pr_err("Wait reported incomplete after being signaled\n"); + goto err_free; + } + + err = 0; +err_free: + dma_fence_signal(f); + dma_fence_put(f); + return err; +} + +struct wait_timer { + struct timer_list timer; + struct dma_fence *f; +}; + +static void wait_timer(struct timer_list *timer) +{ + struct wait_timer *wt = from_timer(wt, timer, timer); + + dma_fence_signal(wt->f); +} + +static int test_wait_timeout(void *arg) +{ + struct wait_timer wt; + int err = -EINVAL; + + timer_setup(&wt.timer, wait_timer, 0); + + wt.f = mock_fence(); + if (!wt.f) + return -ENOMEM; + + if (dma_fence_wait_timeout(wt.f, false, 1) != -ETIME) { + pr_err("Wait reported complete before being signaled\n"); + goto err_free; + } + + mod_timer(&wt.timer, jiffies + 1); + + if (dma_fence_wait_timeout(wt.f, false, 2) == -ETIME) { + if (timer_pending(&wt.timer)) { + pr_notice("Timer did not fire within the jiffie!\n"); + err = 0; /* not our fault! */ + } else { + pr_err("Wait reported incomplete after timeout\n"); + } + goto err_free; + } + + err = 0; +err_free: + del_timer_sync(&wt.timer); + dma_fence_signal(wt.f); + dma_fence_put(wt.f); + return err; +} + +static int test_stub(void *arg) +{ + 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"); + goto err; + } + } + + err = 0; +err: + while (i--) + dma_fence_put(f[i]); + return err; +} + +/* Now off to the races! */ + +struct race_thread { + struct dma_fence __rcu **fences; + struct task_struct *task; + bool before; + int id; +}; + +static void __wait_for_callbacks(struct dma_fence *f) +{ + spin_lock_irq(f->lock); + spin_unlock_irq(f->lock); +} + +static int thread_signal_callback(void *arg) +{ + const struct race_thread *t = arg; + unsigned long pass = 0; + unsigned long miss = 0; + int err = 0; + + while (!err && !kthread_should_stop()) { + struct dma_fence *f1, *f2; + struct simple_cb cb; + + f1 = mock_fence(); + if (!f1) { + err = -ENOMEM; + break; + } + + rcu_assign_pointer(t->fences[t->id], f1); + smp_wmb(); + + rcu_read_lock(); + do { + f2 = dma_fence_get_rcu_safe(&t->fences[!t->id]); + } while (!f2 && !kthread_should_stop()); + rcu_read_unlock(); + + if (t->before) + dma_fence_signal(f1); + + smp_store_mb(cb.seen, false); + if (!f2 || dma_fence_add_callback(f2, &cb.cb, simple_callback)) + miss++, cb.seen = true; + + if (!t->before) + dma_fence_signal(f1); + + if (!cb.seen) { + dma_fence_wait(f2, false); + __wait_for_callbacks(f2); + } + + if (!READ_ONCE(cb.seen)) { + pr_err("Callback not seen on thread %d, pass %lu (%lu misses), signaling %s add_callback; fence signaled? %s\n", + t->id, pass, miss, + t->before ? "before" : "after", + dma_fence_is_signaled(f2) ? "yes" : "no"); + err = -EINVAL; + } + + dma_fence_put(f2); + + rcu_assign_pointer(t->fences[t->id], NULL); + smp_wmb(); + + dma_fence_put(f1); + + pass++; + } + + pr_info("%s[%d] completed %lu passes, %lu misses\n", + __func__, t->id, pass, miss); + return err; +} + +static int race_signal_callback(void *arg) +{ + struct dma_fence __rcu *f[2] = {}; + int ret = 0; + int pass; + + for (pass = 0; !ret && pass <= 1; pass++) { + struct race_thread t[2]; + int i; + + for (i = 0; i < ARRAY_SIZE(t); i++) { + t[i].fences = f; + t[i].id = i; + t[i].before = pass; + t[i].task = kthread_run(thread_signal_callback, &t[i], + "dma-fence:%d", i); + get_task_struct(t[i].task); + } + + msleep(50); + + for (i = 0; i < ARRAY_SIZE(t); i++) { + int err; + + err = kthread_stop(t[i].task); + if (err && !ret) + ret = err; + + put_task_struct(t[i].task); + } + } + + return ret; +} + +int dma_fence(void) +{ + 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), + }; + int ret; + + pr_info("sizeof(dma_fence)=%lu\n", sizeof(struct dma_fence)); + + slab_fences = KMEM_CACHE(mock_fence, + SLAB_TYPESAFE_BY_RCU | + SLAB_HWCACHE_ALIGN); + + ret = subtests(tests, NULL); + + kmem_cache_destroy(slab_fences); + + return ret; +} From patchwork Mon Aug 19 09:59:28 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Chris Wilson X-Patchwork-Id: 11100599 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6BC7C14DB for ; Mon, 19 Aug 2019 09:59:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5ADBC2864F for ; Mon, 19 Aug 2019 09:59:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4F04A28662; Mon, 19 Aug 2019 09:59:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=unavailable version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id BBA2028658 for ; Mon, 19 Aug 2019 09:59:43 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id E83596E0F2; Mon, 19 Aug 2019 09:59:41 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from fireflyinternet.com (mail.fireflyinternet.com [109.228.58.192]) by gabe.freedesktop.org (Postfix) with ESMTPS id 23DB56E0F0; Mon, 19 Aug 2019 09:59:40 +0000 (UTC) X-Default-Received-SPF: pass (skip=forwardok (res=PASS)) x-ip-name=78.156.65.138; Received: from haswell.alporthouse.com (unverified [78.156.65.138]) by fireflyinternet.com (Firefly Internet (M1)) with ESMTP id 18186724-1500050 for multiple; Mon, 19 Aug 2019 10:59:30 +0100 From: Chris Wilson To: dri-devel@lists.freedesktop.org Subject: [PATCH 3/3] dma-fence: Set the timestamp after the notifying the cb_list Date: Mon, 19 Aug 2019 10:59:28 +0100 Message-Id: <20190819095928.32091-3-chris@chris-wilson.co.uk> X-Mailer: git-send-email 2.23.0.rc1 In-Reply-To: <20190819095928.32091-1-chris@chris-wilson.co.uk> References: <20190819095928.32091-1-chris@chris-wilson.co.uk> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: intel-gfx@lists.freedesktop.org, =?utf-8?q?Christian_K=C3=B6nig?= Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP Now that the timestamp and cb_list share the same slot in the fence, with the current order of setting the timestamp before notifying the cb_list, we have to take a temporary copy of the list. If we don't set the timestamp, we can simply process the list in situ. This also gives us the advantage that we get a signal when the cb_list is complete, useful in a few cases that need to serialise against the cb_list. Suggested-by: Christian König Signed-off-by: Chris Wilson Cc: Christian König --- drivers/dma-buf/dma-fence.c | 41 +++++++----------- drivers/dma-buf/st-dma-fence.c | 8 +--- drivers/dma-buf/sync_file.c | 5 +-- drivers/gpu/drm/i915/gt/intel_breadcrumbs.c | 28 +----------- include/linux/dma-fence.h | 47 ++++++++++++++++++++- 5 files changed, 65 insertions(+), 64 deletions(-) diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 2c136aee3e79..972a4b90b820 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -111,47 +111,36 @@ u64 dma_fence_context_alloc(unsigned num) EXPORT_SYMBOL(dma_fence_context_alloc); /** - * dma_fence_signal_locked - signal completion of a fence + * __dma_fence_signal_locked - Perform signaling of a fence * @fence: the fence to signal + * @timestamp: the timsetamp of fence completion * - * Signal completion for software callbacks on a fence, this will unblock - * dma_fence_wait() calls and run all the callbacks added with - * dma_fence_add_callback(). Can be called multiple times, but since a fence - * can only go from the unsignaled to the signaled state and not back, it will - * only be effective the first time. + * See dma_fence_signal() for the typical interface. * - * Unlike dma_fence_signal(), this function must be called with &dma_fence.lock - * held. + * This provides the low-level operations required upon signaling a fence, + * such as processing the callback list and setting the timestamp. * - * Returns 0 on success and a negative error value when @fence has been - * signalled already. + * Requires the caller to hold the fence->lock and already have marked the + * fence as signaled in an exclusive manner. + * + * Great care must be taken in calling this function! */ -int dma_fence_signal_locked(struct dma_fence *fence) +void __dma_fence_signal_locked(struct dma_fence *fence, ktime_t timestamp) { struct dma_fence_cb *cur, *tmp; - struct list_head cb_list; lockdep_assert_held(fence->lock); - if (unlikely(test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, - &fence->flags))) - return -EINVAL; - - /* Stash the cb_list before replacing it with the timestamp */ - list_replace(&fence->cb_list, &cb_list); - - fence->timestamp = ktime_get(); - set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags); - trace_dma_fence_signaled(fence); - - list_for_each_entry_safe(cur, tmp, &cb_list, node) { + list_for_each_entry_safe(cur, tmp, &fence->cb_list, node) { INIT_LIST_HEAD(&cur->node); cur->func(fence, cur); } - return 0; + fence->timestamp = timestamp; /* overwrites fence->cb_list */ + set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags); + trace_dma_fence_signaled(fence); } -EXPORT_SYMBOL(dma_fence_signal_locked); +EXPORT_SYMBOL(__dma_fence_signal_locked); /** * dma_fence_signal - signal completion of a fence diff --git a/drivers/dma-buf/st-dma-fence.c b/drivers/dma-buf/st-dma-fence.c index a365dc7440e5..1fba51a5a46b 100644 --- a/drivers/dma-buf/st-dma-fence.c +++ b/drivers/dma-buf/st-dma-fence.c @@ -434,12 +434,6 @@ struct race_thread { int id; }; -static void __wait_for_callbacks(struct dma_fence *f) -{ - spin_lock_irq(f->lock); - spin_unlock_irq(f->lock); -} - static int thread_signal_callback(void *arg) { const struct race_thread *t = arg; @@ -478,7 +472,7 @@ static int thread_signal_callback(void *arg) if (!cb.seen) { dma_fence_wait(f2, false); - __wait_for_callbacks(f2); + dma_fence_wait_for_notify(f2); } if (!READ_ONCE(cb.seen)) { diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 25c5c071645b..f801dabb33a4 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -384,9 +384,8 @@ static int sync_fill_fence_info(struct dma_fence *fence, sizeof(info->driver_name)); info->status = dma_fence_get_status(fence); - while (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) && - !test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags)) - cpu_relax(); + if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) + dma_fence_wait_for_notify(fence); info->timestamp_ns = test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags) ? ktime_to_ns(fence->timestamp) : diff --git a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c index 09c68dda2098..dbfb3b5348c4 100644 --- a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c +++ b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c @@ -105,29 +105,6 @@ __dma_fence_signal(struct dma_fence *fence) return !test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags); } -static void -__dma_fence_signal__timestamp(struct dma_fence *fence, ktime_t timestamp) -{ - fence->timestamp = timestamp; - set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags); - trace_dma_fence_signaled(fence); -} - -static void -__dma_fence_signal__notify(struct dma_fence *fence, - const struct list_head *list) -{ - struct dma_fence_cb *cur, *tmp; - - lockdep_assert_held(fence->lock); - lockdep_assert_irqs_disabled(); - - list_for_each_entry_safe(cur, tmp, list, node) { - INIT_LIST_HEAD(&cur->node); - cur->func(fence, cur); - } -} - void intel_engine_breadcrumbs_irq(struct intel_engine_cs *engine) { struct intel_breadcrumbs *b = &engine->breadcrumbs; @@ -187,12 +164,9 @@ void intel_engine_breadcrumbs_irq(struct intel_engine_cs *engine) list_for_each_safe(pos, next, &signal) { struct i915_request *rq = list_entry(pos, typeof(*rq), signal_link); - struct list_head cb_list; spin_lock(&rq->lock); - list_replace(&rq->fence.cb_list, &cb_list); - __dma_fence_signal__timestamp(&rq->fence, timestamp); - __dma_fence_signal__notify(&rq->fence, &cb_list); + __dma_fence_signal_locked(&rq->fence, timestamp); spin_unlock(&rq->lock); i915_request_put(rq); diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h index 3347c54f3a87..b93c83f240c2 100644 --- a/include/linux/dma-fence.h +++ b/include/linux/dma-fence.h @@ -357,8 +357,36 @@ dma_fence_get_rcu_safe(struct dma_fence __rcu **fencep) } while (1); } +void __dma_fence_signal_locked(struct dma_fence *fence, ktime_t timestamp); + +/** + * dma_fence_signal_locked - signal completion of a fence + * @fence: the fence to signal + * + * Signal completion for software callbacks on a fence, this will unblock + * dma_fence_wait() calls and run all the callbacks added with + * dma_fence_add_callback(). Can be called multiple times, but since a fence + * can only go from the unsignaled to the signaled state and not back, it will + * only be effective the first time. + * + * Unlike dma_fence_signal(), this function must be called with &dma_fence.lock + * held. + * + * Returns 0 on success and a negative error value when @fence has been + * signalled already. + */ +static inline int dma_fence_signal_locked(struct dma_fence *fence) +{ + if (unlikely(test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, + &fence->flags))) + return -EINVAL; + + __dma_fence_signal_locked(fence, ktime_get()); + return 0; +} + int dma_fence_signal(struct dma_fence *fence); -int dma_fence_signal_locked(struct dma_fence *fence); + signed long dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout); int dma_fence_add_callback(struct dma_fence *fence, @@ -426,6 +454,23 @@ dma_fence_is_signaled(struct dma_fence *fence) return false; } +/** + * dma_fence_wait_for_notify - Wait until the notifications are complete + * @fence: the fence to wait on + * + * After marking the fence as signaled, a number of operations but we + * are completely done, from notifying all the listeners culminating + * in setting the timestamp on the fence. This signals the completion + * of all the callbacks and the end of the siganling operation. + */ +static inline void +dma_fence_wait_for_notify(struct dma_fence *fence) +{ + WARN_ON(!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)); + while (!test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags)) + cpu_relax(); +} + /** * __dma_fence_is_later - return if f1 is chronologically later than f2 * @f1: the first fence's seqno