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 89b9b5a36SMaíra Canal #define MOCK_TIMEOUT (HZ / 5) 99b9b5a36SMaíra Canal 105a993507STvrtko Ursulin /* 115a993507STvrtko Ursulin * DRM scheduler basic tests should check the basic functional correctness of 125a993507STvrtko Ursulin * the scheduler, including some very light smoke testing. More targeted tests, 135a993507STvrtko Ursulin * for example focusing on testing specific bugs and other more complicated test 145a993507STvrtko Ursulin * scenarios, should be implemented in separate source units. 155a993507STvrtko Ursulin */ 165a993507STvrtko Ursulin 175a993507STvrtko Ursulin static int drm_sched_basic_init(struct kunit *test) 185a993507STvrtko Ursulin { 1953e65974STvrtko Ursulin test->priv = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT); 205a993507STvrtko Ursulin 215a993507STvrtko Ursulin return 0; 225a993507STvrtko Ursulin } 235a993507STvrtko Ursulin 245a993507STvrtko Ursulin static void drm_sched_basic_exit(struct kunit *test) 255a993507STvrtko Ursulin { 265a993507STvrtko Ursulin struct drm_mock_scheduler *sched = test->priv; 275a993507STvrtko Ursulin 285a993507STvrtko Ursulin drm_mock_sched_fini(sched); 295a993507STvrtko Ursulin } 305a993507STvrtko Ursulin 3153e65974STvrtko Ursulin static int drm_sched_timeout_init(struct kunit *test) 3253e65974STvrtko Ursulin { 339b9b5a36SMaíra Canal test->priv = drm_mock_sched_new(test, MOCK_TIMEOUT); 3453e65974STvrtko Ursulin 3553e65974STvrtko Ursulin return 0; 3653e65974STvrtko Ursulin } 3753e65974STvrtko Ursulin 385a993507STvrtko Ursulin static void drm_sched_basic_submit(struct kunit *test) 395a993507STvrtko Ursulin { 405a993507STvrtko Ursulin struct drm_mock_scheduler *sched = test->priv; 415a993507STvrtko Ursulin struct drm_mock_sched_entity *entity; 425a993507STvrtko Ursulin struct drm_mock_sched_job *job; 435a993507STvrtko Ursulin unsigned int i; 445a993507STvrtko Ursulin bool done; 455a993507STvrtko Ursulin 465a993507STvrtko Ursulin /* 475a993507STvrtko Ursulin * Submit one job to the scheduler and verify that it gets scheduled 485a993507STvrtko Ursulin * and completed only when the mock hw backend processes it. 495a993507STvrtko Ursulin */ 505a993507STvrtko Ursulin 515a993507STvrtko Ursulin entity = drm_mock_sched_entity_new(test, 525a993507STvrtko Ursulin DRM_SCHED_PRIORITY_NORMAL, 535a993507STvrtko Ursulin sched); 545a993507STvrtko Ursulin job = drm_mock_sched_job_new(test, entity); 555a993507STvrtko Ursulin 565a993507STvrtko Ursulin drm_mock_sched_job_submit(job); 575a993507STvrtko Ursulin 585a993507STvrtko Ursulin done = drm_mock_sched_job_wait_scheduled(job, HZ); 595a993507STvrtko Ursulin KUNIT_ASSERT_TRUE(test, done); 605a993507STvrtko Ursulin 615a993507STvrtko Ursulin done = drm_mock_sched_job_wait_finished(job, HZ / 2); 625a993507STvrtko Ursulin KUNIT_ASSERT_FALSE(test, done); 635a993507STvrtko Ursulin 645a993507STvrtko Ursulin i = drm_mock_sched_advance(sched, 1); 655a993507STvrtko Ursulin KUNIT_ASSERT_EQ(test, i, 1); 665a993507STvrtko Ursulin 675a993507STvrtko Ursulin done = drm_mock_sched_job_wait_finished(job, HZ); 685a993507STvrtko Ursulin KUNIT_ASSERT_TRUE(test, done); 695a993507STvrtko Ursulin 705a993507STvrtko Ursulin drm_mock_sched_entity_free(entity); 715a993507STvrtko Ursulin } 725a993507STvrtko Ursulin 735a993507STvrtko Ursulin struct drm_sched_basic_params { 745a993507STvrtko Ursulin const char *description; 755a993507STvrtko Ursulin unsigned int queue_depth; 765a993507STvrtko Ursulin unsigned int num_entities; 775a993507STvrtko Ursulin unsigned int job_us; 785a993507STvrtko Ursulin bool dep_chain; 795a993507STvrtko Ursulin }; 805a993507STvrtko Ursulin 815a993507STvrtko Ursulin static const struct drm_sched_basic_params drm_sched_basic_cases[] = { 825a993507STvrtko Ursulin { 835a993507STvrtko Ursulin .description = "A queue of jobs in a single entity", 845a993507STvrtko Ursulin .queue_depth = 100, 855a993507STvrtko Ursulin .job_us = 1000, 865a993507STvrtko Ursulin .num_entities = 1, 875a993507STvrtko Ursulin }, 885a993507STvrtko Ursulin { 895a993507STvrtko Ursulin .description = "A chain of dependent jobs across multiple entities", 905a993507STvrtko Ursulin .queue_depth = 100, 915a993507STvrtko Ursulin .job_us = 1000, 925a993507STvrtko Ursulin .num_entities = 1, 935a993507STvrtko Ursulin .dep_chain = true, 945a993507STvrtko Ursulin }, 955a993507STvrtko Ursulin { 965a993507STvrtko Ursulin .description = "Multiple independent job queues", 975a993507STvrtko Ursulin .queue_depth = 100, 985a993507STvrtko Ursulin .job_us = 1000, 995a993507STvrtko Ursulin .num_entities = 4, 1005a993507STvrtko Ursulin }, 1015a993507STvrtko Ursulin { 1025a993507STvrtko Ursulin .description = "Multiple inter-dependent job queues", 1035a993507STvrtko Ursulin .queue_depth = 100, 1045a993507STvrtko Ursulin .job_us = 1000, 1055a993507STvrtko Ursulin .num_entities = 4, 1065a993507STvrtko Ursulin .dep_chain = true, 1075a993507STvrtko Ursulin }, 1085a993507STvrtko Ursulin }; 1095a993507STvrtko Ursulin 1105a993507STvrtko Ursulin static void 1115a993507STvrtko Ursulin drm_sched_basic_desc(const struct drm_sched_basic_params *params, char *desc) 1125a993507STvrtko Ursulin { 1135a993507STvrtko Ursulin strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE); 1145a993507STvrtko Ursulin } 1155a993507STvrtko Ursulin 1165a993507STvrtko Ursulin KUNIT_ARRAY_PARAM(drm_sched_basic, drm_sched_basic_cases, drm_sched_basic_desc); 1175a993507STvrtko Ursulin 1185a993507STvrtko Ursulin static void drm_sched_basic_test(struct kunit *test) 1195a993507STvrtko Ursulin { 1205a993507STvrtko Ursulin const struct drm_sched_basic_params *params = test->param_value; 1215a993507STvrtko Ursulin struct drm_mock_scheduler *sched = test->priv; 1225a993507STvrtko Ursulin struct drm_mock_sched_job *job, *prev = NULL; 1235a993507STvrtko Ursulin struct drm_mock_sched_entity **entity; 1245a993507STvrtko Ursulin unsigned int i, cur_ent = 0; 1255a993507STvrtko Ursulin bool done; 1265a993507STvrtko Ursulin 1275a993507STvrtko Ursulin entity = kunit_kcalloc(test, params->num_entities, sizeof(*entity), 1285a993507STvrtko Ursulin GFP_KERNEL); 1295a993507STvrtko Ursulin KUNIT_ASSERT_NOT_NULL(test, entity); 1305a993507STvrtko Ursulin 1315a993507STvrtko Ursulin for (i = 0; i < params->num_entities; i++) 1325a993507STvrtko Ursulin entity[i] = drm_mock_sched_entity_new(test, 1335a993507STvrtko Ursulin DRM_SCHED_PRIORITY_NORMAL, 1345a993507STvrtko Ursulin sched); 1355a993507STvrtko Ursulin 1365a993507STvrtko Ursulin for (i = 0; i < params->queue_depth; i++) { 1375a993507STvrtko Ursulin job = drm_mock_sched_job_new(test, entity[cur_ent++]); 1385a993507STvrtko Ursulin cur_ent %= params->num_entities; 1395a993507STvrtko Ursulin drm_mock_sched_job_set_duration_us(job, params->job_us); 1405a993507STvrtko Ursulin if (params->dep_chain && prev) 1415a993507STvrtko Ursulin drm_sched_job_add_dependency(&job->base, 1425a993507STvrtko Ursulin dma_fence_get(&prev->base.s_fence->finished)); 1435a993507STvrtko Ursulin drm_mock_sched_job_submit(job); 1445a993507STvrtko Ursulin prev = job; 1455a993507STvrtko Ursulin } 1465a993507STvrtko Ursulin 1475a993507STvrtko Ursulin done = drm_mock_sched_job_wait_finished(job, HZ); 1485a993507STvrtko Ursulin KUNIT_ASSERT_TRUE(test, done); 1495a993507STvrtko Ursulin 1505a993507STvrtko Ursulin for (i = 0; i < params->num_entities; i++) 1515a993507STvrtko Ursulin drm_mock_sched_entity_free(entity[i]); 1525a993507STvrtko Ursulin } 1535a993507STvrtko Ursulin 1545a993507STvrtko Ursulin static void drm_sched_basic_entity_cleanup(struct kunit *test) 1555a993507STvrtko Ursulin { 1565a993507STvrtko Ursulin struct drm_mock_sched_job *job, *mid, *prev = NULL; 1575a993507STvrtko Ursulin struct drm_mock_scheduler *sched = test->priv; 1585a993507STvrtko Ursulin struct drm_mock_sched_entity *entity[4]; 1595a993507STvrtko Ursulin const unsigned int qd = 100; 1605a993507STvrtko Ursulin unsigned int i, cur_ent = 0; 1615a993507STvrtko Ursulin bool done; 1625a993507STvrtko Ursulin 1635a993507STvrtko Ursulin /* 1645a993507STvrtko Ursulin * Submit a queue of jobs across different entities with an explicit 1655a993507STvrtko Ursulin * chain of dependencies between them and trigger entity cleanup while 1665a993507STvrtko Ursulin * the queue is still being processed. 1675a993507STvrtko Ursulin */ 1685a993507STvrtko Ursulin 1695a993507STvrtko Ursulin for (i = 0; i < ARRAY_SIZE(entity); i++) 1705a993507STvrtko Ursulin entity[i] = drm_mock_sched_entity_new(test, 1715a993507STvrtko Ursulin DRM_SCHED_PRIORITY_NORMAL, 1725a993507STvrtko Ursulin sched); 1735a993507STvrtko Ursulin 1745a993507STvrtko Ursulin for (i = 0; i < qd; i++) { 1755a993507STvrtko Ursulin job = drm_mock_sched_job_new(test, entity[cur_ent++]); 1765a993507STvrtko Ursulin cur_ent %= ARRAY_SIZE(entity); 1775a993507STvrtko Ursulin drm_mock_sched_job_set_duration_us(job, 1000); 1785a993507STvrtko Ursulin if (prev) 1795a993507STvrtko Ursulin drm_sched_job_add_dependency(&job->base, 1805a993507STvrtko Ursulin dma_fence_get(&prev->base.s_fence->finished)); 1815a993507STvrtko Ursulin drm_mock_sched_job_submit(job); 1825a993507STvrtko Ursulin if (i == qd / 2) 1835a993507STvrtko Ursulin mid = job; 1845a993507STvrtko Ursulin prev = job; 1855a993507STvrtko Ursulin } 1865a993507STvrtko Ursulin 1875a993507STvrtko Ursulin done = drm_mock_sched_job_wait_finished(mid, HZ); 1885a993507STvrtko Ursulin KUNIT_ASSERT_TRUE(test, done); 1895a993507STvrtko Ursulin 1905a993507STvrtko Ursulin /* Exit with half of the queue still pending to be executed. */ 1915a993507STvrtko Ursulin for (i = 0; i < ARRAY_SIZE(entity); i++) 1925a993507STvrtko Ursulin drm_mock_sched_entity_free(entity[i]); 1935a993507STvrtko Ursulin } 1945a993507STvrtko Ursulin 1955a993507STvrtko Ursulin static struct kunit_case drm_sched_basic_tests[] = { 1965a993507STvrtko Ursulin KUNIT_CASE(drm_sched_basic_submit), 1975a993507STvrtko Ursulin KUNIT_CASE_PARAM(drm_sched_basic_test, drm_sched_basic_gen_params), 1985a993507STvrtko Ursulin KUNIT_CASE(drm_sched_basic_entity_cleanup), 1995a993507STvrtko Ursulin {} 2005a993507STvrtko Ursulin }; 2015a993507STvrtko Ursulin 2025a993507STvrtko Ursulin static struct kunit_suite drm_sched_basic = { 2035a993507STvrtko Ursulin .name = "drm_sched_basic_tests", 2045a993507STvrtko Ursulin .init = drm_sched_basic_init, 2055a993507STvrtko Ursulin .exit = drm_sched_basic_exit, 2065a993507STvrtko Ursulin .test_cases = drm_sched_basic_tests, 2075a993507STvrtko Ursulin }; 2085a993507STvrtko Ursulin 209c2668a0eSPhilipp Stanner static void drm_sched_basic_cancel(struct kunit *test) 210c2668a0eSPhilipp Stanner { 211c2668a0eSPhilipp Stanner struct drm_mock_sched_entity *entity; 212c2668a0eSPhilipp Stanner struct drm_mock_scheduler *sched; 213c2668a0eSPhilipp Stanner struct drm_mock_sched_job *job; 214c2668a0eSPhilipp Stanner bool done; 215c2668a0eSPhilipp Stanner 216c2668a0eSPhilipp Stanner /* 217c2668a0eSPhilipp Stanner * Check that drm_sched_fini() uses the cancel_job() callback to cancel 218c2668a0eSPhilipp Stanner * jobs that are still pending. 219c2668a0eSPhilipp Stanner */ 220c2668a0eSPhilipp Stanner 221c2668a0eSPhilipp Stanner sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT); 222c2668a0eSPhilipp Stanner entity = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, 223c2668a0eSPhilipp Stanner sched); 224c2668a0eSPhilipp Stanner 225c2668a0eSPhilipp Stanner job = drm_mock_sched_job_new(test, entity); 226c2668a0eSPhilipp Stanner 227c2668a0eSPhilipp Stanner drm_mock_sched_job_submit(job); 228c2668a0eSPhilipp Stanner 229c2668a0eSPhilipp Stanner done = drm_mock_sched_job_wait_scheduled(job, HZ); 230c2668a0eSPhilipp Stanner KUNIT_ASSERT_TRUE(test, done); 231c2668a0eSPhilipp Stanner 232c2668a0eSPhilipp Stanner drm_mock_sched_entity_free(entity); 233c2668a0eSPhilipp Stanner drm_mock_sched_fini(sched); 234c2668a0eSPhilipp Stanner 235c2668a0eSPhilipp Stanner KUNIT_ASSERT_EQ(test, job->hw_fence.error, -ECANCELED); 236c2668a0eSPhilipp Stanner } 237c2668a0eSPhilipp Stanner 238c2668a0eSPhilipp Stanner static struct kunit_case drm_sched_cancel_tests[] = { 239c2668a0eSPhilipp Stanner KUNIT_CASE(drm_sched_basic_cancel), 240c2668a0eSPhilipp Stanner {} 241c2668a0eSPhilipp Stanner }; 242c2668a0eSPhilipp Stanner 243c2668a0eSPhilipp Stanner static struct kunit_suite drm_sched_cancel = { 244c2668a0eSPhilipp Stanner .name = "drm_sched_basic_cancel_tests", 245c2668a0eSPhilipp Stanner .init = drm_sched_basic_init, 246c2668a0eSPhilipp Stanner .exit = drm_sched_basic_exit, 247c2668a0eSPhilipp Stanner .test_cases = drm_sched_cancel_tests, 248c2668a0eSPhilipp Stanner }; 249c2668a0eSPhilipp Stanner 25053e65974STvrtko Ursulin static void drm_sched_basic_timeout(struct kunit *test) 25153e65974STvrtko Ursulin { 25253e65974STvrtko Ursulin struct drm_mock_scheduler *sched = test->priv; 25353e65974STvrtko Ursulin struct drm_mock_sched_entity *entity; 25453e65974STvrtko Ursulin struct drm_mock_sched_job *job; 25553e65974STvrtko Ursulin bool done; 25653e65974STvrtko Ursulin 25753e65974STvrtko Ursulin /* 25853e65974STvrtko Ursulin * Submit a single job against a scheduler with the timeout configured 25953e65974STvrtko Ursulin * and verify that the timeout handling will run if the backend fails 26053e65974STvrtko Ursulin * to complete it in time. 26153e65974STvrtko Ursulin */ 26253e65974STvrtko Ursulin 26353e65974STvrtko Ursulin entity = drm_mock_sched_entity_new(test, 26453e65974STvrtko Ursulin DRM_SCHED_PRIORITY_NORMAL, 26553e65974STvrtko Ursulin sched); 26653e65974STvrtko Ursulin job = drm_mock_sched_job_new(test, entity); 26753e65974STvrtko Ursulin 26853e65974STvrtko Ursulin drm_mock_sched_job_submit(job); 26953e65974STvrtko Ursulin 27053e65974STvrtko Ursulin done = drm_mock_sched_job_wait_scheduled(job, HZ); 27153e65974STvrtko Ursulin KUNIT_ASSERT_TRUE(test, done); 27253e65974STvrtko Ursulin 2739b9b5a36SMaíra Canal done = drm_mock_sched_job_wait_finished(job, MOCK_TIMEOUT / 2); 27453e65974STvrtko Ursulin KUNIT_ASSERT_FALSE(test, done); 27553e65974STvrtko Ursulin 27653e65974STvrtko Ursulin KUNIT_ASSERT_EQ(test, 27753e65974STvrtko Ursulin job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT, 27853e65974STvrtko Ursulin 0); 27953e65974STvrtko Ursulin 2809b9b5a36SMaíra Canal done = drm_mock_sched_job_wait_finished(job, MOCK_TIMEOUT); 28153e65974STvrtko Ursulin KUNIT_ASSERT_FALSE(test, done); 28253e65974STvrtko Ursulin 28353e65974STvrtko Ursulin KUNIT_ASSERT_EQ(test, 28453e65974STvrtko Ursulin job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT, 28553e65974STvrtko Ursulin DRM_MOCK_SCHED_JOB_TIMEDOUT); 28653e65974STvrtko Ursulin 28753e65974STvrtko Ursulin drm_mock_sched_entity_free(entity); 28853e65974STvrtko Ursulin } 28953e65974STvrtko Ursulin 290*1472e754SMaíra Canal static void drm_sched_skip_reset(struct kunit *test) 291*1472e754SMaíra Canal { 292*1472e754SMaíra Canal struct drm_mock_scheduler *sched = test->priv; 293*1472e754SMaíra Canal struct drm_mock_sched_entity *entity; 294*1472e754SMaíra Canal struct drm_mock_sched_job *job; 295*1472e754SMaíra Canal unsigned int i; 296*1472e754SMaíra Canal bool done; 297*1472e754SMaíra Canal 298*1472e754SMaíra Canal /* 299*1472e754SMaíra Canal * Submit a single job against a scheduler with the timeout configured 300*1472e754SMaíra Canal * and verify that if the job is still running, the timeout handler 301*1472e754SMaíra Canal * will skip the reset and allow the job to complete. 302*1472e754SMaíra Canal */ 303*1472e754SMaíra Canal 304*1472e754SMaíra Canal entity = drm_mock_sched_entity_new(test, 305*1472e754SMaíra Canal DRM_SCHED_PRIORITY_NORMAL, 306*1472e754SMaíra Canal sched); 307*1472e754SMaíra Canal job = drm_mock_sched_job_new(test, entity); 308*1472e754SMaíra Canal 309*1472e754SMaíra Canal job->flags = DRM_MOCK_SCHED_JOB_DONT_RESET; 310*1472e754SMaíra Canal 311*1472e754SMaíra Canal drm_mock_sched_job_submit(job); 312*1472e754SMaíra Canal 313*1472e754SMaíra Canal done = drm_mock_sched_job_wait_scheduled(job, HZ); 314*1472e754SMaíra Canal KUNIT_ASSERT_TRUE(test, done); 315*1472e754SMaíra Canal 316*1472e754SMaíra Canal done = drm_mock_sched_job_wait_finished(job, 2 * MOCK_TIMEOUT); 317*1472e754SMaíra Canal KUNIT_ASSERT_FALSE(test, done); 318*1472e754SMaíra Canal 319*1472e754SMaíra Canal KUNIT_ASSERT_EQ(test, 320*1472e754SMaíra Canal job->flags & DRM_MOCK_SCHED_JOB_DONT_RESET, 321*1472e754SMaíra Canal 0); 322*1472e754SMaíra Canal 323*1472e754SMaíra Canal i = drm_mock_sched_advance(sched, 1); 324*1472e754SMaíra Canal KUNIT_ASSERT_EQ(test, i, 1); 325*1472e754SMaíra Canal 326*1472e754SMaíra Canal done = drm_mock_sched_job_wait_finished(job, HZ); 327*1472e754SMaíra Canal KUNIT_ASSERT_TRUE(test, done); 328*1472e754SMaíra Canal 329*1472e754SMaíra Canal drm_mock_sched_entity_free(entity); 330*1472e754SMaíra Canal } 331*1472e754SMaíra Canal 33253e65974STvrtko Ursulin static struct kunit_case drm_sched_timeout_tests[] = { 33353e65974STvrtko Ursulin KUNIT_CASE(drm_sched_basic_timeout), 334*1472e754SMaíra Canal KUNIT_CASE(drm_sched_skip_reset), 33553e65974STvrtko Ursulin {} 33653e65974STvrtko Ursulin }; 33753e65974STvrtko Ursulin 33853e65974STvrtko Ursulin static struct kunit_suite drm_sched_timeout = { 33953e65974STvrtko Ursulin .name = "drm_sched_basic_timeout_tests", 34053e65974STvrtko Ursulin .init = drm_sched_timeout_init, 34153e65974STvrtko Ursulin .exit = drm_sched_basic_exit, 34253e65974STvrtko Ursulin .test_cases = drm_sched_timeout_tests, 34353e65974STvrtko Ursulin }; 34453e65974STvrtko Ursulin 3457b765cdaSTvrtko Ursulin static void drm_sched_priorities(struct kunit *test) 3467b765cdaSTvrtko Ursulin { 3477b765cdaSTvrtko Ursulin struct drm_mock_sched_entity *entity[DRM_SCHED_PRIORITY_COUNT]; 3487b765cdaSTvrtko Ursulin struct drm_mock_scheduler *sched = test->priv; 3497b765cdaSTvrtko Ursulin struct drm_mock_sched_job *job; 3507b765cdaSTvrtko Ursulin const unsigned int qd = 100; 3517b765cdaSTvrtko Ursulin unsigned int i, cur_ent = 0; 3527b765cdaSTvrtko Ursulin enum drm_sched_priority p; 3537b765cdaSTvrtko Ursulin bool done; 3547b765cdaSTvrtko Ursulin 3557b765cdaSTvrtko Ursulin /* 3567b765cdaSTvrtko Ursulin * Submit a bunch of jobs against entities configured with different 3577b765cdaSTvrtko Ursulin * priorities. 3587b765cdaSTvrtko Ursulin */ 3597b765cdaSTvrtko Ursulin 3607b765cdaSTvrtko Ursulin BUILD_BUG_ON(DRM_SCHED_PRIORITY_KERNEL > DRM_SCHED_PRIORITY_LOW); 3617b765cdaSTvrtko Ursulin BUILD_BUG_ON(ARRAY_SIZE(entity) != DRM_SCHED_PRIORITY_COUNT); 3627b765cdaSTvrtko Ursulin 3637b765cdaSTvrtko Ursulin for (p = DRM_SCHED_PRIORITY_KERNEL; p <= DRM_SCHED_PRIORITY_LOW; p++) 3647b765cdaSTvrtko Ursulin entity[p] = drm_mock_sched_entity_new(test, p, sched); 3657b765cdaSTvrtko Ursulin 3667b765cdaSTvrtko Ursulin for (i = 0; i < qd; i++) { 3677b765cdaSTvrtko Ursulin job = drm_mock_sched_job_new(test, entity[cur_ent++]); 3687b765cdaSTvrtko Ursulin cur_ent %= ARRAY_SIZE(entity); 3697b765cdaSTvrtko Ursulin drm_mock_sched_job_set_duration_us(job, 1000); 3707b765cdaSTvrtko Ursulin drm_mock_sched_job_submit(job); 3717b765cdaSTvrtko Ursulin } 3727b765cdaSTvrtko Ursulin 3737b765cdaSTvrtko Ursulin done = drm_mock_sched_job_wait_finished(job, HZ); 3747b765cdaSTvrtko Ursulin KUNIT_ASSERT_TRUE(test, done); 3757b765cdaSTvrtko Ursulin 3767b765cdaSTvrtko Ursulin for (i = 0; i < ARRAY_SIZE(entity); i++) 3777b765cdaSTvrtko Ursulin drm_mock_sched_entity_free(entity[i]); 3787b765cdaSTvrtko Ursulin } 3797b765cdaSTvrtko Ursulin 3807b765cdaSTvrtko Ursulin static void drm_sched_change_priority(struct kunit *test) 3817b765cdaSTvrtko Ursulin { 3827b765cdaSTvrtko Ursulin struct drm_mock_sched_entity *entity[DRM_SCHED_PRIORITY_COUNT]; 3837b765cdaSTvrtko Ursulin struct drm_mock_scheduler *sched = test->priv; 3847b765cdaSTvrtko Ursulin struct drm_mock_sched_job *job; 3857b765cdaSTvrtko Ursulin const unsigned int qd = 1000; 3867b765cdaSTvrtko Ursulin unsigned int i, cur_ent = 0; 3877b765cdaSTvrtko Ursulin enum drm_sched_priority p; 3887b765cdaSTvrtko Ursulin 3897b765cdaSTvrtko Ursulin /* 3907b765cdaSTvrtko Ursulin * Submit a bunch of jobs against entities configured with different 3917b765cdaSTvrtko Ursulin * priorities and while waiting for them to complete, periodically keep 3927b765cdaSTvrtko Ursulin * changing their priorities. 3937b765cdaSTvrtko Ursulin * 3947b765cdaSTvrtko Ursulin * We set up the queue-depth (qd) and job duration so the priority 3957b765cdaSTvrtko Ursulin * changing loop has some time to interact with submissions to the 3967b765cdaSTvrtko Ursulin * backend and job completions as they progress. 3977b765cdaSTvrtko Ursulin */ 3987b765cdaSTvrtko Ursulin 3997b765cdaSTvrtko Ursulin for (p = DRM_SCHED_PRIORITY_KERNEL; p <= DRM_SCHED_PRIORITY_LOW; p++) 4007b765cdaSTvrtko Ursulin entity[p] = drm_mock_sched_entity_new(test, p, sched); 4017b765cdaSTvrtko Ursulin 4027b765cdaSTvrtko Ursulin for (i = 0; i < qd; i++) { 4037b765cdaSTvrtko Ursulin job = drm_mock_sched_job_new(test, entity[cur_ent++]); 4047b765cdaSTvrtko Ursulin cur_ent %= ARRAY_SIZE(entity); 4057b765cdaSTvrtko Ursulin drm_mock_sched_job_set_duration_us(job, 1000); 4067b765cdaSTvrtko Ursulin drm_mock_sched_job_submit(job); 4077b765cdaSTvrtko Ursulin } 4087b765cdaSTvrtko Ursulin 4097b765cdaSTvrtko Ursulin do { 4107b765cdaSTvrtko Ursulin drm_sched_entity_set_priority(&entity[cur_ent]->base, 4117b765cdaSTvrtko Ursulin (entity[cur_ent]->base.priority + 1) % 4127b765cdaSTvrtko Ursulin DRM_SCHED_PRIORITY_COUNT); 4137b765cdaSTvrtko Ursulin cur_ent++; 4147b765cdaSTvrtko Ursulin cur_ent %= ARRAY_SIZE(entity); 4157b765cdaSTvrtko Ursulin usleep_range(200, 500); 4167b765cdaSTvrtko Ursulin } while (!drm_mock_sched_job_is_finished(job)); 4177b765cdaSTvrtko Ursulin 4187b765cdaSTvrtko Ursulin for (i = 0; i < ARRAY_SIZE(entity); i++) 4197b765cdaSTvrtko Ursulin drm_mock_sched_entity_free(entity[i]); 4207b765cdaSTvrtko Ursulin } 4217b765cdaSTvrtko Ursulin 4227b765cdaSTvrtko Ursulin static struct kunit_case drm_sched_priority_tests[] = { 4237b765cdaSTvrtko Ursulin KUNIT_CASE(drm_sched_priorities), 4247b765cdaSTvrtko Ursulin KUNIT_CASE(drm_sched_change_priority), 4257b765cdaSTvrtko Ursulin {} 4267b765cdaSTvrtko Ursulin }; 4277b765cdaSTvrtko Ursulin 4287b765cdaSTvrtko Ursulin static struct kunit_suite drm_sched_priority = { 4297b765cdaSTvrtko Ursulin .name = "drm_sched_basic_priority_tests", 4307b765cdaSTvrtko Ursulin .init = drm_sched_basic_init, 4317b765cdaSTvrtko Ursulin .exit = drm_sched_basic_exit, 4327b765cdaSTvrtko Ursulin .test_cases = drm_sched_priority_tests, 4337b765cdaSTvrtko Ursulin }; 4347b765cdaSTvrtko Ursulin 435c85fc5dbSTvrtko Ursulin static void drm_sched_test_modify_sched(struct kunit *test) 436c85fc5dbSTvrtko Ursulin { 437c85fc5dbSTvrtko Ursulin unsigned int i, cur_ent = 0, cur_sched = 0; 438c85fc5dbSTvrtko Ursulin struct drm_mock_sched_entity *entity[13]; 439c85fc5dbSTvrtko Ursulin struct drm_mock_scheduler *sched[3]; 440c85fc5dbSTvrtko Ursulin struct drm_mock_sched_job *job; 441c85fc5dbSTvrtko Ursulin const unsigned int qd = 1000; 442c85fc5dbSTvrtko Ursulin 443c85fc5dbSTvrtko Ursulin /* 444c85fc5dbSTvrtko Ursulin * Submit a bunch of jobs against entities configured with different 445c85fc5dbSTvrtko Ursulin * schedulers and while waiting for them to complete, periodically keep 446c85fc5dbSTvrtko Ursulin * changing schedulers associated with each entity. 447c85fc5dbSTvrtko Ursulin * 448c85fc5dbSTvrtko Ursulin * We set up the queue-depth (qd) and job duration so the sched modify 449c85fc5dbSTvrtko Ursulin * loop has some time to interact with submissions to the backend and 450c85fc5dbSTvrtko Ursulin * job completions as they progress. 451c85fc5dbSTvrtko Ursulin * 452c85fc5dbSTvrtko Ursulin * For the number of schedulers and entities we use primes in order to 453c85fc5dbSTvrtko Ursulin * perturb the entity->sched assignments with less of a regular pattern. 454c85fc5dbSTvrtko Ursulin */ 455c85fc5dbSTvrtko Ursulin 456c85fc5dbSTvrtko Ursulin for (i = 0; i < ARRAY_SIZE(sched); i++) 457c85fc5dbSTvrtko Ursulin sched[i] = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT); 458c85fc5dbSTvrtko Ursulin 459c85fc5dbSTvrtko Ursulin for (i = 0; i < ARRAY_SIZE(entity); i++) 460c85fc5dbSTvrtko Ursulin entity[i] = drm_mock_sched_entity_new(test, 461c85fc5dbSTvrtko Ursulin DRM_SCHED_PRIORITY_NORMAL, 462c85fc5dbSTvrtko Ursulin sched[i % ARRAY_SIZE(sched)]); 463c85fc5dbSTvrtko Ursulin 464c85fc5dbSTvrtko Ursulin for (i = 0; i < qd; i++) { 465c85fc5dbSTvrtko Ursulin job = drm_mock_sched_job_new(test, entity[cur_ent++]); 466c85fc5dbSTvrtko Ursulin cur_ent %= ARRAY_SIZE(entity); 467c85fc5dbSTvrtko Ursulin drm_mock_sched_job_set_duration_us(job, 1000); 468c85fc5dbSTvrtko Ursulin drm_mock_sched_job_submit(job); 469c85fc5dbSTvrtko Ursulin } 470c85fc5dbSTvrtko Ursulin 471c85fc5dbSTvrtko Ursulin do { 472c85fc5dbSTvrtko Ursulin struct drm_gpu_scheduler *modify; 473c85fc5dbSTvrtko Ursulin 474c85fc5dbSTvrtko Ursulin usleep_range(200, 500); 475c85fc5dbSTvrtko Ursulin cur_ent++; 476c85fc5dbSTvrtko Ursulin cur_ent %= ARRAY_SIZE(entity); 477c85fc5dbSTvrtko Ursulin cur_sched++; 478c85fc5dbSTvrtko Ursulin cur_sched %= ARRAY_SIZE(sched); 479c85fc5dbSTvrtko Ursulin modify = &sched[cur_sched]->base; 480c85fc5dbSTvrtko Ursulin drm_sched_entity_modify_sched(&entity[cur_ent]->base, &modify, 481c85fc5dbSTvrtko Ursulin 1); 482c85fc5dbSTvrtko Ursulin } while (!drm_mock_sched_job_is_finished(job)); 483c85fc5dbSTvrtko Ursulin 484c85fc5dbSTvrtko Ursulin for (i = 0; i < ARRAY_SIZE(entity); i++) 485c85fc5dbSTvrtko Ursulin drm_mock_sched_entity_free(entity[i]); 486c85fc5dbSTvrtko Ursulin 487c85fc5dbSTvrtko Ursulin for (i = 0; i < ARRAY_SIZE(sched); i++) 488c85fc5dbSTvrtko Ursulin drm_mock_sched_fini(sched[i]); 489c85fc5dbSTvrtko Ursulin } 490c85fc5dbSTvrtko Ursulin 491c85fc5dbSTvrtko Ursulin static struct kunit_case drm_sched_modify_sched_tests[] = { 492c85fc5dbSTvrtko Ursulin KUNIT_CASE(drm_sched_test_modify_sched), 493c85fc5dbSTvrtko Ursulin {} 494c85fc5dbSTvrtko Ursulin }; 495c85fc5dbSTvrtko Ursulin 496c85fc5dbSTvrtko Ursulin static struct kunit_suite drm_sched_modify_sched = { 497c85fc5dbSTvrtko Ursulin .name = "drm_sched_basic_modify_sched_tests", 498c85fc5dbSTvrtko Ursulin .test_cases = drm_sched_modify_sched_tests, 499c85fc5dbSTvrtko Ursulin }; 500c85fc5dbSTvrtko Ursulin 501909bda22STvrtko Ursulin static void drm_sched_test_credits(struct kunit *test) 502909bda22STvrtko Ursulin { 503909bda22STvrtko Ursulin struct drm_mock_sched_entity *entity; 504909bda22STvrtko Ursulin struct drm_mock_scheduler *sched; 505909bda22STvrtko Ursulin struct drm_mock_sched_job *job[2]; 506909bda22STvrtko Ursulin bool done; 507909bda22STvrtko Ursulin int i; 508909bda22STvrtko Ursulin 509909bda22STvrtko Ursulin /* 510909bda22STvrtko Ursulin * Check that the configured credit limit is respected. 511909bda22STvrtko Ursulin */ 512909bda22STvrtko Ursulin 513909bda22STvrtko Ursulin sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT); 514909bda22STvrtko Ursulin sched->base.credit_limit = 1; 515909bda22STvrtko Ursulin 516909bda22STvrtko Ursulin entity = drm_mock_sched_entity_new(test, 517909bda22STvrtko Ursulin DRM_SCHED_PRIORITY_NORMAL, 518909bda22STvrtko Ursulin sched); 519909bda22STvrtko Ursulin 520909bda22STvrtko Ursulin job[0] = drm_mock_sched_job_new(test, entity); 521909bda22STvrtko Ursulin job[1] = drm_mock_sched_job_new(test, entity); 522909bda22STvrtko Ursulin 523909bda22STvrtko Ursulin drm_mock_sched_job_submit(job[0]); 524909bda22STvrtko Ursulin drm_mock_sched_job_submit(job[1]); 525909bda22STvrtko Ursulin 526909bda22STvrtko Ursulin done = drm_mock_sched_job_wait_scheduled(job[0], HZ); 527909bda22STvrtko Ursulin KUNIT_ASSERT_TRUE(test, done); 528909bda22STvrtko Ursulin 529909bda22STvrtko Ursulin done = drm_mock_sched_job_wait_scheduled(job[1], HZ); 530909bda22STvrtko Ursulin KUNIT_ASSERT_FALSE(test, done); 531909bda22STvrtko Ursulin 532909bda22STvrtko Ursulin i = drm_mock_sched_advance(sched, 1); 533909bda22STvrtko Ursulin KUNIT_ASSERT_EQ(test, i, 1); 534909bda22STvrtko Ursulin 535909bda22STvrtko Ursulin done = drm_mock_sched_job_wait_scheduled(job[1], HZ); 536909bda22STvrtko Ursulin KUNIT_ASSERT_TRUE(test, done); 537909bda22STvrtko Ursulin 538909bda22STvrtko Ursulin i = drm_mock_sched_advance(sched, 1); 539909bda22STvrtko Ursulin KUNIT_ASSERT_EQ(test, i, 1); 540909bda22STvrtko Ursulin 541909bda22STvrtko Ursulin done = drm_mock_sched_job_wait_finished(job[1], HZ); 542909bda22STvrtko Ursulin KUNIT_ASSERT_TRUE(test, done); 543909bda22STvrtko Ursulin 544909bda22STvrtko Ursulin drm_mock_sched_entity_free(entity); 545909bda22STvrtko Ursulin drm_mock_sched_fini(sched); 546909bda22STvrtko Ursulin } 547909bda22STvrtko Ursulin 548909bda22STvrtko Ursulin static struct kunit_case drm_sched_credits_tests[] = { 549909bda22STvrtko Ursulin KUNIT_CASE(drm_sched_test_credits), 550909bda22STvrtko Ursulin {} 551909bda22STvrtko Ursulin }; 552909bda22STvrtko Ursulin 553909bda22STvrtko Ursulin static struct kunit_suite drm_sched_credits = { 554909bda22STvrtko Ursulin .name = "drm_sched_basic_credits_tests", 555909bda22STvrtko Ursulin .test_cases = drm_sched_credits_tests, 556909bda22STvrtko Ursulin }; 557909bda22STvrtko Ursulin 55853e65974STvrtko Ursulin kunit_test_suites(&drm_sched_basic, 5597b765cdaSTvrtko Ursulin &drm_sched_timeout, 560c2668a0eSPhilipp Stanner &drm_sched_cancel, 561c85fc5dbSTvrtko Ursulin &drm_sched_priority, 562909bda22STvrtko Ursulin &drm_sched_modify_sched, 563909bda22STvrtko Ursulin &drm_sched_credits); 564