1/* SPDX-License-Identifier: MIT */
2
3/*
4* Copyright �� 2019 Intel Corporation
5* Copyright �� 2021 Advanced Micro Devices, Inc.
6*/
7
8#include <linux/slab.h>
9#include <linux/spinlock.h>
10#include <linux/dma-resv.h>
11
12#include "selftest.h"
13
14static struct spinlock fence_lock;
15
16static const char *fence_name(struct dma_fence *f)
17{
18	return "selftest";
19}
20
21static const struct dma_fence_ops fence_ops = {
22	.get_driver_name = fence_name,
23	.get_timeline_name = fence_name,
24};
25
26static struct dma_fence *alloc_fence(void)
27{
28	struct dma_fence *f;
29
30	f = kmalloc(sizeof(*f), GFP_KERNEL);
31	if (!f)
32		return NULL;
33
34	dma_fence_init(f, &fence_ops, &fence_lock, 0, 0);
35	return f;
36}
37
38static int sanitycheck(void *arg)
39{
40	struct dma_resv resv;
41	struct dma_fence *f;
42	int r;
43
44	f = alloc_fence();
45	if (!f)
46		return -ENOMEM;
47
48	dma_fence_enable_sw_signaling(f);
49
50	dma_fence_signal(f);
51	dma_fence_put(f);
52
53	dma_resv_init(&resv);
54	r = dma_resv_lock(&resv, NULL);
55	if (r)
56		pr_err("Resv locking failed\n");
57	else
58		dma_resv_unlock(&resv);
59	dma_resv_fini(&resv);
60	return r;
61}
62
63static int test_signaling(void *arg)
64{
65	enum dma_resv_usage usage = (unsigned long)arg;
66	struct dma_resv resv;
67	struct dma_fence *f;
68	int r;
69
70	f = alloc_fence();
71	if (!f)
72		return -ENOMEM;
73
74	dma_fence_enable_sw_signaling(f);
75
76	dma_resv_init(&resv);
77	r = dma_resv_lock(&resv, NULL);
78	if (r) {
79		pr_err("Resv locking failed\n");
80		goto err_free;
81	}
82
83	r = dma_resv_reserve_fences(&resv, 1);
84	if (r) {
85		pr_err("Resv shared slot allocation failed\n");
86		goto err_unlock;
87	}
88
89	dma_resv_add_fence(&resv, f, usage);
90	if (dma_resv_test_signaled(&resv, usage)) {
91		pr_err("Resv unexpectedly signaled\n");
92		r = -EINVAL;
93		goto err_unlock;
94	}
95	dma_fence_signal(f);
96	if (!dma_resv_test_signaled(&resv, usage)) {
97		pr_err("Resv not reporting signaled\n");
98		r = -EINVAL;
99		goto err_unlock;
100	}
101err_unlock:
102	dma_resv_unlock(&resv);
103err_free:
104	dma_resv_fini(&resv);
105	dma_fence_put(f);
106	return r;
107}
108
109static int test_for_each(void *arg)
110{
111	enum dma_resv_usage usage = (unsigned long)arg;
112	struct dma_resv_iter cursor;
113	struct dma_fence *f, *fence;
114	struct dma_resv resv;
115	int r;
116
117	f = alloc_fence();
118	if (!f)
119		return -ENOMEM;
120
121	dma_fence_enable_sw_signaling(f);
122
123	dma_resv_init(&resv);
124	r = dma_resv_lock(&resv, NULL);
125	if (r) {
126		pr_err("Resv locking failed\n");
127		goto err_free;
128	}
129
130	r = dma_resv_reserve_fences(&resv, 1);
131	if (r) {
132		pr_err("Resv shared slot allocation failed\n");
133		goto err_unlock;
134	}
135
136	dma_resv_add_fence(&resv, f, usage);
137
138	r = -ENOENT;
139	dma_resv_for_each_fence(&cursor, &resv, usage, fence) {
140		if (!r) {
141			pr_err("More than one fence found\n");
142			r = -EINVAL;
143			goto err_unlock;
144		}
145		if (f != fence) {
146			pr_err("Unexpected fence\n");
147			r = -EINVAL;
148			goto err_unlock;
149		}
150		if (dma_resv_iter_usage(&cursor) != usage) {
151			pr_err("Unexpected fence usage\n");
152			r = -EINVAL;
153			goto err_unlock;
154		}
155		r = 0;
156	}
157	if (r) {
158		pr_err("No fence found\n");
159		goto err_unlock;
160	}
161	dma_fence_signal(f);
162err_unlock:
163	dma_resv_unlock(&resv);
164err_free:
165	dma_resv_fini(&resv);
166	dma_fence_put(f);
167	return r;
168}
169
170static int test_for_each_unlocked(void *arg)
171{
172	enum dma_resv_usage usage = (unsigned long)arg;
173	struct dma_resv_iter cursor;
174	struct dma_fence *f, *fence;
175	struct dma_resv resv;
176	int r;
177
178	f = alloc_fence();
179	if (!f)
180		return -ENOMEM;
181
182	dma_fence_enable_sw_signaling(f);
183
184	dma_resv_init(&resv);
185	r = dma_resv_lock(&resv, NULL);
186	if (r) {
187		pr_err("Resv locking failed\n");
188		goto err_free;
189	}
190
191	r = dma_resv_reserve_fences(&resv, 1);
192	if (r) {
193		pr_err("Resv shared slot allocation failed\n");
194		dma_resv_unlock(&resv);
195		goto err_free;
196	}
197
198	dma_resv_add_fence(&resv, f, usage);
199	dma_resv_unlock(&resv);
200
201	r = -ENOENT;
202	dma_resv_iter_begin(&cursor, &resv, usage);
203	dma_resv_for_each_fence_unlocked(&cursor, fence) {
204		if (!r) {
205			pr_err("More than one fence found\n");
206			r = -EINVAL;
207			goto err_iter_end;
208		}
209		if (!dma_resv_iter_is_restarted(&cursor)) {
210			pr_err("No restart flag\n");
211			goto err_iter_end;
212		}
213		if (f != fence) {
214			pr_err("Unexpected fence\n");
215			r = -EINVAL;
216			goto err_iter_end;
217		}
218		if (dma_resv_iter_usage(&cursor) != usage) {
219			pr_err("Unexpected fence usage\n");
220			r = -EINVAL;
221			goto err_iter_end;
222		}
223
224		/* We use r as state here */
225		if (r == -ENOENT) {
226			r = -EINVAL;
227			/* That should trigger an restart */
228			cursor.fences = (void*)~0;
229		} else if (r == -EINVAL) {
230			r = 0;
231		}
232	}
233	if (r)
234		pr_err("No fence found\n");
235err_iter_end:
236	dma_resv_iter_end(&cursor);
237	dma_fence_signal(f);
238err_free:
239	dma_resv_fini(&resv);
240	dma_fence_put(f);
241	return r;
242}
243
244static int test_get_fences(void *arg)
245{
246	enum dma_resv_usage usage = (unsigned long)arg;
247	struct dma_fence *f, **fences = NULL;
248	struct dma_resv resv;
249	int r, i;
250
251	f = alloc_fence();
252	if (!f)
253		return -ENOMEM;
254
255	dma_fence_enable_sw_signaling(f);
256
257	dma_resv_init(&resv);
258	r = dma_resv_lock(&resv, NULL);
259	if (r) {
260		pr_err("Resv locking failed\n");
261		goto err_resv;
262	}
263
264	r = dma_resv_reserve_fences(&resv, 1);
265	if (r) {
266		pr_err("Resv shared slot allocation failed\n");
267		dma_resv_unlock(&resv);
268		goto err_resv;
269	}
270
271	dma_resv_add_fence(&resv, f, usage);
272	dma_resv_unlock(&resv);
273
274	r = dma_resv_get_fences(&resv, usage, &i, &fences);
275	if (r) {
276		pr_err("get_fences failed\n");
277		goto err_free;
278	}
279
280	if (i != 1 || fences[0] != f) {
281		pr_err("get_fences returned unexpected fence\n");
282		goto err_free;
283	}
284
285	dma_fence_signal(f);
286err_free:
287	while (i--)
288		dma_fence_put(fences[i]);
289	kfree(fences);
290err_resv:
291	dma_resv_fini(&resv);
292	dma_fence_put(f);
293	return r;
294}
295
296int dma_resv(void)
297{
298	static const struct subtest tests[] = {
299		SUBTEST(sanitycheck),
300		SUBTEST(test_signaling),
301		SUBTEST(test_for_each),
302		SUBTEST(test_for_each_unlocked),
303		SUBTEST(test_get_fences),
304	};
305	enum dma_resv_usage usage;
306	int r;
307
308	spin_lock_init(&fence_lock);
309	for (usage = DMA_RESV_USAGE_KERNEL; usage <= DMA_RESV_USAGE_BOOKKEEP;
310	     ++usage) {
311		r = subtests(tests, (void *)(unsigned long)usage);
312		if (r)
313			return r;
314	}
315	return 0;
316}
317