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