brintos

brintos / linux-shallow public Read only

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