// SPDX-License-Identifier: MIT /* * Copyright (C) 2022 Advanced Micro Devices, Inc. */ #include #include #include #include #include "selftest.h" #define CHAIN_SZ (4 << 10) struct mock_fence { struct dma_fence base; spinlock_t lock; }; static const char *mock_name(struct dma_fence *f) { return "mock"; } static const struct dma_fence_ops mock_ops = { .get_driver_name = mock_name, .get_timeline_name = mock_name, }; static struct dma_fence *mock_fence(void) { struct mock_fence *f; f = kmalloc(sizeof(*f), GFP_KERNEL); if (!f) return NULL; spin_lock_init(&f->lock); dma_fence_init(&f->base, &mock_ops, &f->lock, dma_fence_context_alloc(1), 1); return &f->base; } static struct dma_fence *mock_array(unsigned int num_fences, ...) { struct dma_fence_array *array; struct dma_fence **fences; va_list valist; int i; fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL); if (!fences) goto error_put; va_start(valist, num_fences); for (i = 0; i < num_fences; ++i) fences[i] = va_arg(valist, typeof(*fences)); va_end(valist); array = dma_fence_array_create(num_fences, fences, dma_fence_context_alloc(1), 1, false); if (!array) goto error_free; return &array->base; error_free: kfree(fences); error_put: va_start(valist, num_fences); for (i = 0; i < num_fences; ++i) dma_fence_put(va_arg(valist, typeof(*fences))); va_end(valist); return NULL; } static struct dma_fence *mock_chain(struct dma_fence *prev, struct dma_fence *fence) { struct dma_fence_chain *f; f = dma_fence_chain_alloc(); if (!f) { dma_fence_put(prev); dma_fence_put(fence); return NULL; } dma_fence_chain_init(f, prev, fence, 1); return &f->base; } static int sanitycheck(void *arg) { struct dma_fence *f, *chain, *array; int err = 0; f = mock_fence(); if (!f) return -ENOMEM; dma_fence_enable_sw_signaling(f); array = mock_array(1, f); if (!array) return -ENOMEM; chain = mock_chain(NULL, array); if (!chain) return -ENOMEM; dma_fence_put(chain); return err; } static int unwrap_array(void *arg) { struct dma_fence *fence, *f1, *f2, *array; struct dma_fence_unwrap iter; int err = 0; f1 = mock_fence(); if (!f1) return -ENOMEM; dma_fence_enable_sw_signaling(f1); f2 = mock_fence(); if (!f2) { dma_fence_put(f1); return -ENOMEM; } dma_fence_enable_sw_signaling(f2); array = mock_array(2, f1, f2); if (!array) return -ENOMEM; dma_fence_unwrap_for_each(fence, &iter, array) { if (fence == f1) { f1 = NULL; } else if (fence == f2) { f2 = NULL; } else { pr_err("Unexpected fence!\n"); err = -EINVAL; } } if (f1 || f2) { pr_err("Not all fences seen!\n"); err = -EINVAL; } dma_fence_put(array); return err; } static int unwrap_chain(void *arg) { struct dma_fence *fence, *f1, *f2, *chain; struct dma_fence_unwrap iter; int err = 0; f1 = mock_fence(); if (!f1) return -ENOMEM; dma_fence_enable_sw_signaling(f1); f2 = mock_fence(); if (!f2) { dma_fence_put(f1); return -ENOMEM; } dma_fence_enable_sw_signaling(f2); chain = mock_chain(f1, f2); if (!chain) return -ENOMEM; dma_fence_unwrap_for_each(fence, &iter, chain) { if (fence == f1) { f1 = NULL; } else if (fence == f2) { f2 = NULL; } else { pr_err("Unexpected fence!\n"); err = -EINVAL; } } if (f1 || f2) { pr_err("Not all fences seen!\n"); err = -EINVAL; } dma_fence_put(chain); return err; } static int unwrap_chain_array(void *arg) { struct dma_fence *fence, *f1, *f2, *array, *chain; struct dma_fence_unwrap iter; int err = 0; f1 = mock_fence(); if (!f1) return -ENOMEM; dma_fence_enable_sw_signaling(f1); f2 = mock_fence(); if (!f2) { dma_fence_put(f1); return -ENOMEM; } dma_fence_enable_sw_signaling(f2); array = mock_array(2, f1, f2); if (!array) return -ENOMEM; chain = mock_chain(NULL, array); if (!chain) return -ENOMEM; dma_fence_unwrap_for_each(fence, &iter, chain) { if (fence == f1) { f1 = NULL; } else if (fence == f2) { f2 = NULL; } else { pr_err("Unexpected fence!\n"); err = -EINVAL; } } if (f1 || f2) { pr_err("Not all fences seen!\n"); err = -EINVAL; } dma_fence_put(chain); return err; } static int unwrap_merge(void *arg) { struct dma_fence *fence, *f1, *f2, *f3; struct dma_fence_unwrap iter; int err = 0; f1 = mock_fence(); if (!f1) return -ENOMEM; dma_fence_enable_sw_signaling(f1); f2 = mock_fence(); if (!f2) { err = -ENOMEM; goto error_put_f1; } dma_fence_enable_sw_signaling(f2); f3 = dma_fence_unwrap_merge(f1, f2); if (!f3) { err = -ENOMEM; goto error_put_f2; } dma_fence_unwrap_for_each(fence, &iter, f3) { if (fence == f1) { dma_fence_put(f1); f1 = NULL; } else if (fence == f2) { dma_fence_put(f2); f2 = NULL; } else { pr_err("Unexpected fence!\n"); err = -EINVAL; } } if (f1 || f2) { pr_err("Not all fences seen!\n"); err = -EINVAL; } dma_fence_put(f3); error_put_f2: dma_fence_put(f2); error_put_f1: dma_fence_put(f1); return err; } static int unwrap_merge_complex(void *arg) { struct dma_fence *fence, *f1, *f2, *f3, *f4, *f5; struct dma_fence_unwrap iter; int err = -ENOMEM; f1 = mock_fence(); if (!f1) return -ENOMEM; dma_fence_enable_sw_signaling(f1); f2 = mock_fence(); if (!f2) goto error_put_f1; dma_fence_enable_sw_signaling(f2); f3 = dma_fence_unwrap_merge(f1, f2); if (!f3) goto error_put_f2; /* The resulting array has the fences in reverse */ f4 = dma_fence_unwrap_merge(f2, f1); if (!f4) goto error_put_f3; /* Signaled fences should be filtered, the two arrays merged. */ f5 = dma_fence_unwrap_merge(f3, f4, dma_fence_get_stub()); if (!f5) goto error_put_f4; err = 0; dma_fence_unwrap_for_each(fence, &iter, f5) { if (fence == f1) { dma_fence_put(f1); f1 = NULL; } else if (fence == f2) { dma_fence_put(f2); f2 = NULL; } else { pr_err("Unexpected fence!\n"); err = -EINVAL; } } if (f1 || f2) { pr_err("Not all fences seen!\n"); err = -EINVAL; } dma_fence_put(f5); error_put_f4: dma_fence_put(f4); error_put_f3: dma_fence_put(f3); error_put_f2: dma_fence_put(f2); error_put_f1: dma_fence_put(f1); return err; } int dma_fence_unwrap(void) { static const struct subtest tests[] = { SUBTEST(sanitycheck), SUBTEST(unwrap_array), SUBTEST(unwrap_chain), SUBTEST(unwrap_chain_array), SUBTEST(unwrap_merge), SUBTEST(unwrap_merge_complex), }; return subtests(tests, NULL); }