| /* SPDX-License-Identifier: MIT */ |
| |
| /* |
| * Copyright © 2019 Intel Corporation |
| * Copyright © 2021 Advanced Micro Devices, Inc. |
| */ |
| |
| #include <linux/slab.h> |
| #include <linux/spinlock.h> |
| #include <linux/dma-resv.h> |
| |
| #include "selftest.h" |
| |
| static struct spinlock fence_lock; |
| |
| static const char *fence_name(struct dma_fence *f) |
| { |
| return "selftest"; |
| } |
| |
| static const struct dma_fence_ops fence_ops = { |
| .get_driver_name = fence_name, |
| .get_timeline_name = fence_name, |
| }; |
| |
| static struct dma_fence *alloc_fence(void) |
| { |
| struct dma_fence *f; |
| |
| f = kmalloc(sizeof(*f), GFP_KERNEL); |
| if (!f) |
| return NULL; |
| |
| dma_fence_init(f, &fence_ops, &fence_lock, 0, 0); |
| return f; |
| } |
| |
| static int sanitycheck(void *arg) |
| { |
| struct dma_resv resv; |
| struct dma_fence *f; |
| int r; |
| |
| f = alloc_fence(); |
| if (!f) |
| return -ENOMEM; |
| |
| dma_fence_enable_sw_signaling(f); |
| |
| dma_fence_signal(f); |
| dma_fence_put(f); |
| |
| dma_resv_init(&resv); |
| r = dma_resv_lock(&resv, NULL); |
| if (r) |
| pr_err("Resv locking failed\n"); |
| else |
| dma_resv_unlock(&resv); |
| dma_resv_fini(&resv); |
| return r; |
| } |
| |
| static int test_signaling(void *arg) |
| { |
| enum dma_resv_usage usage = (unsigned long)arg; |
| struct dma_resv resv; |
| struct dma_fence *f; |
| int r; |
| |
| f = alloc_fence(); |
| if (!f) |
| return -ENOMEM; |
| |
| dma_fence_enable_sw_signaling(f); |
| |
| dma_resv_init(&resv); |
| r = dma_resv_lock(&resv, NULL); |
| if (r) { |
| pr_err("Resv locking failed\n"); |
| goto err_free; |
| } |
| |
| r = dma_resv_reserve_fences(&resv, 1); |
| if (r) { |
| pr_err("Resv shared slot allocation failed\n"); |
| goto err_unlock; |
| } |
| |
| dma_resv_add_fence(&resv, f, usage); |
| if (dma_resv_test_signaled(&resv, usage)) { |
| pr_err("Resv unexpectedly signaled\n"); |
| r = -EINVAL; |
| goto err_unlock; |
| } |
| dma_fence_signal(f); |
| if (!dma_resv_test_signaled(&resv, usage)) { |
| pr_err("Resv not reporting signaled\n"); |
| r = -EINVAL; |
| goto err_unlock; |
| } |
| err_unlock: |
| dma_resv_unlock(&resv); |
| err_free: |
| dma_resv_fini(&resv); |
| dma_fence_put(f); |
| return r; |
| } |
| |
| static int test_for_each(void *arg) |
| { |
| enum dma_resv_usage usage = (unsigned long)arg; |
| struct dma_resv_iter cursor; |
| struct dma_fence *f, *fence; |
| struct dma_resv resv; |
| int r; |
| |
| f = alloc_fence(); |
| if (!f) |
| return -ENOMEM; |
| |
| dma_fence_enable_sw_signaling(f); |
| |
| dma_resv_init(&resv); |
| r = dma_resv_lock(&resv, NULL); |
| if (r) { |
| pr_err("Resv locking failed\n"); |
| goto err_free; |
| } |
| |
| r = dma_resv_reserve_fences(&resv, 1); |
| if (r) { |
| pr_err("Resv shared slot allocation failed\n"); |
| goto err_unlock; |
| } |
| |
| dma_resv_add_fence(&resv, f, usage); |
| |
| r = -ENOENT; |
| dma_resv_for_each_fence(&cursor, &resv, usage, fence) { |
| if (!r) { |
| pr_err("More than one fence found\n"); |
| r = -EINVAL; |
| goto err_unlock; |
| } |
| if (f != fence) { |
| pr_err("Unexpected fence\n"); |
| r = -EINVAL; |
| goto err_unlock; |
| } |
| if (dma_resv_iter_usage(&cursor) != usage) { |
| pr_err("Unexpected fence usage\n"); |
| r = -EINVAL; |
| goto err_unlock; |
| } |
| r = 0; |
| } |
| if (r) { |
| pr_err("No fence found\n"); |
| goto err_unlock; |
| } |
| dma_fence_signal(f); |
| err_unlock: |
| dma_resv_unlock(&resv); |
| err_free: |
| dma_resv_fini(&resv); |
| dma_fence_put(f); |
| return r; |
| } |
| |
| static int test_for_each_unlocked(void *arg) |
| { |
| enum dma_resv_usage usage = (unsigned long)arg; |
| struct dma_resv_iter cursor; |
| struct dma_fence *f, *fence; |
| struct dma_resv resv; |
| int r; |
| |
| f = alloc_fence(); |
| if (!f) |
| return -ENOMEM; |
| |
| dma_fence_enable_sw_signaling(f); |
| |
| dma_resv_init(&resv); |
| r = dma_resv_lock(&resv, NULL); |
| if (r) { |
| pr_err("Resv locking failed\n"); |
| goto err_free; |
| } |
| |
| r = dma_resv_reserve_fences(&resv, 1); |
| if (r) { |
| pr_err("Resv shared slot allocation failed\n"); |
| dma_resv_unlock(&resv); |
| goto err_free; |
| } |
| |
| dma_resv_add_fence(&resv, f, usage); |
| dma_resv_unlock(&resv); |
| |
| r = -ENOENT; |
| dma_resv_iter_begin(&cursor, &resv, usage); |
| dma_resv_for_each_fence_unlocked(&cursor, fence) { |
| if (!r) { |
| pr_err("More than one fence found\n"); |
| r = -EINVAL; |
| goto err_iter_end; |
| } |
| if (!dma_resv_iter_is_restarted(&cursor)) { |
| pr_err("No restart flag\n"); |
| goto err_iter_end; |
| } |
| if (f != fence) { |
| pr_err("Unexpected fence\n"); |
| r = -EINVAL; |
| goto err_iter_end; |
| } |
| if (dma_resv_iter_usage(&cursor) != usage) { |
| pr_err("Unexpected fence usage\n"); |
| r = -EINVAL; |
| goto err_iter_end; |
| } |
| |
| /* We use r as state here */ |
| if (r == -ENOENT) { |
| r = -EINVAL; |
| /* That should trigger an restart */ |
| cursor.fences = (void*)~0; |
| } else if (r == -EINVAL) { |
| r = 0; |
| } |
| } |
| if (r) |
| pr_err("No fence found\n"); |
| err_iter_end: |
| dma_resv_iter_end(&cursor); |
| dma_fence_signal(f); |
| err_free: |
| dma_resv_fini(&resv); |
| dma_fence_put(f); |
| return r; |
| } |
| |
| static int test_get_fences(void *arg) |
| { |
| enum dma_resv_usage usage = (unsigned long)arg; |
| struct dma_fence *f, **fences = NULL; |
| struct dma_resv resv; |
| int r, i; |
| |
| f = alloc_fence(); |
| if (!f) |
| return -ENOMEM; |
| |
| dma_fence_enable_sw_signaling(f); |
| |
| dma_resv_init(&resv); |
| r = dma_resv_lock(&resv, NULL); |
| if (r) { |
| pr_err("Resv locking failed\n"); |
| goto err_resv; |
| } |
| |
| r = dma_resv_reserve_fences(&resv, 1); |
| if (r) { |
| pr_err("Resv shared slot allocation failed\n"); |
| dma_resv_unlock(&resv); |
| goto err_resv; |
| } |
| |
| dma_resv_add_fence(&resv, f, usage); |
| dma_resv_unlock(&resv); |
| |
| r = dma_resv_get_fences(&resv, usage, &i, &fences); |
| if (r) { |
| pr_err("get_fences failed\n"); |
| goto err_free; |
| } |
| |
| if (i != 1 || fences[0] != f) { |
| pr_err("get_fences returned unexpected fence\n"); |
| goto err_free; |
| } |
| |
| dma_fence_signal(f); |
| err_free: |
| while (i--) |
| dma_fence_put(fences[i]); |
| kfree(fences); |
| err_resv: |
| dma_resv_fini(&resv); |
| dma_fence_put(f); |
| return r; |
| } |
| |
| int dma_resv(void) |
| { |
| static const struct subtest tests[] = { |
| SUBTEST(sanitycheck), |
| SUBTEST(test_signaling), |
| SUBTEST(test_for_each), |
| SUBTEST(test_for_each_unlocked), |
| SUBTEST(test_get_fences), |
| }; |
| enum dma_resv_usage usage; |
| int r; |
| |
| spin_lock_init(&fence_lock); |
| for (usage = DMA_RESV_USAGE_KERNEL; usage <= DMA_RESV_USAGE_BOOKKEEP; |
| ++usage) { |
| r = subtests(tests, (void *)(unsigned long)usage); |
| if (r) |
| return r; |
| } |
| return 0; |
| } |