xref: /linux/drivers/dma-buf/st-dma-fence-chain.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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/dma-fence-chain.h>
10 #include <linux/kernel.h>
11 #include <linux/kthread.h>
12 #include <linux/mm.h>
13 #include <linux/sched/signal.h>
14 #include <linux/slab.h>
15 #include <linux/spinlock.h>
16 #include <linux/random.h>
17 
18 #include "selftest.h"
19 
20 #define CHAIN_SZ (4 << 10)
21 
22 static struct kmem_cache *slab_fences;
23 
24 static inline struct mock_fence {
25 	struct dma_fence base;
26 	spinlock_t lock;
27 } *to_mock_fence(struct dma_fence *f) {
28 	return container_of(f, struct mock_fence, base);
29 }
30 
31 static const char *mock_name(struct dma_fence *f)
32 {
33 	return "mock";
34 }
35 
36 static void mock_fence_release(struct dma_fence *f)
37 {
38 	kmem_cache_free(slab_fences, to_mock_fence(f));
39 }
40 
41 static const struct dma_fence_ops mock_ops = {
42 	.get_driver_name = mock_name,
43 	.get_timeline_name = mock_name,
44 	.release = mock_fence_release,
45 };
46 
47 static struct dma_fence *mock_fence(void)
48 {
49 	struct mock_fence *f;
50 
51 	f = kmem_cache_alloc(slab_fences, GFP_KERNEL);
52 	if (!f)
53 		return NULL;
54 
55 	spin_lock_init(&f->lock);
56 	dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0);
57 
58 	return &f->base;
59 }
60 
61 static struct dma_fence *mock_chain(struct dma_fence *prev,
62 				    struct dma_fence *fence,
63 				    u64 seqno)
64 {
65 	struct dma_fence_chain *f;
66 
67 	f = dma_fence_chain_alloc();
68 	if (!f)
69 		return NULL;
70 
71 	dma_fence_chain_init(f, dma_fence_get(prev), dma_fence_get(fence),
72 			     seqno);
73 
74 	return &f->base;
75 }
76 
77 static int sanitycheck(void *arg)
78 {
79 	struct dma_fence *f, *chain;
80 	int err = 0;
81 
82 	f = mock_fence();
83 	if (!f)
84 		return -ENOMEM;
85 
86 	chain = mock_chain(NULL, f, 1);
87 	if (chain)
88 		dma_fence_enable_sw_signaling(chain);
89 	else
90 		err = -ENOMEM;
91 
92 	dma_fence_signal(f);
93 	dma_fence_put(f);
94 
95 	dma_fence_put(chain);
96 
97 	return err;
98 }
99 
100 struct fence_chains {
101 	unsigned int chain_length;
102 	struct dma_fence **fences;
103 	struct dma_fence **chains;
104 
105 	struct dma_fence *tail;
106 };
107 
108 static uint64_t seqno_inc(unsigned int i)
109 {
110 	return i + 1;
111 }
112 
113 static int fence_chains_init(struct fence_chains *fc, unsigned int count,
114 			     uint64_t (*seqno_fn)(unsigned int))
115 {
116 	unsigned int i;
117 	int err = 0;
118 
119 	fc->chains = kvmalloc_array(count, sizeof(*fc->chains),
120 				    GFP_KERNEL | __GFP_ZERO);
121 	if (!fc->chains)
122 		return -ENOMEM;
123 
124 	fc->fences = kvmalloc_array(count, sizeof(*fc->fences),
125 				    GFP_KERNEL | __GFP_ZERO);
126 	if (!fc->fences) {
127 		err = -ENOMEM;
128 		goto err_chains;
129 	}
130 
131 	fc->tail = NULL;
132 	for (i = 0; i < count; i++) {
133 		fc->fences[i] = mock_fence();
134 		if (!fc->fences[i]) {
135 			err = -ENOMEM;
136 			goto unwind;
137 		}
138 
139 		fc->chains[i] = mock_chain(fc->tail,
140 					   fc->fences[i],
141 					   seqno_fn(i));
142 		if (!fc->chains[i]) {
143 			err = -ENOMEM;
144 			goto unwind;
145 		}
146 
147 		fc->tail = fc->chains[i];
148 
149 		dma_fence_enable_sw_signaling(fc->chains[i]);
150 	}
151 
152 	fc->chain_length = i;
153 	return 0;
154 
155 unwind:
156 	for (i = 0; i < count; i++) {
157 		dma_fence_put(fc->fences[i]);
158 		dma_fence_put(fc->chains[i]);
159 	}
160 	kvfree(fc->fences);
161 err_chains:
162 	kvfree(fc->chains);
163 	return err;
164 }
165 
166 static void fence_chains_fini(struct fence_chains *fc)
167 {
168 	unsigned int i;
169 
170 	for (i = 0; i < fc->chain_length; i++) {
171 		dma_fence_signal(fc->fences[i]);
172 		dma_fence_put(fc->fences[i]);
173 	}
174 	kvfree(fc->fences);
175 
176 	for (i = 0; i < fc->chain_length; i++)
177 		dma_fence_put(fc->chains[i]);
178 	kvfree(fc->chains);
179 }
180 
181 static int find_seqno(void *arg)
182 {
183 	struct fence_chains fc;
184 	struct dma_fence *fence;
185 	int err;
186 	int i;
187 
188 	err = fence_chains_init(&fc, 64, seqno_inc);
189 	if (err)
190 		return err;
191 
192 	fence = dma_fence_get(fc.tail);
193 	err = dma_fence_chain_find_seqno(&fence, 0);
194 	dma_fence_put(fence);
195 	if (err) {
196 		pr_err("Reported %d for find_seqno(0)!\n", err);
197 		goto err;
198 	}
199 
200 	for (i = 0; i < fc.chain_length; i++) {
201 		fence = dma_fence_get(fc.tail);
202 		err = dma_fence_chain_find_seqno(&fence, i + 1);
203 		dma_fence_put(fence);
204 		if (err) {
205 			pr_err("Reported %d for find_seqno(%d:%d)!\n",
206 			       err, fc.chain_length + 1, i + 1);
207 			goto err;
208 		}
209 		if (fence != fc.chains[i]) {
210 			pr_err("Incorrect fence reported by find_seqno(%d:%d)\n",
211 			       fc.chain_length + 1, i + 1);
212 			err = -EINVAL;
213 			goto err;
214 		}
215 
216 		dma_fence_get(fence);
217 		err = dma_fence_chain_find_seqno(&fence, i + 1);
218 		dma_fence_put(fence);
219 		if (err) {
220 			pr_err("Error reported for finding self\n");
221 			goto err;
222 		}
223 		if (fence != fc.chains[i]) {
224 			pr_err("Incorrect fence reported by find self\n");
225 			err = -EINVAL;
226 			goto err;
227 		}
228 
229 		dma_fence_get(fence);
230 		err = dma_fence_chain_find_seqno(&fence, i + 2);
231 		dma_fence_put(fence);
232 		if (!err) {
233 			pr_err("Error not reported for future fence: find_seqno(%d:%d)!\n",
234 			       i + 1, i + 2);
235 			err = -EINVAL;
236 			goto err;
237 		}
238 
239 		dma_fence_get(fence);
240 		err = dma_fence_chain_find_seqno(&fence, i);
241 		dma_fence_put(fence);
242 		if (err) {
243 			pr_err("Error reported for previous fence!\n");
244 			goto err;
245 		}
246 		if (i > 0 && fence != fc.chains[i - 1]) {
247 			pr_err("Incorrect fence reported by find_seqno(%d:%d)\n",
248 			       i + 1, i);
249 			err = -EINVAL;
250 			goto err;
251 		}
252 	}
253 
254 err:
255 	fence_chains_fini(&fc);
256 	return err;
257 }
258 
259 static int find_signaled(void *arg)
260 {
261 	struct fence_chains fc;
262 	struct dma_fence *fence;
263 	int err;
264 
265 	err = fence_chains_init(&fc, 2, seqno_inc);
266 	if (err)
267 		return err;
268 
269 	dma_fence_signal(fc.fences[0]);
270 
271 	fence = dma_fence_get(fc.tail);
272 	err = dma_fence_chain_find_seqno(&fence, 1);
273 	dma_fence_put(fence);
274 	if (err) {
275 		pr_err("Reported %d for find_seqno()!\n", err);
276 		goto err;
277 	}
278 
279 	if (fence && fence != fc.chains[0]) {
280 		pr_err("Incorrect chain-fence.seqno:%lld reported for completed seqno:1\n",
281 		       fence->seqno);
282 
283 		dma_fence_get(fence);
284 		err = dma_fence_chain_find_seqno(&fence, 1);
285 		dma_fence_put(fence);
286 		if (err)
287 			pr_err("Reported %d for finding self!\n", err);
288 
289 		err = -EINVAL;
290 	}
291 
292 err:
293 	fence_chains_fini(&fc);
294 	return err;
295 }
296 
297 static int find_out_of_order(void *arg)
298 {
299 	struct fence_chains fc;
300 	struct dma_fence *fence;
301 	int err;
302 
303 	err = fence_chains_init(&fc, 3, seqno_inc);
304 	if (err)
305 		return err;
306 
307 	dma_fence_signal(fc.fences[1]);
308 
309 	fence = dma_fence_get(fc.tail);
310 	err = dma_fence_chain_find_seqno(&fence, 2);
311 	dma_fence_put(fence);
312 	if (err) {
313 		pr_err("Reported %d for find_seqno()!\n", err);
314 		goto err;
315 	}
316 
317 	/*
318 	 * We signaled the middle fence (2) of the 1-2-3 chain. The behavior
319 	 * of the dma-fence-chain is to make us wait for all the fences up to
320 	 * the point we want. Since fence 1 is still not signaled, this what
321 	 * we should get as fence to wait upon (fence 2 being garbage
322 	 * collected during the traversal of the chain).
323 	 */
324 	if (fence != fc.chains[0]) {
325 		pr_err("Incorrect chain-fence.seqno:%lld reported for completed seqno:2\n",
326 		       fence ? fence->seqno : 0);
327 
328 		err = -EINVAL;
329 	}
330 
331 err:
332 	fence_chains_fini(&fc);
333 	return err;
334 }
335 
336 static uint64_t seqno_inc2(unsigned int i)
337 {
338 	return 2 * i + 2;
339 }
340 
341 static int find_gap(void *arg)
342 {
343 	struct fence_chains fc;
344 	struct dma_fence *fence;
345 	int err;
346 	int i;
347 
348 	err = fence_chains_init(&fc, 64, seqno_inc2);
349 	if (err)
350 		return err;
351 
352 	for (i = 0; i < fc.chain_length; i++) {
353 		fence = dma_fence_get(fc.tail);
354 		err = dma_fence_chain_find_seqno(&fence, 2 * i + 1);
355 		dma_fence_put(fence);
356 		if (err) {
357 			pr_err("Reported %d for find_seqno(%d:%d)!\n",
358 			       err, fc.chain_length + 1, 2 * i + 1);
359 			goto err;
360 		}
361 		if (fence != fc.chains[i]) {
362 			pr_err("Incorrect fence.seqno:%lld reported by find_seqno(%d:%d)\n",
363 			       fence->seqno,
364 			       fc.chain_length + 1,
365 			       2 * i + 1);
366 			err = -EINVAL;
367 			goto err;
368 		}
369 
370 		dma_fence_get(fence);
371 		err = dma_fence_chain_find_seqno(&fence, 2 * i + 2);
372 		dma_fence_put(fence);
373 		if (err) {
374 			pr_err("Error reported for finding self\n");
375 			goto err;
376 		}
377 		if (fence != fc.chains[i]) {
378 			pr_err("Incorrect fence reported by find self\n");
379 			err = -EINVAL;
380 			goto err;
381 		}
382 	}
383 
384 err:
385 	fence_chains_fini(&fc);
386 	return err;
387 }
388 
389 struct find_race {
390 	struct fence_chains fc;
391 	atomic_t children;
392 };
393 
394 static int __find_race(void *arg)
395 {
396 	struct find_race *data = arg;
397 	int err = 0;
398 
399 	while (!kthread_should_stop()) {
400 		struct dma_fence *fence = dma_fence_get(data->fc.tail);
401 		int seqno;
402 
403 		seqno = get_random_u32_inclusive(1, data->fc.chain_length);
404 
405 		err = dma_fence_chain_find_seqno(&fence, seqno);
406 		if (err) {
407 			pr_err("Failed to find fence seqno:%d\n",
408 			       seqno);
409 			dma_fence_put(fence);
410 			break;
411 		}
412 		if (!fence)
413 			goto signal;
414 
415 		/*
416 		 * We can only find ourselves if we are on fence we were
417 		 * looking for.
418 		 */
419 		if (fence->seqno == seqno) {
420 			err = dma_fence_chain_find_seqno(&fence, seqno);
421 			if (err) {
422 				pr_err("Reported an invalid fence for find-self:%d\n",
423 				       seqno);
424 				dma_fence_put(fence);
425 				break;
426 			}
427 		}
428 
429 		dma_fence_put(fence);
430 
431 signal:
432 		seqno = get_random_u32_below(data->fc.chain_length - 1);
433 		dma_fence_signal(data->fc.fences[seqno]);
434 		cond_resched();
435 	}
436 
437 	if (atomic_dec_and_test(&data->children))
438 		wake_up_var(&data->children);
439 	return err;
440 }
441 
442 static int find_race(void *arg)
443 {
444 	struct find_race data;
445 	int ncpus = num_online_cpus();
446 	struct task_struct **threads;
447 	unsigned long count;
448 	int err;
449 	int i;
450 
451 	err = fence_chains_init(&data.fc, CHAIN_SZ, seqno_inc);
452 	if (err)
453 		return err;
454 
455 	threads = kmalloc_array(ncpus, sizeof(*threads), GFP_KERNEL);
456 	if (!threads) {
457 		err = -ENOMEM;
458 		goto err;
459 	}
460 
461 	atomic_set(&data.children, 0);
462 	for (i = 0; i < ncpus; i++) {
463 		threads[i] = kthread_run(__find_race, &data, "dmabuf/%d", i);
464 		if (IS_ERR(threads[i])) {
465 			ncpus = i;
466 			break;
467 		}
468 		atomic_inc(&data.children);
469 		get_task_struct(threads[i]);
470 	}
471 
472 	wait_var_event_timeout(&data.children,
473 			       !atomic_read(&data.children),
474 			       5 * HZ);
475 
476 	for (i = 0; i < ncpus; i++) {
477 		int ret;
478 
479 		ret = kthread_stop_put(threads[i]);
480 		if (ret && !err)
481 			err = ret;
482 	}
483 	kfree(threads);
484 
485 	count = 0;
486 	for (i = 0; i < data.fc.chain_length; i++)
487 		if (dma_fence_is_signaled(data.fc.fences[i]))
488 			count++;
489 	pr_info("Completed %lu cycles\n", count);
490 
491 err:
492 	fence_chains_fini(&data.fc);
493 	return err;
494 }
495 
496 static int signal_forward(void *arg)
497 {
498 	struct fence_chains fc;
499 	int err;
500 	int i;
501 
502 	err = fence_chains_init(&fc, 64, seqno_inc);
503 	if (err)
504 		return err;
505 
506 	for (i = 0; i < fc.chain_length; i++) {
507 		dma_fence_signal(fc.fences[i]);
508 
509 		if (!dma_fence_is_signaled(fc.chains[i])) {
510 			pr_err("chain[%d] not signaled!\n", i);
511 			err = -EINVAL;
512 			goto err;
513 		}
514 
515 		if (i + 1 < fc.chain_length &&
516 		    dma_fence_is_signaled(fc.chains[i + 1])) {
517 			pr_err("chain[%d] is signaled!\n", i);
518 			err = -EINVAL;
519 			goto err;
520 		}
521 	}
522 
523 err:
524 	fence_chains_fini(&fc);
525 	return err;
526 }
527 
528 static int signal_backward(void *arg)
529 {
530 	struct fence_chains fc;
531 	int err;
532 	int i;
533 
534 	err = fence_chains_init(&fc, 64, seqno_inc);
535 	if (err)
536 		return err;
537 
538 	for (i = fc.chain_length; i--; ) {
539 		dma_fence_signal(fc.fences[i]);
540 
541 		if (i > 0 && dma_fence_is_signaled(fc.chains[i])) {
542 			pr_err("chain[%d] is signaled!\n", i);
543 			err = -EINVAL;
544 			goto err;
545 		}
546 	}
547 
548 	for (i = 0; i < fc.chain_length; i++) {
549 		if (!dma_fence_is_signaled(fc.chains[i])) {
550 			pr_err("chain[%d] was not signaled!\n", i);
551 			err = -EINVAL;
552 			goto err;
553 		}
554 	}
555 
556 err:
557 	fence_chains_fini(&fc);
558 	return err;
559 }
560 
561 static int __wait_fence_chains(void *arg)
562 {
563 	struct fence_chains *fc = arg;
564 
565 	if (dma_fence_wait(fc->tail, false))
566 		return -EIO;
567 
568 	return 0;
569 }
570 
571 static int wait_forward(void *arg)
572 {
573 	struct fence_chains fc;
574 	struct task_struct *tsk;
575 	int err;
576 	int i;
577 
578 	err = fence_chains_init(&fc, CHAIN_SZ, seqno_inc);
579 	if (err)
580 		return err;
581 
582 	tsk = kthread_run(__wait_fence_chains, &fc, "dmabuf/wait");
583 	if (IS_ERR(tsk)) {
584 		err = PTR_ERR(tsk);
585 		goto err;
586 	}
587 	get_task_struct(tsk);
588 	yield_to(tsk, true);
589 
590 	for (i = 0; i < fc.chain_length; i++)
591 		dma_fence_signal(fc.fences[i]);
592 
593 	err = kthread_stop_put(tsk);
594 
595 err:
596 	fence_chains_fini(&fc);
597 	return err;
598 }
599 
600 static int wait_backward(void *arg)
601 {
602 	struct fence_chains fc;
603 	struct task_struct *tsk;
604 	int err;
605 	int i;
606 
607 	err = fence_chains_init(&fc, CHAIN_SZ, seqno_inc);
608 	if (err)
609 		return err;
610 
611 	tsk = kthread_run(__wait_fence_chains, &fc, "dmabuf/wait");
612 	if (IS_ERR(tsk)) {
613 		err = PTR_ERR(tsk);
614 		goto err;
615 	}
616 	get_task_struct(tsk);
617 	yield_to(tsk, true);
618 
619 	for (i = fc.chain_length; i--; )
620 		dma_fence_signal(fc.fences[i]);
621 
622 	err = kthread_stop_put(tsk);
623 
624 err:
625 	fence_chains_fini(&fc);
626 	return err;
627 }
628 
629 static void randomise_fences(struct fence_chains *fc)
630 {
631 	unsigned int count = fc->chain_length;
632 
633 	/* Fisher-Yates shuffle courtesy of Knuth */
634 	while (--count) {
635 		unsigned int swp;
636 
637 		swp = get_random_u32_below(count + 1);
638 		if (swp == count)
639 			continue;
640 
641 		swap(fc->fences[count], fc->fences[swp]);
642 	}
643 }
644 
645 static int wait_random(void *arg)
646 {
647 	struct fence_chains fc;
648 	struct task_struct *tsk;
649 	int err;
650 	int i;
651 
652 	err = fence_chains_init(&fc, CHAIN_SZ, seqno_inc);
653 	if (err)
654 		return err;
655 
656 	randomise_fences(&fc);
657 
658 	tsk = kthread_run(__wait_fence_chains, &fc, "dmabuf/wait");
659 	if (IS_ERR(tsk)) {
660 		err = PTR_ERR(tsk);
661 		goto err;
662 	}
663 	get_task_struct(tsk);
664 	yield_to(tsk, true);
665 
666 	for (i = 0; i < fc.chain_length; i++)
667 		dma_fence_signal(fc.fences[i]);
668 
669 	err = kthread_stop_put(tsk);
670 
671 err:
672 	fence_chains_fini(&fc);
673 	return err;
674 }
675 
676 int dma_fence_chain(void)
677 {
678 	static const struct subtest tests[] = {
679 		SUBTEST(sanitycheck),
680 		SUBTEST(find_seqno),
681 		SUBTEST(find_signaled),
682 		SUBTEST(find_out_of_order),
683 		SUBTEST(find_gap),
684 		SUBTEST(find_race),
685 		SUBTEST(signal_forward),
686 		SUBTEST(signal_backward),
687 		SUBTEST(wait_forward),
688 		SUBTEST(wait_backward),
689 		SUBTEST(wait_random),
690 	};
691 	int ret;
692 
693 	pr_info("sizeof(dma_fence_chain)=%zu\n",
694 		sizeof(struct dma_fence_chain));
695 
696 	slab_fences = KMEM_CACHE(mock_fence,
697 				 SLAB_TYPESAFE_BY_RCU |
698 				 SLAB_HWCACHE_ALIGN);
699 	if (!slab_fences)
700 		return -ENOMEM;
701 
702 	ret = subtests(tests, NULL);
703 
704 	kmem_cache_destroy(slab_fences);
705 	return ret;
706 }
707