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