summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2019-08-19 05:59:27 -0400
committerChris Wilson <chris@chris-wilson.co.uk>2019-08-19 13:09:46 -0400
commit2989f6451084aed3f8cc9992477f7a9bf57a3716 (patch)
treef14b0e6feff554f35a848ac10ab9177461eefd4d
parent9536b64ac0d6e3151963a11441dde7ade045fb29 (diff)
dma-buf: Add selftests for dma-fence
Exercise the dma-fence API exported to drivers. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: https://patchwork.freedesktop.org/patch/msgid/20190819095928.32091-2-chris@chris-wilson.co.uk
-rw-r--r--drivers/dma-buf/Makefile5
-rw-r--r--drivers/dma-buf/selftests.h1
-rw-r--r--drivers/dma-buf/st-dma-fence.c573
3 files changed, 578 insertions, 1 deletions
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
5obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o 5obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
6obj-$(CONFIG_UDMABUF) += udmabuf.o 6obj-$(CONFIG_UDMABUF) += udmabuf.o
7 7
8dmabuf_selftests-y := selftest.o 8dmabuf_selftests-y := \
9 selftest.o \
10 st-dma-fence.o
11
9obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o 12obj-$(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 @@
10 * Tests are executed in order by igt/dmabuf_selftest 10 * Tests are executed in order by igt/dmabuf_selftest
11 */ 11 */
12selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */ 12selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */
13selftest(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..3d69405952aa
--- /dev/null
+++ b/drivers/dma-buf/st-dma-fence.c
@@ -0,0 +1,573 @@
1/* SPDX-License-Identifier: MIT */
2
3/*
4 * Copyright © 2019 Intel Corporation
5 */
6
7#include <linux/delay.h>
8#include <linux/dma-fence.h>
9#include <linux/kernel.h>
10#include <linux/kthread.h>
11#include <linux/sched/signal.h>
12#include <linux/slab.h>
13#include <linux/spinlock.h>
14
15#include "selftest.h"
16
17static struct kmem_cache *slab_fences;
18
19static struct mock_fence {
20 struct dma_fence base;
21 struct spinlock lock;
22} *to_mock_fence(struct dma_fence *f) {
23 return container_of(f, struct mock_fence, base);
24}
25
26static const char *mock_name(struct dma_fence *f)
27{
28 return "mock";
29}
30
31static void mock_fence_release(struct dma_fence *f)
32{
33 kmem_cache_free(slab_fences, to_mock_fence(f));
34}
35
36struct wait_cb {
37 struct dma_fence_cb cb;
38 struct task_struct *task;
39};
40
41static void mock_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
42{
43 wake_up_process(container_of(cb, struct wait_cb, cb)->task);
44}
45
46static long mock_wait(struct dma_fence *f, bool intr, long timeout)
47{
48 const int state = intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
49 struct wait_cb cb = { .task = current };
50
51 if (dma_fence_add_callback(f, &cb.cb, mock_wakeup))
52 return timeout;
53
54 while (timeout) {
55 set_current_state(state);
56
57 if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags))
58 break;
59
60 if (signal_pending_state(state, current))
61 break;
62
63 timeout = schedule_timeout(timeout);
64 }
65 __set_current_state(TASK_RUNNING);
66
67 if (!dma_fence_remove_callback(f, &cb.cb))
68 return timeout;
69
70 if (signal_pending_state(state, current))
71 return -ERESTARTSYS;
72
73 return -ETIME;
74}
75
76static const struct dma_fence_ops mock_ops = {
77 .get_driver_name = mock_name,
78 .get_timeline_name = mock_name,
79 .wait = mock_wait,
80 .release = mock_fence_release,
81};
82
83static struct dma_fence *mock_fence(void)
84{
85 struct mock_fence *f;
86
87 f = kmem_cache_alloc(slab_fences, GFP_KERNEL);
88 if (!f)
89 return NULL;
90
91 spin_lock_init(&f->lock);
92 dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0);
93
94 return &f->base;
95}
96
97static int sanitycheck(void *arg)
98{
99 struct dma_fence *f;
100
101 f = mock_fence();
102 if (!f)
103 return -ENOMEM;
104
105 dma_fence_signal(f);
106 dma_fence_put(f);
107
108 return 0;
109}
110
111static int test_signaling(void *arg)
112{
113 struct dma_fence *f;
114 int err = -EINVAL;
115
116 f = mock_fence();
117 if (!f)
118 return -ENOMEM;
119
120 if (dma_fence_is_signaled(f)) {
121 pr_err("Fence unexpectedly signaled on creation\n");
122 goto err_free;
123 }
124
125 if (dma_fence_signal(f)) {
126 pr_err("Fence reported being already signaled\n");
127 goto err_free;
128 }
129
130 if (!dma_fence_is_signaled(f)) {
131 pr_err("Fence not reporting signaled\n");
132 goto err_free;
133 }
134
135 if (!dma_fence_signal(f)) {
136 pr_err("Fence reported not being already signaled\n");
137 goto err_free;
138 }
139
140 err = 0;
141err_free:
142 dma_fence_put(f);
143 return err;
144}
145
146struct simple_cb {
147 struct dma_fence_cb cb;
148 bool seen;
149};
150
151static void simple_callback(struct dma_fence *f, struct dma_fence_cb *cb)
152{
153 smp_store_mb(container_of(cb, struct simple_cb, cb)->seen, true);
154}
155
156static int test_add_callback(void *arg)
157{
158 struct simple_cb cb = {};
159 struct dma_fence *f;
160 int err = -EINVAL;
161
162 f = mock_fence();
163 if (!f)
164 return -ENOMEM;
165
166 if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
167 pr_err("Failed to add callback, fence already signaled!\n");
168 goto err_free;
169 }
170
171 dma_fence_signal(f);
172 if (!cb.seen) {
173 pr_err("Callback failed!\n");
174 goto err_free;
175 }
176
177 err = 0;
178err_free:
179 dma_fence_put(f);
180 return err;
181}
182
183static int test_late_add_callback(void *arg)
184{
185 struct simple_cb cb = {};
186 struct dma_fence *f;
187 int err = -EINVAL;
188
189 f = mock_fence();
190 if (!f)
191 return -ENOMEM;
192
193 dma_fence_signal(f);
194
195 if (!dma_fence_add_callback(f, &cb.cb, simple_callback)) {
196 pr_err("Added callback, but fence was already signaled!\n");
197 goto err_free;
198 }
199
200 dma_fence_signal(f);
201 if (cb.seen) {
202 pr_err("Callback called after failed attachment !\n");
203 goto err_free;
204 }
205
206 err = 0;
207err_free:
208 dma_fence_put(f);
209 return err;
210}
211
212static int test_rm_callback(void *arg)
213{
214 struct simple_cb cb = {};
215 struct dma_fence *f;
216 int err = -EINVAL;
217
218 f = mock_fence();
219 if (!f)
220 return -ENOMEM;
221
222 if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
223 pr_err("Failed to add callback, fence already signaled!\n");
224 goto err_free;
225 }
226
227 if (!dma_fence_remove_callback(f, &cb.cb)) {
228 pr_err("Failed to remove callback!\n");
229 goto err_free;
230 }
231
232 dma_fence_signal(f);
233 if (cb.seen) {
234 pr_err("Callback still signaled after removal!\n");
235 goto err_free;
236 }
237
238 err = 0;
239err_free:
240 dma_fence_put(f);
241 return err;
242}
243
244static int test_late_rm_callback(void *arg)
245{
246 struct simple_cb cb = {};
247 struct dma_fence *f;
248 int err = -EINVAL;
249
250 f = mock_fence();
251 if (!f)
252 return -ENOMEM;
253
254 if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
255 pr_err("Failed to add callback, fence already signaled!\n");
256 goto err_free;
257 }
258
259 dma_fence_signal(f);
260 if (!cb.seen) {
261 pr_err("Callback failed!\n");
262 goto err_free;
263 }
264
265 if (dma_fence_remove_callback(f, &cb.cb)) {
266 pr_err("Callback removal succeed after being executed!\n");
267 goto err_free;
268 }
269
270 err = 0;
271err_free:
272 dma_fence_put(f);
273 return err;
274}
275
276static int test_status(void *arg)
277{
278 struct dma_fence *f;
279 int err = -EINVAL;
280
281 f = mock_fence();
282 if (!f)
283 return -ENOMEM;
284
285 if (dma_fence_get_status(f)) {
286 pr_err("Fence unexpectedly has signaled status on creation\n");
287 goto err_free;
288 }
289
290 dma_fence_signal(f);
291 if (!dma_fence_get_status(f)) {
292 pr_err("Fence not reporting signaled status\n");
293 goto err_free;
294 }
295
296 err = 0;
297err_free:
298 dma_fence_put(f);
299 return err;
300}
301
302static int test_error(void *arg)
303{
304 struct dma_fence *f;
305 int err = -EINVAL;
306
307 f = mock_fence();
308 if (!f)
309 return -ENOMEM;
310
311 dma_fence_set_error(f, -EIO);
312
313 if (dma_fence_get_status(f)) {
314 pr_err("Fence unexpectedly has error status before signal\n");
315 goto err_free;
316 }
317
318 dma_fence_signal(f);
319 if (dma_fence_get_status(f) != -EIO) {
320 pr_err("Fence not reporting error status, got %d\n",
321 dma_fence_get_status(f));
322 goto err_free;
323 }
324
325 err = 0;
326err_free:
327 dma_fence_put(f);
328 return err;
329}
330
331static int test_wait(void *arg)
332{
333 struct dma_fence *f;
334 int err = -EINVAL;
335
336 f = mock_fence();
337 if (!f)
338 return -ENOMEM;
339
340 if (dma_fence_wait_timeout(f, false, 0) != -ETIME) {
341 pr_err("Wait reported complete before being signaled\n");
342 goto err_free;
343 }
344
345 dma_fence_signal(f);
346
347 if (dma_fence_wait_timeout(f, false, 0) != 0) {
348 pr_err("Wait reported incomplete after being signaled\n");
349 goto err_free;
350 }
351
352 err = 0;
353err_free:
354 dma_fence_signal(f);
355 dma_fence_put(f);
356 return err;
357}
358
359struct wait_timer {
360 struct timer_list timer;
361 struct dma_fence *f;
362};
363
364static void wait_timer(struct timer_list *timer)
365{
366 struct wait_timer *wt = from_timer(wt, timer, timer);
367
368 dma_fence_signal(wt->f);
369}
370
371static int test_wait_timeout(void *arg)
372{
373 struct wait_timer wt;
374 int err = -EINVAL;
375
376 timer_setup(&wt.timer, wait_timer, 0);
377
378 wt.f = mock_fence();
379 if (!wt.f)
380 return -ENOMEM;
381
382 if (dma_fence_wait_timeout(wt.f, false, 1) != -ETIME) {
383 pr_err("Wait reported complete before being signaled\n");
384 goto err_free;
385 }
386
387 mod_timer(&wt.timer, jiffies + 1);
388
389 if (dma_fence_wait_timeout(wt.f, false, 2) == -ETIME) {
390 if (timer_pending(&wt.timer)) {
391 pr_notice("Timer did not fire within the jiffie!\n");
392 err = 0; /* not our fault! */
393 } else {
394 pr_err("Wait reported incomplete after timeout\n");
395 }
396 goto err_free;
397 }
398
399 err = 0;
400err_free:
401 del_timer_sync(&wt.timer);
402 dma_fence_signal(wt.f);
403 dma_fence_put(wt.f);
404 return err;
405}
406
407static int test_stub(void *arg)
408{
409 struct dma_fence *f[64];
410 int err = -EINVAL;
411 int i;
412
413 for (i = 0; i < ARRAY_SIZE(f); i++) {
414 f[i] = dma_fence_get_stub();
415 if (!dma_fence_is_signaled(f[i])) {
416 pr_err("Obtained unsignaled stub fence!\n");
417 goto err;
418 }
419 }
420
421 err = 0;
422err:
423 while (i--)
424 dma_fence_put(f[i]);
425 return err;
426}
427
428/* Now off to the races! */
429
430struct race_thread {
431 struct dma_fence __rcu **fences;
432 struct task_struct *task;
433 bool before;
434 int id;
435};
436
437static void __wait_for_callbacks(struct dma_fence *f)
438{
439 spin_lock_irq(f->lock);
440 spin_unlock_irq(f->lock);
441}
442
443static int thread_signal_callback(void *arg)
444{
445 const struct race_thread *t = arg;
446 unsigned long pass = 0;
447 unsigned long miss = 0;
448 int err = 0;
449
450 while (!err && !kthread_should_stop()) {
451 struct dma_fence *f1, *f2;
452 struct simple_cb cb;
453
454 f1 = mock_fence();
455 if (!f1) {
456 err = -ENOMEM;
457 break;
458 }
459
460 rcu_assign_pointer(t->fences[t->id], f1);
461 smp_wmb();
462
463 rcu_read_lock();
464 do {
465 f2 = dma_fence_get_rcu_safe(&t->fences[!t->id]);
466 } while (!f2 && !kthread_should_stop());
467 rcu_read_unlock();
468
469 if (t->before)
470 dma_fence_signal(f1);
471
472 smp_store_mb(cb.seen, false);
473 if (!f2 || dma_fence_add_callback(f2, &cb.cb, simple_callback))
474 miss++, cb.seen = true;
475
476 if (!t->before)
477 dma_fence_signal(f1);
478
479 if (!cb.seen) {
480 dma_fence_wait(f2, false);
481 __wait_for_callbacks(f2);
482 }
483
484 if (!READ_ONCE(cb.seen)) {
485 pr_err("Callback not seen on thread %d, pass %lu (%lu misses), signaling %s add_callback; fence signaled? %s\n",
486 t->id, pass, miss,
487 t->before ? "before" : "after",
488 dma_fence_is_signaled(f2) ? "yes" : "no");
489 err = -EINVAL;
490 }
491
492 dma_fence_put(f2);
493
494 rcu_assign_pointer(t->fences[t->id], NULL);
495 smp_wmb();
496
497 dma_fence_put(f1);
498
499 pass++;
500 }
501
502 pr_info("%s[%d] completed %lu passes, %lu misses\n",
503 __func__, t->id, pass, miss);
504 return err;
505}
506
507static int race_signal_callback(void *arg)
508{
509 struct dma_fence __rcu *f[2] = {};
510 int ret = 0;
511 int pass;
512
513 for (pass = 0; !ret && pass <= 1; pass++) {
514 struct race_thread t[2];
515 int i;
516
517 for (i = 0; i < ARRAY_SIZE(t); i++) {
518 t[i].fences = f;
519 t[i].id = i;
520 t[i].before = pass;
521 t[i].task = kthread_run(thread_signal_callback, &t[i],
522 "dma-fence:%d", i);
523 get_task_struct(t[i].task);
524 }
525
526 msleep(50);
527
528 for (i = 0; i < ARRAY_SIZE(t); i++) {
529 int err;
530
531 err = kthread_stop(t[i].task);
532 if (err && !ret)
533 ret = err;
534
535 put_task_struct(t[i].task);
536 }
537 }
538
539 return ret;
540}
541
542int dma_fence(void)
543{
544 static const struct subtest tests[] = {
545 SUBTEST(sanitycheck),
546 SUBTEST(test_signaling),
547 SUBTEST(test_add_callback),
548 SUBTEST(test_late_add_callback),
549 SUBTEST(test_rm_callback),
550 SUBTEST(test_late_rm_callback),
551 SUBTEST(test_status),
552 SUBTEST(test_error),
553 SUBTEST(test_wait),
554 SUBTEST(test_wait_timeout),
555 SUBTEST(test_stub),
556 SUBTEST(race_signal_callback),
557 };
558 int ret;
559
560 pr_info("sizeof(dma_fence)=%lu\n", sizeof(struct dma_fence));
561
562 slab_fences = KMEM_CACHE(mock_fence,
563 SLAB_TYPESAFE_BY_RCU |
564 SLAB_HWCACHE_ALIGN);
565 if (!slab_fences)
566 return -ENOMEM;
567
568 ret = subtests(tests, NULL);
569
570 kmem_cache_destroy(slab_fences);
571
572 return ret;
573}