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