xref: /openbmc/linux/drivers/dma-buf/st-dma-resv.c (revision 39f555fb)
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_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 
63 static 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 	}
101 err_unlock:
102 	dma_resv_unlock(&resv);
103 err_free:
104 	dma_resv_fini(&resv);
105 	dma_fence_put(f);
106 	return r;
107 }
108 
109 static 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);
162 err_unlock:
163 	dma_resv_unlock(&resv);
164 err_free:
165 	dma_resv_fini(&resv);
166 	dma_fence_put(f);
167 	return r;
168 }
169 
170 static 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");
235 err_iter_end:
236 	dma_resv_iter_end(&cursor);
237 	dma_fence_signal(f);
238 err_free:
239 	dma_resv_fini(&resv);
240 	dma_fence_put(f);
241 	return r;
242 }
243 
244 static 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);
286 err_free:
287 	while (i--)
288 		dma_fence_put(fences[i]);
289 	kfree(fences);
290 err_resv:
291 	dma_resv_fini(&resv);
292 	dma_fence_put(f);
293 	return r;
294 }
295 
296 int 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