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(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
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(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
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(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 }
101err_unlock:
102 dma_resv_unlock(obj: &resv);
103err_free:
104 dma_resv_fini(obj: &resv);
105 dma_fence_put(fence: 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(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);
162err_unlock:
163 dma_resv_unlock(obj: &resv);
164err_free:
165 dma_resv_fini(obj: &resv);
166 dma_fence_put(fence: 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(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");
235err_iter_end:
236 dma_resv_iter_end(cursor: &cursor);
237 dma_fence_signal(fence: f);
238err_free:
239 dma_resv_fini(obj: &resv);
240 dma_fence_put(fence: 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(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);
286err_free:
287 while (i--)
288 dma_fence_put(fence: fences[i]);
289 kfree(objp: fences);
290err_resv:
291 dma_resv_fini(obj: &resv);
292 dma_fence_put(fence: 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

source code of linux/drivers/dma-buf/st-dma-resv.c