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