summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/scheduler/tests/sched_tests.h
blob: 27caf8285fb74b9f3c9ce2daa1c44d4a0c967e92 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Valve Corporation */

#ifndef _SCHED_TESTS_H_
#define _SCHED_TESTS_H_

#include <kunit/test.h>
#include <linux/atomic.h>
#include <linux/completion.h>
#include <linux/dma-fence.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/list.h>
#include <linux/atomic.h>
#include <linux/mutex.h>
#include <linux/types.h>

#include <drm/gpu_scheduler.h>

/*
 * DOC: Mock DRM scheduler data structures
 *
 * drm_mock_* data structures are used to implement a mock "GPU".
 *
 * They subclass the core DRM scheduler objects and add their data on top, which
 * enables tracking the submitted jobs and simulating their execution with the
 * attributes as specified by the test case.
 */

/**
 * struct drm_mock_scheduler - implements a trivial mock GPU execution engine
 *
 * @base: DRM scheduler base class
 * @test: Backpointer to owning the kunit test case
 * @lock: Lock to protect the simulated @hw_timeline, @job_list and @done_list
 * @job_list: List of jobs submitted to the mock GPU
 * @done_list: List of jobs completed by the mock GPU
 * @hw_timeline: Simulated hardware timeline has a @context, @next_seqno and
 *		 @cur_seqno for implementing a struct dma_fence signaling the
 *		 simulated job completion.
 *
 * Trivial mock GPU execution engine tracks submitted jobs and enables
 * completing them strictly in submission order.
 */
struct drm_mock_scheduler {
	struct drm_gpu_scheduler base;

	struct kunit		*test;

	spinlock_t		lock;
	struct list_head	job_list;
	struct list_head	done_list;

	struct {
		u64		context;
		atomic_t	next_seqno;
		unsigned int	cur_seqno;
	} hw_timeline;
};

/**
 * struct drm_mock_sched_entity - implements a mock GPU sched entity
 *
 * @base: DRM scheduler entity base class
 * @test: Backpointer to owning the kunit test case
 *
 * Mock GPU sched entity is used by the test cases to submit jobs to the mock
 * scheduler.
 */
struct drm_mock_sched_entity {
	struct drm_sched_entity base;

	struct kunit		*test;
};

/**
 * struct drm_mock_sched_job - implements a mock GPU job
 *
 * @base: DRM sched job base class
 * @done: Completion signaling job completion.
 * @flags: Flags designating job state.
 * @link: List head element used by job tracking by the drm_mock_scheduler
 * @timer: Timer used for simulating job execution duration
 * @duration_us: Simulated job duration in micro seconds, or zero if in manual
 *		 timeline advance mode
 * @finish_at: Absolute time when the jobs with set duration will complete
 * @lock: Lock used for @hw_fence
 * @hw_fence: Fence returned to DRM scheduler as the hardware fence
 * @test: Backpointer to owning the kunit test case
 *
 * Mock GPU sched job is used by the test cases to submit jobs to the mock
 * scheduler.
 */
struct drm_mock_sched_job {
	struct drm_sched_job	base;

	struct completion	done;

#define DRM_MOCK_SCHED_JOB_DONE		0x1
#define DRM_MOCK_SCHED_JOB_TIMEDOUT	0x2
	unsigned long		flags;

	struct list_head	link;
	struct hrtimer		timer;

	unsigned int		duration_us;
	ktime_t			finish_at;

	spinlock_t		lock;
	struct dma_fence	hw_fence;

	struct kunit		*test;
};

static inline struct drm_mock_scheduler *
drm_sched_to_mock_sched(struct drm_gpu_scheduler *sched)
{
	return container_of(sched, struct drm_mock_scheduler, base);
};

static inline struct drm_mock_sched_entity *
drm_sched_entity_to_mock_entity(struct drm_sched_entity *sched_entity)
{
	return container_of(sched_entity, struct drm_mock_sched_entity, base);
};

static inline struct drm_mock_sched_job *
drm_sched_job_to_mock_job(struct drm_sched_job *sched_job)
{
	return container_of(sched_job, struct drm_mock_sched_job, base);
};

struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test,
					      long timeout);
void drm_mock_sched_fini(struct drm_mock_scheduler *sched);
unsigned int drm_mock_sched_advance(struct drm_mock_scheduler *sched,
				    unsigned int num);

struct drm_mock_sched_entity *
drm_mock_sched_entity_new(struct kunit *test,
			  enum drm_sched_priority priority,
			  struct drm_mock_scheduler *sched);
void drm_mock_sched_entity_free(struct drm_mock_sched_entity *entity);

struct drm_mock_sched_job *
drm_mock_sched_job_new(struct kunit *test,
		       struct drm_mock_sched_entity *entity);

/**
 * drm_mock_sched_job_submit - Arm and submit a job in one go
 *
 * @job: Job to arm and submit
 */
static inline void drm_mock_sched_job_submit(struct drm_mock_sched_job *job)
{
	drm_sched_job_arm(&job->base);
	drm_sched_entity_push_job(&job->base);
}

/**
 * drm_mock_sched_job_set_duration_us - Set a job duration
 *
 * @job: Job to set the duration for
 * @duration_us: Duration in micro seconds
 *
 * Jobs with duration set will be automatically completed by the mock scheduler
 * as the timeline progresses, unless a job without a set duration is
 * encountered in the timelime in which case calling drm_mock_sched_advance()
 * will be required to bump the timeline.
 */
static inline void
drm_mock_sched_job_set_duration_us(struct drm_mock_sched_job *job,
				   unsigned int duration_us)
{
	job->duration_us = duration_us;
}

/**
 * drm_mock_sched_job_is_finished - Check if a job is finished
 *
 * @job: Job to check
 *
 * Returns: true if finished
 */
static inline bool
drm_mock_sched_job_is_finished(struct drm_mock_sched_job *job)
{
	return job->flags & DRM_MOCK_SCHED_JOB_DONE;
}

/**
 * drm_mock_sched_job_wait_finished - Wait until a job is finished
 *
 * @job: Job to wait for
 * @timeout: Wait time in jiffies
 *
 * Returns: true if finished within the timeout provided, otherwise false
 */
static inline bool
drm_mock_sched_job_wait_finished(struct drm_mock_sched_job *job, long timeout)
{
	if (job->flags & DRM_MOCK_SCHED_JOB_DONE)
		return true;

	return wait_for_completion_timeout(&job->done, timeout) != 0;
}

/**
 * drm_mock_sched_job_wait_scheduled - Wait until a job is scheduled
 *
 * @job: Job to wait for
 * @timeout: Wait time in jiffies
 *
 * Returns: true if scheduled within the timeout provided, otherwise false
 */
static inline bool
drm_mock_sched_job_wait_scheduled(struct drm_mock_sched_job *job, long timeout)
{
	KUNIT_ASSERT_EQ(job->test, job->flags & DRM_MOCK_SCHED_JOB_DONE, 0);

	return dma_fence_wait_timeout(&job->base.s_fence->scheduled,
				      false,
				      timeout) != 0;
}

#endif