xref: /linux/drivers/gpu/drm/scheduler/tests/tests_basic.c (revision 909bda2206a698be88d0351910446932a9843d58)
15a993507STvrtko Ursulin // SPDX-License-Identifier: GPL-2.0
25a993507STvrtko Ursulin /* Copyright (c) 2025 Valve Corporation */
35a993507STvrtko Ursulin 
47b765cdaSTvrtko Ursulin #include <linux/delay.h>
57b765cdaSTvrtko Ursulin 
65a993507STvrtko Ursulin #include "sched_tests.h"
75a993507STvrtko Ursulin 
85a993507STvrtko Ursulin /*
95a993507STvrtko Ursulin  * DRM scheduler basic tests should check the basic functional correctness of
105a993507STvrtko Ursulin  * the scheduler, including some very light smoke testing. More targeted tests,
115a993507STvrtko Ursulin  * for example focusing on testing specific bugs and other more complicated test
125a993507STvrtko Ursulin  * scenarios, should be implemented in separate source units.
135a993507STvrtko Ursulin  */
145a993507STvrtko Ursulin 
155a993507STvrtko Ursulin static int drm_sched_basic_init(struct kunit *test)
165a993507STvrtko Ursulin {
1753e65974STvrtko Ursulin 	test->priv = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
185a993507STvrtko Ursulin 
195a993507STvrtko Ursulin 	return 0;
205a993507STvrtko Ursulin }
215a993507STvrtko Ursulin 
225a993507STvrtko Ursulin static void drm_sched_basic_exit(struct kunit *test)
235a993507STvrtko Ursulin {
245a993507STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
255a993507STvrtko Ursulin 
265a993507STvrtko Ursulin 	drm_mock_sched_fini(sched);
275a993507STvrtko Ursulin }
285a993507STvrtko Ursulin 
2953e65974STvrtko Ursulin static int drm_sched_timeout_init(struct kunit *test)
3053e65974STvrtko Ursulin {
3153e65974STvrtko Ursulin 	test->priv = drm_mock_sched_new(test, HZ);
3253e65974STvrtko Ursulin 
3353e65974STvrtko Ursulin 	return 0;
3453e65974STvrtko Ursulin }
3553e65974STvrtko Ursulin 
365a993507STvrtko Ursulin static void drm_sched_basic_submit(struct kunit *test)
375a993507STvrtko Ursulin {
385a993507STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
395a993507STvrtko Ursulin 	struct drm_mock_sched_entity *entity;
405a993507STvrtko Ursulin 	struct drm_mock_sched_job *job;
415a993507STvrtko Ursulin 	unsigned int i;
425a993507STvrtko Ursulin 	bool done;
435a993507STvrtko Ursulin 
445a993507STvrtko Ursulin 	/*
455a993507STvrtko Ursulin 	 * Submit one job to the scheduler and verify that it gets scheduled
465a993507STvrtko Ursulin 	 * and completed only when the mock hw backend processes it.
475a993507STvrtko Ursulin 	 */
485a993507STvrtko Ursulin 
495a993507STvrtko Ursulin 	entity = drm_mock_sched_entity_new(test,
505a993507STvrtko Ursulin 					   DRM_SCHED_PRIORITY_NORMAL,
515a993507STvrtko Ursulin 					   sched);
525a993507STvrtko Ursulin 	job = drm_mock_sched_job_new(test, entity);
535a993507STvrtko Ursulin 
545a993507STvrtko Ursulin 	drm_mock_sched_job_submit(job);
555a993507STvrtko Ursulin 
565a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_scheduled(job, HZ);
575a993507STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
585a993507STvrtko Ursulin 
595a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ / 2);
605a993507STvrtko Ursulin 	KUNIT_ASSERT_FALSE(test, done);
615a993507STvrtko Ursulin 
625a993507STvrtko Ursulin 	i = drm_mock_sched_advance(sched, 1);
635a993507STvrtko Ursulin 	KUNIT_ASSERT_EQ(test, i, 1);
645a993507STvrtko Ursulin 
655a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ);
665a993507STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
675a993507STvrtko Ursulin 
685a993507STvrtko Ursulin 	drm_mock_sched_entity_free(entity);
695a993507STvrtko Ursulin }
705a993507STvrtko Ursulin 
715a993507STvrtko Ursulin struct drm_sched_basic_params {
725a993507STvrtko Ursulin 	const char *description;
735a993507STvrtko Ursulin 	unsigned int queue_depth;
745a993507STvrtko Ursulin 	unsigned int num_entities;
755a993507STvrtko Ursulin 	unsigned int job_us;
765a993507STvrtko Ursulin 	bool dep_chain;
775a993507STvrtko Ursulin };
785a993507STvrtko Ursulin 
795a993507STvrtko Ursulin static const struct drm_sched_basic_params drm_sched_basic_cases[] = {
805a993507STvrtko Ursulin 	{
815a993507STvrtko Ursulin 		.description = "A queue of jobs in a single entity",
825a993507STvrtko Ursulin 		.queue_depth = 100,
835a993507STvrtko Ursulin 		.job_us = 1000,
845a993507STvrtko Ursulin 		.num_entities = 1,
855a993507STvrtko Ursulin 	},
865a993507STvrtko Ursulin 	{
875a993507STvrtko Ursulin 		.description = "A chain of dependent jobs across multiple entities",
885a993507STvrtko Ursulin 		.queue_depth = 100,
895a993507STvrtko Ursulin 		.job_us = 1000,
905a993507STvrtko Ursulin 		.num_entities = 1,
915a993507STvrtko Ursulin 		.dep_chain = true,
925a993507STvrtko Ursulin 	},
935a993507STvrtko Ursulin 	{
945a993507STvrtko Ursulin 		.description = "Multiple independent job queues",
955a993507STvrtko Ursulin 		.queue_depth = 100,
965a993507STvrtko Ursulin 		.job_us = 1000,
975a993507STvrtko Ursulin 		.num_entities = 4,
985a993507STvrtko Ursulin 	},
995a993507STvrtko Ursulin 	{
1005a993507STvrtko Ursulin 		.description = "Multiple inter-dependent job queues",
1015a993507STvrtko Ursulin 		.queue_depth = 100,
1025a993507STvrtko Ursulin 		.job_us = 1000,
1035a993507STvrtko Ursulin 		.num_entities = 4,
1045a993507STvrtko Ursulin 		.dep_chain = true,
1055a993507STvrtko Ursulin 	},
1065a993507STvrtko Ursulin };
1075a993507STvrtko Ursulin 
1085a993507STvrtko Ursulin static void
1095a993507STvrtko Ursulin drm_sched_basic_desc(const struct drm_sched_basic_params *params, char *desc)
1105a993507STvrtko Ursulin {
1115a993507STvrtko Ursulin 	strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);
1125a993507STvrtko Ursulin }
1135a993507STvrtko Ursulin 
1145a993507STvrtko Ursulin KUNIT_ARRAY_PARAM(drm_sched_basic, drm_sched_basic_cases, drm_sched_basic_desc);
1155a993507STvrtko Ursulin 
1165a993507STvrtko Ursulin static void drm_sched_basic_test(struct kunit *test)
1175a993507STvrtko Ursulin {
1185a993507STvrtko Ursulin 	const struct drm_sched_basic_params *params = test->param_value;
1195a993507STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
1205a993507STvrtko Ursulin 	struct drm_mock_sched_job *job, *prev = NULL;
1215a993507STvrtko Ursulin 	struct drm_mock_sched_entity **entity;
1225a993507STvrtko Ursulin 	unsigned int i, cur_ent = 0;
1235a993507STvrtko Ursulin 	bool done;
1245a993507STvrtko Ursulin 
1255a993507STvrtko Ursulin 	entity = kunit_kcalloc(test, params->num_entities, sizeof(*entity),
1265a993507STvrtko Ursulin 			       GFP_KERNEL);
1275a993507STvrtko Ursulin 	KUNIT_ASSERT_NOT_NULL(test, entity);
1285a993507STvrtko Ursulin 
1295a993507STvrtko Ursulin 	for (i = 0; i < params->num_entities; i++)
1305a993507STvrtko Ursulin 		entity[i] = drm_mock_sched_entity_new(test,
1315a993507STvrtko Ursulin 						      DRM_SCHED_PRIORITY_NORMAL,
1325a993507STvrtko Ursulin 						      sched);
1335a993507STvrtko Ursulin 
1345a993507STvrtko Ursulin 	for (i = 0; i < params->queue_depth; i++) {
1355a993507STvrtko Ursulin 		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
1365a993507STvrtko Ursulin 		cur_ent %= params->num_entities;
1375a993507STvrtko Ursulin 		drm_mock_sched_job_set_duration_us(job, params->job_us);
1385a993507STvrtko Ursulin 		if (params->dep_chain && prev)
1395a993507STvrtko Ursulin 			drm_sched_job_add_dependency(&job->base,
1405a993507STvrtko Ursulin 						     dma_fence_get(&prev->base.s_fence->finished));
1415a993507STvrtko Ursulin 		drm_mock_sched_job_submit(job);
1425a993507STvrtko Ursulin 		prev = job;
1435a993507STvrtko Ursulin 	}
1445a993507STvrtko Ursulin 
1455a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ);
1465a993507STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
1475a993507STvrtko Ursulin 
1485a993507STvrtko Ursulin 	for (i = 0; i < params->num_entities; i++)
1495a993507STvrtko Ursulin 		drm_mock_sched_entity_free(entity[i]);
1505a993507STvrtko Ursulin }
1515a993507STvrtko Ursulin 
1525a993507STvrtko Ursulin static void drm_sched_basic_entity_cleanup(struct kunit *test)
1535a993507STvrtko Ursulin {
1545a993507STvrtko Ursulin 	struct drm_mock_sched_job *job, *mid, *prev = NULL;
1555a993507STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
1565a993507STvrtko Ursulin 	struct drm_mock_sched_entity *entity[4];
1575a993507STvrtko Ursulin 	const unsigned int qd = 100;
1585a993507STvrtko Ursulin 	unsigned int i, cur_ent = 0;
1595a993507STvrtko Ursulin 	bool done;
1605a993507STvrtko Ursulin 
1615a993507STvrtko Ursulin 	/*
1625a993507STvrtko Ursulin 	 * Submit a queue of jobs across different entities with an explicit
1635a993507STvrtko Ursulin 	 * chain of dependencies between them and trigger entity cleanup while
1645a993507STvrtko Ursulin 	 * the queue is still being processed.
1655a993507STvrtko Ursulin 	 */
1665a993507STvrtko Ursulin 
1675a993507STvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(entity); i++)
1685a993507STvrtko Ursulin 		entity[i] = drm_mock_sched_entity_new(test,
1695a993507STvrtko Ursulin 						      DRM_SCHED_PRIORITY_NORMAL,
1705a993507STvrtko Ursulin 						      sched);
1715a993507STvrtko Ursulin 
1725a993507STvrtko Ursulin 	for (i = 0; i < qd; i++) {
1735a993507STvrtko Ursulin 		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
1745a993507STvrtko Ursulin 		cur_ent %= ARRAY_SIZE(entity);
1755a993507STvrtko Ursulin 		drm_mock_sched_job_set_duration_us(job, 1000);
1765a993507STvrtko Ursulin 		if (prev)
1775a993507STvrtko Ursulin 			drm_sched_job_add_dependency(&job->base,
1785a993507STvrtko Ursulin 						     dma_fence_get(&prev->base.s_fence->finished));
1795a993507STvrtko Ursulin 		drm_mock_sched_job_submit(job);
1805a993507STvrtko Ursulin 		if (i == qd / 2)
1815a993507STvrtko Ursulin 			mid = job;
1825a993507STvrtko Ursulin 		prev = job;
1835a993507STvrtko Ursulin 	}
1845a993507STvrtko Ursulin 
1855a993507STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(mid, HZ);
1865a993507STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
1875a993507STvrtko Ursulin 
1885a993507STvrtko Ursulin 	/* Exit with half of the queue still pending to be executed. */
1895a993507STvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(entity); i++)
1905a993507STvrtko Ursulin 		drm_mock_sched_entity_free(entity[i]);
1915a993507STvrtko Ursulin }
1925a993507STvrtko Ursulin 
1935a993507STvrtko Ursulin static struct kunit_case drm_sched_basic_tests[] = {
1945a993507STvrtko Ursulin 	KUNIT_CASE(drm_sched_basic_submit),
1955a993507STvrtko Ursulin 	KUNIT_CASE_PARAM(drm_sched_basic_test, drm_sched_basic_gen_params),
1965a993507STvrtko Ursulin 	KUNIT_CASE(drm_sched_basic_entity_cleanup),
1975a993507STvrtko Ursulin 	{}
1985a993507STvrtko Ursulin };
1995a993507STvrtko Ursulin 
2005a993507STvrtko Ursulin static struct kunit_suite drm_sched_basic = {
2015a993507STvrtko Ursulin 	.name = "drm_sched_basic_tests",
2025a993507STvrtko Ursulin 	.init = drm_sched_basic_init,
2035a993507STvrtko Ursulin 	.exit = drm_sched_basic_exit,
2045a993507STvrtko Ursulin 	.test_cases = drm_sched_basic_tests,
2055a993507STvrtko Ursulin };
2065a993507STvrtko Ursulin 
20753e65974STvrtko Ursulin static void drm_sched_basic_timeout(struct kunit *test)
20853e65974STvrtko Ursulin {
20953e65974STvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
21053e65974STvrtko Ursulin 	struct drm_mock_sched_entity *entity;
21153e65974STvrtko Ursulin 	struct drm_mock_sched_job *job;
21253e65974STvrtko Ursulin 	bool done;
21353e65974STvrtko Ursulin 
21453e65974STvrtko Ursulin 	/*
21553e65974STvrtko Ursulin 	 * Submit a single job against a scheduler with the timeout configured
21653e65974STvrtko Ursulin 	 * and verify that the timeout handling will run if the backend fails
21753e65974STvrtko Ursulin 	 * to complete it in time.
21853e65974STvrtko Ursulin 	 */
21953e65974STvrtko Ursulin 
22053e65974STvrtko Ursulin 	entity = drm_mock_sched_entity_new(test,
22153e65974STvrtko Ursulin 					   DRM_SCHED_PRIORITY_NORMAL,
22253e65974STvrtko Ursulin 					   sched);
22353e65974STvrtko Ursulin 	job = drm_mock_sched_job_new(test, entity);
22453e65974STvrtko Ursulin 
22553e65974STvrtko Ursulin 	drm_mock_sched_job_submit(job);
22653e65974STvrtko Ursulin 
22753e65974STvrtko Ursulin 	done = drm_mock_sched_job_wait_scheduled(job, HZ);
22853e65974STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
22953e65974STvrtko Ursulin 
23053e65974STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ / 2);
23153e65974STvrtko Ursulin 	KUNIT_ASSERT_FALSE(test, done);
23253e65974STvrtko Ursulin 
23353e65974STvrtko Ursulin 	KUNIT_ASSERT_EQ(test,
23453e65974STvrtko Ursulin 			job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT,
23553e65974STvrtko Ursulin 			0);
23653e65974STvrtko Ursulin 
23753e65974STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ);
23853e65974STvrtko Ursulin 	KUNIT_ASSERT_FALSE(test, done);
23953e65974STvrtko Ursulin 
24053e65974STvrtko Ursulin 	KUNIT_ASSERT_EQ(test,
24153e65974STvrtko Ursulin 			job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT,
24253e65974STvrtko Ursulin 			DRM_MOCK_SCHED_JOB_TIMEDOUT);
24353e65974STvrtko Ursulin 
24453e65974STvrtko Ursulin 	drm_mock_sched_entity_free(entity);
24553e65974STvrtko Ursulin }
24653e65974STvrtko Ursulin 
24753e65974STvrtko Ursulin static struct kunit_case drm_sched_timeout_tests[] = {
24853e65974STvrtko Ursulin 	KUNIT_CASE(drm_sched_basic_timeout),
24953e65974STvrtko Ursulin 	{}
25053e65974STvrtko Ursulin };
25153e65974STvrtko Ursulin 
25253e65974STvrtko Ursulin static struct kunit_suite drm_sched_timeout = {
25353e65974STvrtko Ursulin 	.name = "drm_sched_basic_timeout_tests",
25453e65974STvrtko Ursulin 	.init = drm_sched_timeout_init,
25553e65974STvrtko Ursulin 	.exit = drm_sched_basic_exit,
25653e65974STvrtko Ursulin 	.test_cases = drm_sched_timeout_tests,
25753e65974STvrtko Ursulin };
25853e65974STvrtko Ursulin 
2597b765cdaSTvrtko Ursulin static void drm_sched_priorities(struct kunit *test)
2607b765cdaSTvrtko Ursulin {
2617b765cdaSTvrtko Ursulin 	struct drm_mock_sched_entity *entity[DRM_SCHED_PRIORITY_COUNT];
2627b765cdaSTvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
2637b765cdaSTvrtko Ursulin 	struct drm_mock_sched_job *job;
2647b765cdaSTvrtko Ursulin 	const unsigned int qd = 100;
2657b765cdaSTvrtko Ursulin 	unsigned int i, cur_ent = 0;
2667b765cdaSTvrtko Ursulin 	enum drm_sched_priority p;
2677b765cdaSTvrtko Ursulin 	bool done;
2687b765cdaSTvrtko Ursulin 
2697b765cdaSTvrtko Ursulin 	/*
2707b765cdaSTvrtko Ursulin 	 * Submit a bunch of jobs against entities configured with different
2717b765cdaSTvrtko Ursulin 	 * priorities.
2727b765cdaSTvrtko Ursulin 	 */
2737b765cdaSTvrtko Ursulin 
2747b765cdaSTvrtko Ursulin 	BUILD_BUG_ON(DRM_SCHED_PRIORITY_KERNEL > DRM_SCHED_PRIORITY_LOW);
2757b765cdaSTvrtko Ursulin 	BUILD_BUG_ON(ARRAY_SIZE(entity) != DRM_SCHED_PRIORITY_COUNT);
2767b765cdaSTvrtko Ursulin 
2777b765cdaSTvrtko Ursulin 	for (p = DRM_SCHED_PRIORITY_KERNEL; p <= DRM_SCHED_PRIORITY_LOW; p++)
2787b765cdaSTvrtko Ursulin 		entity[p] = drm_mock_sched_entity_new(test, p, sched);
2797b765cdaSTvrtko Ursulin 
2807b765cdaSTvrtko Ursulin 	for (i = 0; i < qd; i++) {
2817b765cdaSTvrtko Ursulin 		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
2827b765cdaSTvrtko Ursulin 		cur_ent %= ARRAY_SIZE(entity);
2837b765cdaSTvrtko Ursulin 		drm_mock_sched_job_set_duration_us(job, 1000);
2847b765cdaSTvrtko Ursulin 		drm_mock_sched_job_submit(job);
2857b765cdaSTvrtko Ursulin 	}
2867b765cdaSTvrtko Ursulin 
2877b765cdaSTvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job, HZ);
2887b765cdaSTvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
2897b765cdaSTvrtko Ursulin 
2907b765cdaSTvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(entity); i++)
2917b765cdaSTvrtko Ursulin 		drm_mock_sched_entity_free(entity[i]);
2927b765cdaSTvrtko Ursulin }
2937b765cdaSTvrtko Ursulin 
2947b765cdaSTvrtko Ursulin static void drm_sched_change_priority(struct kunit *test)
2957b765cdaSTvrtko Ursulin {
2967b765cdaSTvrtko Ursulin 	struct drm_mock_sched_entity *entity[DRM_SCHED_PRIORITY_COUNT];
2977b765cdaSTvrtko Ursulin 	struct drm_mock_scheduler *sched = test->priv;
2987b765cdaSTvrtko Ursulin 	struct drm_mock_sched_job *job;
2997b765cdaSTvrtko Ursulin 	const unsigned int qd = 1000;
3007b765cdaSTvrtko Ursulin 	unsigned int i, cur_ent = 0;
3017b765cdaSTvrtko Ursulin 	enum drm_sched_priority p;
3027b765cdaSTvrtko Ursulin 
3037b765cdaSTvrtko Ursulin 	/*
3047b765cdaSTvrtko Ursulin 	 * Submit a bunch of jobs against entities configured with different
3057b765cdaSTvrtko Ursulin 	 * priorities and while waiting for them to complete, periodically keep
3067b765cdaSTvrtko Ursulin 	 * changing their priorities.
3077b765cdaSTvrtko Ursulin 	 *
3087b765cdaSTvrtko Ursulin 	 * We set up the queue-depth (qd) and job duration so the priority
3097b765cdaSTvrtko Ursulin 	 * changing loop has some time to interact with submissions to the
3107b765cdaSTvrtko Ursulin 	 * backend and job completions as they progress.
3117b765cdaSTvrtko Ursulin 	 */
3127b765cdaSTvrtko Ursulin 
3137b765cdaSTvrtko Ursulin 	for (p = DRM_SCHED_PRIORITY_KERNEL; p <= DRM_SCHED_PRIORITY_LOW; p++)
3147b765cdaSTvrtko Ursulin 		entity[p] = drm_mock_sched_entity_new(test, p, sched);
3157b765cdaSTvrtko Ursulin 
3167b765cdaSTvrtko Ursulin 	for (i = 0; i < qd; i++) {
3177b765cdaSTvrtko Ursulin 		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
3187b765cdaSTvrtko Ursulin 		cur_ent %= ARRAY_SIZE(entity);
3197b765cdaSTvrtko Ursulin 		drm_mock_sched_job_set_duration_us(job, 1000);
3207b765cdaSTvrtko Ursulin 		drm_mock_sched_job_submit(job);
3217b765cdaSTvrtko Ursulin 	}
3227b765cdaSTvrtko Ursulin 
3237b765cdaSTvrtko Ursulin 	do {
3247b765cdaSTvrtko Ursulin 		drm_sched_entity_set_priority(&entity[cur_ent]->base,
3257b765cdaSTvrtko Ursulin 					      (entity[cur_ent]->base.priority + 1) %
3267b765cdaSTvrtko Ursulin 					      DRM_SCHED_PRIORITY_COUNT);
3277b765cdaSTvrtko Ursulin 		cur_ent++;
3287b765cdaSTvrtko Ursulin 		cur_ent %= ARRAY_SIZE(entity);
3297b765cdaSTvrtko Ursulin 		usleep_range(200, 500);
3307b765cdaSTvrtko Ursulin 	} while (!drm_mock_sched_job_is_finished(job));
3317b765cdaSTvrtko Ursulin 
3327b765cdaSTvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(entity); i++)
3337b765cdaSTvrtko Ursulin 		drm_mock_sched_entity_free(entity[i]);
3347b765cdaSTvrtko Ursulin }
3357b765cdaSTvrtko Ursulin 
3367b765cdaSTvrtko Ursulin static struct kunit_case drm_sched_priority_tests[] = {
3377b765cdaSTvrtko Ursulin 	KUNIT_CASE(drm_sched_priorities),
3387b765cdaSTvrtko Ursulin 	KUNIT_CASE(drm_sched_change_priority),
3397b765cdaSTvrtko Ursulin 	{}
3407b765cdaSTvrtko Ursulin };
3417b765cdaSTvrtko Ursulin 
3427b765cdaSTvrtko Ursulin static struct kunit_suite drm_sched_priority = {
3437b765cdaSTvrtko Ursulin 	.name = "drm_sched_basic_priority_tests",
3447b765cdaSTvrtko Ursulin 	.init = drm_sched_basic_init,
3457b765cdaSTvrtko Ursulin 	.exit = drm_sched_basic_exit,
3467b765cdaSTvrtko Ursulin 	.test_cases = drm_sched_priority_tests,
3477b765cdaSTvrtko Ursulin };
3487b765cdaSTvrtko Ursulin 
349c85fc5dbSTvrtko Ursulin static void drm_sched_test_modify_sched(struct kunit *test)
350c85fc5dbSTvrtko Ursulin {
351c85fc5dbSTvrtko Ursulin 	unsigned int i, cur_ent = 0, cur_sched = 0;
352c85fc5dbSTvrtko Ursulin 	struct drm_mock_sched_entity *entity[13];
353c85fc5dbSTvrtko Ursulin 	struct drm_mock_scheduler *sched[3];
354c85fc5dbSTvrtko Ursulin 	struct drm_mock_sched_job *job;
355c85fc5dbSTvrtko Ursulin 	const unsigned int qd = 1000;
356c85fc5dbSTvrtko Ursulin 
357c85fc5dbSTvrtko Ursulin 	/*
358c85fc5dbSTvrtko Ursulin 	 * Submit a bunch of jobs against entities configured with different
359c85fc5dbSTvrtko Ursulin 	 * schedulers and while waiting for them to complete, periodically keep
360c85fc5dbSTvrtko Ursulin 	 * changing schedulers associated with each entity.
361c85fc5dbSTvrtko Ursulin 	 *
362c85fc5dbSTvrtko Ursulin 	 * We set up the queue-depth (qd) and job duration so the sched modify
363c85fc5dbSTvrtko Ursulin 	 * loop has some time to interact with submissions to the backend and
364c85fc5dbSTvrtko Ursulin 	 * job completions as they progress.
365c85fc5dbSTvrtko Ursulin 	 *
366c85fc5dbSTvrtko Ursulin 	 * For the number of schedulers and entities we use primes in order to
367c85fc5dbSTvrtko Ursulin 	 * perturb the entity->sched assignments with less of a regular pattern.
368c85fc5dbSTvrtko Ursulin 	 */
369c85fc5dbSTvrtko Ursulin 
370c85fc5dbSTvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(sched); i++)
371c85fc5dbSTvrtko Ursulin 		sched[i] = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
372c85fc5dbSTvrtko Ursulin 
373c85fc5dbSTvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(entity); i++)
374c85fc5dbSTvrtko Ursulin 		entity[i] = drm_mock_sched_entity_new(test,
375c85fc5dbSTvrtko Ursulin 						      DRM_SCHED_PRIORITY_NORMAL,
376c85fc5dbSTvrtko Ursulin 						      sched[i % ARRAY_SIZE(sched)]);
377c85fc5dbSTvrtko Ursulin 
378c85fc5dbSTvrtko Ursulin 	for (i = 0; i < qd; i++) {
379c85fc5dbSTvrtko Ursulin 		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
380c85fc5dbSTvrtko Ursulin 		cur_ent %= ARRAY_SIZE(entity);
381c85fc5dbSTvrtko Ursulin 		drm_mock_sched_job_set_duration_us(job, 1000);
382c85fc5dbSTvrtko Ursulin 		drm_mock_sched_job_submit(job);
383c85fc5dbSTvrtko Ursulin 	}
384c85fc5dbSTvrtko Ursulin 
385c85fc5dbSTvrtko Ursulin 	do {
386c85fc5dbSTvrtko Ursulin 		struct drm_gpu_scheduler *modify;
387c85fc5dbSTvrtko Ursulin 
388c85fc5dbSTvrtko Ursulin 		usleep_range(200, 500);
389c85fc5dbSTvrtko Ursulin 		cur_ent++;
390c85fc5dbSTvrtko Ursulin 		cur_ent %= ARRAY_SIZE(entity);
391c85fc5dbSTvrtko Ursulin 		cur_sched++;
392c85fc5dbSTvrtko Ursulin 		cur_sched %= ARRAY_SIZE(sched);
393c85fc5dbSTvrtko Ursulin 		modify = &sched[cur_sched]->base;
394c85fc5dbSTvrtko Ursulin 		drm_sched_entity_modify_sched(&entity[cur_ent]->base, &modify,
395c85fc5dbSTvrtko Ursulin 					      1);
396c85fc5dbSTvrtko Ursulin 	} while (!drm_mock_sched_job_is_finished(job));
397c85fc5dbSTvrtko Ursulin 
398c85fc5dbSTvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(entity); i++)
399c85fc5dbSTvrtko Ursulin 		drm_mock_sched_entity_free(entity[i]);
400c85fc5dbSTvrtko Ursulin 
401c85fc5dbSTvrtko Ursulin 	for (i = 0; i < ARRAY_SIZE(sched); i++)
402c85fc5dbSTvrtko Ursulin 		drm_mock_sched_fini(sched[i]);
403c85fc5dbSTvrtko Ursulin }
404c85fc5dbSTvrtko Ursulin 
405c85fc5dbSTvrtko Ursulin static struct kunit_case drm_sched_modify_sched_tests[] = {
406c85fc5dbSTvrtko Ursulin 	KUNIT_CASE(drm_sched_test_modify_sched),
407c85fc5dbSTvrtko Ursulin 	{}
408c85fc5dbSTvrtko Ursulin };
409c85fc5dbSTvrtko Ursulin 
410c85fc5dbSTvrtko Ursulin static struct kunit_suite drm_sched_modify_sched = {
411c85fc5dbSTvrtko Ursulin 	.name = "drm_sched_basic_modify_sched_tests",
412c85fc5dbSTvrtko Ursulin 	.test_cases = drm_sched_modify_sched_tests,
413c85fc5dbSTvrtko Ursulin };
414c85fc5dbSTvrtko Ursulin 
415*909bda22STvrtko Ursulin static void drm_sched_test_credits(struct kunit *test)
416*909bda22STvrtko Ursulin {
417*909bda22STvrtko Ursulin 	struct drm_mock_sched_entity *entity;
418*909bda22STvrtko Ursulin 	struct drm_mock_scheduler *sched;
419*909bda22STvrtko Ursulin 	struct drm_mock_sched_job *job[2];
420*909bda22STvrtko Ursulin 	bool done;
421*909bda22STvrtko Ursulin 	int i;
422*909bda22STvrtko Ursulin 
423*909bda22STvrtko Ursulin 	/*
424*909bda22STvrtko Ursulin 	 * Check that the configured credit limit is respected.
425*909bda22STvrtko Ursulin 	 */
426*909bda22STvrtko Ursulin 
427*909bda22STvrtko Ursulin 	sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
428*909bda22STvrtko Ursulin 	sched->base.credit_limit = 1;
429*909bda22STvrtko Ursulin 
430*909bda22STvrtko Ursulin 	entity = drm_mock_sched_entity_new(test,
431*909bda22STvrtko Ursulin 					   DRM_SCHED_PRIORITY_NORMAL,
432*909bda22STvrtko Ursulin 					   sched);
433*909bda22STvrtko Ursulin 
434*909bda22STvrtko Ursulin 	job[0] = drm_mock_sched_job_new(test, entity);
435*909bda22STvrtko Ursulin 	job[1] = drm_mock_sched_job_new(test, entity);
436*909bda22STvrtko Ursulin 
437*909bda22STvrtko Ursulin 	drm_mock_sched_job_submit(job[0]);
438*909bda22STvrtko Ursulin 	drm_mock_sched_job_submit(job[1]);
439*909bda22STvrtko Ursulin 
440*909bda22STvrtko Ursulin 	done = drm_mock_sched_job_wait_scheduled(job[0], HZ);
441*909bda22STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
442*909bda22STvrtko Ursulin 
443*909bda22STvrtko Ursulin 	done = drm_mock_sched_job_wait_scheduled(job[1], HZ);
444*909bda22STvrtko Ursulin 	KUNIT_ASSERT_FALSE(test, done);
445*909bda22STvrtko Ursulin 
446*909bda22STvrtko Ursulin 	i = drm_mock_sched_advance(sched, 1);
447*909bda22STvrtko Ursulin 	KUNIT_ASSERT_EQ(test, i, 1);
448*909bda22STvrtko Ursulin 
449*909bda22STvrtko Ursulin 	done = drm_mock_sched_job_wait_scheduled(job[1], HZ);
450*909bda22STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
451*909bda22STvrtko Ursulin 
452*909bda22STvrtko Ursulin 	i = drm_mock_sched_advance(sched, 1);
453*909bda22STvrtko Ursulin 	KUNIT_ASSERT_EQ(test, i, 1);
454*909bda22STvrtko Ursulin 
455*909bda22STvrtko Ursulin 	done = drm_mock_sched_job_wait_finished(job[1], HZ);
456*909bda22STvrtko Ursulin 	KUNIT_ASSERT_TRUE(test, done);
457*909bda22STvrtko Ursulin 
458*909bda22STvrtko Ursulin 	drm_mock_sched_entity_free(entity);
459*909bda22STvrtko Ursulin 	drm_mock_sched_fini(sched);
460*909bda22STvrtko Ursulin }
461*909bda22STvrtko Ursulin 
462*909bda22STvrtko Ursulin static struct kunit_case drm_sched_credits_tests[] = {
463*909bda22STvrtko Ursulin 	KUNIT_CASE(drm_sched_test_credits),
464*909bda22STvrtko Ursulin 	{}
465*909bda22STvrtko Ursulin };
466*909bda22STvrtko Ursulin 
467*909bda22STvrtko Ursulin static struct kunit_suite drm_sched_credits = {
468*909bda22STvrtko Ursulin 	.name = "drm_sched_basic_credits_tests",
469*909bda22STvrtko Ursulin 	.test_cases = drm_sched_credits_tests,
470*909bda22STvrtko Ursulin };
471*909bda22STvrtko Ursulin 
47253e65974STvrtko Ursulin kunit_test_suites(&drm_sched_basic,
4737b765cdaSTvrtko Ursulin 		  &drm_sched_timeout,
474c85fc5dbSTvrtko Ursulin 		  &drm_sched_priority,
475*909bda22STvrtko Ursulin 		  &drm_sched_modify_sched,
476*909bda22STvrtko Ursulin 		  &drm_sched_credits);
477