xref: /linux/drivers/gpu/drm/scheduler/tests/tests_basic.c (revision 53e65974924ec3e66b2cdf71780f089b338fed33)
15a993507STvrtko Ursulin // SPDX-License-Identifier: GPL-2.0
25a993507STvrtko Ursulin /* Copyright (c) 2025 Valve Corporation */
35a993507STvrtko Ursulin 
45a993507STvrtko Ursulin #include "sched_tests.h"
55a993507STvrtko Ursulin 
65a993507STvrtko Ursulin /*
75a993507STvrtko Ursulin  * DRM scheduler basic tests should check the basic functional correctness of
85a993507STvrtko Ursulin  * the scheduler, including some very light smoke testing. More targeted tests,
95a993507STvrtko Ursulin  * for example focusing on testing specific bugs and other more complicated test
105a993507STvrtko Ursulin  * scenarios, should be implemented in separate source units.
115a993507STvrtko Ursulin  */
125a993507STvrtko Ursulin 
135a993507STvrtko Ursulin static int drm_sched_basic_init(struct kunit *test)
145a993507STvrtko Ursulin {
15*53e65974STvrtko Ursulin 	test->priv = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
165a993507STvrtko Ursulin 
175a993507STvrtko Ursulin 	return 0;
185a993507STvrtko Ursulin }
195a993507STvrtko Ursulin 
205a993507STvrtko Ursulin static void drm_sched_basic_exit(struct kunit *test)
215a993507STvrtko Ursulin {
225a993507STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
235a993507STvrtko Ursulin 
245a993507STvrtko Ursulin 	drm_mock_sched_fini(sched);
255a993507STvrtko Ursulin }
265a993507STvrtko Ursulin 
27*53e65974STvrtko Ursulin static int drm_sched_timeout_init(struct kunit *test)
28*53e65974STvrtko Ursulin {
29*53e65974STvrtko Ursulin 	test->priv = drm_mock_sched_new(test, HZ);
30*53e65974STvrtko Ursulin 
31*53e65974STvrtko Ursulin 	return 0;
32*53e65974STvrtko Ursulin }
33*53e65974STvrtko Ursulin 
345a993507STvrtko Ursulin static void drm_sched_basic_submit(struct kunit *test)
355a993507STvrtko Ursulin {
365a993507STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
375a993507STvrtko Ursulin 	struct drm_mock_sched_entity *entity;
385a993507STvrtko Ursulin 	struct drm_mock_sched_job *job;
395a993507STvrtko Ursulin 	unsigned int i;
405a993507STvrtko Ursulin 	bool done;
415a993507STvrtko Ursulin 
425a993507STvrtko Ursulin 	/*
435a993507STvrtko Ursulin 	 * Submit one job to the scheduler and verify that it gets scheduled
445a993507STvrtko Ursulin 	 * and completed only when the mock hw backend processes it.
455a993507STvrtko Ursulin 	 */
465a993507STvrtko Ursulin 
475a993507STvrtko Ursulin 	entity = drm_mock_sched_entity_new(test,
485a993507STvrtko Ursulin 					   DRM_SCHED_PRIORITY_NORMAL,
495a993507STvrtko Ursulin 					   sched);
505a993507STvrtko Ursulin 	job = drm_mock_sched_job_new(test, entity);
515a993507STvrtko Ursulin 
525a993507STvrtko Ursulin 	drm_mock_sched_job_submit(job);
535a993507STvrtko Ursulin 
545a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_scheduled(job, HZ);
555a993507STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
565a993507STvrtko Ursulin 
575a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ / 2);
585a993507STvrtko Ursulin 	KUNIT_ASSERT_FALSE(test, done);
595a993507STvrtko Ursulin 
605a993507STvrtko Ursulin 	i = drm_mock_sched_advance(sched, 1);
615a993507STvrtko Ursulin 	KUNIT_ASSERT_EQ(test, i, 1);
625a993507STvrtko Ursulin 
635a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ);
645a993507STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
655a993507STvrtko Ursulin 
665a993507STvrtko Ursulin 	drm_mock_sched_entity_free(entity);
675a993507STvrtko Ursulin }
685a993507STvrtko Ursulin 
695a993507STvrtko Ursulin struct drm_sched_basic_params {
705a993507STvrtko Ursulin 	const char *description;
715a993507STvrtko Ursulin 	unsigned int queue_depth;
725a993507STvrtko Ursulin 	unsigned int num_entities;
735a993507STvrtko Ursulin 	unsigned int job_us;
745a993507STvrtko Ursulin 	bool dep_chain;
755a993507STvrtko Ursulin };
765a993507STvrtko Ursulin 
775a993507STvrtko Ursulin static const struct drm_sched_basic_params drm_sched_basic_cases[] = {
785a993507STvrtko Ursulin 	{
795a993507STvrtko Ursulin 		.description = "A queue of jobs in a single entity",
805a993507STvrtko Ursulin 		.queue_depth = 100,
815a993507STvrtko Ursulin 		.job_us = 1000,
825a993507STvrtko Ursulin 		.num_entities = 1,
835a993507STvrtko Ursulin 	},
845a993507STvrtko Ursulin 	{
855a993507STvrtko Ursulin 		.description = "A chain of dependent jobs across multiple entities",
865a993507STvrtko Ursulin 		.queue_depth = 100,
875a993507STvrtko Ursulin 		.job_us = 1000,
885a993507STvrtko Ursulin 		.num_entities = 1,
895a993507STvrtko Ursulin 		.dep_chain = true,
905a993507STvrtko Ursulin 	},
915a993507STvrtko Ursulin 	{
925a993507STvrtko Ursulin 		.description = "Multiple independent job queues",
935a993507STvrtko Ursulin 		.queue_depth = 100,
945a993507STvrtko Ursulin 		.job_us = 1000,
955a993507STvrtko Ursulin 		.num_entities = 4,
965a993507STvrtko Ursulin 	},
975a993507STvrtko Ursulin 	{
985a993507STvrtko Ursulin 		.description = "Multiple inter-dependent job queues",
995a993507STvrtko Ursulin 		.queue_depth = 100,
1005a993507STvrtko Ursulin 		.job_us = 1000,
1015a993507STvrtko Ursulin 		.num_entities = 4,
1025a993507STvrtko Ursulin 		.dep_chain = true,
1035a993507STvrtko Ursulin 	},
1045a993507STvrtko Ursulin };
1055a993507STvrtko Ursulin 
1065a993507STvrtko Ursulin static void
1075a993507STvrtko Ursulin drm_sched_basic_desc(const struct drm_sched_basic_params *params, char *desc)
1085a993507STvrtko Ursulin {
1095a993507STvrtko Ursulin 	strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);
1105a993507STvrtko Ursulin }
1115a993507STvrtko Ursulin 
1125a993507STvrtko Ursulin KUNIT_ARRAY_PARAM(drm_sched_basic, drm_sched_basic_cases, drm_sched_basic_desc);
1135a993507STvrtko Ursulin 
1145a993507STvrtko Ursulin static void drm_sched_basic_test(struct kunit *test)
1155a993507STvrtko Ursulin {
1165a993507STvrtko Ursulin 	const struct drm_sched_basic_params *params = test->param_value;
1175a993507STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
1185a993507STvrtko Ursulin 	struct drm_mock_sched_job *job, *prev = NULL;
1195a993507STvrtko Ursulin 	struct drm_mock_sched_entity **entity;
1205a993507STvrtko Ursulin 	unsigned int i, cur_ent = 0;
1215a993507STvrtko Ursulin 	bool done;
1225a993507STvrtko Ursulin 
1235a993507STvrtko Ursulin 	entity = kunit_kcalloc(test, params->num_entities, sizeof(*entity),
1245a993507STvrtko Ursulin 			       GFP_KERNEL);
1255a993507STvrtko Ursulin 	KUNIT_ASSERT_NOT_NULL(test, entity);
1265a993507STvrtko Ursulin 
1275a993507STvrtko Ursulin 	for (i = 0; i < params->num_entities; i++)
1285a993507STvrtko Ursulin 		entity[i] = drm_mock_sched_entity_new(test,
1295a993507STvrtko Ursulin 						      DRM_SCHED_PRIORITY_NORMAL,
1305a993507STvrtko Ursulin 						      sched);
1315a993507STvrtko Ursulin 
1325a993507STvrtko Ursulin 	for (i = 0; i < params->queue_depth; i++) {
1335a993507STvrtko Ursulin 		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
1345a993507STvrtko Ursulin 		cur_ent %= params->num_entities;
1355a993507STvrtko Ursulin 		drm_mock_sched_job_set_duration_us(job, params->job_us);
1365a993507STvrtko Ursulin 		if (params->dep_chain && prev)
1375a993507STvrtko Ursulin 			drm_sched_job_add_dependency(&job->base,
1385a993507STvrtko Ursulin 						     dma_fence_get(&prev->base.s_fence->finished));
1395a993507STvrtko Ursulin 		drm_mock_sched_job_submit(job);
1405a993507STvrtko Ursulin 		prev = job;
1415a993507STvrtko Ursulin 	}
1425a993507STvrtko Ursulin 
1435a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ);
1445a993507STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
1455a993507STvrtko Ursulin 
1465a993507STvrtko Ursulin 	for (i = 0; i < params->num_entities; i++)
1475a993507STvrtko Ursulin 		drm_mock_sched_entity_free(entity[i]);
1485a993507STvrtko Ursulin }
1495a993507STvrtko Ursulin 
1505a993507STvrtko Ursulin static void drm_sched_basic_entity_cleanup(struct kunit *test)
1515a993507STvrtko Ursulin {
1525a993507STvrtko Ursulin 	struct drm_mock_sched_job *job, *mid, *prev = NULL;
1535a993507STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
1545a993507STvrtko Ursulin 	struct drm_mock_sched_entity *entity[4];
1555a993507STvrtko Ursulin 	const unsigned int qd = 100;
1565a993507STvrtko Ursulin 	unsigned int i, cur_ent = 0;
1575a993507STvrtko Ursulin 	bool done;
1585a993507STvrtko Ursulin 
1595a993507STvrtko Ursulin 	/*
1605a993507STvrtko Ursulin 	 * Submit a queue of jobs across different entities with an explicit
1615a993507STvrtko Ursulin 	 * chain of dependencies between them and trigger entity cleanup while
1625a993507STvrtko Ursulin 	 * the queue is still being processed.
1635a993507STvrtko Ursulin 	 */
1645a993507STvrtko Ursulin 
1655a993507STvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(entity); i++)
1665a993507STvrtko Ursulin 		entity[i] = drm_mock_sched_entity_new(test,
1675a993507STvrtko Ursulin 						      DRM_SCHED_PRIORITY_NORMAL,
1685a993507STvrtko Ursulin 						      sched);
1695a993507STvrtko Ursulin 
1705a993507STvrtko Ursulin 	for (i = 0; i < qd; i++) {
1715a993507STvrtko Ursulin 		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
1725a993507STvrtko Ursulin 		cur_ent %= ARRAY_SIZE(entity);
1735a993507STvrtko Ursulin 		drm_mock_sched_job_set_duration_us(job, 1000);
1745a993507STvrtko Ursulin 		if (prev)
1755a993507STvrtko Ursulin 			drm_sched_job_add_dependency(&job->base,
1765a993507STvrtko Ursulin 						     dma_fence_get(&prev->base.s_fence->finished));
1775a993507STvrtko Ursulin 		drm_mock_sched_job_submit(job);
1785a993507STvrtko Ursulin 		if (i == qd / 2)
1795a993507STvrtko Ursulin 			mid = job;
1805a993507STvrtko Ursulin 		prev = job;
1815a993507STvrtko Ursulin 	}
1825a993507STvrtko Ursulin 
1835a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(mid, HZ);
1845a993507STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
1855a993507STvrtko Ursulin 
1865a993507STvrtko Ursulin 	/* Exit with half of the queue still pending to be executed. */
1875a993507STvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(entity); i++)
1885a993507STvrtko Ursulin 		drm_mock_sched_entity_free(entity[i]);
1895a993507STvrtko Ursulin }
1905a993507STvrtko Ursulin 
1915a993507STvrtko Ursulin static struct kunit_case drm_sched_basic_tests[] = {
1925a993507STvrtko Ursulin 	KUNIT_CASE(drm_sched_basic_submit),
1935a993507STvrtko Ursulin 	KUNIT_CASE_PARAM(drm_sched_basic_test, drm_sched_basic_gen_params),
1945a993507STvrtko Ursulin 	KUNIT_CASE(drm_sched_basic_entity_cleanup),
1955a993507STvrtko Ursulin 	{}
1965a993507STvrtko Ursulin };
1975a993507STvrtko Ursulin 
1985a993507STvrtko Ursulin static struct kunit_suite drm_sched_basic = {
1995a993507STvrtko Ursulin 	.name = "drm_sched_basic_tests",
2005a993507STvrtko Ursulin 	.init = drm_sched_basic_init,
2015a993507STvrtko Ursulin 	.exit = drm_sched_basic_exit,
2025a993507STvrtko Ursulin 	.test_cases = drm_sched_basic_tests,
2035a993507STvrtko Ursulin };
2045a993507STvrtko Ursulin 
205*53e65974STvrtko Ursulin static void drm_sched_basic_timeout(struct kunit *test)
206*53e65974STvrtko Ursulin {
207*53e65974STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
208*53e65974STvrtko Ursulin 	struct drm_mock_sched_entity *entity;
209*53e65974STvrtko Ursulin 	struct drm_mock_sched_job *job;
210*53e65974STvrtko Ursulin 	bool done;
211*53e65974STvrtko Ursulin 
212*53e65974STvrtko Ursulin 	/*
213*53e65974STvrtko Ursulin 	 * Submit a single job against a scheduler with the timeout configured
214*53e65974STvrtko Ursulin 	 * and verify that the timeout handling will run if the backend fails
215*53e65974STvrtko Ursulin 	 * to complete it in time.
216*53e65974STvrtko Ursulin 	 */
217*53e65974STvrtko Ursulin 
218*53e65974STvrtko Ursulin 	entity = drm_mock_sched_entity_new(test,
219*53e65974STvrtko Ursulin 					   DRM_SCHED_PRIORITY_NORMAL,
220*53e65974STvrtko Ursulin 					   sched);
221*53e65974STvrtko Ursulin 	job = drm_mock_sched_job_new(test, entity);
222*53e65974STvrtko Ursulin 
223*53e65974STvrtko Ursulin 	drm_mock_sched_job_submit(job);
224*53e65974STvrtko Ursulin 
225*53e65974STvrtko Ursulin 	done = drm_mock_sched_job_wait_scheduled(job, HZ);
226*53e65974STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
227*53e65974STvrtko Ursulin 
228*53e65974STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ / 2);
229*53e65974STvrtko Ursulin 	KUNIT_ASSERT_FALSE(test, done);
230*53e65974STvrtko Ursulin 
231*53e65974STvrtko Ursulin 	KUNIT_ASSERT_EQ(test,
232*53e65974STvrtko Ursulin 			job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT,
233*53e65974STvrtko Ursulin 			0);
234*53e65974STvrtko Ursulin 
235*53e65974STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ);
236*53e65974STvrtko Ursulin 	KUNIT_ASSERT_FALSE(test, done);
237*53e65974STvrtko Ursulin 
238*53e65974STvrtko Ursulin 	KUNIT_ASSERT_EQ(test,
239*53e65974STvrtko Ursulin 			job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT,
240*53e65974STvrtko Ursulin 			DRM_MOCK_SCHED_JOB_TIMEDOUT);
241*53e65974STvrtko Ursulin 
242*53e65974STvrtko Ursulin 	drm_mock_sched_entity_free(entity);
243*53e65974STvrtko Ursulin }
244*53e65974STvrtko Ursulin 
245*53e65974STvrtko Ursulin static struct kunit_case drm_sched_timeout_tests[] = {
246*53e65974STvrtko Ursulin 	KUNIT_CASE(drm_sched_basic_timeout),
247*53e65974STvrtko Ursulin 	{}
248*53e65974STvrtko Ursulin };
249*53e65974STvrtko Ursulin 
250*53e65974STvrtko Ursulin static struct kunit_suite drm_sched_timeout = {
251*53e65974STvrtko Ursulin 	.name = "drm_sched_basic_timeout_tests",
252*53e65974STvrtko Ursulin 	.init = drm_sched_timeout_init,
253*53e65974STvrtko Ursulin 	.exit = drm_sched_basic_exit,
254*53e65974STvrtko Ursulin 	.test_cases = drm_sched_timeout_tests,
255*53e65974STvrtko Ursulin };
256*53e65974STvrtko Ursulin 
257*53e65974STvrtko Ursulin kunit_test_suites(&drm_sched_basic,
258*53e65974STvrtko Ursulin 		  &drm_sched_timeout);
259