1 | // SPDX-License-Identifier: GPL-2.0 AND MIT |
2 | /* |
3 | * Copyright © 2023 Intel Corporation |
4 | */ |
5 | #include <linux/dma-resv.h> |
6 | #include <linux/kthread.h> |
7 | #include <linux/delay.h> |
8 | #include <linux/timer.h> |
9 | #include <linux/jiffies.h> |
10 | #include <linux/mutex.h> |
11 | #include <linux/ww_mutex.h> |
12 | |
13 | #include <drm/ttm/ttm_resource.h> |
14 | #include <drm/ttm/ttm_placement.h> |
15 | #include <drm/ttm/ttm_tt.h> |
16 | |
17 | #include "ttm_kunit_helpers.h" |
18 | |
19 | #define BO_SIZE SZ_8K |
20 | |
21 | struct ttm_bo_test_case { |
22 | const char *description; |
23 | bool interruptible; |
24 | bool no_wait; |
25 | }; |
26 | |
27 | static const struct ttm_bo_test_case ttm_bo_reserved_cases[] = { |
28 | { |
29 | .description = "Cannot be interrupted and sleeps" , |
30 | .interruptible = false, |
31 | .no_wait = false, |
32 | }, |
33 | { |
34 | .description = "Cannot be interrupted, locks straight away" , |
35 | .interruptible = false, |
36 | .no_wait = true, |
37 | }, |
38 | { |
39 | .description = "Can be interrupted, sleeps" , |
40 | .interruptible = true, |
41 | .no_wait = false, |
42 | }, |
43 | }; |
44 | |
45 | static void ttm_bo_init_case_desc(const struct ttm_bo_test_case *t, |
46 | char *desc) |
47 | { |
48 | strscpy(desc, t->description, KUNIT_PARAM_DESC_SIZE); |
49 | } |
50 | |
51 | KUNIT_ARRAY_PARAM(ttm_bo_reserve, ttm_bo_reserved_cases, ttm_bo_init_case_desc); |
52 | |
53 | static void ttm_bo_reserve_optimistic_no_ticket(struct kunit *test) |
54 | { |
55 | const struct ttm_bo_test_case *params = test->param_value; |
56 | struct ttm_buffer_object *bo; |
57 | int err; |
58 | |
59 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
60 | |
61 | err = ttm_bo_reserve(bo, interruptible: params->interruptible, no_wait: params->no_wait, NULL); |
62 | KUNIT_ASSERT_EQ(test, err, 0); |
63 | |
64 | dma_resv_unlock(obj: bo->base.resv); |
65 | } |
66 | |
67 | static void ttm_bo_reserve_locked_no_sleep(struct kunit *test) |
68 | { |
69 | struct ttm_buffer_object *bo; |
70 | bool interruptible = false; |
71 | bool no_wait = true; |
72 | int err; |
73 | |
74 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
75 | |
76 | /* Let's lock it beforehand */ |
77 | dma_resv_lock(obj: bo->base.resv, NULL); |
78 | |
79 | err = ttm_bo_reserve(bo, interruptible, no_wait, NULL); |
80 | dma_resv_unlock(obj: bo->base.resv); |
81 | |
82 | KUNIT_ASSERT_EQ(test, err, -EBUSY); |
83 | } |
84 | |
85 | static void ttm_bo_reserve_no_wait_ticket(struct kunit *test) |
86 | { |
87 | struct ttm_buffer_object *bo; |
88 | struct ww_acquire_ctx ctx; |
89 | bool interruptible = false; |
90 | bool no_wait = true; |
91 | int err; |
92 | |
93 | ww_acquire_init(ctx: &ctx, ww_class: &reservation_ww_class); |
94 | |
95 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
96 | |
97 | err = ttm_bo_reserve(bo, interruptible, no_wait, ticket: &ctx); |
98 | KUNIT_ASSERT_EQ(test, err, -EBUSY); |
99 | |
100 | ww_acquire_fini(ctx: &ctx); |
101 | } |
102 | |
103 | static void ttm_bo_reserve_double_resv(struct kunit *test) |
104 | { |
105 | struct ttm_buffer_object *bo; |
106 | struct ww_acquire_ctx ctx; |
107 | bool interruptible = false; |
108 | bool no_wait = false; |
109 | int err; |
110 | |
111 | ww_acquire_init(ctx: &ctx, ww_class: &reservation_ww_class); |
112 | |
113 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
114 | |
115 | err = ttm_bo_reserve(bo, interruptible, no_wait, ticket: &ctx); |
116 | KUNIT_ASSERT_EQ(test, err, 0); |
117 | |
118 | err = ttm_bo_reserve(bo, interruptible, no_wait, ticket: &ctx); |
119 | |
120 | dma_resv_unlock(obj: bo->base.resv); |
121 | ww_acquire_fini(ctx: &ctx); |
122 | |
123 | KUNIT_ASSERT_EQ(test, err, -EALREADY); |
124 | } |
125 | |
126 | /* |
127 | * A test case heavily inspired by ww_test_edeadlk_normal(). It injects |
128 | * a deadlock by manipulating the sequence number of the context that holds |
129 | * dma_resv lock of bo2 so the other context is "wounded" and has to back off |
130 | * (indicated by -EDEADLK). The subtest checks if ttm_bo_reserve() properly |
131 | * propagates that error. |
132 | */ |
133 | static void ttm_bo_reserve_deadlock(struct kunit *test) |
134 | { |
135 | struct ttm_buffer_object *bo1, *bo2; |
136 | struct ww_acquire_ctx ctx1, ctx2; |
137 | bool interruptible = false; |
138 | bool no_wait = false; |
139 | int err; |
140 | |
141 | bo1 = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
142 | bo2 = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
143 | |
144 | ww_acquire_init(ctx: &ctx1, ww_class: &reservation_ww_class); |
145 | mutex_lock(&bo2->base.resv->lock.base); |
146 | |
147 | /* The deadlock will be caught by WW mutex, don't warn about it */ |
148 | lock_release(lock: &bo2->base.resv->lock.base.dep_map, ip: 1); |
149 | |
150 | bo2->base.resv->lock.ctx = &ctx2; |
151 | ctx2 = ctx1; |
152 | ctx2.stamp--; /* Make the context holding the lock younger */ |
153 | |
154 | err = ttm_bo_reserve(bo: bo1, interruptible, no_wait, ticket: &ctx1); |
155 | KUNIT_ASSERT_EQ(test, err, 0); |
156 | |
157 | err = ttm_bo_reserve(bo: bo2, interruptible, no_wait, ticket: &ctx1); |
158 | KUNIT_ASSERT_EQ(test, err, -EDEADLK); |
159 | |
160 | dma_resv_unlock(obj: bo1->base.resv); |
161 | ww_acquire_fini(ctx: &ctx1); |
162 | } |
163 | |
164 | #if IS_BUILTIN(CONFIG_DRM_TTM_KUNIT_TEST) |
165 | struct signal_timer { |
166 | struct timer_list timer; |
167 | struct ww_acquire_ctx *ctx; |
168 | }; |
169 | |
170 | static void signal_for_ttm_bo_reserve(struct timer_list *t) |
171 | { |
172 | struct signal_timer *s_timer = from_timer(s_timer, t, timer); |
173 | struct task_struct *task = s_timer->ctx->task; |
174 | |
175 | do_send_sig_info(SIGTERM, SEND_SIG_PRIV, task, PIDTYPE_PID); |
176 | } |
177 | |
178 | static int threaded_ttm_bo_reserve(void *arg) |
179 | { |
180 | struct ttm_buffer_object *bo = arg; |
181 | struct signal_timer s_timer; |
182 | struct ww_acquire_ctx ctx; |
183 | bool interruptible = true; |
184 | bool no_wait = false; |
185 | int err; |
186 | |
187 | ww_acquire_init(&ctx, &reservation_ww_class); |
188 | |
189 | /* Prepare a signal that will interrupt the reservation attempt */ |
190 | timer_setup_on_stack(&s_timer.timer, &signal_for_ttm_bo_reserve, 0); |
191 | s_timer.ctx = &ctx; |
192 | |
193 | mod_timer(&s_timer.timer, msecs_to_jiffies(100)); |
194 | |
195 | err = ttm_bo_reserve(bo, interruptible, no_wait, &ctx); |
196 | |
197 | timer_delete_sync(&s_timer.timer); |
198 | destroy_timer_on_stack(&s_timer.timer); |
199 | |
200 | ww_acquire_fini(&ctx); |
201 | |
202 | return err; |
203 | } |
204 | |
205 | static void ttm_bo_reserve_interrupted(struct kunit *test) |
206 | { |
207 | struct ttm_buffer_object *bo; |
208 | struct task_struct *task; |
209 | int err; |
210 | |
211 | bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE); |
212 | |
213 | task = kthread_create(threaded_ttm_bo_reserve, bo, "ttm-bo-reserve" ); |
214 | |
215 | if (IS_ERR(task)) |
216 | KUNIT_FAIL(test, "Couldn't create ttm bo reserve task\n" ); |
217 | |
218 | /* Take a lock so the threaded reserve has to wait */ |
219 | mutex_lock(&bo->base.resv->lock.base); |
220 | |
221 | wake_up_process(task); |
222 | msleep(20); |
223 | err = kthread_stop(task); |
224 | |
225 | mutex_unlock(&bo->base.resv->lock.base); |
226 | |
227 | KUNIT_ASSERT_EQ(test, err, -ERESTARTSYS); |
228 | } |
229 | #endif /* IS_BUILTIN(CONFIG_DRM_TTM_KUNIT_TEST) */ |
230 | |
231 | static void ttm_bo_unreserve_basic(struct kunit *test) |
232 | { |
233 | struct ttm_test_devices *priv = test->priv; |
234 | struct ttm_buffer_object *bo; |
235 | struct ttm_device *ttm_dev; |
236 | struct ttm_resource *res1, *res2; |
237 | struct ttm_place *place; |
238 | struct ttm_resource_manager *man; |
239 | unsigned int bo_prio = TTM_MAX_BO_PRIORITY - 1; |
240 | uint32_t mem_type = TTM_PL_SYSTEM; |
241 | int err; |
242 | |
243 | place = ttm_place_kunit_init(test, mem_type, flags: 0); |
244 | |
245 | ttm_dev = kunit_kzalloc(test, size: sizeof(*ttm_dev), GFP_KERNEL); |
246 | KUNIT_ASSERT_NOT_NULL(test, ttm_dev); |
247 | |
248 | err = ttm_device_kunit_init(priv, ttm: ttm_dev, use_dma_alloc: false, use_dma32: false); |
249 | KUNIT_ASSERT_EQ(test, err, 0); |
250 | priv->ttm_dev = ttm_dev; |
251 | |
252 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
253 | bo->priority = bo_prio; |
254 | |
255 | err = ttm_resource_alloc(bo, place, res: &res1); |
256 | KUNIT_ASSERT_EQ(test, err, 0); |
257 | |
258 | bo->resource = res1; |
259 | |
260 | /* Add a dummy resource to populate LRU */ |
261 | ttm_resource_alloc(bo, place, res: &res2); |
262 | |
263 | dma_resv_lock(obj: bo->base.resv, NULL); |
264 | ttm_bo_unreserve(bo); |
265 | |
266 | man = ttm_manager_type(bdev: priv->ttm_dev, mem_type); |
267 | KUNIT_ASSERT_EQ(test, |
268 | list_is_last(&res1->lru, &man->lru[bo->priority]), 1); |
269 | |
270 | ttm_resource_free(bo, res: &res2); |
271 | ttm_resource_free(bo, res: &res1); |
272 | } |
273 | |
274 | static void ttm_bo_unreserve_pinned(struct kunit *test) |
275 | { |
276 | struct ttm_test_devices *priv = test->priv; |
277 | struct ttm_buffer_object *bo; |
278 | struct ttm_device *ttm_dev; |
279 | struct ttm_resource *res1, *res2; |
280 | struct ttm_place *place; |
281 | uint32_t mem_type = TTM_PL_SYSTEM; |
282 | int err; |
283 | |
284 | ttm_dev = kunit_kzalloc(test, size: sizeof(*ttm_dev), GFP_KERNEL); |
285 | KUNIT_ASSERT_NOT_NULL(test, ttm_dev); |
286 | |
287 | err = ttm_device_kunit_init(priv, ttm: ttm_dev, use_dma_alloc: false, use_dma32: false); |
288 | KUNIT_ASSERT_EQ(test, err, 0); |
289 | priv->ttm_dev = ttm_dev; |
290 | |
291 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
292 | place = ttm_place_kunit_init(test, mem_type, flags: 0); |
293 | |
294 | dma_resv_lock(obj: bo->base.resv, NULL); |
295 | ttm_bo_pin(bo); |
296 | |
297 | err = ttm_resource_alloc(bo, place, res: &res1); |
298 | KUNIT_ASSERT_EQ(test, err, 0); |
299 | bo->resource = res1; |
300 | |
301 | /* Add a dummy resource to the pinned list */ |
302 | err = ttm_resource_alloc(bo, place, res: &res2); |
303 | KUNIT_ASSERT_EQ(test, err, 0); |
304 | KUNIT_ASSERT_EQ(test, |
305 | list_is_last(&res2->lru, &priv->ttm_dev->pinned), 1); |
306 | |
307 | ttm_bo_unreserve(bo); |
308 | KUNIT_ASSERT_EQ(test, |
309 | list_is_last(&res1->lru, &priv->ttm_dev->pinned), 1); |
310 | |
311 | ttm_resource_free(bo, res: &res1); |
312 | ttm_resource_free(bo, res: &res2); |
313 | } |
314 | |
315 | static void ttm_bo_unreserve_bulk(struct kunit *test) |
316 | { |
317 | struct ttm_test_devices *priv = test->priv; |
318 | struct ttm_lru_bulk_move lru_bulk_move; |
319 | struct ttm_lru_bulk_move_pos *pos; |
320 | struct ttm_buffer_object *bo1, *bo2; |
321 | struct ttm_resource *res1, *res2; |
322 | struct ttm_device *ttm_dev; |
323 | struct ttm_place *place; |
324 | uint32_t mem_type = TTM_PL_SYSTEM; |
325 | unsigned int bo_priority = 0; |
326 | int err; |
327 | |
328 | ttm_lru_bulk_move_init(bulk: &lru_bulk_move); |
329 | |
330 | place = ttm_place_kunit_init(test, mem_type, flags: 0); |
331 | |
332 | ttm_dev = kunit_kzalloc(test, size: sizeof(*ttm_dev), GFP_KERNEL); |
333 | KUNIT_ASSERT_NOT_NULL(test, ttm_dev); |
334 | |
335 | err = ttm_device_kunit_init(priv, ttm: ttm_dev, use_dma_alloc: false, use_dma32: false); |
336 | KUNIT_ASSERT_EQ(test, err, 0); |
337 | priv->ttm_dev = ttm_dev; |
338 | |
339 | bo1 = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
340 | bo2 = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
341 | |
342 | dma_resv_lock(obj: bo1->base.resv, NULL); |
343 | ttm_bo_set_bulk_move(bo: bo1, bulk: &lru_bulk_move); |
344 | dma_resv_unlock(obj: bo1->base.resv); |
345 | |
346 | err = ttm_resource_alloc(bo: bo1, place, res: &res1); |
347 | KUNIT_ASSERT_EQ(test, err, 0); |
348 | bo1->resource = res1; |
349 | |
350 | dma_resv_lock(obj: bo2->base.resv, NULL); |
351 | ttm_bo_set_bulk_move(bo: bo2, bulk: &lru_bulk_move); |
352 | dma_resv_unlock(obj: bo2->base.resv); |
353 | |
354 | err = ttm_resource_alloc(bo: bo2, place, res: &res2); |
355 | KUNIT_ASSERT_EQ(test, err, 0); |
356 | bo2->resource = res2; |
357 | |
358 | ttm_bo_reserve(bo: bo1, interruptible: false, no_wait: false, NULL); |
359 | ttm_bo_unreserve(bo: bo1); |
360 | |
361 | pos = &lru_bulk_move.pos[mem_type][bo_priority]; |
362 | KUNIT_ASSERT_PTR_EQ(test, res1, pos->last); |
363 | |
364 | ttm_resource_free(bo: bo1, res: &res1); |
365 | ttm_resource_free(bo: bo2, res: &res2); |
366 | } |
367 | |
368 | static void ttm_bo_put_basic(struct kunit *test) |
369 | { |
370 | struct ttm_test_devices *priv = test->priv; |
371 | struct ttm_buffer_object *bo; |
372 | struct ttm_resource *res; |
373 | struct ttm_device *ttm_dev; |
374 | struct ttm_place *place; |
375 | uint32_t mem_type = TTM_PL_SYSTEM; |
376 | int err; |
377 | |
378 | place = ttm_place_kunit_init(test, mem_type, flags: 0); |
379 | |
380 | ttm_dev = kunit_kzalloc(test, size: sizeof(*ttm_dev), GFP_KERNEL); |
381 | KUNIT_ASSERT_NOT_NULL(test, ttm_dev); |
382 | |
383 | err = ttm_device_kunit_init(priv, ttm: ttm_dev, use_dma_alloc: false, use_dma32: false); |
384 | KUNIT_ASSERT_EQ(test, err, 0); |
385 | priv->ttm_dev = ttm_dev; |
386 | |
387 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
388 | bo->type = ttm_bo_type_device; |
389 | |
390 | err = ttm_resource_alloc(bo, place, res: &res); |
391 | KUNIT_ASSERT_EQ(test, err, 0); |
392 | bo->resource = res; |
393 | |
394 | dma_resv_lock(obj: bo->base.resv, NULL); |
395 | err = ttm_tt_create(bo, zero_alloc: false); |
396 | dma_resv_unlock(obj: bo->base.resv); |
397 | KUNIT_EXPECT_EQ(test, err, 0); |
398 | |
399 | ttm_bo_put(bo); |
400 | } |
401 | |
402 | static const char *mock_name(struct dma_fence *f) |
403 | { |
404 | return "kunit-ttm-bo-put" ; |
405 | } |
406 | |
407 | static const struct dma_fence_ops mock_fence_ops = { |
408 | .get_driver_name = mock_name, |
409 | .get_timeline_name = mock_name, |
410 | }; |
411 | |
412 | static void ttm_bo_put_shared_resv(struct kunit *test) |
413 | { |
414 | struct ttm_test_devices *priv = test->priv; |
415 | struct ttm_buffer_object *bo; |
416 | struct dma_resv *external_resv; |
417 | struct dma_fence *fence; |
418 | /* A dummy DMA fence lock */ |
419 | spinlock_t fence_lock; |
420 | struct ttm_device *ttm_dev; |
421 | int err; |
422 | |
423 | ttm_dev = kunit_kzalloc(test, size: sizeof(*ttm_dev), GFP_KERNEL); |
424 | KUNIT_ASSERT_NOT_NULL(test, ttm_dev); |
425 | |
426 | err = ttm_device_kunit_init(priv, ttm: ttm_dev, use_dma_alloc: false, use_dma32: false); |
427 | KUNIT_ASSERT_EQ(test, err, 0); |
428 | priv->ttm_dev = ttm_dev; |
429 | |
430 | external_resv = kunit_kzalloc(test, size: sizeof(*ttm_dev), GFP_KERNEL); |
431 | KUNIT_ASSERT_NOT_NULL(test, external_resv); |
432 | |
433 | dma_resv_init(obj: external_resv); |
434 | |
435 | fence = kunit_kzalloc(test, size: sizeof(*fence), GFP_KERNEL); |
436 | KUNIT_ASSERT_NOT_NULL(test, fence); |
437 | |
438 | spin_lock_init(&fence_lock); |
439 | dma_fence_init(fence, ops: &mock_fence_ops, lock: &fence_lock, context: 0, seqno: 0); |
440 | |
441 | dma_resv_lock(obj: external_resv, NULL); |
442 | dma_resv_reserve_fences(obj: external_resv, num_fences: 1); |
443 | dma_resv_add_fence(obj: external_resv, fence, usage: DMA_RESV_USAGE_BOOKKEEP); |
444 | dma_resv_unlock(obj: external_resv); |
445 | |
446 | dma_fence_signal(fence); |
447 | |
448 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
449 | bo->type = ttm_bo_type_device; |
450 | bo->base.resv = external_resv; |
451 | |
452 | ttm_bo_put(bo); |
453 | } |
454 | |
455 | static void ttm_bo_pin_basic(struct kunit *test) |
456 | { |
457 | struct ttm_test_devices *priv = test->priv; |
458 | struct ttm_buffer_object *bo; |
459 | struct ttm_device *ttm_dev; |
460 | unsigned int no_pins = 3; |
461 | int err; |
462 | |
463 | ttm_dev = kunit_kzalloc(test, size: sizeof(*ttm_dev), GFP_KERNEL); |
464 | KUNIT_ASSERT_NOT_NULL(test, ttm_dev); |
465 | |
466 | err = ttm_device_kunit_init(priv, ttm: ttm_dev, use_dma_alloc: false, use_dma32: false); |
467 | KUNIT_ASSERT_EQ(test, err, 0); |
468 | priv->ttm_dev = ttm_dev; |
469 | |
470 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
471 | |
472 | for (int i = 0; i < no_pins; i++) { |
473 | dma_resv_lock(obj: bo->base.resv, NULL); |
474 | ttm_bo_pin(bo); |
475 | dma_resv_unlock(obj: bo->base.resv); |
476 | } |
477 | |
478 | KUNIT_ASSERT_EQ(test, bo->pin_count, no_pins); |
479 | } |
480 | |
481 | static void ttm_bo_pin_unpin_resource(struct kunit *test) |
482 | { |
483 | struct ttm_test_devices *priv = test->priv; |
484 | struct ttm_lru_bulk_move lru_bulk_move; |
485 | struct ttm_lru_bulk_move_pos *pos; |
486 | struct ttm_buffer_object *bo; |
487 | struct ttm_resource *res; |
488 | struct ttm_device *ttm_dev; |
489 | struct ttm_place *place; |
490 | uint32_t mem_type = TTM_PL_SYSTEM; |
491 | unsigned int bo_priority = 0; |
492 | int err; |
493 | |
494 | ttm_lru_bulk_move_init(bulk: &lru_bulk_move); |
495 | |
496 | place = ttm_place_kunit_init(test, mem_type, flags: 0); |
497 | |
498 | ttm_dev = kunit_kzalloc(test, size: sizeof(*ttm_dev), GFP_KERNEL); |
499 | KUNIT_ASSERT_NOT_NULL(test, ttm_dev); |
500 | |
501 | err = ttm_device_kunit_init(priv, ttm: ttm_dev, use_dma_alloc: false, use_dma32: false); |
502 | KUNIT_ASSERT_EQ(test, err, 0); |
503 | priv->ttm_dev = ttm_dev; |
504 | |
505 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
506 | |
507 | err = ttm_resource_alloc(bo, place, res: &res); |
508 | KUNIT_ASSERT_EQ(test, err, 0); |
509 | bo->resource = res; |
510 | |
511 | dma_resv_lock(obj: bo->base.resv, NULL); |
512 | ttm_bo_set_bulk_move(bo, bulk: &lru_bulk_move); |
513 | ttm_bo_pin(bo); |
514 | dma_resv_unlock(obj: bo->base.resv); |
515 | |
516 | pos = &lru_bulk_move.pos[mem_type][bo_priority]; |
517 | |
518 | KUNIT_ASSERT_EQ(test, bo->pin_count, 1); |
519 | KUNIT_ASSERT_NULL(test, pos->first); |
520 | KUNIT_ASSERT_NULL(test, pos->last); |
521 | |
522 | dma_resv_lock(obj: bo->base.resv, NULL); |
523 | ttm_bo_unpin(bo); |
524 | dma_resv_unlock(obj: bo->base.resv); |
525 | |
526 | KUNIT_ASSERT_PTR_EQ(test, res, pos->last); |
527 | KUNIT_ASSERT_EQ(test, bo->pin_count, 0); |
528 | |
529 | ttm_resource_free(bo, res: &res); |
530 | } |
531 | |
532 | static void ttm_bo_multiple_pin_one_unpin(struct kunit *test) |
533 | { |
534 | struct ttm_test_devices *priv = test->priv; |
535 | struct ttm_lru_bulk_move lru_bulk_move; |
536 | struct ttm_lru_bulk_move_pos *pos; |
537 | struct ttm_buffer_object *bo; |
538 | struct ttm_resource *res; |
539 | struct ttm_device *ttm_dev; |
540 | struct ttm_place *place; |
541 | uint32_t mem_type = TTM_PL_SYSTEM; |
542 | unsigned int bo_priority = 0; |
543 | int err; |
544 | |
545 | ttm_lru_bulk_move_init(bulk: &lru_bulk_move); |
546 | |
547 | place = ttm_place_kunit_init(test, mem_type, flags: 0); |
548 | |
549 | ttm_dev = kunit_kzalloc(test, size: sizeof(*ttm_dev), GFP_KERNEL); |
550 | KUNIT_ASSERT_NOT_NULL(test, ttm_dev); |
551 | |
552 | err = ttm_device_kunit_init(priv, ttm: ttm_dev, use_dma_alloc: false, use_dma32: false); |
553 | KUNIT_ASSERT_EQ(test, err, 0); |
554 | priv->ttm_dev = ttm_dev; |
555 | |
556 | bo = ttm_bo_kunit_init(test, devs: test->priv, BO_SIZE); |
557 | |
558 | err = ttm_resource_alloc(bo, place, res: &res); |
559 | KUNIT_ASSERT_EQ(test, err, 0); |
560 | bo->resource = res; |
561 | |
562 | dma_resv_lock(obj: bo->base.resv, NULL); |
563 | ttm_bo_set_bulk_move(bo, bulk: &lru_bulk_move); |
564 | |
565 | /* Multiple pins */ |
566 | ttm_bo_pin(bo); |
567 | ttm_bo_pin(bo); |
568 | |
569 | dma_resv_unlock(obj: bo->base.resv); |
570 | |
571 | pos = &lru_bulk_move.pos[mem_type][bo_priority]; |
572 | |
573 | KUNIT_ASSERT_EQ(test, bo->pin_count, 2); |
574 | KUNIT_ASSERT_NULL(test, pos->first); |
575 | KUNIT_ASSERT_NULL(test, pos->last); |
576 | |
577 | dma_resv_lock(obj: bo->base.resv, NULL); |
578 | ttm_bo_unpin(bo); |
579 | dma_resv_unlock(obj: bo->base.resv); |
580 | |
581 | KUNIT_ASSERT_EQ(test, bo->pin_count, 1); |
582 | KUNIT_ASSERT_NULL(test, pos->first); |
583 | KUNIT_ASSERT_NULL(test, pos->last); |
584 | |
585 | dma_resv_lock(obj: bo->base.resv, NULL); |
586 | ttm_bo_unpin(bo); |
587 | dma_resv_unlock(obj: bo->base.resv); |
588 | |
589 | ttm_resource_free(bo, res: &res); |
590 | } |
591 | |
592 | static struct kunit_case ttm_bo_test_cases[] = { |
593 | KUNIT_CASE_PARAM(ttm_bo_reserve_optimistic_no_ticket, |
594 | ttm_bo_reserve_gen_params), |
595 | KUNIT_CASE(ttm_bo_reserve_locked_no_sleep), |
596 | KUNIT_CASE(ttm_bo_reserve_no_wait_ticket), |
597 | KUNIT_CASE(ttm_bo_reserve_double_resv), |
598 | #if IS_BUILTIN(CONFIG_DRM_TTM_KUNIT_TEST) |
599 | KUNIT_CASE(ttm_bo_reserve_interrupted), |
600 | #endif |
601 | KUNIT_CASE(ttm_bo_reserve_deadlock), |
602 | KUNIT_CASE(ttm_bo_unreserve_basic), |
603 | KUNIT_CASE(ttm_bo_unreserve_pinned), |
604 | KUNIT_CASE(ttm_bo_unreserve_bulk), |
605 | KUNIT_CASE(ttm_bo_put_basic), |
606 | KUNIT_CASE(ttm_bo_put_shared_resv), |
607 | KUNIT_CASE(ttm_bo_pin_basic), |
608 | KUNIT_CASE(ttm_bo_pin_unpin_resource), |
609 | KUNIT_CASE(ttm_bo_multiple_pin_one_unpin), |
610 | {} |
611 | }; |
612 | |
613 | static struct kunit_suite ttm_bo_test_suite = { |
614 | .name = "ttm_bo" , |
615 | .init = ttm_test_devices_init, |
616 | .exit = ttm_test_devices_fini, |
617 | .test_cases = ttm_bo_test_cases, |
618 | }; |
619 | |
620 | kunit_test_suites(&ttm_bo_test_suite); |
621 | |
622 | MODULE_LICENSE("GPL" ); |
623 | |