/* SPDX-License-Identifier: MIT */ /* * Copyright © 2019 Intel Corporation * Copyright © 2021 Advanced Micro Devices, Inc. */ #include #include #include #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; }