1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* Copyright (c) 2025 Valve Corporation */ 3 4 #ifndef _SCHED_TESTS_H_ 5 #define _SCHED_TESTS_H_ 6 7 #include <kunit/test.h> 8 #include <linux/atomic.h> 9 #include <linux/completion.h> 10 #include <linux/dma-fence.h> 11 #include <linux/hrtimer.h> 12 #include <linux/ktime.h> 13 #include <linux/list.h> 14 #include <linux/atomic.h> 15 #include <linux/mutex.h> 16 #include <linux/types.h> 17 18 #include <drm/gpu_scheduler.h> 19 20 /* 21 * DOC: Mock DRM scheduler data structures 22 * 23 * drm_mock_* data structures are used to implement a mock "GPU". 24 * 25 * They subclass the core DRM scheduler objects and add their data on top, which 26 * enables tracking the submitted jobs and simulating their execution with the 27 * attributes as specified by the test case. 28 */ 29 30 /** 31 * struct drm_mock_scheduler - implements a trivial mock GPU execution engine 32 * 33 * @base: DRM scheduler base class 34 * @test: Backpointer to owning the kunit test case 35 * @lock: Lock to protect the simulated @hw_timeline, @job_list and @done_list 36 * @job_list: List of jobs submitted to the mock GPU 37 * @done_list: List of jobs completed by the mock GPU 38 * @hw_timeline: Simulated hardware timeline has a @context, @next_seqno and 39 * @cur_seqno for implementing a struct dma_fence signaling the 40 * simulated job completion. 41 * 42 * Trivial mock GPU execution engine tracks submitted jobs and enables 43 * completing them strictly in submission order. 44 */ 45 struct drm_mock_scheduler { 46 struct drm_gpu_scheduler base; 47 48 struct kunit *test; 49 50 spinlock_t lock; 51 struct list_head job_list; 52 53 struct { 54 u64 context; 55 atomic_t next_seqno; 56 unsigned int cur_seqno; 57 } hw_timeline; 58 }; 59 60 /** 61 * struct drm_mock_sched_entity - implements a mock GPU sched entity 62 * 63 * @base: DRM scheduler entity base class 64 * @test: Backpointer to owning the kunit test case 65 * 66 * Mock GPU sched entity is used by the test cases to submit jobs to the mock 67 * scheduler. 68 */ 69 struct drm_mock_sched_entity { 70 struct drm_sched_entity base; 71 72 struct kunit *test; 73 }; 74 75 /** 76 * struct drm_mock_sched_job - implements a mock GPU job 77 * 78 * @base: DRM sched job base class 79 * @done: Completion signaling job completion. 80 * @flags: Flags designating job state. 81 * @link: List head element used by job tracking by the drm_mock_scheduler 82 * @timer: Timer used for simulating job execution duration 83 * @duration_us: Simulated job duration in micro seconds, or zero if in manual 84 * timeline advance mode 85 * @finish_at: Absolute time when the jobs with set duration will complete 86 * @lock: Lock used for @hw_fence 87 * @hw_fence: Fence returned to DRM scheduler as the hardware fence 88 * @test: Backpointer to owning the kunit test case 89 * 90 * Mock GPU sched job is used by the test cases to submit jobs to the mock 91 * scheduler. 92 */ 93 struct drm_mock_sched_job { 94 struct drm_sched_job base; 95 96 struct completion done; 97 98 #define DRM_MOCK_SCHED_JOB_DONE 0x1 99 #define DRM_MOCK_SCHED_JOB_TIMEDOUT 0x2 100 #define DRM_MOCK_SCHED_JOB_DONT_RESET 0x4 101 #define DRM_MOCK_SCHED_JOB_RESET_SKIPPED 0x8 102 unsigned long flags; 103 104 struct list_head link; 105 struct hrtimer timer; 106 107 unsigned int duration_us; 108 ktime_t finish_at; 109 110 struct dma_fence hw_fence; 111 112 struct kunit *test; 113 }; 114 115 static inline struct drm_mock_scheduler * 116 drm_sched_to_mock_sched(struct drm_gpu_scheduler *sched) 117 { 118 return container_of(sched, struct drm_mock_scheduler, base); 119 }; 120 121 static inline struct drm_mock_sched_entity * 122 drm_sched_entity_to_mock_entity(struct drm_sched_entity *sched_entity) 123 { 124 return container_of(sched_entity, struct drm_mock_sched_entity, base); 125 }; 126 127 static inline struct drm_mock_sched_job * 128 drm_sched_job_to_mock_job(struct drm_sched_job *sched_job) 129 { 130 return container_of(sched_job, struct drm_mock_sched_job, base); 131 }; 132 133 struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test, 134 long timeout); 135 void drm_mock_sched_fini(struct drm_mock_scheduler *sched); 136 unsigned int drm_mock_sched_advance(struct drm_mock_scheduler *sched, 137 unsigned int num); 138 139 struct drm_mock_sched_entity * 140 drm_mock_sched_entity_new(struct kunit *test, 141 enum drm_sched_priority priority, 142 struct drm_mock_scheduler *sched); 143 void drm_mock_sched_entity_free(struct drm_mock_sched_entity *entity); 144 145 struct drm_mock_sched_job * 146 drm_mock_sched_job_new(struct kunit *test, 147 struct drm_mock_sched_entity *entity); 148 149 /** 150 * drm_mock_sched_job_submit - Arm and submit a job in one go 151 * 152 * @job: Job to arm and submit 153 */ 154 static inline void drm_mock_sched_job_submit(struct drm_mock_sched_job *job) 155 { 156 drm_sched_job_arm(&job->base); 157 drm_sched_entity_push_job(&job->base); 158 } 159 160 /** 161 * drm_mock_sched_job_set_duration_us - Set a job duration 162 * 163 * @job: Job to set the duration for 164 * @duration_us: Duration in micro seconds 165 * 166 * Jobs with duration set will be automatically completed by the mock scheduler 167 * as the timeline progresses, unless a job without a set duration is 168 * encountered in the timelime in which case calling drm_mock_sched_advance() 169 * will be required to bump the timeline. 170 */ 171 static inline void 172 drm_mock_sched_job_set_duration_us(struct drm_mock_sched_job *job, 173 unsigned int duration_us) 174 { 175 job->duration_us = duration_us; 176 } 177 178 /** 179 * drm_mock_sched_job_is_finished - Check if a job is finished 180 * 181 * @job: Job to check 182 * 183 * Returns: true if finished 184 */ 185 static inline bool 186 drm_mock_sched_job_is_finished(struct drm_mock_sched_job *job) 187 { 188 return job->flags & DRM_MOCK_SCHED_JOB_DONE; 189 } 190 191 /** 192 * drm_mock_sched_job_wait_finished - Wait until a job is finished 193 * 194 * @job: Job to wait for 195 * @timeout: Wait time in jiffies 196 * 197 * Returns: true if finished within the timeout provided, otherwise false 198 */ 199 static inline bool 200 drm_mock_sched_job_wait_finished(struct drm_mock_sched_job *job, long timeout) 201 { 202 if (job->flags & DRM_MOCK_SCHED_JOB_DONE) 203 return true; 204 205 return wait_for_completion_timeout(&job->done, timeout) != 0; 206 } 207 208 /** 209 * drm_mock_sched_job_wait_scheduled - Wait until a job is scheduled 210 * 211 * @job: Job to wait for 212 * @timeout: Wait time in jiffies 213 * 214 * Returns: true if scheduled within the timeout provided, otherwise false 215 */ 216 static inline bool 217 drm_mock_sched_job_wait_scheduled(struct drm_mock_sched_job *job, long timeout) 218 { 219 KUNIT_ASSERT_EQ(job->test, job->flags & DRM_MOCK_SCHED_JOB_DONE, 0); 220 221 return dma_fence_wait_timeout(&job->base.s_fence->scheduled, 222 false, 223 timeout) != 0; 224 } 225 226 #endif 227