File Index
Files and directories
Sign in
FILE

tests_basic.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/scheduler/tests/tests_basic.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (c) 2025 Valve Corporation */
  3
  4#include <linux/delay.h>
  5
  6#include "sched_tests.h"
  7
  8#define MOCK_TIMEOUT (HZ / 5)
  9
 10/*
 11 * DRM scheduler basic tests should check the basic functional correctness of
 12 * the scheduler, including some very light smoke testing. More targeted tests,
 13 * for example focusing on testing specific bugs and other more complicated test
 14 * scenarios, should be implemented in separate source units.
 15 */
 16
 17static int drm_sched_basic_init(struct kunit *test)
 18{
 19	test->priv = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
 20
 21	return 0;
 22}
 23
 24static void drm_sched_basic_exit(struct kunit *test)
 25{
 26	struct drm_mock_scheduler *sched = test->priv;
 27
 28	drm_mock_sched_fini(sched);
 29}
 30
 31static int drm_sched_timeout_init(struct kunit *test)
 32{
 33	test->priv = drm_mock_sched_new(test, MOCK_TIMEOUT);
 34
 35	return 0;
 36}
 37
 38static void drm_sched_basic_submit(struct kunit *test)
 39{
 40	struct drm_mock_scheduler *sched = test->priv;
 41	struct drm_mock_sched_entity *entity;
 42	struct drm_mock_sched_job *job;
 43	unsigned int i;
 44	bool done;
 45
 46	/*
 47	 * Submit one job to the scheduler and verify that it gets scheduled
 48	 * and completed only when the mock hw backend processes it.
 49	 */
 50
 51	entity = drm_mock_sched_entity_new(test,
 52					   DRM_SCHED_PRIORITY_NORMAL,
 53					   sched);
 54	job = drm_mock_sched_job_new(test, entity);
 55
 56	drm_mock_sched_job_submit(job);
 57
 58	done = drm_mock_sched_job_wait_scheduled(job, HZ);
 59	KUNIT_ASSERT_TRUE(test, done);
 60
 61	done = drm_mock_sched_job_wait_finished(job, HZ / 2);
 62	KUNIT_ASSERT_FALSE(test, done);
 63
 64	i = drm_mock_sched_advance(sched, 1);
 65	KUNIT_ASSERT_EQ(test, i, 1);
 66
 67	done = drm_mock_sched_job_wait_finished(job, HZ);
 68	KUNIT_ASSERT_TRUE(test, done);
 69
 70	drm_mock_sched_entity_free(entity);
 71}
 72
 73struct drm_sched_basic_params {
 74	const char *description;
 75	unsigned int queue_depth;
 76	unsigned int num_entities;
 77	unsigned int job_us;
 78	bool dep_chain;
 79};
 80
 81static const struct drm_sched_basic_params drm_sched_basic_cases[] = {
 82	{
 83		.description = "A queue of jobs in a single entity",
 84		.queue_depth = 100,
 85		.job_us = 1000,
 86		.num_entities = 1,
 87	},
 88	{
 89		.description = "A chain of dependent jobs across multiple entities",
 90		.queue_depth = 100,
 91		.job_us = 1000,
 92		.num_entities = 1,
 93		.dep_chain = true,
 94	},
 95	{
 96		.description = "Multiple independent job queues",
 97		.queue_depth = 100,
 98		.job_us = 1000,
 99		.num_entities = 4,
100	},
101	{
102		.description = "Multiple inter-dependent job queues",
103		.queue_depth = 100,
104		.job_us = 1000,
105		.num_entities = 4,
106		.dep_chain = true,
107	},
108};
109
110static void
111drm_sched_basic_desc(const struct drm_sched_basic_params *params, char *desc)
112{
113	strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);
114}
115
116KUNIT_ARRAY_PARAM(drm_sched_basic, drm_sched_basic_cases, drm_sched_basic_desc);
117
118static void drm_sched_basic_test(struct kunit *test)
119{
120	const struct drm_sched_basic_params *params = test->param_value;
121	struct drm_mock_scheduler *sched = test->priv;
122	struct drm_mock_sched_job *job, *prev = NULL;
123	struct drm_mock_sched_entity **entity;
124	unsigned int i, cur_ent = 0;
125	bool done;
126
127	entity = kunit_kcalloc(test, params->num_entities, sizeof(*entity),
128			       GFP_KERNEL);
129	KUNIT_ASSERT_NOT_NULL(test, entity);
130
131	for (i = 0; i < params->num_entities; i++)
132		entity[i] = drm_mock_sched_entity_new(test,
133						      DRM_SCHED_PRIORITY_NORMAL,
134						      sched);
135
136	for (i = 0; i < params->queue_depth; i++) {
137		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
138		cur_ent %= params->num_entities;
139		drm_mock_sched_job_set_duration_us(job, params->job_us);
140		if (params->dep_chain && prev)
141			drm_sched_job_add_dependency(&job->base,
142						     dma_fence_get(&prev->base.s_fence->finished));
143		drm_mock_sched_job_submit(job);
144		prev = job;
145	}
146
147	done = drm_mock_sched_job_wait_finished(job, HZ);
148	KUNIT_ASSERT_TRUE(test, done);
149
150	for (i = 0; i < params->num_entities; i++)
151		drm_mock_sched_entity_free(entity[i]);
152}
153
154static void drm_sched_basic_entity_cleanup(struct kunit *test)
155{
156	struct drm_mock_sched_job *job, *mid, *prev = NULL;
157	struct drm_mock_scheduler *sched = test->priv;
158	struct drm_mock_sched_entity *entity[4];
159	const unsigned int qd = 100;
160	unsigned int i, cur_ent = 0;
161	bool done;
162
163	/*
164	 * Submit a queue of jobs across different entities with an explicit
165	 * chain of dependencies between them and trigger entity cleanup while
166	 * the queue is still being processed.
167	 */
168
169	for (i = 0; i < ARRAY_SIZE(entity); i++)
170		entity[i] = drm_mock_sched_entity_new(test,
171						      DRM_SCHED_PRIORITY_NORMAL,
172						      sched);
173
174	for (i = 0; i < qd; i++) {
175		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
176		cur_ent %= ARRAY_SIZE(entity);
177		drm_mock_sched_job_set_duration_us(job, 1000);
178		if (prev)
179			drm_sched_job_add_dependency(&job->base,
180						     dma_fence_get(&prev->base.s_fence->finished));
181		drm_mock_sched_job_submit(job);
182		if (i == qd / 2)
183			mid = job;
184		prev = job;
185	}
186
187	done = drm_mock_sched_job_wait_finished(mid, HZ);
188	KUNIT_ASSERT_TRUE(test, done);
189
190	/* Exit with half of the queue still pending to be executed. */
191	for (i = 0; i < ARRAY_SIZE(entity); i++)
192		drm_mock_sched_entity_free(entity[i]);
193}
194
195static struct kunit_case drm_sched_basic_tests[] = {
196	KUNIT_CASE(drm_sched_basic_submit),
197	KUNIT_CASE_PARAM(drm_sched_basic_test, drm_sched_basic_gen_params),
198	KUNIT_CASE(drm_sched_basic_entity_cleanup),
199	{}
200};
201
202static struct kunit_suite drm_sched_basic = {
203	.name = "drm_sched_basic_tests",
204	.init = drm_sched_basic_init,
205	.exit = drm_sched_basic_exit,
206	.test_cases = drm_sched_basic_tests,
207};
208
209static void drm_sched_basic_cancel(struct kunit *test)
210{
211	struct drm_mock_sched_entity *entity;
212	struct drm_mock_scheduler *sched;
213	struct drm_mock_sched_job *job;
214	bool done;
215
216	/*
217	 * Check that drm_sched_fini() uses the cancel_job() callback to cancel
218	 * jobs that are still pending.
219	 */
220
221	sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
222	entity = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL,
223					   sched);
224
225	job = drm_mock_sched_job_new(test, entity);
226
227	drm_mock_sched_job_submit(job);
228
229	done = drm_mock_sched_job_wait_scheduled(job, HZ);
230	KUNIT_ASSERT_TRUE(test, done);
231
232	drm_mock_sched_entity_free(entity);
233	drm_mock_sched_fini(sched);
234
235	KUNIT_ASSERT_EQ(test, job->hw_fence.error, -ECANCELED);
236}
237
238static struct kunit_case drm_sched_cancel_tests[] = {
239	KUNIT_CASE(drm_sched_basic_cancel),
240	{}
241};
242
243static struct kunit_suite drm_sched_cancel = {
244	.name = "drm_sched_basic_cancel_tests",
245	.init = drm_sched_basic_init,
246	.exit = drm_sched_basic_exit,
247	.test_cases = drm_sched_cancel_tests,
248};
249
250static void drm_sched_basic_timeout(struct kunit *test)
251{
252	struct drm_mock_scheduler *sched = test->priv;
253	struct drm_mock_sched_entity *entity;
254	struct drm_mock_sched_job *job;
255	bool done;
256
257	/*
258	 * Submit a single job against a scheduler with the timeout configured
259	 * and verify that the timeout handling will run if the backend fails
260	 * to complete it in time.
261	 */
262
263	entity = drm_mock_sched_entity_new(test,
264					   DRM_SCHED_PRIORITY_NORMAL,
265					   sched);
266	job = drm_mock_sched_job_new(test, entity);
267
268	drm_mock_sched_job_submit(job);
269
270	done = drm_mock_sched_job_wait_scheduled(job, HZ);
271	KUNIT_ASSERT_TRUE(test, done);
272
273	done = drm_mock_sched_job_wait_finished(job, MOCK_TIMEOUT / 2);
274	KUNIT_ASSERT_FALSE(test, done);
275
276	KUNIT_ASSERT_EQ(test,
277			job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT,
278			0);
279
280	done = drm_mock_sched_job_wait_finished(job, MOCK_TIMEOUT);
281	KUNIT_ASSERT_FALSE(test, done);
282
283	KUNIT_ASSERT_EQ(test,
284			job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT,
285			DRM_MOCK_SCHED_JOB_TIMEDOUT);
286
287	drm_mock_sched_entity_free(entity);
288}
289
290static void drm_sched_skip_reset(struct kunit *test)
291{
292	struct drm_mock_scheduler *sched = test->priv;
293	struct drm_mock_sched_entity *entity;
294	struct drm_mock_sched_job *job;
295	unsigned int i;
296	bool done;
297
298	/*
299	 * Submit a single job against a scheduler with the timeout configured
300	 * and verify that if the job is still running, the timeout handler
301	 * will skip the reset and allow the job to complete.
302	 */
303
304	entity = drm_mock_sched_entity_new(test,
305					   DRM_SCHED_PRIORITY_NORMAL,
306					   sched);
307	job = drm_mock_sched_job_new(test, entity);
308
309	job->flags = DRM_MOCK_SCHED_JOB_DONT_RESET;
310
311	drm_mock_sched_job_submit(job);
312
313	done = drm_mock_sched_job_wait_scheduled(job, HZ);
314	KUNIT_ASSERT_TRUE(test, done);
315
316	done = drm_mock_sched_job_wait_finished(job, 2 * MOCK_TIMEOUT);
317	KUNIT_ASSERT_FALSE(test, done);
318
319	KUNIT_ASSERT_EQ(test,
320			job->flags & DRM_MOCK_SCHED_JOB_RESET_SKIPPED,
321			DRM_MOCK_SCHED_JOB_RESET_SKIPPED);
322
323	i = drm_mock_sched_advance(sched, 1);
324	KUNIT_ASSERT_EQ(test, i, 1);
325
326	done = drm_mock_sched_job_wait_finished(job, HZ);
327	KUNIT_ASSERT_TRUE(test, done);
328
329	drm_mock_sched_entity_free(entity);
330}
331
332static struct kunit_case drm_sched_timeout_tests[] = {
333	KUNIT_CASE(drm_sched_basic_timeout),
334	KUNIT_CASE(drm_sched_skip_reset),
335	{}
336};
337
338static struct kunit_suite drm_sched_timeout = {
339	.name = "drm_sched_basic_timeout_tests",
340	.init = drm_sched_timeout_init,
341	.exit = drm_sched_basic_exit,
342	.test_cases = drm_sched_timeout_tests,
343};
344
345static void drm_sched_priorities(struct kunit *test)
346{
347	struct drm_mock_sched_entity *entity[DRM_SCHED_PRIORITY_COUNT];
348	struct drm_mock_scheduler *sched = test->priv;
349	struct drm_mock_sched_job *job;
350	const unsigned int qd = 100;
351	unsigned int i, cur_ent = 0;
352	enum drm_sched_priority p;
353	bool done;
354
355	/*
356	 * Submit a bunch of jobs against entities configured with different
357	 * priorities.
358	 */
359
360	BUILD_BUG_ON(DRM_SCHED_PRIORITY_KERNEL > DRM_SCHED_PRIORITY_LOW);
361	BUILD_BUG_ON(ARRAY_SIZE(entity) != DRM_SCHED_PRIORITY_COUNT);
362
363	for (p = DRM_SCHED_PRIORITY_KERNEL; p <= DRM_SCHED_PRIORITY_LOW; p++)
364		entity[p] = drm_mock_sched_entity_new(test, p, sched);
365
366	for (i = 0; i < qd; i++) {
367		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
368		cur_ent %= ARRAY_SIZE(entity);
369		drm_mock_sched_job_set_duration_us(job, 1000);
370		drm_mock_sched_job_submit(job);
371	}
372
373	done = drm_mock_sched_job_wait_finished(job, HZ);
374	KUNIT_ASSERT_TRUE(test, done);
375
376	for (i = 0; i < ARRAY_SIZE(entity); i++)
377		drm_mock_sched_entity_free(entity[i]);
378}
379
380static void drm_sched_change_priority(struct kunit *test)
381{
382	struct drm_mock_sched_entity *entity[DRM_SCHED_PRIORITY_COUNT];
383	struct drm_mock_scheduler *sched = test->priv;
384	struct drm_mock_sched_job *job;
385	const unsigned int qd = 1000;
386	unsigned int i, cur_ent = 0;
387	enum drm_sched_priority p;
388
389	/*
390	 * Submit a bunch of jobs against entities configured with different
391	 * priorities and while waiting for them to complete, periodically keep
392	 * changing their priorities.
393	 *
394	 * We set up the queue-depth (qd) and job duration so the priority
395	 * changing loop has some time to interact with submissions to the
396	 * backend and job completions as they progress.
397	 */
398
399	for (p = DRM_SCHED_PRIORITY_KERNEL; p <= DRM_SCHED_PRIORITY_LOW; p++)
400		entity[p] = drm_mock_sched_entity_new(test, p, sched);
401
402	for (i = 0; i < qd; i++) {
403		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
404		cur_ent %= ARRAY_SIZE(entity);
405		drm_mock_sched_job_set_duration_us(job, 1000);
406		drm_mock_sched_job_submit(job);
407	}
408
409	do {
410		drm_sched_entity_set_priority(&entity[cur_ent]->base,
411					      (entity[cur_ent]->base.priority + 1) %
412					      DRM_SCHED_PRIORITY_COUNT);
413		cur_ent++;
414		cur_ent %= ARRAY_SIZE(entity);
415		usleep_range(200, 500);
416	} while (!drm_mock_sched_job_is_finished(job));
417
418	for (i = 0; i < ARRAY_SIZE(entity); i++)
419		drm_mock_sched_entity_free(entity[i]);
420}
421
422static struct kunit_case drm_sched_priority_tests[] = {
423	KUNIT_CASE(drm_sched_priorities),
424	KUNIT_CASE(drm_sched_change_priority),
425	{}
426};
427
428static struct kunit_suite drm_sched_priority = {
429	.name = "drm_sched_basic_priority_tests",
430	.init = drm_sched_basic_init,
431	.exit = drm_sched_basic_exit,
432	.test_cases = drm_sched_priority_tests,
433};
434
435static void drm_sched_test_modify_sched(struct kunit *test)
436{
437	unsigned int i, cur_ent = 0, cur_sched = 0;
438	struct drm_mock_sched_entity *entity[13];
439	struct drm_mock_scheduler *sched[3];
440	struct drm_mock_sched_job *job;
441	const unsigned int qd = 1000;
442
443	/*
444	 * Submit a bunch of jobs against entities configured with different
445	 * schedulers and while waiting for them to complete, periodically keep
446	 * changing schedulers associated with each entity.
447	 *
448	 * We set up the queue-depth (qd) and job duration so the sched modify
449	 * loop has some time to interact with submissions to the backend and
450	 * job completions as they progress.
451	 *
452	 * For the number of schedulers and entities we use primes in order to
453	 * perturb the entity->sched assignments with less of a regular pattern.
454	 */
455
456	for (i = 0; i < ARRAY_SIZE(sched); i++)
457		sched[i] = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
458
459	for (i = 0; i < ARRAY_SIZE(entity); i++)
460		entity[i] = drm_mock_sched_entity_new(test,
461						      DRM_SCHED_PRIORITY_NORMAL,
462						      sched[i % ARRAY_SIZE(sched)]);
463
464	for (i = 0; i < qd; i++) {
465		job = drm_mock_sched_job_new(test, entity[cur_ent++]);
466		cur_ent %= ARRAY_SIZE(entity);
467		drm_mock_sched_job_set_duration_us(job, 1000);
468		drm_mock_sched_job_submit(job);
469	}
470
471	do {
472		struct drm_gpu_scheduler *modify;
473
474		usleep_range(200, 500);
475		cur_ent++;
476		cur_ent %= ARRAY_SIZE(entity);
477		cur_sched++;
478		cur_sched %= ARRAY_SIZE(sched);
479		modify = &sched[cur_sched]->base;
480		drm_sched_entity_modify_sched(&entity[cur_ent]->base, &modify,
481					      1);
482	} while (!drm_mock_sched_job_is_finished(job));
483
484	for (i = 0; i < ARRAY_SIZE(entity); i++)
485		drm_mock_sched_entity_free(entity[i]);
486
487	for (i = 0; i < ARRAY_SIZE(sched); i++)
488		drm_mock_sched_fini(sched[i]);
489}
490
491static struct kunit_case drm_sched_modify_sched_tests[] = {
492	KUNIT_CASE(drm_sched_test_modify_sched),
493	{}
494};
495
496static struct kunit_suite drm_sched_modify_sched = {
497	.name = "drm_sched_basic_modify_sched_tests",
498	.test_cases = drm_sched_modify_sched_tests,
499};
500
501static void drm_sched_test_credits(struct kunit *test)
502{
503	struct drm_mock_sched_entity *entity;
504	struct drm_mock_scheduler *sched;
505	struct drm_mock_sched_job *job[2];
506	bool done;
507	int i;
508
509	/*
510	 * Check that the configured credit limit is respected.
511	 */
512
513	sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
514	sched->base.credit_limit = 1;
515
516	entity = drm_mock_sched_entity_new(test,
517					   DRM_SCHED_PRIORITY_NORMAL,
518					   sched);
519
520	job[0] = drm_mock_sched_job_new(test, entity);
521	job[1] = drm_mock_sched_job_new(test, entity);
522
523	drm_mock_sched_job_submit(job[0]);
524	drm_mock_sched_job_submit(job[1]);
525
526	done = drm_mock_sched_job_wait_scheduled(job[0], HZ);
527	KUNIT_ASSERT_TRUE(test, done);
528
529	done = drm_mock_sched_job_wait_scheduled(job[1], HZ);
530	KUNIT_ASSERT_FALSE(test, done);
531
532	i = drm_mock_sched_advance(sched, 1);
533	KUNIT_ASSERT_EQ(test, i, 1);
534
535	done = drm_mock_sched_job_wait_scheduled(job[1], HZ);
536	KUNIT_ASSERT_TRUE(test, done);
537
538	i = drm_mock_sched_advance(sched, 1);
539	KUNIT_ASSERT_EQ(test, i, 1);
540
541	done = drm_mock_sched_job_wait_finished(job[1], HZ);
542	KUNIT_ASSERT_TRUE(test, done);
543
544	drm_mock_sched_entity_free(entity);
545	drm_mock_sched_fini(sched);
546}
547
548static struct kunit_case drm_sched_credits_tests[] = {
549	KUNIT_CASE(drm_sched_test_credits),
550	{}
551};
552
553static struct kunit_suite drm_sched_credits = {
554	.name = "drm_sched_basic_credits_tests",
555	.test_cases = drm_sched_credits_tests,
556};
557
558kunit_test_suites(&drm_sched_basic,
559		  &drm_sched_timeout,
560		  &drm_sched_cancel,
561		  &drm_sched_priority,
562		  &drm_sched_modify_sched,
563		  &drm_sched_credits);
~/ Read-only file preview.