xref: /linux/drivers/gpu/drm/scheduler/tests/tests_basic.c (revision 53e65974924ec3e66b2cdf71780f089b338fed33)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2025 Valve Corporation */
3 
4 #include "sched_tests.h"
5 
6 /*
7  * DRM scheduler basic tests should check the basic functional correctness of
8  * the scheduler, including some very light smoke testing. More targeted tests,
9  * for example focusing on testing specific bugs and other more complicated test
10  * scenarios, should be implemented in separate source units.
11  */
12 
13 static int drm_sched_basic_init(struct kunit *test)
14 {
15 	test->priv = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
16 
17 	return 0;
18 }
19 
20 static void drm_sched_basic_exit(struct kunit *test)
21 {
22 	struct drm_mock_scheduler *sched = test->priv;
23 
24 	drm_mock_sched_fini(sched);
25 }
26 
27 static int drm_sched_timeout_init(struct kunit *test)
28 {
29 	test->priv = drm_mock_sched_new(test, HZ);
30 
31 	return 0;
32 }
33 
34 static void drm_sched_basic_submit(struct kunit *test)
35 {
36 	struct drm_mock_scheduler *sched = test->priv;
37 	struct drm_mock_sched_entity *entity;
38 	struct drm_mock_sched_job *job;
39 	unsigned int i;
40 	bool done;
41 
42 	/*
43 	 * Submit one job to the scheduler and verify that it gets scheduled
44 	 * and completed only when the mock hw backend processes it.
45 	 */
46 
47 	entity = drm_mock_sched_entity_new(test,
48 					   DRM_SCHED_PRIORITY_NORMAL,
49 					   sched);
50 	job = drm_mock_sched_job_new(test, entity);
51 
52 	drm_mock_sched_job_submit(job);
53 
54 	done = drm_mock_sched_job_wait_scheduled(job, HZ);
55 	KUNIT_ASSERT_TRUE(test, done);
56 
57 	done = drm_mock_sched_job_wait_finished(job, HZ / 2);
58 	KUNIT_ASSERT_FALSE(test, done);
59 
60 	i = drm_mock_sched_advance(sched, 1);
61 	KUNIT_ASSERT_EQ(test, i, 1);
62 
63 	done = drm_mock_sched_job_wait_finished(job, HZ);
64 	KUNIT_ASSERT_TRUE(test, done);
65 
66 	drm_mock_sched_entity_free(entity);
67 }
68 
69 struct drm_sched_basic_params {
70 	const char *description;
71 	unsigned int queue_depth;
72 	unsigned int num_entities;
73 	unsigned int job_us;
74 	bool dep_chain;
75 };
76 
77 static const struct drm_sched_basic_params drm_sched_basic_cases[] = {
78 	{
79 		.description = "A queue of jobs in a single entity",
80 		.queue_depth = 100,
81 		.job_us = 1000,
82 		.num_entities = 1,
83 	},
84 	{
85 		.description = "A chain of dependent jobs across multiple entities",
86 		.queue_depth = 100,
87 		.job_us = 1000,
88 		.num_entities = 1,
89 		.dep_chain = true,
90 	},
91 	{
92 		.description = "Multiple independent job queues",
93 		.queue_depth = 100,
94 		.job_us = 1000,
95 		.num_entities = 4,
96 	},
97 	{
98 		.description = "Multiple inter-dependent job queues",
99 		.queue_depth = 100,
100 		.job_us = 1000,
101 		.num_entities = 4,
102 		.dep_chain = true,
103 	},
104 };
105 
106 static void
107 drm_sched_basic_desc(const struct drm_sched_basic_params *params, char *desc)
108 {
109 	strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);
110 }
111 
112 KUNIT_ARRAY_PARAM(drm_sched_basic, drm_sched_basic_cases, drm_sched_basic_desc);
113 
114 static void drm_sched_basic_test(struct kunit *test)
115 {
116 	const struct drm_sched_basic_params *params = test->param_value;
117 	struct drm_mock_scheduler *sched = test->priv;
118 	struct drm_mock_sched_job *job, *prev = NULL;
119 	struct drm_mock_sched_entity **entity;
120 	unsigned int i, cur_ent = 0;
121 	bool done;
122 
123 	entity = kunit_kcalloc(test, params->num_entities, sizeof(*entity),
124 			       GFP_KERNEL);
125 	KUNIT_ASSERT_NOT_NULL(test, entity);
126 
127 	for (i = 0; i < params->num_entities; i++)
128 		entity[i] = drm_mock_sched_entity_new(test,
129 						      DRM_SCHED_PRIORITY_NORMAL,
130 						      sched);
131 
132 	for (i = 0; i < params->queue_depth; i++) {
133 		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
134 		cur_ent %= params->num_entities;
135 		drm_mock_sched_job_set_duration_us(job, params->job_us);
136 		if (params->dep_chain && prev)
137 			drm_sched_job_add_dependency(&job->base,
138 						     dma_fence_get(&prev->base.s_fence->finished));
139 		drm_mock_sched_job_submit(job);
140 		prev = job;
141 	}
142 
143 	done = drm_mock_sched_job_wait_finished(job, HZ);
144 	KUNIT_ASSERT_TRUE(test, done);
145 
146 	for (i = 0; i < params->num_entities; i++)
147 		drm_mock_sched_entity_free(entity[i]);
148 }
149 
150 static void drm_sched_basic_entity_cleanup(struct kunit *test)
151 {
152 	struct drm_mock_sched_job *job, *mid, *prev = NULL;
153 	struct drm_mock_scheduler *sched = test->priv;
154 	struct drm_mock_sched_entity *entity[4];
155 	const unsigned int qd = 100;
156 	unsigned int i, cur_ent = 0;
157 	bool done;
158 
159 	/*
160 	 * Submit a queue of jobs across different entities with an explicit
161 	 * chain of dependencies between them and trigger entity cleanup while
162 	 * the queue is still being processed.
163 	 */
164 
165 	for (i = 0; i < ARRAY_SIZE(entity); i++)
166 		entity[i] = drm_mock_sched_entity_new(test,
167 						      DRM_SCHED_PRIORITY_NORMAL,
168 						      sched);
169 
170 	for (i = 0; i < qd; i++) {
171 		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
172 		cur_ent %= ARRAY_SIZE(entity);
173 		drm_mock_sched_job_set_duration_us(job, 1000);
174 		if (prev)
175 			drm_sched_job_add_dependency(&job->base,
176 						     dma_fence_get(&prev->base.s_fence->finished));
177 		drm_mock_sched_job_submit(job);
178 		if (i == qd / 2)
179 			mid = job;
180 		prev = job;
181 	}
182 
183 	done = drm_mock_sched_job_wait_finished(mid, HZ);
184 	KUNIT_ASSERT_TRUE(test, done);
185 
186 	/* Exit with half of the queue still pending to be executed. */
187 	for (i = 0; i < ARRAY_SIZE(entity); i++)
188 		drm_mock_sched_entity_free(entity[i]);
189 }
190 
191 static struct kunit_case drm_sched_basic_tests[] = {
192 	KUNIT_CASE(drm_sched_basic_submit),
193 	KUNIT_CASE_PARAM(drm_sched_basic_test, drm_sched_basic_gen_params),
194 	KUNIT_CASE(drm_sched_basic_entity_cleanup),
195 	{}
196 };
197 
198 static struct kunit_suite drm_sched_basic = {
199 	.name = "drm_sched_basic_tests",
200 	.init = drm_sched_basic_init,
201 	.exit = drm_sched_basic_exit,
202 	.test_cases = drm_sched_basic_tests,
203 };
204 
205 static void drm_sched_basic_timeout(struct kunit *test)
206 {
207 	struct drm_mock_scheduler *sched = test->priv;
208 	struct drm_mock_sched_entity *entity;
209 	struct drm_mock_sched_job *job;
210 	bool done;
211 
212 	/*
213 	 * Submit a single job against a scheduler with the timeout configured
214 	 * and verify that the timeout handling will run if the backend fails
215 	 * to complete it in time.
216 	 */
217 
218 	entity = drm_mock_sched_entity_new(test,
219 					   DRM_SCHED_PRIORITY_NORMAL,
220 					   sched);
221 	job = drm_mock_sched_job_new(test, entity);
222 
223 	drm_mock_sched_job_submit(job);
224 
225 	done = drm_mock_sched_job_wait_scheduled(job, HZ);
226 	KUNIT_ASSERT_TRUE(test, done);
227 
228 	done = drm_mock_sched_job_wait_finished(job, HZ / 2);
229 	KUNIT_ASSERT_FALSE(test, done);
230 
231 	KUNIT_ASSERT_EQ(test,
232 			job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT,
233 			0);
234 
235 	done = drm_mock_sched_job_wait_finished(job, HZ);
236 	KUNIT_ASSERT_FALSE(test, done);
237 
238 	KUNIT_ASSERT_EQ(test,
239 			job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT,
240 			DRM_MOCK_SCHED_JOB_TIMEDOUT);
241 
242 	drm_mock_sched_entity_free(entity);
243 }
244 
245 static struct kunit_case drm_sched_timeout_tests[] = {
246 	KUNIT_CASE(drm_sched_basic_timeout),
247 	{}
248 };
249 
250 static struct kunit_suite drm_sched_timeout = {
251 	.name = "drm_sched_basic_timeout_tests",
252 	.init = drm_sched_timeout_init,
253 	.exit = drm_sched_basic_exit,
254 	.test_cases = drm_sched_timeout_tests,
255 };
256 
257 kunit_test_suites(&drm_sched_basic,
258 		  &drm_sched_timeout);
259