1// SPDX-License-Identifier: MIT
2
3/*
4 * Copyright (C) 2022 Advanced Micro Devices, Inc.
5 */
6
7#include <linux/dma-fence.h>
8#include <linux/dma-fence-array.h>
9#include <linux/dma-fence-chain.h>
10#include <linux/dma-fence-unwrap.h>
11
12#include "selftest.h"
13
14#define CHAIN_SZ (4 << 10)
15
16struct mock_fence {
17	struct dma_fence base;
18	spinlock_t lock;
19};
20
21static const char *mock_name(struct dma_fence *f)
22{
23	return "mock";
24}
25
26static const struct dma_fence_ops mock_ops = {
27	.get_driver_name = mock_name,
28	.get_timeline_name = mock_name,
29};
30
31static struct dma_fence *mock_fence(void)
32{
33	struct mock_fence *f;
34
35	f = kmalloc(sizeof(*f), GFP_KERNEL);
36	if (!f)
37		return NULL;
38
39	spin_lock_init(&f->lock);
40	dma_fence_init(&f->base, &mock_ops, &f->lock,
41		       dma_fence_context_alloc(1), 1);
42
43	return &f->base;
44}
45
46static struct dma_fence *mock_array(unsigned int num_fences, ...)
47{
48	struct dma_fence_array *array;
49	struct dma_fence **fences;
50	va_list valist;
51	int i;
52
53	fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL);
54	if (!fences)
55		goto error_put;
56
57	va_start(valist, num_fences);
58	for (i = 0; i < num_fences; ++i)
59		fences[i] = va_arg(valist, typeof(*fences));
60	va_end(valist);
61
62	array = dma_fence_array_create(num_fences, fences,
63				       dma_fence_context_alloc(1),
64				       1, false);
65	if (!array)
66		goto error_free;
67	return &array->base;
68
69error_free:
70	kfree(fences);
71
72error_put:
73	va_start(valist, num_fences);
74	for (i = 0; i < num_fences; ++i)
75		dma_fence_put(va_arg(valist, typeof(*fences)));
76	va_end(valist);
77	return NULL;
78}
79
80static struct dma_fence *mock_chain(struct dma_fence *prev,
81				    struct dma_fence *fence)
82{
83	struct dma_fence_chain *f;
84
85	f = dma_fence_chain_alloc();
86	if (!f) {
87		dma_fence_put(prev);
88		dma_fence_put(fence);
89		return NULL;
90	}
91
92	dma_fence_chain_init(f, prev, fence, 1);
93	return &f->base;
94}
95
96static int sanitycheck(void *arg)
97{
98	struct dma_fence *f, *chain, *array;
99	int err = 0;
100
101	f = mock_fence();
102	if (!f)
103		return -ENOMEM;
104
105	dma_fence_enable_sw_signaling(f);
106
107	array = mock_array(1, f);
108	if (!array)
109		return -ENOMEM;
110
111	chain = mock_chain(NULL, array);
112	if (!chain)
113		return -ENOMEM;
114
115	dma_fence_put(chain);
116	return err;
117}
118
119static int unwrap_array(void *arg)
120{
121	struct dma_fence *fence, *f1, *f2, *array;
122	struct dma_fence_unwrap iter;
123	int err = 0;
124
125	f1 = mock_fence();
126	if (!f1)
127		return -ENOMEM;
128
129	dma_fence_enable_sw_signaling(f1);
130
131	f2 = mock_fence();
132	if (!f2) {
133		dma_fence_put(f1);
134		return -ENOMEM;
135	}
136
137	dma_fence_enable_sw_signaling(f2);
138
139	array = mock_array(2, f1, f2);
140	if (!array)
141		return -ENOMEM;
142
143	dma_fence_unwrap_for_each(fence, &iter, array) {
144		if (fence == f1) {
145			f1 = NULL;
146		} else if (fence == f2) {
147			f2 = NULL;
148		} else {
149			pr_err("Unexpected fence!\n");
150			err = -EINVAL;
151		}
152	}
153
154	if (f1 || f2) {
155		pr_err("Not all fences seen!\n");
156		err = -EINVAL;
157	}
158
159	dma_fence_put(array);
160	return err;
161}
162
163static int unwrap_chain(void *arg)
164{
165	struct dma_fence *fence, *f1, *f2, *chain;
166	struct dma_fence_unwrap iter;
167	int err = 0;
168
169	f1 = mock_fence();
170	if (!f1)
171		return -ENOMEM;
172
173	dma_fence_enable_sw_signaling(f1);
174
175	f2 = mock_fence();
176	if (!f2) {
177		dma_fence_put(f1);
178		return -ENOMEM;
179	}
180
181	dma_fence_enable_sw_signaling(f2);
182
183	chain = mock_chain(f1, f2);
184	if (!chain)
185		return -ENOMEM;
186
187	dma_fence_unwrap_for_each(fence, &iter, chain) {
188		if (fence == f1) {
189			f1 = NULL;
190		} else if (fence == f2) {
191			f2 = NULL;
192		} else {
193			pr_err("Unexpected fence!\n");
194			err = -EINVAL;
195		}
196	}
197
198	if (f1 || f2) {
199		pr_err("Not all fences seen!\n");
200		err = -EINVAL;
201	}
202
203	dma_fence_put(chain);
204	return err;
205}
206
207static int unwrap_chain_array(void *arg)
208{
209	struct dma_fence *fence, *f1, *f2, *array, *chain;
210	struct dma_fence_unwrap iter;
211	int err = 0;
212
213	f1 = mock_fence();
214	if (!f1)
215		return -ENOMEM;
216
217	dma_fence_enable_sw_signaling(f1);
218
219	f2 = mock_fence();
220	if (!f2) {
221		dma_fence_put(f1);
222		return -ENOMEM;
223	}
224
225	dma_fence_enable_sw_signaling(f2);
226
227	array = mock_array(2, f1, f2);
228	if (!array)
229		return -ENOMEM;
230
231	chain = mock_chain(NULL, array);
232	if (!chain)
233		return -ENOMEM;
234
235	dma_fence_unwrap_for_each(fence, &iter, chain) {
236		if (fence == f1) {
237			f1 = NULL;
238		} else if (fence == f2) {
239			f2 = NULL;
240		} else {
241			pr_err("Unexpected fence!\n");
242			err = -EINVAL;
243		}
244	}
245
246	if (f1 || f2) {
247		pr_err("Not all fences seen!\n");
248		err = -EINVAL;
249	}
250
251	dma_fence_put(chain);
252	return err;
253}
254
255static int unwrap_merge(void *arg)
256{
257	struct dma_fence *fence, *f1, *f2, *f3;
258	struct dma_fence_unwrap iter;
259	int err = 0;
260
261	f1 = mock_fence();
262	if (!f1)
263		return -ENOMEM;
264
265	dma_fence_enable_sw_signaling(f1);
266
267	f2 = mock_fence();
268	if (!f2) {
269		err = -ENOMEM;
270		goto error_put_f1;
271	}
272
273	dma_fence_enable_sw_signaling(f2);
274
275	f3 = dma_fence_unwrap_merge(f1, f2);
276	if (!f3) {
277		err = -ENOMEM;
278		goto error_put_f2;
279	}
280
281	dma_fence_unwrap_for_each(fence, &iter, f3) {
282		if (fence == f1) {
283			dma_fence_put(f1);
284			f1 = NULL;
285		} else if (fence == f2) {
286			dma_fence_put(f2);
287			f2 = NULL;
288		} else {
289			pr_err("Unexpected fence!\n");
290			err = -EINVAL;
291		}
292	}
293
294	if (f1 || f2) {
295		pr_err("Not all fences seen!\n");
296		err = -EINVAL;
297	}
298
299	dma_fence_put(f3);
300error_put_f2:
301	dma_fence_put(f2);
302error_put_f1:
303	dma_fence_put(f1);
304	return err;
305}
306
307static int unwrap_merge_complex(void *arg)
308{
309	struct dma_fence *fence, *f1, *f2, *f3, *f4, *f5;
310	struct dma_fence_unwrap iter;
311	int err = -ENOMEM;
312
313	f1 = mock_fence();
314	if (!f1)
315		return -ENOMEM;
316
317	dma_fence_enable_sw_signaling(f1);
318
319	f2 = mock_fence();
320	if (!f2)
321		goto error_put_f1;
322
323	dma_fence_enable_sw_signaling(f2);
324
325	f3 = dma_fence_unwrap_merge(f1, f2);
326	if (!f3)
327		goto error_put_f2;
328
329	/* The resulting array has the fences in reverse */
330	f4 = dma_fence_unwrap_merge(f2, f1);
331	if (!f4)
332		goto error_put_f3;
333
334	/* Signaled fences should be filtered, the two arrays merged. */
335	f5 = dma_fence_unwrap_merge(f3, f4, dma_fence_get_stub());
336	if (!f5)
337		goto error_put_f4;
338
339	err = 0;
340	dma_fence_unwrap_for_each(fence, &iter, f5) {
341		if (fence == f1) {
342			dma_fence_put(f1);
343			f1 = NULL;
344		} else if (fence == f2) {
345			dma_fence_put(f2);
346			f2 = NULL;
347		} else {
348			pr_err("Unexpected fence!\n");
349			err = -EINVAL;
350		}
351	}
352
353	if (f1 || f2) {
354		pr_err("Not all fences seen!\n");
355		err = -EINVAL;
356	}
357
358	dma_fence_put(f5);
359error_put_f4:
360	dma_fence_put(f4);
361error_put_f3:
362	dma_fence_put(f3);
363error_put_f2:
364	dma_fence_put(f2);
365error_put_f1:
366	dma_fence_put(f1);
367	return err;
368}
369
370int dma_fence_unwrap(void)
371{
372	static const struct subtest tests[] = {
373		SUBTEST(sanitycheck),
374		SUBTEST(unwrap_array),
375		SUBTEST(unwrap_chain),
376		SUBTEST(unwrap_chain_array),
377		SUBTEST(unwrap_merge),
378		SUBTEST(unwrap_merge_complex),
379	};
380
381	return subtests(tests, NULL);
382}
383