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