xref: /openbmc/linux/drivers/dma-buf/st-dma-resv.c (revision 75ab2b36)
11d51775cSChristian König /* SPDX-License-Identifier: MIT */
21d51775cSChristian König 
31d51775cSChristian König /*
41d51775cSChristian König * Copyright © 2019 Intel Corporation
51d51775cSChristian König * Copyright © 2021 Advanced Micro Devices, Inc.
61d51775cSChristian König */
71d51775cSChristian König 
81d51775cSChristian König #include <linux/slab.h>
91d51775cSChristian König #include <linux/spinlock.h>
101d51775cSChristian König #include <linux/dma-resv.h>
111d51775cSChristian König 
121d51775cSChristian König #include "selftest.h"
131d51775cSChristian König 
141d51775cSChristian König static struct spinlock fence_lock;
151d51775cSChristian König 
161d51775cSChristian König static const char *fence_name(struct dma_fence *f)
171d51775cSChristian König {
181d51775cSChristian König 	return "selftest";
191d51775cSChristian König }
201d51775cSChristian König 
211d51775cSChristian König static const struct dma_fence_ops fence_ops = {
221d51775cSChristian König 	.get_driver_name = fence_name,
231d51775cSChristian König 	.get_timeline_name = fence_name,
241d51775cSChristian König };
251d51775cSChristian König 
261d51775cSChristian König static struct dma_fence *alloc_fence(void)
271d51775cSChristian König {
281d51775cSChristian König 	struct dma_fence *f;
291d51775cSChristian König 
301d51775cSChristian König 	f = kmalloc(sizeof(*f), GFP_KERNEL);
311d51775cSChristian König 	if (!f)
321d51775cSChristian König 		return NULL;
331d51775cSChristian König 
341d51775cSChristian König 	dma_fence_init(f, &fence_ops, &fence_lock, 0, 0);
351d51775cSChristian König 	return f;
361d51775cSChristian König }
371d51775cSChristian König 
381d51775cSChristian König static int sanitycheck(void *arg)
391d51775cSChristian König {
401d51775cSChristian König 	struct dma_resv resv;
411d51775cSChristian König 	struct dma_fence *f;
421d51775cSChristian König 	int r;
431d51775cSChristian König 
441d51775cSChristian König 	f = alloc_fence();
451d51775cSChristian König 	if (!f)
461d51775cSChristian König 		return -ENOMEM;
471d51775cSChristian König 
481d51775cSChristian König 	dma_fence_signal(f);
491d51775cSChristian König 	dma_fence_put(f);
501d51775cSChristian König 
511d51775cSChristian König 	dma_resv_init(&resv);
521d51775cSChristian König 	r = dma_resv_lock(&resv, NULL);
531d51775cSChristian König 	if (r)
541d51775cSChristian König 		pr_err("Resv locking failed\n");
551d51775cSChristian König 	else
561d51775cSChristian König 		dma_resv_unlock(&resv);
571d51775cSChristian König 	dma_resv_fini(&resv);
581d51775cSChristian König 	return r;
591d51775cSChristian König }
601d51775cSChristian König 
611d51775cSChristian König static int test_signaling(void *arg, bool shared)
621d51775cSChristian König {
631d51775cSChristian König 	struct dma_resv resv;
641d51775cSChristian König 	struct dma_fence *f;
651d51775cSChristian König 	int r;
661d51775cSChristian König 
671d51775cSChristian König 	f = alloc_fence();
681d51775cSChristian König 	if (!f)
691d51775cSChristian König 		return -ENOMEM;
701d51775cSChristian König 
711d51775cSChristian König 	dma_resv_init(&resv);
721d51775cSChristian König 	r = dma_resv_lock(&resv, NULL);
731d51775cSChristian König 	if (r) {
741d51775cSChristian König 		pr_err("Resv locking failed\n");
751d51775cSChristian König 		goto err_free;
761d51775cSChristian König 	}
771d51775cSChristian König 
781d51775cSChristian König 	if (shared) {
791d51775cSChristian König 		r = dma_resv_reserve_shared(&resv, 1);
801d51775cSChristian König 		if (r) {
811d51775cSChristian König 			pr_err("Resv shared slot allocation failed\n");
821d51775cSChristian König 			goto err_unlock;
831d51775cSChristian König 		}
841d51775cSChristian König 
851d51775cSChristian König 		dma_resv_add_shared_fence(&resv, f);
861d51775cSChristian König 	} else {
871d51775cSChristian König 		dma_resv_add_excl_fence(&resv, f);
881d51775cSChristian König 	}
891d51775cSChristian König 
901d51775cSChristian König 	if (dma_resv_test_signaled(&resv, shared)) {
911d51775cSChristian König 		pr_err("Resv unexpectedly signaled\n");
921d51775cSChristian König 		r = -EINVAL;
931d51775cSChristian König 		goto err_unlock;
941d51775cSChristian König 	}
951d51775cSChristian König 	dma_fence_signal(f);
961d51775cSChristian König 	if (!dma_resv_test_signaled(&resv, shared)) {
971d51775cSChristian König 		pr_err("Resv not reporting signaled\n");
981d51775cSChristian König 		r = -EINVAL;
991d51775cSChristian König 		goto err_unlock;
1001d51775cSChristian König 	}
1011d51775cSChristian König err_unlock:
1021d51775cSChristian König 	dma_resv_unlock(&resv);
1031d51775cSChristian König err_free:
1041d51775cSChristian König 	dma_resv_fini(&resv);
1051d51775cSChristian König 	dma_fence_put(f);
1061d51775cSChristian König 	return r;
1071d51775cSChristian König }
1081d51775cSChristian König 
1091d51775cSChristian König static int test_excl_signaling(void *arg)
1101d51775cSChristian König {
1111d51775cSChristian König 	return test_signaling(arg, false);
1121d51775cSChristian König }
1131d51775cSChristian König 
1141d51775cSChristian König static int test_shared_signaling(void *arg)
1151d51775cSChristian König {
1161d51775cSChristian König 	return test_signaling(arg, true);
1171d51775cSChristian König }
1181d51775cSChristian König 
1191d51775cSChristian König static int test_for_each(void *arg, bool shared)
1201d51775cSChristian König {
1211d51775cSChristian König 	struct dma_resv_iter cursor;
1221d51775cSChristian König 	struct dma_fence *f, *fence;
1231d51775cSChristian König 	struct dma_resv resv;
1241d51775cSChristian König 	int r;
1251d51775cSChristian König 
1261d51775cSChristian König 	f = alloc_fence();
1271d51775cSChristian König 	if (!f)
1281d51775cSChristian König 		return -ENOMEM;
1291d51775cSChristian König 
1301d51775cSChristian König 	dma_resv_init(&resv);
1311d51775cSChristian König 	r = dma_resv_lock(&resv, NULL);
1321d51775cSChristian König 	if (r) {
1331d51775cSChristian König 		pr_err("Resv locking failed\n");
1341d51775cSChristian König 		goto err_free;
1351d51775cSChristian König 	}
1361d51775cSChristian König 
1371d51775cSChristian König 	if (shared) {
1381d51775cSChristian König 		r = dma_resv_reserve_shared(&resv, 1);
1391d51775cSChristian König 		if (r) {
1401d51775cSChristian König 			pr_err("Resv shared slot allocation failed\n");
1411d51775cSChristian König 			goto err_unlock;
1421d51775cSChristian König 		}
1431d51775cSChristian König 
1441d51775cSChristian König 		dma_resv_add_shared_fence(&resv, f);
1451d51775cSChristian König 	} else {
1461d51775cSChristian König 		dma_resv_add_excl_fence(&resv, f);
1471d51775cSChristian König 	}
1481d51775cSChristian König 
1491d51775cSChristian König 	r = -ENOENT;
1501d51775cSChristian König 	dma_resv_for_each_fence(&cursor, &resv, shared, fence) {
1511d51775cSChristian König 		if (!r) {
1521d51775cSChristian König 			pr_err("More than one fence found\n");
1531d51775cSChristian König 			r = -EINVAL;
1541d51775cSChristian König 			goto err_unlock;
1551d51775cSChristian König 		}
1561d51775cSChristian König 		if (f != fence) {
1571d51775cSChristian König 			pr_err("Unexpected fence\n");
1581d51775cSChristian König 			r = -EINVAL;
1591d51775cSChristian König 			goto err_unlock;
1601d51775cSChristian König 		}
1611d51775cSChristian König 		if (dma_resv_iter_is_exclusive(&cursor) != !shared) {
1621d51775cSChristian König 			pr_err("Unexpected fence usage\n");
1631d51775cSChristian König 			r = -EINVAL;
1641d51775cSChristian König 			goto err_unlock;
1651d51775cSChristian König 		}
1661d51775cSChristian König 		r = 0;
1671d51775cSChristian König 	}
1681d51775cSChristian König 	if (r) {
1691d51775cSChristian König 		pr_err("No fence found\n");
1701d51775cSChristian König 		goto err_unlock;
1711d51775cSChristian König 	}
1721d51775cSChristian König 	dma_fence_signal(f);
1731d51775cSChristian König err_unlock:
1741d51775cSChristian König 	dma_resv_unlock(&resv);
1751d51775cSChristian König err_free:
1761d51775cSChristian König 	dma_resv_fini(&resv);
1771d51775cSChristian König 	dma_fence_put(f);
1781d51775cSChristian König 	return r;
1791d51775cSChristian König }
1801d51775cSChristian König 
1811d51775cSChristian König static int test_excl_for_each(void *arg)
1821d51775cSChristian König {
1831d51775cSChristian König 	return test_for_each(arg, false);
1841d51775cSChristian König }
1851d51775cSChristian König 
1861d51775cSChristian König static int test_shared_for_each(void *arg)
1871d51775cSChristian König {
1881d51775cSChristian König 	return test_for_each(arg, true);
1891d51775cSChristian König }
1901d51775cSChristian König 
1911d51775cSChristian König static int test_for_each_unlocked(void *arg, bool shared)
1921d51775cSChristian König {
1931d51775cSChristian König 	struct dma_resv_iter cursor;
1941d51775cSChristian König 	struct dma_fence *f, *fence;
1951d51775cSChristian König 	struct dma_resv resv;
1961d51775cSChristian König 	int r;
1971d51775cSChristian König 
1981d51775cSChristian König 	f = alloc_fence();
1991d51775cSChristian König 	if (!f)
2001d51775cSChristian König 		return -ENOMEM;
2011d51775cSChristian König 
2021d51775cSChristian König 	dma_resv_init(&resv);
2031d51775cSChristian König 	r = dma_resv_lock(&resv, NULL);
2041d51775cSChristian König 	if (r) {
2051d51775cSChristian König 		pr_err("Resv locking failed\n");
2061d51775cSChristian König 		goto err_free;
2071d51775cSChristian König 	}
2081d51775cSChristian König 
2091d51775cSChristian König 	if (shared) {
2101d51775cSChristian König 		r = dma_resv_reserve_shared(&resv, 1);
2111d51775cSChristian König 		if (r) {
2121d51775cSChristian König 			pr_err("Resv shared slot allocation failed\n");
2131d51775cSChristian König 			dma_resv_unlock(&resv);
2141d51775cSChristian König 			goto err_free;
2151d51775cSChristian König 		}
2161d51775cSChristian König 
2171d51775cSChristian König 		dma_resv_add_shared_fence(&resv, f);
2181d51775cSChristian König 	} else {
2191d51775cSChristian König 		dma_resv_add_excl_fence(&resv, f);
2201d51775cSChristian König 	}
2211d51775cSChristian König 	dma_resv_unlock(&resv);
2221d51775cSChristian König 
2231d51775cSChristian König 	r = -ENOENT;
2241d51775cSChristian König 	dma_resv_iter_begin(&cursor, &resv, shared);
2251d51775cSChristian König 	dma_resv_for_each_fence_unlocked(&cursor, fence) {
2261d51775cSChristian König 		if (!r) {
2271d51775cSChristian König 			pr_err("More than one fence found\n");
2281d51775cSChristian König 			r = -EINVAL;
2291d51775cSChristian König 			goto err_iter_end;
2301d51775cSChristian König 		}
2311d51775cSChristian König 		if (!dma_resv_iter_is_restarted(&cursor)) {
2321d51775cSChristian König 			pr_err("No restart flag\n");
2331d51775cSChristian König 			goto err_iter_end;
2341d51775cSChristian König 		}
2351d51775cSChristian König 		if (f != fence) {
2361d51775cSChristian König 			pr_err("Unexpected fence\n");
2371d51775cSChristian König 			r = -EINVAL;
2381d51775cSChristian König 			goto err_iter_end;
2391d51775cSChristian König 		}
2401d51775cSChristian König 		if (dma_resv_iter_is_exclusive(&cursor) != !shared) {
2411d51775cSChristian König 			pr_err("Unexpected fence usage\n");
2421d51775cSChristian König 			r = -EINVAL;
2431d51775cSChristian König 			goto err_iter_end;
2441d51775cSChristian König 		}
2451d51775cSChristian König 
2461d51775cSChristian König 		/* We use r as state here */
2471d51775cSChristian König 		if (r == -ENOENT) {
2481d51775cSChristian König 			r = -EINVAL;
2491d51775cSChristian König 			/* That should trigger an restart */
2501d51775cSChristian König 			cursor.seq--;
2511d51775cSChristian König 		} else if (r == -EINVAL) {
2521d51775cSChristian König 			r = 0;
2531d51775cSChristian König 		}
2541d51775cSChristian König 	}
2551d51775cSChristian König 	if (r)
2561d51775cSChristian König 		pr_err("No fence found\n");
2571d51775cSChristian König err_iter_end:
2581d51775cSChristian König 	dma_resv_iter_end(&cursor);
2591d51775cSChristian König 	dma_fence_signal(f);
2601d51775cSChristian König err_free:
2611d51775cSChristian König 	dma_resv_fini(&resv);
2621d51775cSChristian König 	dma_fence_put(f);
2631d51775cSChristian König 	return r;
2641d51775cSChristian König }
2651d51775cSChristian König 
2661d51775cSChristian König static int test_excl_for_each_unlocked(void *arg)
2671d51775cSChristian König {
2681d51775cSChristian König 	return test_for_each_unlocked(arg, false);
2691d51775cSChristian König }
2701d51775cSChristian König 
2711d51775cSChristian König static int test_shared_for_each_unlocked(void *arg)
2721d51775cSChristian König {
2731d51775cSChristian König 	return test_for_each_unlocked(arg, true);
2741d51775cSChristian König }
2751d51775cSChristian König 
2761d51775cSChristian König static int test_get_fences(void *arg, bool shared)
2771d51775cSChristian König {
278*75ab2b36SChristian König 	struct dma_fence *f, **fences = NULL;
2791d51775cSChristian König 	struct dma_resv resv;
2801d51775cSChristian König 	int r, i;
2811d51775cSChristian König 
2821d51775cSChristian König 	f = alloc_fence();
2831d51775cSChristian König 	if (!f)
2841d51775cSChristian König 		return -ENOMEM;
2851d51775cSChristian König 
2861d51775cSChristian König 	dma_resv_init(&resv);
2871d51775cSChristian König 	r = dma_resv_lock(&resv, NULL);
2881d51775cSChristian König 	if (r) {
2891d51775cSChristian König 		pr_err("Resv locking failed\n");
29055d5e4f9SArnd Bergmann 		goto err_resv;
2911d51775cSChristian König 	}
2921d51775cSChristian König 
2931d51775cSChristian König 	if (shared) {
2941d51775cSChristian König 		r = dma_resv_reserve_shared(&resv, 1);
2951d51775cSChristian König 		if (r) {
2961d51775cSChristian König 			pr_err("Resv shared slot allocation failed\n");
2971d51775cSChristian König 			dma_resv_unlock(&resv);
29855d5e4f9SArnd Bergmann 			goto err_resv;
2991d51775cSChristian König 		}
3001d51775cSChristian König 
3011d51775cSChristian König 		dma_resv_add_shared_fence(&resv, f);
3021d51775cSChristian König 	} else {
3031d51775cSChristian König 		dma_resv_add_excl_fence(&resv, f);
3041d51775cSChristian König 	}
3051d51775cSChristian König 	dma_resv_unlock(&resv);
3061d51775cSChristian König 
307*75ab2b36SChristian König 	r = dma_resv_get_fences(&resv, shared, &i, &fences);
3081d51775cSChristian König 	if (r) {
3091d51775cSChristian König 		pr_err("get_fences failed\n");
3101d51775cSChristian König 		goto err_free;
3111d51775cSChristian König 	}
3121d51775cSChristian König 
3131d51775cSChristian König 	if (i != 1 || fences[0] != f) {
314*75ab2b36SChristian König 		pr_err("get_fences returned unexpected fence\n");
3151d51775cSChristian König 		goto err_free;
3161d51775cSChristian König 	}
3171d51775cSChristian König 
3181d51775cSChristian König 	dma_fence_signal(f);
3191d51775cSChristian König err_free:
3201d51775cSChristian König 	while (i--)
3211d51775cSChristian König 		dma_fence_put(fences[i]);
3221d51775cSChristian König 	kfree(fences);
32355d5e4f9SArnd Bergmann err_resv:
3241d51775cSChristian König 	dma_resv_fini(&resv);
3251d51775cSChristian König 	dma_fence_put(f);
3261d51775cSChristian König 	return r;
3271d51775cSChristian König }
3281d51775cSChristian König 
3291d51775cSChristian König static int test_excl_get_fences(void *arg)
3301d51775cSChristian König {
3311d51775cSChristian König 	return test_get_fences(arg, false);
3321d51775cSChristian König }
3331d51775cSChristian König 
3341d51775cSChristian König static int test_shared_get_fences(void *arg)
3351d51775cSChristian König {
3361d51775cSChristian König 	return test_get_fences(arg, true);
3371d51775cSChristian König }
3381d51775cSChristian König 
3391d51775cSChristian König int dma_resv(void)
3401d51775cSChristian König {
3411d51775cSChristian König 	static const struct subtest tests[] = {
3421d51775cSChristian König 		SUBTEST(sanitycheck),
3431d51775cSChristian König 		SUBTEST(test_excl_signaling),
3441d51775cSChristian König 		SUBTEST(test_shared_signaling),
3451d51775cSChristian König 		SUBTEST(test_excl_for_each),
3461d51775cSChristian König 		SUBTEST(test_shared_for_each),
3471d51775cSChristian König 		SUBTEST(test_excl_for_each_unlocked),
3481d51775cSChristian König 		SUBTEST(test_shared_for_each_unlocked),
3491d51775cSChristian König 		SUBTEST(test_excl_get_fences),
3501d51775cSChristian König 		SUBTEST(test_shared_get_fences),
3511d51775cSChristian König 	};
3521d51775cSChristian König 
3531d51775cSChristian König 	spin_lock_init(&fence_lock);
3541d51775cSChristian König 	return subtests(tests, NULL);
3551d51775cSChristian König }
356