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