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 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 *
drm_sched_to_mock_sched(struct drm_gpu_scheduler * sched)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 *
drm_sched_entity_to_mock_entity(struct drm_sched_entity * 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 *
drm_sched_job_to_mock_job(struct drm_sched_job * 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 */
drm_mock_sched_job_submit(struct drm_mock_sched_job * job)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
drm_mock_sched_job_set_duration_us(struct drm_mock_sched_job * job,unsigned int duration_us)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
drm_mock_sched_job_is_finished(struct drm_mock_sched_job * job)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
drm_mock_sched_job_wait_finished(struct drm_mock_sched_job * job,long timeout)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
drm_mock_sched_job_wait_scheduled(struct drm_mock_sched_job * job,long timeout)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