Commit | Line | Data |
---|---|---|
791ff39a | 1 | /* |
10be98a7 | 2 | * SPDX-License-Identifier: MIT |
791ff39a | 3 | * |
10be98a7 | 4 | * Copyright © 2017 Intel Corporation |
791ff39a CW |
5 | */ |
6 | ||
a47cd45a CW |
7 | #include <linux/prime_numbers.h> |
8 | ||
10be98a7 | 9 | #include "gem/i915_gem_pm.h" |
cb823ed9 | 10 | #include "gt/intel_gt.h" |
66101975 | 11 | #include "gt/intel_gt_requests.h" |
112ed2d3 CW |
12 | #include "gt/intel_reset.h" |
13 | #include "i915_selftest.h" | |
14 | ||
10be98a7 CW |
15 | #include "gem/selftests/igt_gem_utils.h" |
16 | #include "selftests/i915_random.h" | |
17 | #include "selftests/igt_flush_test.h" | |
18 | #include "selftests/igt_live_test.h" | |
19 | #include "selftests/igt_reset.h" | |
20 | #include "selftests/igt_spinner.h" | |
21 | #include "selftests/mock_drm.h" | |
22 | #include "selftests/mock_gem_device.h" | |
791ff39a | 23 | |
791ff39a | 24 | #include "huge_gem_object.h" |
10be98a7 | 25 | #include "igt_gem_utils.h" |
791ff39a CW |
26 | |
27 | #define DW_PER_PAGE (PAGE_SIZE / sizeof(u32)) | |
28 | ||
a47cd45a CW |
29 | static int live_nop_switch(void *arg) |
30 | { | |
31 | const unsigned int nctx = 1024; | |
32 | struct drm_i915_private *i915 = arg; | |
33 | struct intel_engine_cs *engine; | |
34 | struct i915_gem_context **ctx; | |
35 | enum intel_engine_id id; | |
e4a8c813 | 36 | struct igt_live_test t; |
a47cd45a | 37 | struct drm_file *file; |
a47cd45a CW |
38 | unsigned long n; |
39 | int err = -ENODEV; | |
40 | ||
41 | /* | |
42 | * Create as many contexts as we can feasibly get away with | |
43 | * and check we can switch between them rapidly. | |
44 | * | |
45 | * Serves as very simple stress test for submission and HW switching | |
46 | * between contexts. | |
47 | */ | |
48 | ||
49 | if (!DRIVER_CAPS(i915)->has_logical_contexts) | |
50 | return 0; | |
51 | ||
52 | file = mock_file(i915); | |
53 | if (IS_ERR(file)) | |
54 | return PTR_ERR(file); | |
55 | ||
a47cd45a CW |
56 | ctx = kcalloc(nctx, sizeof(*ctx), GFP_KERNEL); |
57 | if (!ctx) { | |
58 | err = -ENOMEM; | |
a4e7ccda | 59 | goto out_file; |
a47cd45a CW |
60 | } |
61 | ||
62 | for (n = 0; n < nctx; n++) { | |
3aa9945a | 63 | ctx[n] = live_context(i915, file); |
a47cd45a CW |
64 | if (IS_ERR(ctx[n])) { |
65 | err = PTR_ERR(ctx[n]); | |
a4e7ccda | 66 | goto out_file; |
a47cd45a CW |
67 | } |
68 | } | |
69 | ||
70 | for_each_engine(engine, i915, id) { | |
71 | struct i915_request *rq; | |
72 | unsigned long end_time, prime; | |
73 | ktime_t times[2] = {}; | |
74 | ||
75 | times[0] = ktime_get_raw(); | |
76 | for (n = 0; n < nctx; n++) { | |
46472b3e | 77 | rq = igt_request_alloc(ctx[n], engine); |
a47cd45a CW |
78 | if (IS_ERR(rq)) { |
79 | err = PTR_ERR(rq); | |
a4e7ccda | 80 | goto out_file; |
a47cd45a CW |
81 | } |
82 | i915_request_add(rq); | |
83 | } | |
2f530945 | 84 | if (i915_request_wait(rq, 0, HZ / 5) < 0) { |
a47cd45a | 85 | pr_err("Failed to populated %d contexts\n", nctx); |
cb823ed9 | 86 | intel_gt_set_wedged(&i915->gt); |
a47cd45a | 87 | err = -EIO; |
a4e7ccda | 88 | goto out_file; |
a47cd45a CW |
89 | } |
90 | ||
91 | times[1] = ktime_get_raw(); | |
92 | ||
93 | pr_info("Populated %d contexts on %s in %lluns\n", | |
94 | nctx, engine->name, ktime_to_ns(times[1] - times[0])); | |
95 | ||
e4a8c813 | 96 | err = igt_live_test_begin(&t, i915, __func__, engine->name); |
a47cd45a | 97 | if (err) |
a4e7ccda | 98 | goto out_file; |
a47cd45a CW |
99 | |
100 | end_time = jiffies + i915_selftest.timeout_jiffies; | |
101 | for_each_prime_number_from(prime, 2, 8192) { | |
102 | times[1] = ktime_get_raw(); | |
103 | ||
104 | for (n = 0; n < prime; n++) { | |
46472b3e | 105 | rq = igt_request_alloc(ctx[n % nctx], engine); |
a47cd45a CW |
106 | if (IS_ERR(rq)) { |
107 | err = PTR_ERR(rq); | |
a4e7ccda | 108 | goto out_file; |
a47cd45a CW |
109 | } |
110 | ||
111 | /* | |
112 | * This space is left intentionally blank. | |
113 | * | |
114 | * We do not actually want to perform any | |
115 | * action with this request, we just want | |
116 | * to measure the latency in allocation | |
117 | * and submission of our breadcrumbs - | |
118 | * ensuring that the bare request is sufficient | |
119 | * for the system to work (i.e. proper HEAD | |
120 | * tracking of the rings, interrupt handling, | |
121 | * etc). It also gives us the lowest bounds | |
122 | * for latency. | |
123 | */ | |
124 | ||
125 | i915_request_add(rq); | |
126 | } | |
2f530945 | 127 | if (i915_request_wait(rq, 0, HZ / 5) < 0) { |
a47cd45a CW |
128 | pr_err("Switching between %ld contexts timed out\n", |
129 | prime); | |
cb823ed9 | 130 | intel_gt_set_wedged(&i915->gt); |
a47cd45a CW |
131 | break; |
132 | } | |
133 | ||
134 | times[1] = ktime_sub(ktime_get_raw(), times[1]); | |
135 | if (prime == 2) | |
136 | times[0] = times[1]; | |
137 | ||
138 | if (__igt_timeout(end_time, NULL)) | |
139 | break; | |
140 | } | |
141 | ||
e4a8c813 | 142 | err = igt_live_test_end(&t); |
a47cd45a | 143 | if (err) |
a4e7ccda | 144 | goto out_file; |
a47cd45a CW |
145 | |
146 | pr_info("Switch latencies on %s: 1 = %lluns, %lu = %lluns\n", | |
147 | engine->name, | |
148 | ktime_to_ns(times[0]), | |
149 | prime - 1, div64_u64(ktime_to_ns(times[1]), prime - 1)); | |
150 | } | |
151 | ||
a4e7ccda | 152 | out_file: |
a47cd45a CW |
153 | mock_file_free(i915, file); |
154 | return err; | |
155 | } | |
156 | ||
50d16d44 CW |
157 | struct parallel_switch { |
158 | struct task_struct *tsk; | |
159 | struct intel_context *ce[2]; | |
160 | }; | |
161 | ||
162 | static int __live_parallel_switch1(void *data) | |
163 | { | |
164 | struct parallel_switch *arg = data; | |
50d16d44 CW |
165 | IGT_TIMEOUT(end_time); |
166 | unsigned long count; | |
167 | ||
168 | count = 0; | |
169 | do { | |
170 | struct i915_request *rq = NULL; | |
171 | int err, n; | |
172 | ||
173 | for (n = 0; n < ARRAY_SIZE(arg->ce); n++) { | |
174 | i915_request_put(rq); | |
175 | ||
50d16d44 | 176 | rq = i915_request_create(arg->ce[n]); |
7e805762 | 177 | if (IS_ERR(rq)) |
50d16d44 | 178 | return PTR_ERR(rq); |
50d16d44 CW |
179 | |
180 | i915_request_get(rq); | |
181 | i915_request_add(rq); | |
50d16d44 CW |
182 | } |
183 | ||
184 | err = 0; | |
185 | if (i915_request_wait(rq, 0, HZ / 5) < 0) | |
186 | err = -ETIME; | |
187 | i915_request_put(rq); | |
188 | if (err) | |
189 | return err; | |
190 | ||
191 | count++; | |
192 | } while (!__igt_timeout(end_time, NULL)); | |
193 | ||
194 | pr_info("%s: %lu switches (sync)\n", arg->ce[0]->engine->name, count); | |
195 | return 0; | |
196 | } | |
197 | ||
198 | static int __live_parallel_switchN(void *data) | |
199 | { | |
200 | struct parallel_switch *arg = data; | |
50d16d44 CW |
201 | IGT_TIMEOUT(end_time); |
202 | unsigned long count; | |
203 | int n; | |
204 | ||
205 | count = 0; | |
206 | do { | |
207 | for (n = 0; n < ARRAY_SIZE(arg->ce); n++) { | |
208 | struct i915_request *rq; | |
209 | ||
50d16d44 | 210 | rq = i915_request_create(arg->ce[n]); |
7e805762 | 211 | if (IS_ERR(rq)) |
50d16d44 | 212 | return PTR_ERR(rq); |
50d16d44 CW |
213 | |
214 | i915_request_add(rq); | |
50d16d44 CW |
215 | } |
216 | ||
217 | count++; | |
218 | } while (!__igt_timeout(end_time, NULL)); | |
219 | ||
220 | pr_info("%s: %lu switches (many)\n", arg->ce[0]->engine->name, count); | |
221 | return 0; | |
222 | } | |
223 | ||
224 | static int live_parallel_switch(void *arg) | |
225 | { | |
226 | struct drm_i915_private *i915 = arg; | |
227 | static int (* const func[])(void *arg) = { | |
228 | __live_parallel_switch1, | |
229 | __live_parallel_switchN, | |
230 | NULL, | |
231 | }; | |
232 | struct parallel_switch *data = NULL; | |
233 | struct i915_gem_engines *engines; | |
234 | struct i915_gem_engines_iter it; | |
235 | int (* const *fn)(void *arg); | |
236 | struct i915_gem_context *ctx; | |
237 | struct intel_context *ce; | |
238 | struct drm_file *file; | |
239 | int n, m, count; | |
240 | int err = 0; | |
241 | ||
242 | /* | |
243 | * Check we can process switches on all engines simultaneously. | |
244 | */ | |
245 | ||
246 | if (!DRIVER_CAPS(i915)->has_logical_contexts) | |
247 | return 0; | |
248 | ||
249 | file = mock_file(i915); | |
250 | if (IS_ERR(file)) | |
251 | return PTR_ERR(file); | |
252 | ||
50d16d44 CW |
253 | ctx = live_context(i915, file); |
254 | if (IS_ERR(ctx)) { | |
255 | err = PTR_ERR(ctx); | |
a4e7ccda | 256 | goto out_file; |
50d16d44 CW |
257 | } |
258 | ||
259 | engines = i915_gem_context_lock_engines(ctx); | |
260 | count = engines->num_engines; | |
261 | ||
262 | data = kcalloc(count, sizeof(*data), GFP_KERNEL); | |
263 | if (!data) { | |
264 | i915_gem_context_unlock_engines(ctx); | |
265 | err = -ENOMEM; | |
a4e7ccda | 266 | goto out; |
50d16d44 CW |
267 | } |
268 | ||
269 | m = 0; /* Use the first context as our template for the engines */ | |
270 | for_each_gem_engine(ce, engines, it) { | |
271 | err = intel_context_pin(ce); | |
272 | if (err) { | |
273 | i915_gem_context_unlock_engines(ctx); | |
a4e7ccda | 274 | goto out; |
50d16d44 CW |
275 | } |
276 | data[m++].ce[0] = intel_context_get(ce); | |
277 | } | |
278 | i915_gem_context_unlock_engines(ctx); | |
279 | ||
280 | /* Clone the same set of engines into the other contexts */ | |
281 | for (n = 1; n < ARRAY_SIZE(data->ce); n++) { | |
282 | ctx = live_context(i915, file); | |
283 | if (IS_ERR(ctx)) { | |
284 | err = PTR_ERR(ctx); | |
a4e7ccda | 285 | goto out; |
50d16d44 CW |
286 | } |
287 | ||
288 | for (m = 0; m < count; m++) { | |
289 | if (!data[m].ce[0]) | |
290 | continue; | |
291 | ||
292 | ce = intel_context_create(ctx, data[m].ce[0]->engine); | |
293 | if (IS_ERR(ce)) | |
a4e7ccda | 294 | goto out; |
50d16d44 CW |
295 | |
296 | err = intel_context_pin(ce); | |
297 | if (err) { | |
298 | intel_context_put(ce); | |
a4e7ccda | 299 | goto out; |
50d16d44 CW |
300 | } |
301 | ||
302 | data[m].ce[n] = ce; | |
303 | } | |
304 | } | |
305 | ||
50d16d44 CW |
306 | for (fn = func; !err && *fn; fn++) { |
307 | struct igt_live_test t; | |
308 | int n; | |
309 | ||
310 | mutex_lock(&i915->drm.struct_mutex); | |
311 | err = igt_live_test_begin(&t, i915, __func__, ""); | |
312 | mutex_unlock(&i915->drm.struct_mutex); | |
313 | if (err) | |
314 | break; | |
315 | ||
316 | for (n = 0; n < count; n++) { | |
317 | if (!data[n].ce[0]) | |
318 | continue; | |
319 | ||
320 | data[n].tsk = kthread_run(*fn, &data[n], | |
321 | "igt/parallel:%s", | |
322 | data[n].ce[0]->engine->name); | |
323 | if (IS_ERR(data[n].tsk)) { | |
324 | err = PTR_ERR(data[n].tsk); | |
325 | break; | |
326 | } | |
327 | get_task_struct(data[n].tsk); | |
328 | } | |
329 | ||
330 | for (n = 0; n < count; n++) { | |
331 | int status; | |
332 | ||
333 | if (IS_ERR_OR_NULL(data[n].tsk)) | |
334 | continue; | |
335 | ||
336 | status = kthread_stop(data[n].tsk); | |
337 | if (status && !err) | |
338 | err = status; | |
339 | ||
340 | put_task_struct(data[n].tsk); | |
341 | data[n].tsk = NULL; | |
342 | } | |
343 | ||
344 | mutex_lock(&i915->drm.struct_mutex); | |
345 | if (igt_live_test_end(&t)) | |
346 | err = -EIO; | |
347 | mutex_unlock(&i915->drm.struct_mutex); | |
348 | } | |
349 | ||
a4e7ccda | 350 | out: |
50d16d44 CW |
351 | for (n = 0; n < count; n++) { |
352 | for (m = 0; m < ARRAY_SIZE(data->ce); m++) { | |
353 | if (!data[n].ce[m]) | |
354 | continue; | |
355 | ||
356 | intel_context_unpin(data[n].ce[m]); | |
357 | intel_context_put(data[n].ce[m]); | |
358 | } | |
359 | } | |
50d16d44 | 360 | kfree(data); |
a4e7ccda | 361 | out_file: |
50d16d44 CW |
362 | mock_file_free(i915, file); |
363 | return err; | |
364 | } | |
365 | ||
791ff39a CW |
366 | static unsigned long real_page_count(struct drm_i915_gem_object *obj) |
367 | { | |
368 | return huge_gem_object_phys_size(obj) >> PAGE_SHIFT; | |
369 | } | |
370 | ||
371 | static unsigned long fake_page_count(struct drm_i915_gem_object *obj) | |
372 | { | |
373 | return huge_gem_object_dma_size(obj) >> PAGE_SHIFT; | |
374 | } | |
375 | ||
75b974a8 CW |
376 | static int gpu_fill(struct intel_context *ce, |
377 | struct drm_i915_gem_object *obj, | |
791ff39a CW |
378 | unsigned int dw) |
379 | { | |
791ff39a | 380 | struct i915_vma *vma; |
791ff39a CW |
381 | int err; |
382 | ||
75b974a8 CW |
383 | GEM_BUG_ON(obj->base.size > ce->vm->total); |
384 | GEM_BUG_ON(!intel_engine_can_store_dword(ce->engine)); | |
791ff39a | 385 | |
75b974a8 | 386 | vma = i915_vma_instance(obj, ce->vm, NULL); |
791ff39a CW |
387 | if (IS_ERR(vma)) |
388 | return PTR_ERR(vma); | |
389 | ||
791ff39a CW |
390 | err = i915_vma_pin(vma, 0, 0, PIN_HIGH | PIN_USER); |
391 | if (err) | |
392 | return err; | |
393 | ||
18851edf MA |
394 | /* |
395 | * Within the GTT the huge objects maps every page onto | |
791ff39a CW |
396 | * its 1024 real pages (using phys_pfn = dma_pfn % 1024). |
397 | * We set the nth dword within the page using the nth | |
398 | * mapping via the GTT - this should exercise the GTT mapping | |
399 | * whilst checking that each context provides a unique view | |
400 | * into the object. | |
401 | */ | |
75b974a8 | 402 | err = igt_gpu_fill_dw(ce, vma, |
18851edf MA |
403 | (dw * real_page_count(obj)) << PAGE_SHIFT | |
404 | (dw * sizeof(u32)), | |
405 | real_page_count(obj), | |
406 | dw); | |
791ff39a CW |
407 | i915_vma_unpin(vma); |
408 | ||
791ff39a CW |
409 | return err; |
410 | } | |
411 | ||
412 | static int cpu_fill(struct drm_i915_gem_object *obj, u32 value) | |
413 | { | |
414 | const bool has_llc = HAS_LLC(to_i915(obj->base.dev)); | |
415 | unsigned int n, m, need_flush; | |
416 | int err; | |
417 | ||
f0e4a063 | 418 | err = i915_gem_object_prepare_write(obj, &need_flush); |
791ff39a CW |
419 | if (err) |
420 | return err; | |
421 | ||
422 | for (n = 0; n < real_page_count(obj); n++) { | |
423 | u32 *map; | |
424 | ||
425 | map = kmap_atomic(i915_gem_object_get_page(obj, n)); | |
426 | for (m = 0; m < DW_PER_PAGE; m++) | |
427 | map[m] = value; | |
428 | if (!has_llc) | |
429 | drm_clflush_virt_range(map, PAGE_SIZE); | |
430 | kunmap_atomic(map); | |
431 | } | |
432 | ||
f0e4a063 | 433 | i915_gem_object_finish_access(obj); |
c0a51fd0 CK |
434 | obj->read_domains = I915_GEM_DOMAIN_GTT | I915_GEM_DOMAIN_CPU; |
435 | obj->write_domain = 0; | |
791ff39a CW |
436 | return 0; |
437 | } | |
438 | ||
e0695db7 CW |
439 | static noinline int cpu_check(struct drm_i915_gem_object *obj, |
440 | unsigned int idx, unsigned int max) | |
791ff39a CW |
441 | { |
442 | unsigned int n, m, needs_flush; | |
443 | int err; | |
444 | ||
f0e4a063 | 445 | err = i915_gem_object_prepare_read(obj, &needs_flush); |
791ff39a CW |
446 | if (err) |
447 | return err; | |
448 | ||
449 | for (n = 0; n < real_page_count(obj); n++) { | |
450 | u32 *map; | |
451 | ||
452 | map = kmap_atomic(i915_gem_object_get_page(obj, n)); | |
453 | if (needs_flush & CLFLUSH_BEFORE) | |
454 | drm_clflush_virt_range(map, PAGE_SIZE); | |
455 | ||
456 | for (m = 0; m < max; m++) { | |
457 | if (map[m] != m) { | |
e0695db7 CW |
458 | pr_err("%pS: Invalid value at object %d page %d/%ld, offset %d/%d: found %x expected %x\n", |
459 | __builtin_return_address(0), idx, | |
460 | n, real_page_count(obj), m, max, | |
461 | map[m], m); | |
791ff39a CW |
462 | err = -EINVAL; |
463 | goto out_unmap; | |
464 | } | |
465 | } | |
466 | ||
467 | for (; m < DW_PER_PAGE; m++) { | |
250f8c81 | 468 | if (map[m] != STACK_MAGIC) { |
e0695db7 CW |
469 | pr_err("%pS: Invalid value at object %d page %d, offset %d: found %x expected %x (uninitialised)\n", |
470 | __builtin_return_address(0), idx, n, m, | |
471 | map[m], STACK_MAGIC); | |
791ff39a CW |
472 | err = -EINVAL; |
473 | goto out_unmap; | |
474 | } | |
475 | } | |
476 | ||
477 | out_unmap: | |
478 | kunmap_atomic(map); | |
479 | if (err) | |
480 | break; | |
481 | } | |
482 | ||
f0e4a063 | 483 | i915_gem_object_finish_access(obj); |
791ff39a CW |
484 | return err; |
485 | } | |
486 | ||
f9eb63b9 CW |
487 | static int file_add_object(struct drm_file *file, |
488 | struct drm_i915_gem_object *obj) | |
489 | { | |
490 | int err; | |
491 | ||
492 | GEM_BUG_ON(obj->base.handle_count); | |
493 | ||
494 | /* tie the object to the drm_file for easy reaping */ | |
495 | err = idr_alloc(&file->object_idr, &obj->base, 1, 0, GFP_KERNEL); | |
496 | if (err < 0) | |
497 | return err; | |
498 | ||
499 | i915_gem_object_get(obj); | |
500 | obj->base.handle_count++; | |
501 | return 0; | |
502 | } | |
503 | ||
791ff39a | 504 | static struct drm_i915_gem_object * |
75b974a8 | 505 | create_test_object(struct i915_address_space *vm, |
791ff39a CW |
506 | struct drm_file *file, |
507 | struct list_head *objects) | |
508 | { | |
509 | struct drm_i915_gem_object *obj; | |
791ff39a | 510 | u64 size; |
791ff39a CW |
511 | int err; |
512 | ||
9e953980 | 513 | /* Keep in GEM's good graces */ |
66101975 | 514 | intel_gt_retire_requests(vm->gt); |
9e953980 | 515 | |
791ff39a CW |
516 | size = min(vm->total / 2, 1024ull * DW_PER_PAGE * PAGE_SIZE); |
517 | size = round_down(size, DW_PER_PAGE * PAGE_SIZE); | |
518 | ||
75b974a8 | 519 | obj = huge_gem_object(vm->i915, DW_PER_PAGE * PAGE_SIZE, size); |
791ff39a CW |
520 | if (IS_ERR(obj)) |
521 | return obj; | |
522 | ||
f9eb63b9 | 523 | err = file_add_object(file, obj); |
791ff39a CW |
524 | i915_gem_object_put(obj); |
525 | if (err) | |
526 | return ERR_PTR(err); | |
527 | ||
250f8c81 | 528 | err = cpu_fill(obj, STACK_MAGIC); |
791ff39a CW |
529 | if (err) { |
530 | pr_err("Failed to fill object with cpu, err=%d\n", | |
531 | err); | |
532 | return ERR_PTR(err); | |
533 | } | |
534 | ||
535 | list_add_tail(&obj->st_link, objects); | |
536 | return obj; | |
537 | } | |
538 | ||
539 | static unsigned long max_dwords(struct drm_i915_gem_object *obj) | |
540 | { | |
541 | unsigned long npages = fake_page_count(obj); | |
542 | ||
543 | GEM_BUG_ON(!IS_ALIGNED(npages, DW_PER_PAGE)); | |
544 | return npages / DW_PER_PAGE; | |
545 | } | |
546 | ||
f2085c8e CW |
547 | static void throttle_release(struct i915_request **q, int count) |
548 | { | |
549 | int i; | |
550 | ||
551 | for (i = 0; i < count; i++) { | |
552 | if (IS_ERR_OR_NULL(q[i])) | |
553 | continue; | |
554 | ||
555 | i915_request_put(fetch_and_zero(&q[i])); | |
556 | } | |
557 | } | |
558 | ||
559 | static int throttle(struct intel_context *ce, | |
560 | struct i915_request **q, int count) | |
561 | { | |
562 | int i; | |
563 | ||
564 | if (!IS_ERR_OR_NULL(q[0])) { | |
565 | if (i915_request_wait(q[0], | |
566 | I915_WAIT_INTERRUPTIBLE, | |
567 | MAX_SCHEDULE_TIMEOUT) < 0) | |
568 | return -EINTR; | |
569 | ||
570 | i915_request_put(q[0]); | |
571 | } | |
572 | ||
573 | for (i = 0; i < count - 1; i++) | |
574 | q[i] = q[i + 1]; | |
575 | ||
576 | q[i] = intel_context_create_request(ce); | |
577 | if (IS_ERR(q[i])) | |
578 | return PTR_ERR(q[i]); | |
579 | ||
580 | i915_request_get(q[i]); | |
581 | i915_request_add(q[i]); | |
582 | ||
583 | return 0; | |
584 | } | |
585 | ||
791ff39a CW |
586 | static int igt_ctx_exec(void *arg) |
587 | { | |
588 | struct drm_i915_private *i915 = arg; | |
e0695db7 CW |
589 | struct intel_engine_cs *engine; |
590 | enum intel_engine_id id; | |
6e128141 | 591 | int err = -ENODEV; |
791ff39a | 592 | |
0fdbe58c CW |
593 | /* |
594 | * Create a few different contexts (with different mm) and write | |
791ff39a CW |
595 | * through each ctx/mm using the GPU making sure those writes end |
596 | * up in the expected pages of our obj. | |
597 | */ | |
598 | ||
0fdbe58c CW |
599 | if (!DRIVER_CAPS(i915)->has_logical_contexts) |
600 | return 0; | |
601 | ||
e0695db7 CW |
602 | for_each_engine(engine, i915, id) { |
603 | struct drm_i915_gem_object *obj = NULL; | |
604 | unsigned long ncontexts, ndwords, dw; | |
f2085c8e | 605 | struct i915_request *tq[5] = {}; |
e0695db7 CW |
606 | struct igt_live_test t; |
607 | struct drm_file *file; | |
608 | IGT_TIMEOUT(end_time); | |
609 | LIST_HEAD(objects); | |
610 | ||
611 | if (!intel_engine_can_store_dword(engine)) | |
612 | continue; | |
613 | ||
614 | if (!engine->context_size) | |
615 | continue; /* No logical context support in HW */ | |
616 | ||
617 | file = mock_file(i915); | |
618 | if (IS_ERR(file)) | |
619 | return PTR_ERR(file); | |
620 | ||
e0695db7 CW |
621 | err = igt_live_test_begin(&t, i915, __func__, engine->name); |
622 | if (err) | |
a4e7ccda | 623 | goto out_file; |
e0695db7 CW |
624 | |
625 | ncontexts = 0; | |
626 | ndwords = 0; | |
627 | dw = 0; | |
628 | while (!time_after(jiffies, end_time)) { | |
629 | struct i915_gem_context *ctx; | |
75b974a8 | 630 | struct intel_context *ce; |
e0695db7 | 631 | |
c4e64881 | 632 | ctx = kernel_context(i915); |
e0695db7 CW |
633 | if (IS_ERR(ctx)) { |
634 | err = PTR_ERR(ctx); | |
a4e7ccda | 635 | goto out_file; |
e0695db7 CW |
636 | } |
637 | ||
75b974a8 | 638 | ce = i915_gem_context_get_engine(ctx, engine->legacy_idx); |
21b0c32b | 639 | GEM_BUG_ON(IS_ERR(ce)); |
75b974a8 | 640 | |
e0695db7 | 641 | if (!obj) { |
75b974a8 | 642 | obj = create_test_object(ce->vm, file, &objects); |
e0695db7 CW |
643 | if (IS_ERR(obj)) { |
644 | err = PTR_ERR(obj); | |
75b974a8 | 645 | intel_context_put(ce); |
c4e64881 | 646 | kernel_context_close(ctx); |
a4e7ccda | 647 | goto out_file; |
e0695db7 CW |
648 | } |
649 | } | |
650 | ||
75b974a8 | 651 | err = gpu_fill(ce, obj, dw); |
e0695db7 | 652 | if (err) { |
2935ed53 | 653 | pr_err("Failed to fill dword %lu [%lu/%lu] with gpu (%s) [full-ppgtt? %s], err=%d\n", |
e0695db7 | 654 | ndwords, dw, max_dwords(obj), |
2935ed53 | 655 | engine->name, |
a4e7ccda CW |
656 | yesno(!!rcu_access_pointer(ctx->vm)), |
657 | err); | |
f2085c8e | 658 | intel_context_put(ce); |
c4e64881 | 659 | kernel_context_close(ctx); |
a4e7ccda | 660 | goto out_file; |
f2085c8e CW |
661 | } |
662 | ||
663 | err = throttle(ce, tq, ARRAY_SIZE(tq)); | |
664 | if (err) { | |
665 | intel_context_put(ce); | |
c4e64881 | 666 | kernel_context_close(ctx); |
a4e7ccda | 667 | goto out_file; |
e0695db7 CW |
668 | } |
669 | ||
670 | if (++dw == max_dwords(obj)) { | |
671 | obj = NULL; | |
672 | dw = 0; | |
673 | } | |
674 | ||
675 | ndwords++; | |
676 | ncontexts++; | |
f2085c8e CW |
677 | |
678 | intel_context_put(ce); | |
c4e64881 | 679 | kernel_context_close(ctx); |
e0695db7 CW |
680 | } |
681 | ||
682 | pr_info("Submitted %lu contexts to %s, filling %lu dwords\n", | |
683 | ncontexts, engine->name, ndwords); | |
684 | ||
685 | ncontexts = dw = 0; | |
686 | list_for_each_entry(obj, &objects, st_link) { | |
687 | unsigned int rem = | |
688 | min_t(unsigned int, ndwords - dw, max_dwords(obj)); | |
689 | ||
690 | err = cpu_check(obj, ncontexts++, rem); | |
691 | if (err) | |
692 | break; | |
693 | ||
694 | dw += rem; | |
695 | } | |
696 | ||
a4e7ccda | 697 | out_file: |
f2085c8e | 698 | throttle_release(tq, ARRAY_SIZE(tq)); |
e0695db7 CW |
699 | if (igt_live_test_end(&t)) |
700 | err = -EIO; | |
e0695db7 CW |
701 | |
702 | mock_file_free(i915, file); | |
703 | if (err) | |
704 | return err; | |
6582f4f6 CW |
705 | |
706 | i915_gem_drain_freed_objects(i915); | |
e0695db7 CW |
707 | } |
708 | ||
709 | return 0; | |
710 | } | |
711 | ||
712 | static int igt_shared_ctx_exec(void *arg) | |
713 | { | |
714 | struct drm_i915_private *i915 = arg; | |
f2085c8e | 715 | struct i915_request *tq[5] = {}; |
e0695db7 CW |
716 | struct i915_gem_context *parent; |
717 | struct intel_engine_cs *engine; | |
718 | enum intel_engine_id id; | |
719 | struct igt_live_test t; | |
720 | struct drm_file *file; | |
721 | int err = 0; | |
722 | ||
723 | /* | |
724 | * Create a few different contexts with the same mm and write | |
725 | * through each ctx using the GPU making sure those writes end | |
726 | * up in the expected pages of our obj. | |
727 | */ | |
728 | if (!DRIVER_CAPS(i915)->has_logical_contexts) | |
729 | return 0; | |
730 | ||
ef47a0e0 CW |
731 | file = mock_file(i915); |
732 | if (IS_ERR(file)) | |
733 | return PTR_ERR(file); | |
734 | ||
e0695db7 CW |
735 | parent = live_context(i915, file); |
736 | if (IS_ERR(parent)) { | |
737 | err = PTR_ERR(parent); | |
a4e7ccda | 738 | goto out_file; |
e0695db7 CW |
739 | } |
740 | ||
e568ac38 | 741 | if (!parent->vm) { /* not full-ppgtt; nothing to share */ |
e0695db7 | 742 | err = 0; |
a4e7ccda | 743 | goto out_file; |
e0695db7 CW |
744 | } |
745 | ||
e4a8c813 | 746 | err = igt_live_test_begin(&t, i915, __func__, ""); |
8dd0f8d3 | 747 | if (err) |
a4e7ccda | 748 | goto out_file; |
8dd0f8d3 | 749 | |
e0695db7 CW |
750 | for_each_engine(engine, i915, id) { |
751 | unsigned long ncontexts, ndwords, dw; | |
752 | struct drm_i915_gem_object *obj = NULL; | |
753 | IGT_TIMEOUT(end_time); | |
754 | LIST_HEAD(objects); | |
791ff39a | 755 | |
e0695db7 CW |
756 | if (!intel_engine_can_store_dword(engine)) |
757 | continue; | |
791ff39a | 758 | |
e0695db7 CW |
759 | dw = 0; |
760 | ndwords = 0; | |
761 | ncontexts = 0; | |
762 | while (!time_after(jiffies, end_time)) { | |
763 | struct i915_gem_context *ctx; | |
75b974a8 | 764 | struct intel_context *ce; |
c9d08cc3 | 765 | |
e0695db7 CW |
766 | ctx = kernel_context(i915); |
767 | if (IS_ERR(ctx)) { | |
768 | err = PTR_ERR(ctx); | |
769 | goto out_test; | |
770 | } | |
0fdbe58c | 771 | |
a4e7ccda | 772 | mutex_lock(&ctx->mutex); |
e568ac38 | 773 | __assign_ppgtt(ctx, parent->vm); |
a4e7ccda | 774 | mutex_unlock(&ctx->mutex); |
f2f5c061 | 775 | |
75b974a8 | 776 | ce = i915_gem_context_get_engine(ctx, engine->legacy_idx); |
21b0c32b CW |
777 | GEM_BUG_ON(IS_ERR(ce)); |
778 | ||
ca83d584 | 779 | if (!obj) { |
75b974a8 | 780 | obj = create_test_object(parent->vm, file, &objects); |
791ff39a CW |
781 | if (IS_ERR(obj)) { |
782 | err = PTR_ERR(obj); | |
75b974a8 | 783 | intel_context_put(ce); |
e0695db7 CW |
784 | kernel_context_close(ctx); |
785 | goto out_test; | |
791ff39a CW |
786 | } |
787 | } | |
788 | ||
75b974a8 | 789 | err = gpu_fill(ce, obj, dw); |
791ff39a | 790 | if (err) { |
2935ed53 | 791 | pr_err("Failed to fill dword %lu [%lu/%lu] with gpu (%s) [full-ppgtt? %s], err=%d\n", |
791ff39a | 792 | ndwords, dw, max_dwords(obj), |
2935ed53 | 793 | engine->name, |
a4e7ccda CW |
794 | yesno(!!rcu_access_pointer(ctx->vm)), |
795 | err); | |
f2085c8e CW |
796 | intel_context_put(ce); |
797 | kernel_context_close(ctx); | |
798 | goto out_test; | |
799 | } | |
800 | ||
801 | err = throttle(ce, tq, ARRAY_SIZE(tq)); | |
802 | if (err) { | |
803 | intel_context_put(ce); | |
804 | kernel_context_close(ctx); | |
e0695db7 | 805 | goto out_test; |
791ff39a CW |
806 | } |
807 | ||
ca83d584 CW |
808 | if (++dw == max_dwords(obj)) { |
809 | obj = NULL; | |
791ff39a | 810 | dw = 0; |
ca83d584 | 811 | } |
e0695db7 | 812 | |
791ff39a | 813 | ndwords++; |
e0695db7 | 814 | ncontexts++; |
f2085c8e CW |
815 | |
816 | intel_context_put(ce); | |
817 | kernel_context_close(ctx); | |
791ff39a | 818 | } |
e0695db7 CW |
819 | pr_info("Submitted %lu contexts to %s, filling %lu dwords\n", |
820 | ncontexts, engine->name, ndwords); | |
791ff39a | 821 | |
e0695db7 CW |
822 | ncontexts = dw = 0; |
823 | list_for_each_entry(obj, &objects, st_link) { | |
824 | unsigned int rem = | |
825 | min_t(unsigned int, ndwords - dw, max_dwords(obj)); | |
791ff39a | 826 | |
e0695db7 CW |
827 | err = cpu_check(obj, ncontexts++, rem); |
828 | if (err) | |
829 | goto out_test; | |
791ff39a | 830 | |
e0695db7 CW |
831 | dw += rem; |
832 | } | |
6582f4f6 | 833 | |
6582f4f6 | 834 | i915_gem_drain_freed_objects(i915); |
791ff39a | 835 | } |
e0695db7 | 836 | out_test: |
f2085c8e | 837 | throttle_release(tq, ARRAY_SIZE(tq)); |
e4a8c813 | 838 | if (igt_live_test_end(&t)) |
7c2f5bc5 | 839 | err = -EIO; |
a4e7ccda | 840 | out_file: |
791ff39a CW |
841 | mock_file_free(i915, file); |
842 | return err; | |
843 | } | |
844 | ||
c06ee6ff TU |
845 | static struct i915_vma *rpcs_query_batch(struct i915_vma *vma) |
846 | { | |
847 | struct drm_i915_gem_object *obj; | |
848 | u32 *cmd; | |
849 | int err; | |
850 | ||
851 | if (INTEL_GEN(vma->vm->i915) < 8) | |
852 | return ERR_PTR(-EINVAL); | |
853 | ||
854 | obj = i915_gem_object_create_internal(vma->vm->i915, PAGE_SIZE); | |
855 | if (IS_ERR(obj)) | |
856 | return ERR_CAST(obj); | |
857 | ||
858 | cmd = i915_gem_object_pin_map(obj, I915_MAP_WB); | |
859 | if (IS_ERR(cmd)) { | |
860 | err = PTR_ERR(cmd); | |
861 | goto err; | |
862 | } | |
863 | ||
864 | *cmd++ = MI_STORE_REGISTER_MEM_GEN8; | |
865 | *cmd++ = i915_mmio_reg_offset(GEN8_R_PWR_CLK_STATE); | |
866 | *cmd++ = lower_32_bits(vma->node.start); | |
867 | *cmd++ = upper_32_bits(vma->node.start); | |
868 | *cmd = MI_BATCH_BUFFER_END; | |
869 | ||
a679f58d | 870 | __i915_gem_object_flush_map(obj, 0, 64); |
c06ee6ff TU |
871 | i915_gem_object_unpin_map(obj); |
872 | ||
75b974a8 CW |
873 | intel_gt_chipset_flush(vma->vm->gt); |
874 | ||
c06ee6ff TU |
875 | vma = i915_vma_instance(obj, vma->vm, NULL); |
876 | if (IS_ERR(vma)) { | |
877 | err = PTR_ERR(vma); | |
878 | goto err; | |
879 | } | |
880 | ||
881 | err = i915_vma_pin(vma, 0, 0, PIN_USER); | |
882 | if (err) | |
883 | goto err; | |
884 | ||
885 | return vma; | |
886 | ||
887 | err: | |
888 | i915_gem_object_put(obj); | |
889 | return ERR_PTR(err); | |
890 | } | |
891 | ||
892 | static int | |
893 | emit_rpcs_query(struct drm_i915_gem_object *obj, | |
1b1ae407 | 894 | struct intel_context *ce, |
c06ee6ff TU |
895 | struct i915_request **rq_out) |
896 | { | |
897 | struct i915_request *rq; | |
898 | struct i915_vma *batch; | |
899 | struct i915_vma *vma; | |
900 | int err; | |
901 | ||
1b1ae407 | 902 | GEM_BUG_ON(!intel_engine_can_store_dword(ce->engine)); |
c06ee6ff | 903 | |
f5d974f9 | 904 | vma = i915_vma_instance(obj, ce->vm, NULL); |
c06ee6ff TU |
905 | if (IS_ERR(vma)) |
906 | return PTR_ERR(vma); | |
907 | ||
6951e589 | 908 | i915_gem_object_lock(obj); |
c06ee6ff | 909 | err = i915_gem_object_set_to_gtt_domain(obj, false); |
6951e589 | 910 | i915_gem_object_unlock(obj); |
c06ee6ff TU |
911 | if (err) |
912 | return err; | |
913 | ||
914 | err = i915_vma_pin(vma, 0, 0, PIN_USER); | |
915 | if (err) | |
916 | return err; | |
917 | ||
918 | batch = rpcs_query_batch(vma); | |
919 | if (IS_ERR(batch)) { | |
920 | err = PTR_ERR(batch); | |
921 | goto err_vma; | |
922 | } | |
923 | ||
1b1ae407 | 924 | rq = i915_request_create(ce); |
c06ee6ff TU |
925 | if (IS_ERR(rq)) { |
926 | err = PTR_ERR(rq); | |
927 | goto err_batch; | |
928 | } | |
929 | ||
1b1ae407 CW |
930 | err = rq->engine->emit_bb_start(rq, |
931 | batch->node.start, batch->node.size, | |
932 | 0); | |
c06ee6ff TU |
933 | if (err) |
934 | goto err_request; | |
935 | ||
6951e589 | 936 | i915_vma_lock(batch); |
70d6894d CW |
937 | err = i915_request_await_object(rq, batch->obj, false); |
938 | if (err == 0) | |
939 | err = i915_vma_move_to_active(batch, rq, 0); | |
6951e589 | 940 | i915_vma_unlock(batch); |
c06ee6ff TU |
941 | if (err) |
942 | goto skip_request; | |
943 | ||
6951e589 | 944 | i915_vma_lock(vma); |
70d6894d CW |
945 | err = i915_request_await_object(rq, vma->obj, true); |
946 | if (err == 0) | |
947 | err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE); | |
6951e589 | 948 | i915_vma_unlock(vma); |
c06ee6ff TU |
949 | if (err) |
950 | goto skip_request; | |
951 | ||
2850748e | 952 | i915_vma_unpin_and_release(&batch, 0); |
c06ee6ff TU |
953 | i915_vma_unpin(vma); |
954 | ||
955 | *rq_out = i915_request_get(rq); | |
956 | ||
957 | i915_request_add(rq); | |
958 | ||
959 | return 0; | |
960 | ||
961 | skip_request: | |
962 | i915_request_skip(rq, err); | |
963 | err_request: | |
964 | i915_request_add(rq); | |
965 | err_batch: | |
2850748e | 966 | i915_vma_unpin_and_release(&batch, 0); |
c06ee6ff TU |
967 | err_vma: |
968 | i915_vma_unpin(vma); | |
969 | ||
970 | return err; | |
971 | } | |
972 | ||
973 | #define TEST_IDLE BIT(0) | |
974 | #define TEST_BUSY BIT(1) | |
975 | #define TEST_RESET BIT(2) | |
976 | ||
977 | static int | |
f277bc0c | 978 | __sseu_prepare(const char *name, |
c06ee6ff | 979 | unsigned int flags, |
1b1ae407 | 980 | struct intel_context *ce, |
2a4a2754 | 981 | struct igt_spinner **spin) |
c06ee6ff | 982 | { |
2a4a2754 CW |
983 | struct i915_request *rq; |
984 | int ret; | |
c06ee6ff | 985 | |
2a4a2754 CW |
986 | *spin = NULL; |
987 | if (!(flags & (TEST_BUSY | TEST_RESET))) | |
988 | return 0; | |
c06ee6ff | 989 | |
2a4a2754 CW |
990 | *spin = kzalloc(sizeof(**spin), GFP_KERNEL); |
991 | if (!*spin) | |
992 | return -ENOMEM; | |
c06ee6ff | 993 | |
f277bc0c | 994 | ret = igt_spinner_init(*spin, ce->engine->gt); |
2a4a2754 CW |
995 | if (ret) |
996 | goto err_free; | |
c06ee6ff | 997 | |
f277bc0c | 998 | rq = igt_spinner_create_request(*spin, ce, MI_NOOP); |
2a4a2754 CW |
999 | if (IS_ERR(rq)) { |
1000 | ret = PTR_ERR(rq); | |
1001 | goto err_fini; | |
1002 | } | |
c06ee6ff | 1003 | |
2a4a2754 | 1004 | i915_request_add(rq); |
c06ee6ff | 1005 | |
2a4a2754 CW |
1006 | if (!igt_wait_for_spinner(*spin, rq)) { |
1007 | pr_err("%s: Spinner failed to start!\n", name); | |
1008 | ret = -ETIMEDOUT; | |
1009 | goto err_end; | |
c06ee6ff TU |
1010 | } |
1011 | ||
2a4a2754 CW |
1012 | return 0; |
1013 | ||
1014 | err_end: | |
1015 | igt_spinner_end(*spin); | |
1016 | err_fini: | |
1017 | igt_spinner_fini(*spin); | |
1018 | err_free: | |
1019 | kfree(fetch_and_zero(spin)); | |
c06ee6ff TU |
1020 | return ret; |
1021 | } | |
1022 | ||
1023 | static int | |
f277bc0c | 1024 | __read_slice_count(struct intel_context *ce, |
c06ee6ff TU |
1025 | struct drm_i915_gem_object *obj, |
1026 | struct igt_spinner *spin, | |
1027 | u32 *rpcs) | |
1028 | { | |
1029 | struct i915_request *rq = NULL; | |
1030 | u32 s_mask, s_shift; | |
1031 | unsigned int cnt; | |
1032 | u32 *buf, val; | |
1033 | long ret; | |
1034 | ||
1b1ae407 | 1035 | ret = emit_rpcs_query(obj, ce, &rq); |
c06ee6ff TU |
1036 | if (ret) |
1037 | return ret; | |
1038 | ||
1039 | if (spin) | |
1040 | igt_spinner_end(spin); | |
1041 | ||
2f530945 | 1042 | ret = i915_request_wait(rq, 0, MAX_SCHEDULE_TIMEOUT); |
c06ee6ff TU |
1043 | i915_request_put(rq); |
1044 | if (ret < 0) | |
1045 | return ret; | |
1046 | ||
1047 | buf = i915_gem_object_pin_map(obj, I915_MAP_WB); | |
1048 | if (IS_ERR(buf)) { | |
1049 | ret = PTR_ERR(buf); | |
1050 | return ret; | |
1051 | } | |
1052 | ||
f277bc0c | 1053 | if (INTEL_GEN(ce->engine->i915) >= 11) { |
c06ee6ff TU |
1054 | s_mask = GEN11_RPCS_S_CNT_MASK; |
1055 | s_shift = GEN11_RPCS_S_CNT_SHIFT; | |
1056 | } else { | |
1057 | s_mask = GEN8_RPCS_S_CNT_MASK; | |
1058 | s_shift = GEN8_RPCS_S_CNT_SHIFT; | |
1059 | } | |
1060 | ||
1061 | val = *buf; | |
1062 | cnt = (val & s_mask) >> s_shift; | |
1063 | *rpcs = val; | |
1064 | ||
1065 | i915_gem_object_unpin_map(obj); | |
1066 | ||
1067 | return cnt; | |
1068 | } | |
1069 | ||
1070 | static int | |
1071 | __check_rpcs(const char *name, u32 rpcs, int slices, unsigned int expected, | |
1072 | const char *prefix, const char *suffix) | |
1073 | { | |
1074 | if (slices == expected) | |
1075 | return 0; | |
1076 | ||
1077 | if (slices < 0) { | |
1078 | pr_err("%s: %s read slice count failed with %d%s\n", | |
1079 | name, prefix, slices, suffix); | |
1080 | return slices; | |
1081 | } | |
1082 | ||
1083 | pr_err("%s: %s slice count %d is not %u%s\n", | |
1084 | name, prefix, slices, expected, suffix); | |
1085 | ||
1086 | pr_info("RPCS=0x%x; %u%sx%u%s\n", | |
1087 | rpcs, slices, | |
1088 | (rpcs & GEN8_RPCS_S_CNT_ENABLE) ? "*" : "", | |
1089 | (rpcs & GEN8_RPCS_SS_CNT_MASK) >> GEN8_RPCS_SS_CNT_SHIFT, | |
1090 | (rpcs & GEN8_RPCS_SS_CNT_ENABLE) ? "*" : ""); | |
1091 | ||
1092 | return -EINVAL; | |
1093 | } | |
1094 | ||
1095 | static int | |
f277bc0c | 1096 | __sseu_finish(const char *name, |
c06ee6ff | 1097 | unsigned int flags, |
1b1ae407 | 1098 | struct intel_context *ce, |
c06ee6ff TU |
1099 | struct drm_i915_gem_object *obj, |
1100 | unsigned int expected, | |
1101 | struct igt_spinner *spin) | |
1102 | { | |
1b1ae407 | 1103 | unsigned int slices = hweight32(ce->engine->sseu.slice_mask); |
c06ee6ff TU |
1104 | u32 rpcs = 0; |
1105 | int ret = 0; | |
1106 | ||
1107 | if (flags & TEST_RESET) { | |
cb823ed9 | 1108 | ret = intel_engine_reset(ce->engine, "sseu"); |
c06ee6ff TU |
1109 | if (ret) |
1110 | goto out; | |
1111 | } | |
1112 | ||
f277bc0c | 1113 | ret = __read_slice_count(ce, obj, |
c06ee6ff TU |
1114 | flags & TEST_RESET ? NULL : spin, &rpcs); |
1115 | ret = __check_rpcs(name, rpcs, ret, expected, "Context", "!"); | |
1116 | if (ret) | |
1117 | goto out; | |
1118 | ||
f277bc0c | 1119 | ret = __read_slice_count(ce->engine->kernel_context, obj, NULL, &rpcs); |
c06ee6ff TU |
1120 | ret = __check_rpcs(name, rpcs, ret, slices, "Kernel context", "!"); |
1121 | ||
1122 | out: | |
1123 | if (spin) | |
1124 | igt_spinner_end(spin); | |
1125 | ||
1126 | if ((flags & TEST_IDLE) && ret == 0) { | |
66101975 | 1127 | ret = intel_gt_wait_for_idle(ce->engine->gt, |
f33a8a51 | 1128 | MAX_SCHEDULE_TIMEOUT); |
c06ee6ff TU |
1129 | if (ret) |
1130 | return ret; | |
1131 | ||
f277bc0c | 1132 | ret = __read_slice_count(ce, obj, NULL, &rpcs); |
c06ee6ff TU |
1133 | ret = __check_rpcs(name, rpcs, ret, expected, |
1134 | "Context", " after idle!"); | |
1135 | } | |
1136 | ||
1137 | return ret; | |
1138 | } | |
1139 | ||
1140 | static int | |
f277bc0c | 1141 | __sseu_test(const char *name, |
c06ee6ff | 1142 | unsigned int flags, |
1b1ae407 | 1143 | struct intel_context *ce, |
c06ee6ff TU |
1144 | struct drm_i915_gem_object *obj, |
1145 | struct intel_sseu sseu) | |
1146 | { | |
1147 | struct igt_spinner *spin = NULL; | |
c06ee6ff TU |
1148 | int ret; |
1149 | ||
f277bc0c | 1150 | ret = __sseu_prepare(name, flags, ce, &spin); |
c06ee6ff | 1151 | if (ret) |
f7f28de7 | 1152 | return ret; |
c06ee6ff | 1153 | |
7e805762 | 1154 | ret = intel_context_reconfigure_sseu(ce, sseu); |
c06ee6ff | 1155 | if (ret) |
2a4a2754 | 1156 | goto out_spin; |
c06ee6ff | 1157 | |
f277bc0c | 1158 | ret = __sseu_finish(name, flags, ce, obj, |
c06ee6ff TU |
1159 | hweight32(sseu.slice_mask), spin); |
1160 | ||
2a4a2754 | 1161 | out_spin: |
c06ee6ff TU |
1162 | if (spin) { |
1163 | igt_spinner_end(spin); | |
1164 | igt_spinner_fini(spin); | |
1165 | kfree(spin); | |
1166 | } | |
c06ee6ff TU |
1167 | return ret; |
1168 | } | |
1169 | ||
1170 | static int | |
1171 | __igt_ctx_sseu(struct drm_i915_private *i915, | |
1172 | const char *name, | |
1173 | unsigned int flags) | |
1174 | { | |
8a68d464 | 1175 | struct intel_engine_cs *engine = i915->engine[RCS0]; |
c06ee6ff TU |
1176 | struct drm_i915_gem_object *obj; |
1177 | struct i915_gem_context *ctx; | |
1b1ae407 | 1178 | struct intel_context *ce; |
c06ee6ff | 1179 | struct intel_sseu pg_sseu; |
c06ee6ff TU |
1180 | struct drm_file *file; |
1181 | int ret; | |
1182 | ||
8f856c74 | 1183 | if (INTEL_GEN(i915) < 9 || !engine) |
c06ee6ff TU |
1184 | return 0; |
1185 | ||
1186 | if (!RUNTIME_INFO(i915)->sseu.has_slice_pg) | |
1187 | return 0; | |
1188 | ||
8f856c74 | 1189 | if (hweight32(engine->sseu.slice_mask) < 2) |
c06ee6ff TU |
1190 | return 0; |
1191 | ||
1192 | /* | |
1193 | * Gen11 VME friendly power-gated configuration with half enabled | |
1194 | * sub-slices. | |
1195 | */ | |
8f856c74 | 1196 | pg_sseu = engine->sseu; |
c06ee6ff TU |
1197 | pg_sseu.slice_mask = 1; |
1198 | pg_sseu.subslice_mask = | |
8f856c74 | 1199 | ~(~0 << (hweight32(engine->sseu.subslice_mask) / 2)); |
c06ee6ff TU |
1200 | |
1201 | pr_info("SSEU subtest '%s', flags=%x, def_slices=%u, pg_slices=%u\n", | |
8f856c74 | 1202 | name, flags, hweight32(engine->sseu.slice_mask), |
c06ee6ff TU |
1203 | hweight32(pg_sseu.slice_mask)); |
1204 | ||
1205 | file = mock_file(i915); | |
1206 | if (IS_ERR(file)) | |
1207 | return PTR_ERR(file); | |
1208 | ||
1209 | if (flags & TEST_RESET) | |
cb823ed9 | 1210 | igt_global_reset_lock(&i915->gt); |
c06ee6ff | 1211 | |
3aa9945a | 1212 | ctx = live_context(i915, file); |
c06ee6ff TU |
1213 | if (IS_ERR(ctx)) { |
1214 | ret = PTR_ERR(ctx); | |
1215 | goto out_unlock; | |
1216 | } | |
e4106dae | 1217 | i915_gem_context_clear_bannable(ctx); /* to reset and beyond! */ |
c06ee6ff TU |
1218 | |
1219 | obj = i915_gem_object_create_internal(i915, PAGE_SIZE); | |
1220 | if (IS_ERR(obj)) { | |
1221 | ret = PTR_ERR(obj); | |
1222 | goto out_unlock; | |
1223 | } | |
1224 | ||
5e2a0419 | 1225 | ce = i915_gem_context_get_engine(ctx, RCS0); |
1b1ae407 CW |
1226 | if (IS_ERR(ce)) { |
1227 | ret = PTR_ERR(ce); | |
60a0933b | 1228 | goto out_put; |
1b1ae407 CW |
1229 | } |
1230 | ||
1231 | ret = intel_context_pin(ce); | |
1232 | if (ret) | |
1233 | goto out_context; | |
1234 | ||
c06ee6ff | 1235 | /* First set the default mask. */ |
f277bc0c | 1236 | ret = __sseu_test(name, flags, ce, obj, engine->sseu); |
c06ee6ff TU |
1237 | if (ret) |
1238 | goto out_fail; | |
1239 | ||
1240 | /* Then set a power-gated configuration. */ | |
f277bc0c | 1241 | ret = __sseu_test(name, flags, ce, obj, pg_sseu); |
c06ee6ff TU |
1242 | if (ret) |
1243 | goto out_fail; | |
1244 | ||
1245 | /* Back to defaults. */ | |
f277bc0c | 1246 | ret = __sseu_test(name, flags, ce, obj, engine->sseu); |
c06ee6ff TU |
1247 | if (ret) |
1248 | goto out_fail; | |
1249 | ||
1250 | /* One last power-gated configuration for the road. */ | |
f277bc0c | 1251 | ret = __sseu_test(name, flags, ce, obj, pg_sseu); |
c06ee6ff TU |
1252 | if (ret) |
1253 | goto out_fail; | |
1254 | ||
1255 | out_fail: | |
7e805762 | 1256 | if (igt_flush_test(i915)) |
c06ee6ff TU |
1257 | ret = -EIO; |
1258 | ||
1b1ae407 CW |
1259 | intel_context_unpin(ce); |
1260 | out_context: | |
1261 | intel_context_put(ce); | |
60a0933b | 1262 | out_put: |
1b1ae407 | 1263 | i915_gem_object_put(obj); |
c06ee6ff TU |
1264 | |
1265 | out_unlock: | |
c06ee6ff | 1266 | if (flags & TEST_RESET) |
cb823ed9 | 1267 | igt_global_reset_unlock(&i915->gt); |
c06ee6ff TU |
1268 | |
1269 | mock_file_free(i915, file); | |
1270 | ||
1271 | if (ret) | |
1272 | pr_err("%s: Failed with %d!\n", name, ret); | |
1273 | ||
1274 | return ret; | |
1275 | } | |
1276 | ||
1277 | static int igt_ctx_sseu(void *arg) | |
1278 | { | |
1279 | struct { | |
1280 | const char *name; | |
1281 | unsigned int flags; | |
1282 | } *phase, phases[] = { | |
1283 | { .name = "basic", .flags = 0 }, | |
1284 | { .name = "idle", .flags = TEST_IDLE }, | |
1285 | { .name = "busy", .flags = TEST_BUSY }, | |
1286 | { .name = "busy-reset", .flags = TEST_BUSY | TEST_RESET }, | |
1287 | { .name = "busy-idle", .flags = TEST_BUSY | TEST_IDLE }, | |
1288 | { .name = "reset-idle", .flags = TEST_RESET | TEST_IDLE }, | |
1289 | }; | |
1290 | unsigned int i; | |
1291 | int ret = 0; | |
1292 | ||
1293 | for (i = 0, phase = phases; ret == 0 && i < ARRAY_SIZE(phases); | |
1294 | i++, phase++) | |
1295 | ret = __igt_ctx_sseu(arg, phase->name, phase->flags); | |
1296 | ||
1297 | return ret; | |
1298 | } | |
1299 | ||
250f8c81 JB |
1300 | static int igt_ctx_readonly(void *arg) |
1301 | { | |
1302 | struct drm_i915_private *i915 = arg; | |
1303 | struct drm_i915_gem_object *obj = NULL; | |
f2085c8e | 1304 | struct i915_request *tq[5] = {}; |
e568ac38 | 1305 | struct i915_address_space *vm; |
8dd0f8d3 | 1306 | struct i915_gem_context *ctx; |
e0695db7 | 1307 | unsigned long idx, ndwords, dw; |
e4a8c813 | 1308 | struct igt_live_test t; |
250f8c81 JB |
1309 | struct drm_file *file; |
1310 | I915_RND_STATE(prng); | |
1311 | IGT_TIMEOUT(end_time); | |
1312 | LIST_HEAD(objects); | |
250f8c81 JB |
1313 | int err = -ENODEV; |
1314 | ||
1315 | /* | |
1316 | * Create a few read-only objects (with the occasional writable object) | |
1317 | * and try to write into these object checking that the GPU discards | |
1318 | * any write to a read-only object. | |
1319 | */ | |
1320 | ||
1321 | file = mock_file(i915); | |
1322 | if (IS_ERR(file)) | |
1323 | return PTR_ERR(file); | |
1324 | ||
e4a8c813 | 1325 | err = igt_live_test_begin(&t, i915, __func__, ""); |
8dd0f8d3 | 1326 | if (err) |
a4e7ccda | 1327 | goto out_file; |
8dd0f8d3 | 1328 | |
3aa9945a | 1329 | ctx = live_context(i915, file); |
250f8c81 JB |
1330 | if (IS_ERR(ctx)) { |
1331 | err = PTR_ERR(ctx); | |
a4e7ccda | 1332 | goto out_file; |
250f8c81 JB |
1333 | } |
1334 | ||
a4e7ccda CW |
1335 | rcu_read_lock(); |
1336 | vm = rcu_dereference(ctx->vm) ?: &i915->ggtt.alias->vm; | |
e568ac38 | 1337 | if (!vm || !vm->has_read_only) { |
a4e7ccda | 1338 | rcu_read_unlock(); |
250f8c81 | 1339 | err = 0; |
a4e7ccda | 1340 | goto out_file; |
250f8c81 | 1341 | } |
a4e7ccda | 1342 | rcu_read_unlock(); |
250f8c81 JB |
1343 | |
1344 | ndwords = 0; | |
1345 | dw = 0; | |
1346 | while (!time_after(jiffies, end_time)) { | |
75b974a8 CW |
1347 | struct i915_gem_engines_iter it; |
1348 | struct intel_context *ce; | |
250f8c81 | 1349 | |
75b974a8 CW |
1350 | for_each_gem_engine(ce, |
1351 | i915_gem_context_lock_engines(ctx), it) { | |
1352 | if (!intel_engine_can_store_dword(ce->engine)) | |
250f8c81 JB |
1353 | continue; |
1354 | ||
1355 | if (!obj) { | |
75b974a8 | 1356 | obj = create_test_object(ce->vm, file, &objects); |
250f8c81 JB |
1357 | if (IS_ERR(obj)) { |
1358 | err = PTR_ERR(obj); | |
75b974a8 | 1359 | i915_gem_context_unlock_engines(ctx); |
a4e7ccda | 1360 | goto out_file; |
250f8c81 JB |
1361 | } |
1362 | ||
3e977ac6 CW |
1363 | if (prandom_u32_state(&prng) & 1) |
1364 | i915_gem_object_set_readonly(obj); | |
250f8c81 JB |
1365 | } |
1366 | ||
75b974a8 | 1367 | err = gpu_fill(ce, obj, dw); |
250f8c81 | 1368 | if (err) { |
2935ed53 | 1369 | pr_err("Failed to fill dword %lu [%lu/%lu] with gpu (%s) [full-ppgtt? %s], err=%d\n", |
250f8c81 | 1370 | ndwords, dw, max_dwords(obj), |
a4e7ccda CW |
1371 | ce->engine->name, |
1372 | yesno(!!rcu_access_pointer(ctx->vm)), | |
1373 | err); | |
75b974a8 | 1374 | i915_gem_context_unlock_engines(ctx); |
a4e7ccda | 1375 | goto out_file; |
250f8c81 JB |
1376 | } |
1377 | ||
f2085c8e CW |
1378 | err = throttle(ce, tq, ARRAY_SIZE(tq)); |
1379 | if (err) { | |
1380 | i915_gem_context_unlock_engines(ctx); | |
a4e7ccda | 1381 | goto out_file; |
f2085c8e CW |
1382 | } |
1383 | ||
250f8c81 JB |
1384 | if (++dw == max_dwords(obj)) { |
1385 | obj = NULL; | |
1386 | dw = 0; | |
1387 | } | |
1388 | ndwords++; | |
1389 | } | |
75b974a8 | 1390 | i915_gem_context_unlock_engines(ctx); |
250f8c81 JB |
1391 | } |
1392 | pr_info("Submitted %lu dwords (across %u engines)\n", | |
8a68d464 | 1393 | ndwords, RUNTIME_INFO(i915)->num_engines); |
250f8c81 JB |
1394 | |
1395 | dw = 0; | |
e0695db7 | 1396 | idx = 0; |
250f8c81 JB |
1397 | list_for_each_entry(obj, &objects, st_link) { |
1398 | unsigned int rem = | |
1399 | min_t(unsigned int, ndwords - dw, max_dwords(obj)); | |
1400 | unsigned int num_writes; | |
1401 | ||
1402 | num_writes = rem; | |
3e977ac6 | 1403 | if (i915_gem_object_is_readonly(obj)) |
250f8c81 JB |
1404 | num_writes = 0; |
1405 | ||
e0695db7 | 1406 | err = cpu_check(obj, idx++, num_writes); |
250f8c81 JB |
1407 | if (err) |
1408 | break; | |
1409 | ||
1410 | dw += rem; | |
1411 | } | |
1412 | ||
a4e7ccda | 1413 | out_file: |
f2085c8e | 1414 | throttle_release(tq, ARRAY_SIZE(tq)); |
e4a8c813 | 1415 | if (igt_live_test_end(&t)) |
250f8c81 | 1416 | err = -EIO; |
250f8c81 JB |
1417 | |
1418 | mock_file_free(i915, file); | |
1419 | return err; | |
1420 | } | |
1421 | ||
a4e7ccda | 1422 | static int check_scratch(struct i915_address_space *vm, u64 offset) |
c5def85c CW |
1423 | { |
1424 | struct drm_mm_node *node = | |
a4e7ccda | 1425 | __drm_mm_interval_first(&vm->mm, |
c5def85c CW |
1426 | offset, offset + sizeof(u32) - 1); |
1427 | if (!node || node->start > offset) | |
1428 | return 0; | |
1429 | ||
1430 | GEM_BUG_ON(offset >= node->start + node->size); | |
1431 | ||
1432 | pr_err("Target offset 0x%08x_%08x overlaps with a node in the mm!\n", | |
1433 | upper_32_bits(offset), lower_32_bits(offset)); | |
1434 | return -EINVAL; | |
1435 | } | |
1436 | ||
1437 | static int write_to_scratch(struct i915_gem_context *ctx, | |
1438 | struct intel_engine_cs *engine, | |
1439 | u64 offset, u32 value) | |
1440 | { | |
1441 | struct drm_i915_private *i915 = ctx->i915; | |
1442 | struct drm_i915_gem_object *obj; | |
a4e7ccda | 1443 | struct i915_address_space *vm; |
c5def85c CW |
1444 | struct i915_request *rq; |
1445 | struct i915_vma *vma; | |
1446 | u32 *cmd; | |
1447 | int err; | |
1448 | ||
1449 | GEM_BUG_ON(offset < I915_GTT_PAGE_SIZE); | |
1450 | ||
1451 | obj = i915_gem_object_create_internal(i915, PAGE_SIZE); | |
1452 | if (IS_ERR(obj)) | |
1453 | return PTR_ERR(obj); | |
1454 | ||
1455 | cmd = i915_gem_object_pin_map(obj, I915_MAP_WB); | |
1456 | if (IS_ERR(cmd)) { | |
1457 | err = PTR_ERR(cmd); | |
1458 | goto err; | |
1459 | } | |
1460 | ||
1461 | *cmd++ = MI_STORE_DWORD_IMM_GEN4; | |
1462 | if (INTEL_GEN(i915) >= 8) { | |
1463 | *cmd++ = lower_32_bits(offset); | |
1464 | *cmd++ = upper_32_bits(offset); | |
1465 | } else { | |
1466 | *cmd++ = 0; | |
1467 | *cmd++ = offset; | |
1468 | } | |
1469 | *cmd++ = value; | |
1470 | *cmd = MI_BATCH_BUFFER_END; | |
a679f58d | 1471 | __i915_gem_object_flush_map(obj, 0, 64); |
c5def85c CW |
1472 | i915_gem_object_unpin_map(obj); |
1473 | ||
75b974a8 CW |
1474 | intel_gt_chipset_flush(engine->gt); |
1475 | ||
a4e7ccda CW |
1476 | vm = i915_gem_context_get_vm_rcu(ctx); |
1477 | vma = i915_vma_instance(obj, vm, NULL); | |
c5def85c CW |
1478 | if (IS_ERR(vma)) { |
1479 | err = PTR_ERR(vma); | |
a4e7ccda | 1480 | goto err_vm; |
c5def85c CW |
1481 | } |
1482 | ||
1483 | err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_OFFSET_FIXED); | |
1484 | if (err) | |
a4e7ccda | 1485 | goto err_vm; |
c5def85c | 1486 | |
a4e7ccda | 1487 | err = check_scratch(vm, offset); |
c5def85c CW |
1488 | if (err) |
1489 | goto err_unpin; | |
1490 | ||
46472b3e | 1491 | rq = igt_request_alloc(ctx, engine); |
c5def85c CW |
1492 | if (IS_ERR(rq)) { |
1493 | err = PTR_ERR(rq); | |
1494 | goto err_unpin; | |
1495 | } | |
1496 | ||
1497 | err = engine->emit_bb_start(rq, vma->node.start, vma->node.size, 0); | |
1498 | if (err) | |
1499 | goto err_request; | |
1500 | ||
6951e589 | 1501 | i915_vma_lock(vma); |
70d6894d CW |
1502 | err = i915_request_await_object(rq, vma->obj, false); |
1503 | if (err == 0) | |
1504 | err = i915_vma_move_to_active(vma, rq, 0); | |
6951e589 | 1505 | i915_vma_unlock(vma); |
c5def85c CW |
1506 | if (err) |
1507 | goto skip_request; | |
1508 | ||
2850748e | 1509 | i915_vma_unpin_and_release(&vma, 0); |
c5def85c CW |
1510 | |
1511 | i915_request_add(rq); | |
1512 | ||
a4e7ccda | 1513 | i915_vm_put(vm); |
c5def85c CW |
1514 | return 0; |
1515 | ||
1516 | skip_request: | |
1517 | i915_request_skip(rq, err); | |
1518 | err_request: | |
1519 | i915_request_add(rq); | |
1520 | err_unpin: | |
1521 | i915_vma_unpin(vma); | |
a4e7ccda CW |
1522 | err_vm: |
1523 | i915_vm_put(vm); | |
c5def85c CW |
1524 | err: |
1525 | i915_gem_object_put(obj); | |
1526 | return err; | |
1527 | } | |
1528 | ||
1529 | static int read_from_scratch(struct i915_gem_context *ctx, | |
1530 | struct intel_engine_cs *engine, | |
1531 | u64 offset, u32 *value) | |
1532 | { | |
1533 | struct drm_i915_private *i915 = ctx->i915; | |
1534 | struct drm_i915_gem_object *obj; | |
a4e7ccda | 1535 | struct i915_address_space *vm; |
c5def85c CW |
1536 | const u32 RCS_GPR0 = 0x2600; /* not all engines have their own GPR! */ |
1537 | const u32 result = 0x100; | |
1538 | struct i915_request *rq; | |
1539 | struct i915_vma *vma; | |
1540 | u32 *cmd; | |
1541 | int err; | |
1542 | ||
1543 | GEM_BUG_ON(offset < I915_GTT_PAGE_SIZE); | |
1544 | ||
1545 | obj = i915_gem_object_create_internal(i915, PAGE_SIZE); | |
1546 | if (IS_ERR(obj)) | |
1547 | return PTR_ERR(obj); | |
1548 | ||
1549 | cmd = i915_gem_object_pin_map(obj, I915_MAP_WB); | |
1550 | if (IS_ERR(cmd)) { | |
1551 | err = PTR_ERR(cmd); | |
1552 | goto err; | |
1553 | } | |
1554 | ||
1555 | memset(cmd, POISON_INUSE, PAGE_SIZE); | |
1556 | if (INTEL_GEN(i915) >= 8) { | |
1557 | *cmd++ = MI_LOAD_REGISTER_MEM_GEN8; | |
1558 | *cmd++ = RCS_GPR0; | |
1559 | *cmd++ = lower_32_bits(offset); | |
1560 | *cmd++ = upper_32_bits(offset); | |
1561 | *cmd++ = MI_STORE_REGISTER_MEM_GEN8; | |
1562 | *cmd++ = RCS_GPR0; | |
1563 | *cmd++ = result; | |
1564 | *cmd++ = 0; | |
1565 | } else { | |
1566 | *cmd++ = MI_LOAD_REGISTER_MEM; | |
1567 | *cmd++ = RCS_GPR0; | |
1568 | *cmd++ = offset; | |
1569 | *cmd++ = MI_STORE_REGISTER_MEM; | |
1570 | *cmd++ = RCS_GPR0; | |
1571 | *cmd++ = result; | |
1572 | } | |
1573 | *cmd = MI_BATCH_BUFFER_END; | |
c5def85c | 1574 | |
a679f58d CW |
1575 | i915_gem_object_flush_map(obj); |
1576 | i915_gem_object_unpin_map(obj); | |
c5def85c | 1577 | |
75b974a8 CW |
1578 | intel_gt_chipset_flush(engine->gt); |
1579 | ||
a4e7ccda CW |
1580 | vm = i915_gem_context_get_vm_rcu(ctx); |
1581 | vma = i915_vma_instance(obj, vm, NULL); | |
c5def85c CW |
1582 | if (IS_ERR(vma)) { |
1583 | err = PTR_ERR(vma); | |
a4e7ccda | 1584 | goto err_vm; |
c5def85c CW |
1585 | } |
1586 | ||
1587 | err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_OFFSET_FIXED); | |
1588 | if (err) | |
a4e7ccda | 1589 | goto err_vm; |
c5def85c | 1590 | |
a4e7ccda | 1591 | err = check_scratch(vm, offset); |
c5def85c CW |
1592 | if (err) |
1593 | goto err_unpin; | |
1594 | ||
46472b3e | 1595 | rq = igt_request_alloc(ctx, engine); |
c5def85c CW |
1596 | if (IS_ERR(rq)) { |
1597 | err = PTR_ERR(rq); | |
1598 | goto err_unpin; | |
1599 | } | |
1600 | ||
1601 | err = engine->emit_bb_start(rq, vma->node.start, vma->node.size, 0); | |
1602 | if (err) | |
1603 | goto err_request; | |
1604 | ||
6951e589 | 1605 | i915_vma_lock(vma); |
70d6894d CW |
1606 | err = i915_request_await_object(rq, vma->obj, true); |
1607 | if (err == 0) | |
1608 | err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE); | |
6951e589 | 1609 | i915_vma_unlock(vma); |
c5def85c CW |
1610 | if (err) |
1611 | goto skip_request; | |
1612 | ||
1613 | i915_vma_unpin(vma); | |
1614 | i915_vma_close(vma); | |
1615 | ||
1616 | i915_request_add(rq); | |
1617 | ||
6951e589 | 1618 | i915_gem_object_lock(obj); |
c5def85c | 1619 | err = i915_gem_object_set_to_cpu_domain(obj, false); |
6951e589 | 1620 | i915_gem_object_unlock(obj); |
c5def85c | 1621 | if (err) |
a4e7ccda | 1622 | goto err_vm; |
c5def85c CW |
1623 | |
1624 | cmd = i915_gem_object_pin_map(obj, I915_MAP_WB); | |
1625 | if (IS_ERR(cmd)) { | |
1626 | err = PTR_ERR(cmd); | |
a4e7ccda | 1627 | goto err_vm; |
c5def85c CW |
1628 | } |
1629 | ||
1630 | *value = cmd[result / sizeof(*cmd)]; | |
1631 | i915_gem_object_unpin_map(obj); | |
1632 | i915_gem_object_put(obj); | |
1633 | ||
1634 | return 0; | |
1635 | ||
1636 | skip_request: | |
1637 | i915_request_skip(rq, err); | |
1638 | err_request: | |
1639 | i915_request_add(rq); | |
1640 | err_unpin: | |
1641 | i915_vma_unpin(vma); | |
a4e7ccda CW |
1642 | err_vm: |
1643 | i915_vm_put(vm); | |
c5def85c CW |
1644 | err: |
1645 | i915_gem_object_put(obj); | |
1646 | return err; | |
1647 | } | |
1648 | ||
1649 | static int igt_vm_isolation(void *arg) | |
1650 | { | |
1651 | struct drm_i915_private *i915 = arg; | |
1652 | struct i915_gem_context *ctx_a, *ctx_b; | |
1653 | struct intel_engine_cs *engine; | |
e4a8c813 | 1654 | struct igt_live_test t; |
c5def85c CW |
1655 | struct drm_file *file; |
1656 | I915_RND_STATE(prng); | |
1657 | unsigned long count; | |
c5def85c CW |
1658 | unsigned int id; |
1659 | u64 vm_total; | |
1660 | int err; | |
1661 | ||
1662 | if (INTEL_GEN(i915) < 7) | |
1663 | return 0; | |
1664 | ||
1665 | /* | |
1666 | * The simple goal here is that a write into one context is not | |
1667 | * observed in a second (separate page tables and scratch). | |
1668 | */ | |
1669 | ||
1670 | file = mock_file(i915); | |
1671 | if (IS_ERR(file)) | |
1672 | return PTR_ERR(file); | |
1673 | ||
e4a8c813 | 1674 | err = igt_live_test_begin(&t, i915, __func__, ""); |
c5def85c | 1675 | if (err) |
a4e7ccda | 1676 | goto out_file; |
c5def85c | 1677 | |
3aa9945a | 1678 | ctx_a = live_context(i915, file); |
c5def85c CW |
1679 | if (IS_ERR(ctx_a)) { |
1680 | err = PTR_ERR(ctx_a); | |
a4e7ccda | 1681 | goto out_file; |
c5def85c CW |
1682 | } |
1683 | ||
3aa9945a | 1684 | ctx_b = live_context(i915, file); |
c5def85c CW |
1685 | if (IS_ERR(ctx_b)) { |
1686 | err = PTR_ERR(ctx_b); | |
a4e7ccda | 1687 | goto out_file; |
c5def85c CW |
1688 | } |
1689 | ||
1690 | /* We can only test vm isolation, if the vm are distinct */ | |
e568ac38 | 1691 | if (ctx_a->vm == ctx_b->vm) |
a4e7ccda | 1692 | goto out_file; |
c5def85c | 1693 | |
e568ac38 CW |
1694 | vm_total = ctx_a->vm->total; |
1695 | GEM_BUG_ON(ctx_b->vm->total != vm_total); | |
c5def85c CW |
1696 | vm_total -= I915_GTT_PAGE_SIZE; |
1697 | ||
c5def85c CW |
1698 | count = 0; |
1699 | for_each_engine(engine, i915, id) { | |
1700 | IGT_TIMEOUT(end_time); | |
1701 | unsigned long this = 0; | |
1702 | ||
1703 | if (!intel_engine_can_store_dword(engine)) | |
1704 | continue; | |
1705 | ||
1706 | while (!__igt_timeout(end_time, NULL)) { | |
1707 | u32 value = 0xc5c5c5c5; | |
1708 | u64 offset; | |
1709 | ||
1710 | div64_u64_rem(i915_prandom_u64_state(&prng), | |
1711 | vm_total, &offset); | |
bf1315b8 | 1712 | offset = round_down(offset, alignof_dword); |
c5def85c CW |
1713 | offset += I915_GTT_PAGE_SIZE; |
1714 | ||
1715 | err = write_to_scratch(ctx_a, engine, | |
1716 | offset, 0xdeadbeef); | |
1717 | if (err == 0) | |
1718 | err = read_from_scratch(ctx_b, engine, | |
1719 | offset, &value); | |
1720 | if (err) | |
a4e7ccda | 1721 | goto out_file; |
c5def85c CW |
1722 | |
1723 | if (value) { | |
1724 | pr_err("%s: Read %08x from scratch (offset 0x%08x_%08x), after %lu reads!\n", | |
1725 | engine->name, value, | |
1726 | upper_32_bits(offset), | |
1727 | lower_32_bits(offset), | |
1728 | this); | |
1729 | err = -EINVAL; | |
a4e7ccda | 1730 | goto out_file; |
c5def85c CW |
1731 | } |
1732 | ||
1733 | this++; | |
1734 | } | |
1735 | count += this; | |
1736 | } | |
1737 | pr_info("Checked %lu scratch offsets across %d engines\n", | |
8a68d464 | 1738 | count, RUNTIME_INFO(i915)->num_engines); |
c5def85c | 1739 | |
a4e7ccda | 1740 | out_file: |
e4a8c813 | 1741 | if (igt_live_test_end(&t)) |
c5def85c | 1742 | err = -EIO; |
c5def85c CW |
1743 | mock_file_free(i915, file); |
1744 | return err; | |
1745 | } | |
1746 | ||
1fe2d6f9 CW |
1747 | static bool skip_unused_engines(struct intel_context *ce, void *data) |
1748 | { | |
1749 | return !ce->state; | |
1750 | } | |
1751 | ||
85fddf0b CW |
1752 | static void mock_barrier_task(void *data) |
1753 | { | |
1754 | unsigned int *counter = data; | |
1755 | ||
1756 | ++*counter; | |
1757 | } | |
1758 | ||
1759 | static int mock_context_barrier(void *arg) | |
1760 | { | |
1761 | #undef pr_fmt | |
1762 | #define pr_fmt(x) "context_barrier_task():" # x | |
1763 | struct drm_i915_private *i915 = arg; | |
1764 | struct i915_gem_context *ctx; | |
1765 | struct i915_request *rq; | |
85fddf0b CW |
1766 | unsigned int counter; |
1767 | int err; | |
1768 | ||
1769 | /* | |
1770 | * The context barrier provides us with a callback after it emits | |
1771 | * a request; useful for retiring old state after loading new. | |
1772 | */ | |
1773 | ||
85fddf0b | 1774 | ctx = mock_context(i915, "mock"); |
a4e7ccda CW |
1775 | if (!ctx) |
1776 | return -ENOMEM; | |
85fddf0b CW |
1777 | |
1778 | counter = 0; | |
e0695db7 | 1779 | err = context_barrier_task(ctx, 0, |
1fe2d6f9 | 1780 | NULL, NULL, mock_barrier_task, &counter); |
85fddf0b CW |
1781 | if (err) { |
1782 | pr_err("Failed at line %d, err=%d\n", __LINE__, err); | |
1783 | goto out; | |
1784 | } | |
1785 | if (counter == 0) { | |
1786 | pr_err("Did not retire immediately with 0 engines\n"); | |
1787 | err = -EINVAL; | |
1788 | goto out; | |
1789 | } | |
1790 | ||
1791 | counter = 0; | |
e0695db7 | 1792 | err = context_barrier_task(ctx, ALL_ENGINES, |
1fe2d6f9 CW |
1793 | skip_unused_engines, |
1794 | NULL, | |
1795 | mock_barrier_task, | |
1796 | &counter); | |
85fddf0b CW |
1797 | if (err) { |
1798 | pr_err("Failed at line %d, err=%d\n", __LINE__, err); | |
1799 | goto out; | |
1800 | } | |
1801 | if (counter == 0) { | |
62c8e423 | 1802 | pr_err("Did not retire immediately for all unused engines\n"); |
85fddf0b CW |
1803 | err = -EINVAL; |
1804 | goto out; | |
1805 | } | |
1806 | ||
46472b3e | 1807 | rq = igt_request_alloc(ctx, i915->engine[RCS0]); |
85fddf0b CW |
1808 | if (IS_ERR(rq)) { |
1809 | pr_err("Request allocation failed!\n"); | |
1810 | goto out; | |
1811 | } | |
1812 | i915_request_add(rq); | |
85fddf0b CW |
1813 | |
1814 | counter = 0; | |
1815 | context_barrier_inject_fault = BIT(RCS0); | |
e0695db7 | 1816 | err = context_barrier_task(ctx, ALL_ENGINES, |
1fe2d6f9 | 1817 | NULL, NULL, mock_barrier_task, &counter); |
85fddf0b CW |
1818 | context_barrier_inject_fault = 0; |
1819 | if (err == -ENXIO) | |
1820 | err = 0; | |
1821 | else | |
1822 | pr_err("Did not hit fault injection!\n"); | |
1823 | if (counter != 0) { | |
1824 | pr_err("Invoked callback on error!\n"); | |
1825 | err = -EIO; | |
1826 | } | |
1827 | if (err) | |
1828 | goto out; | |
1829 | ||
1830 | counter = 0; | |
e0695db7 | 1831 | err = context_barrier_task(ctx, ALL_ENGINES, |
1fe2d6f9 CW |
1832 | skip_unused_engines, |
1833 | NULL, | |
1834 | mock_barrier_task, | |
1835 | &counter); | |
85fddf0b CW |
1836 | if (err) { |
1837 | pr_err("Failed at line %d, err=%d\n", __LINE__, err); | |
1838 | goto out; | |
1839 | } | |
1840 | mock_device_flush(i915); | |
1841 | if (counter == 0) { | |
1842 | pr_err("Did not retire on each active engines\n"); | |
1843 | err = -EINVAL; | |
1844 | goto out; | |
1845 | } | |
1846 | ||
1847 | out: | |
1848 | mock_context_close(ctx); | |
85fddf0b CW |
1849 | return err; |
1850 | #undef pr_fmt | |
1851 | #define pr_fmt(x) x | |
1852 | } | |
1853 | ||
09a4c02e CW |
1854 | int i915_gem_context_mock_selftests(void) |
1855 | { | |
1856 | static const struct i915_subtest tests[] = { | |
85fddf0b | 1857 | SUBTEST(mock_context_barrier), |
09a4c02e CW |
1858 | }; |
1859 | struct drm_i915_private *i915; | |
1860 | int err; | |
1861 | ||
1862 | i915 = mock_gem_device(); | |
1863 | if (!i915) | |
1864 | return -ENOMEM; | |
1865 | ||
1866 | err = i915_subtests(tests, i915); | |
1867 | ||
a24362ea | 1868 | drm_dev_put(&i915->drm); |
09a4c02e CW |
1869 | return err; |
1870 | } | |
1871 | ||
cb823ed9 | 1872 | int i915_gem_context_live_selftests(struct drm_i915_private *i915) |
791ff39a CW |
1873 | { |
1874 | static const struct i915_subtest tests[] = { | |
a47cd45a | 1875 | SUBTEST(live_nop_switch), |
50d16d44 | 1876 | SUBTEST(live_parallel_switch), |
791ff39a | 1877 | SUBTEST(igt_ctx_exec), |
250f8c81 | 1878 | SUBTEST(igt_ctx_readonly), |
c06ee6ff | 1879 | SUBTEST(igt_ctx_sseu), |
e0695db7 | 1880 | SUBTEST(igt_shared_ctx_exec), |
c5def85c | 1881 | SUBTEST(igt_vm_isolation), |
791ff39a | 1882 | }; |
92fdf8d4 | 1883 | |
cb823ed9 | 1884 | if (intel_gt_is_wedged(&i915->gt)) |
31c9bd78 CW |
1885 | return 0; |
1886 | ||
cb823ed9 | 1887 | return i915_live_subtests(tests, i915); |
791ff39a | 1888 | } |