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(size: sizeof(*f), GFP_KERNEL); |
31 | if (!f) |
32 | return NULL; |
33 | |
34 | dma_fence_init(fence: f, ops: &fence_ops, lock: &fence_lock, context: 0, seqno: 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(fence: f); |
49 | |
50 | dma_fence_signal(fence: f); |
51 | dma_fence_put(fence: f); |
52 | |
53 | dma_resv_init(obj: &resv); |
54 | r = dma_resv_lock(obj: &resv, NULL); |
55 | if (r) |
56 | pr_err("Resv locking failed\n" ); |
57 | else |
58 | dma_resv_unlock(obj: &resv); |
59 | dma_resv_fini(obj: &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(fence: f); |
75 | |
76 | dma_resv_init(obj: &resv); |
77 | r = dma_resv_lock(obj: &resv, NULL); |
78 | if (r) { |
79 | pr_err("Resv locking failed\n" ); |
80 | goto err_free; |
81 | } |
82 | |
83 | r = dma_resv_reserve_fences(obj: &resv, num_fences: 1); |
84 | if (r) { |
85 | pr_err("Resv shared slot allocation failed\n" ); |
86 | goto err_unlock; |
87 | } |
88 | |
89 | dma_resv_add_fence(obj: &resv, fence: f, usage); |
90 | if (dma_resv_test_signaled(obj: &resv, usage)) { |
91 | pr_err("Resv unexpectedly signaled\n" ); |
92 | r = -EINVAL; |
93 | goto err_unlock; |
94 | } |
95 | dma_fence_signal(fence: f); |
96 | if (!dma_resv_test_signaled(obj: &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(obj: &resv); |
103 | err_free: |
104 | dma_resv_fini(obj: &resv); |
105 | dma_fence_put(fence: 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(fence: f); |
122 | |
123 | dma_resv_init(obj: &resv); |
124 | r = dma_resv_lock(obj: &resv, NULL); |
125 | if (r) { |
126 | pr_err("Resv locking failed\n" ); |
127 | goto err_free; |
128 | } |
129 | |
130 | r = dma_resv_reserve_fences(obj: &resv, num_fences: 1); |
131 | if (r) { |
132 | pr_err("Resv shared slot allocation failed\n" ); |
133 | goto err_unlock; |
134 | } |
135 | |
136 | dma_resv_add_fence(obj: &resv, fence: 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: &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(fence: f); |
162 | err_unlock: |
163 | dma_resv_unlock(obj: &resv); |
164 | err_free: |
165 | dma_resv_fini(obj: &resv); |
166 | dma_fence_put(fence: 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(fence: f); |
183 | |
184 | dma_resv_init(obj: &resv); |
185 | r = dma_resv_lock(obj: &resv, NULL); |
186 | if (r) { |
187 | pr_err("Resv locking failed\n" ); |
188 | goto err_free; |
189 | } |
190 | |
191 | r = dma_resv_reserve_fences(obj: &resv, num_fences: 1); |
192 | if (r) { |
193 | pr_err("Resv shared slot allocation failed\n" ); |
194 | dma_resv_unlock(obj: &resv); |
195 | goto err_free; |
196 | } |
197 | |
198 | dma_resv_add_fence(obj: &resv, fence: f, usage); |
199 | dma_resv_unlock(obj: &resv); |
200 | |
201 | r = -ENOENT; |
202 | dma_resv_iter_begin(cursor: &cursor, obj: &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: &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: &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: &cursor); |
237 | dma_fence_signal(fence: f); |
238 | err_free: |
239 | dma_resv_fini(obj: &resv); |
240 | dma_fence_put(fence: 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(fence: f); |
256 | |
257 | dma_resv_init(obj: &resv); |
258 | r = dma_resv_lock(obj: &resv, NULL); |
259 | if (r) { |
260 | pr_err("Resv locking failed\n" ); |
261 | goto err_resv; |
262 | } |
263 | |
264 | r = dma_resv_reserve_fences(obj: &resv, num_fences: 1); |
265 | if (r) { |
266 | pr_err("Resv shared slot allocation failed\n" ); |
267 | dma_resv_unlock(obj: &resv); |
268 | goto err_resv; |
269 | } |
270 | |
271 | dma_resv_add_fence(obj: &resv, fence: f, usage); |
272 | dma_resv_unlock(obj: &resv); |
273 | |
274 | r = dma_resv_get_fences(obj: &resv, usage, num_fences: &i, fences: &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(fence: f); |
286 | err_free: |
287 | while (i--) |
288 | dma_fence_put(fence: fences[i]); |
289 | kfree(objp: fences); |
290 | err_resv: |
291 | dma_resv_fini(obj: &resv); |
292 | dma_fence_put(fence: 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 | |