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