1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright © 2019 Intel Corporation
4 */
5
6#include <drm/drm_print.h>
7
8#include "i915_selftest.h"
9#include "intel_engine_heartbeat.h"
10#include "intel_engine_pm.h"
11#include "intel_gt.h"
12
13#include "gem/selftests/mock_context.h"
14#include "selftests/igt_flush_test.h"
15#include "selftests/mock_drm.h"
16
17static int request_sync(struct i915_request *rq)
18{
19 struct intel_timeline *tl = i915_request_timeline(rq);
20 long timeout;
21 int err = 0;
22
23 intel_timeline_get(timeline: tl);
24 i915_request_get(rq);
25
26 /* Opencode i915_request_add() so we can keep the timeline locked. */
27 __i915_request_commit(request: rq);
28 rq->sched.attr.priority = I915_PRIORITY_BARRIER;
29 __i915_request_queue_bh(rq);
30
31 timeout = i915_request_wait(rq, flags: 0, HZ / 10);
32 if (timeout < 0)
33 err = timeout;
34 else
35 i915_request_retire_upto(rq);
36
37 lockdep_unpin_lock(&tl->mutex, rq->cookie);
38 mutex_unlock(lock: &tl->mutex);
39
40 i915_request_put(rq);
41 intel_timeline_put(timeline: tl);
42
43 return err;
44}
45
46static int context_sync(struct intel_context *ce)
47{
48 struct intel_timeline *tl = ce->timeline;
49 int err = 0;
50
51 mutex_lock(&tl->mutex);
52 do {
53 struct i915_request *rq;
54 long timeout;
55
56 if (list_empty(head: &tl->requests))
57 break;
58
59 rq = list_last_entry(&tl->requests, typeof(*rq), link);
60 i915_request_get(rq);
61
62 timeout = i915_request_wait(rq, flags: 0, HZ / 10);
63 if (timeout < 0)
64 err = timeout;
65 else
66 i915_request_retire_upto(rq);
67
68 i915_request_put(rq);
69 } while (!err);
70 mutex_unlock(lock: &tl->mutex);
71
72 /* Wait for all barriers to complete (remote CPU) before we check */
73 i915_active_unlock_wait(ref: &ce->active);
74 return err;
75}
76
77static int __live_context_size(struct intel_engine_cs *engine)
78{
79 struct intel_context *ce;
80 struct i915_request *rq;
81 void *vaddr;
82 int err;
83
84 ce = intel_context_create(engine);
85 if (IS_ERR(ptr: ce))
86 return PTR_ERR(ptr: ce);
87
88 err = intel_context_pin(ce);
89 if (err)
90 goto err;
91
92 vaddr = i915_gem_object_pin_map_unlocked(obj: ce->state->obj,
93 type: intel_gt_coherent_map_type(gt: engine->gt,
94 obj: ce->state->obj,
95 always_coherent: false));
96 if (IS_ERR(ptr: vaddr)) {
97 err = PTR_ERR(ptr: vaddr);
98 intel_context_unpin(ce);
99 goto err;
100 }
101
102 /*
103 * Note that execlists also applies a redzone which it checks on
104 * context unpin when debugging. We are using the same location
105 * and same poison value so that our checks overlap. Despite the
106 * redundancy, we want to keep this little selftest so that we
107 * get coverage of any and all submission backends, and we can
108 * always extend this test to ensure we trick the HW into a
109 * compromising position wrt to the various sections that need
110 * to be written into the context state.
111 *
112 * TLDR; this overlaps with the execlists redzone.
113 */
114 vaddr += engine->context_size - I915_GTT_PAGE_SIZE;
115 memset(vaddr, POISON_INUSE, I915_GTT_PAGE_SIZE);
116
117 rq = intel_context_create_request(ce);
118 intel_context_unpin(ce);
119 if (IS_ERR(ptr: rq)) {
120 err = PTR_ERR(ptr: rq);
121 goto err_unpin;
122 }
123
124 err = request_sync(rq);
125 if (err)
126 goto err_unpin;
127
128 /* Force the context switch */
129 rq = intel_engine_create_kernel_request(engine);
130 if (IS_ERR(ptr: rq)) {
131 err = PTR_ERR(ptr: rq);
132 goto err_unpin;
133 }
134 err = request_sync(rq);
135 if (err)
136 goto err_unpin;
137
138 if (memchr_inv(p: vaddr, POISON_INUSE, I915_GTT_PAGE_SIZE)) {
139 pr_err("%s context overwrote trailing red-zone!", engine->name);
140 err = -EINVAL;
141 }
142
143err_unpin:
144 i915_gem_object_unpin_map(obj: ce->state->obj);
145err:
146 intel_context_put(ce);
147 return err;
148}
149
150static int live_context_size(void *arg)
151{
152 struct intel_gt *gt = arg;
153 struct intel_engine_cs *engine;
154 enum intel_engine_id id;
155 int err = 0;
156
157 /*
158 * Check that our context sizes are correct by seeing if the
159 * HW tries to write past the end of one.
160 */
161
162 for_each_engine(engine, gt, id) {
163 struct file *saved;
164
165 if (!engine->context_size)
166 continue;
167
168 intel_engine_pm_get(engine);
169
170 /*
171 * Hide the old default state -- we lie about the context size
172 * and get confused when the default state is smaller than
173 * expected. For our do nothing request, inheriting the
174 * active state is sufficient, we are only checking that we
175 * don't use more than we planned.
176 */
177 saved = fetch_and_zero(&engine->default_state);
178
179 /* Overlaps with the execlists redzone */
180 engine->context_size += I915_GTT_PAGE_SIZE;
181
182 err = __live_context_size(engine);
183
184 engine->context_size -= I915_GTT_PAGE_SIZE;
185
186 engine->default_state = saved;
187
188 intel_engine_pm_put(engine);
189
190 if (err)
191 break;
192 }
193
194 return err;
195}
196
197static int __live_active_context(struct intel_engine_cs *engine)
198{
199 unsigned long saved_heartbeat;
200 struct intel_context *ce;
201 int pass;
202 int err;
203
204 /*
205 * We keep active contexts alive until after a subsequent context
206 * switch as the final write from the context-save will be after
207 * we retire the final request. We track when we unpin the context,
208 * under the presumption that the final pin is from the last request,
209 * and instead of immediately unpinning the context, we add a task
210 * to unpin the context from the next idle-barrier.
211 *
212 * This test makes sure that the context is kept alive until a
213 * subsequent idle-barrier (emitted when the engine wakeref hits 0
214 * with no more outstanding requests).
215 *
216 * In GuC submission mode we don't use idle barriers and we instead
217 * get a message from the GuC to signal that it is safe to unpin the
218 * context from memory.
219 */
220 if (intel_engine_uses_guc(engine))
221 return 0;
222
223 if (intel_engine_pm_is_awake(engine)) {
224 pr_err("%s is awake before starting %s!\n",
225 engine->name, __func__);
226 return -EINVAL;
227 }
228
229 ce = intel_context_create(engine);
230 if (IS_ERR(ptr: ce))
231 return PTR_ERR(ptr: ce);
232
233 saved_heartbeat = engine->props.heartbeat_interval_ms;
234 engine->props.heartbeat_interval_ms = 0;
235
236 for (pass = 0; pass <= 2; pass++) {
237 struct i915_request *rq;
238
239 intel_engine_pm_get(engine);
240
241 rq = intel_context_create_request(ce);
242 if (IS_ERR(ptr: rq)) {
243 err = PTR_ERR(ptr: rq);
244 goto out_engine;
245 }
246
247 err = request_sync(rq);
248 if (err)
249 goto out_engine;
250
251 /* Context will be kept active until after an idle-barrier. */
252 if (i915_active_is_idle(ref: &ce->active)) {
253 pr_err("context is not active; expected idle-barrier (%s pass %d)\n",
254 engine->name, pass);
255 err = -EINVAL;
256 goto out_engine;
257 }
258
259 if (!intel_engine_pm_is_awake(engine)) {
260 pr_err("%s is asleep before idle-barrier\n",
261 engine->name);
262 err = -EINVAL;
263 goto out_engine;
264 }
265
266out_engine:
267 intel_engine_pm_put(engine);
268 if (err)
269 goto err;
270 }
271
272 /* Now make sure our idle-barriers are flushed */
273 err = intel_engine_flush_barriers(engine);
274 if (err)
275 goto err;
276
277 /* Wait for the barrier and in the process wait for engine to park */
278 err = context_sync(ce: engine->kernel_context);
279 if (err)
280 goto err;
281
282 if (!i915_active_is_idle(ref: &ce->active)) {
283 pr_err("context is still active!");
284 err = -EINVAL;
285 }
286
287 intel_engine_pm_flush(engine);
288
289 if (intel_engine_pm_is_awake(engine)) {
290 struct drm_printer p = drm_dbg_printer(drm: &engine->i915->drm,
291 category: DRM_UT_DRIVER, NULL);
292
293 intel_engine_dump(engine, m: &p,
294 header: "%s is still awake:%d after idle-barriers\n",
295 engine->name,
296 atomic_read(v: &engine->wakeref.count));
297 GEM_TRACE_DUMP();
298
299 err = -EINVAL;
300 goto err;
301 }
302
303err:
304 engine->props.heartbeat_interval_ms = saved_heartbeat;
305 intel_context_put(ce);
306 return err;
307}
308
309static int live_active_context(void *arg)
310{
311 struct intel_gt *gt = arg;
312 struct intel_engine_cs *engine;
313 enum intel_engine_id id;
314 int err = 0;
315
316 for_each_engine(engine, gt, id) {
317 err = __live_active_context(engine);
318 if (err)
319 break;
320
321 err = igt_flush_test(i915: gt->i915);
322 if (err)
323 break;
324 }
325
326 return err;
327}
328
329static int __remote_sync(struct intel_context *ce, struct intel_context *remote)
330{
331 struct i915_request *rq;
332 int err;
333
334 err = intel_context_pin(ce: remote);
335 if (err)
336 return err;
337
338 rq = intel_context_create_request(ce);
339 if (IS_ERR(ptr: rq)) {
340 err = PTR_ERR(ptr: rq);
341 goto unpin;
342 }
343
344 err = intel_context_prepare_remote_request(ce: remote, rq);
345 if (err) {
346 i915_request_add(rq);
347 goto unpin;
348 }
349
350 err = request_sync(rq);
351
352unpin:
353 intel_context_unpin(ce: remote);
354 return err;
355}
356
357static int __live_remote_context(struct intel_engine_cs *engine)
358{
359 struct intel_context *local, *remote;
360 unsigned long saved_heartbeat;
361 int pass;
362 int err;
363
364 /*
365 * Check that our idle barriers do not interfere with normal
366 * activity tracking. In particular, check that operating
367 * on the context image remotely (intel_context_prepare_remote_request),
368 * which inserts foreign fences into intel_context.active, does not
369 * clobber the idle-barrier.
370 *
371 * In GuC submission mode we don't use idle barriers.
372 */
373 if (intel_engine_uses_guc(engine))
374 return 0;
375
376 if (intel_engine_pm_is_awake(engine)) {
377 pr_err("%s is awake before starting %s!\n",
378 engine->name, __func__);
379 return -EINVAL;
380 }
381
382 remote = intel_context_create(engine);
383 if (IS_ERR(ptr: remote))
384 return PTR_ERR(ptr: remote);
385
386 local = intel_context_create(engine);
387 if (IS_ERR(ptr: local)) {
388 err = PTR_ERR(ptr: local);
389 goto err_remote;
390 }
391
392 saved_heartbeat = engine->props.heartbeat_interval_ms;
393 engine->props.heartbeat_interval_ms = 0;
394 intel_engine_pm_get(engine);
395
396 for (pass = 0; pass <= 2; pass++) {
397 err = __remote_sync(ce: local, remote);
398 if (err)
399 break;
400
401 err = __remote_sync(ce: engine->kernel_context, remote);
402 if (err)
403 break;
404
405 if (i915_active_is_idle(ref: &remote->active)) {
406 pr_err("remote context is not active; expected idle-barrier (%s pass %d)\n",
407 engine->name, pass);
408 err = -EINVAL;
409 break;
410 }
411 }
412
413 intel_engine_pm_put(engine);
414 engine->props.heartbeat_interval_ms = saved_heartbeat;
415
416 intel_context_put(ce: local);
417err_remote:
418 intel_context_put(ce: remote);
419 return err;
420}
421
422static int live_remote_context(void *arg)
423{
424 struct intel_gt *gt = arg;
425 struct intel_engine_cs *engine;
426 enum intel_engine_id id;
427 int err = 0;
428
429 for_each_engine(engine, gt, id) {
430 err = __live_remote_context(engine);
431 if (err)
432 break;
433
434 err = igt_flush_test(i915: gt->i915);
435 if (err)
436 break;
437 }
438
439 return err;
440}
441
442int intel_context_live_selftests(struct drm_i915_private *i915)
443{
444 static const struct i915_subtest tests[] = {
445 SUBTEST(live_context_size),
446 SUBTEST(live_active_context),
447 SUBTEST(live_remote_context),
448 };
449 struct intel_gt *gt = to_gt(i915);
450
451 if (intel_gt_is_wedged(gt))
452 return 0;
453
454 return intel_gt_live_subtests(tests, gt);
455}
456

source code of linux/drivers/gpu/drm/i915/gt/selftest_context.c