| // SPDX-License-Identifier: MIT | 
 |  | 
 | /* | 
 |  * Copyright (C) 2022 Advanced Micro Devices, Inc. | 
 |  */ | 
 |  | 
 | #include <linux/dma-fence.h> | 
 | #include <linux/dma-fence-array.h> | 
 | #include <linux/dma-fence-chain.h> | 
 | #include <linux/dma-fence-unwrap.h> | 
 |  | 
 | #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); | 
 | } |