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