blob: 40f2ebd8f67c873e43c7ec62fdecf8696aea8d86 [file] [log] [blame]
Chris Wilson721d8742016-10-27 11:32:47 +01001/*
2 * Copyright © 2016 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
Chris Wilson976ed7c2017-08-24 12:16:17 +010024#include "config.h"
25
Chris Wilson721d8742016-10-27 11:32:47 +010026#include <sys/poll.h>
Chris Wilsona19ef052017-07-31 14:15:59 +010027#include <sys/ioctl.h>
Chris Wilson976ed7c2017-08-24 12:16:17 +010028#include <sched.h>
Chris Wilson721d8742016-10-27 11:32:47 +010029
30#include "igt.h"
31#include "igt_vgem.h"
Chris Wilson61f8de72017-07-20 10:08:28 +010032#include "igt_rand.h"
Chris Wilson976ed7c2017-08-24 12:16:17 +010033#include "igt_sysfs.h"
Chris Wilson721d8742016-10-27 11:32:47 +010034
Chris Wilson49f44c72016-11-14 21:24:52 +000035#define LOCAL_PARAM_HAS_SCHEDULER 41
Chris Wilsonaf0e1c52017-02-21 18:25:58 +000036#define LOCAL_CONTEXT_PARAM_PRIORITY 6
Chris Wilson721d8742016-10-27 11:32:47 +010037
38#define LO 0
39#define HI 1
40#define NOISE 2
41
42#define MAX_PRIO 1023
Chris Wilsonda553ff2017-09-22 10:32:44 +010043#define MIN_PRIO -1023
Chris Wilson721d8742016-10-27 11:32:47 +010044
45#define BUSY_QLEN 8
46
47IGT_TEST_DESCRIPTION("Check that we can control the order of execution");
48
Chris Wilsonaf0e1c52017-02-21 18:25:58 +000049static int __ctx_set_priority(int fd, uint32_t ctx, int prio)
Chris Wilson721d8742016-10-27 11:32:47 +010050{
51 struct local_i915_gem_context_param param;
52
53 memset(&param, 0, sizeof(param));
54 param.context = ctx;
55 param.size = 0;
56 param.param = LOCAL_CONTEXT_PARAM_PRIORITY;
57 param.value = prio;
58
Chris Wilsonaf0e1c52017-02-21 18:25:58 +000059 return __gem_context_set_param(fd, &param);
60}
61
62static void ctx_set_priority(int fd, uint32_t ctx, int prio)
63{
64 igt_assert_eq(__ctx_set_priority(fd, ctx, prio), 0);
65}
66
67static void ctx_has_priority(int fd)
68{
69 igt_require(__ctx_set_priority(fd, 0, MAX_PRIO) == 0);
Chris Wilson721d8742016-10-27 11:32:47 +010070}
71
72static void store_dword(int fd, uint32_t ctx, unsigned ring,
73 uint32_t target, uint32_t offset, uint32_t value,
74 uint32_t cork, unsigned write_domain)
75{
76 const int gen = intel_gen(intel_get_drm_devid(fd));
77 struct drm_i915_gem_exec_object2 obj[3];
78 struct drm_i915_gem_relocation_entry reloc;
79 struct drm_i915_gem_execbuffer2 execbuf;
80 uint32_t batch[16];
81 int i;
82
83 memset(&execbuf, 0, sizeof(execbuf));
Chris Wilson4de67b22017-01-02 11:05:21 +000084 execbuf.buffers_ptr = to_user_pointer(obj + !cork);
Chris Wilson721d8742016-10-27 11:32:47 +010085 execbuf.buffer_count = 2 + !!cork;
86 execbuf.flags = ring;
87 if (gen < 6)
88 execbuf.flags |= I915_EXEC_SECURE;
89 execbuf.rsvd1 = ctx;
90
91 memset(obj, 0, sizeof(obj));
92 obj[0].handle = cork;
93 obj[1].handle = target;
94 obj[2].handle = gem_create(fd, 4096);
95
96 memset(&reloc, 0, sizeof(reloc));
97 reloc.target_handle = obj[1].handle;
98 reloc.presumed_offset = 0;
99 reloc.offset = sizeof(uint32_t);
100 reloc.delta = offset;
101 reloc.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
102 reloc.write_domain = write_domain;
Chris Wilson4de67b22017-01-02 11:05:21 +0000103 obj[2].relocs_ptr = to_user_pointer(&reloc);
Chris Wilson721d8742016-10-27 11:32:47 +0100104 obj[2].relocation_count = 1;
105
106 i = 0;
107 batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
108 if (gen >= 8) {
109 batch[++i] = offset;
110 batch[++i] = 0;
111 } else if (gen >= 4) {
112 batch[++i] = 0;
113 batch[++i] = offset;
114 reloc.offset += sizeof(uint32_t);
115 } else {
116 batch[i]--;
117 batch[++i] = offset;
118 }
119 batch[++i] = value;
120 batch[++i] = MI_BATCH_BUFFER_END;
121 gem_write(fd, obj[2].handle, 0, batch, sizeof(batch));
122 gem_execbuf(fd, &execbuf);
123 gem_close(fd, obj[2].handle);
124}
125
126static uint32_t *make_busy(int fd, uint32_t target, unsigned ring)
127{
128 const int gen = intel_gen(intel_get_drm_devid(fd));
129 struct drm_i915_gem_exec_object2 obj[2];
130 struct drm_i915_gem_relocation_entry reloc[2];
131 struct drm_i915_gem_execbuffer2 execbuf;
132 uint32_t *batch;
133 int i;
134
135 memset(&execbuf, 0, sizeof(execbuf));
Chris Wilson4de67b22017-01-02 11:05:21 +0000136 execbuf.buffers_ptr = to_user_pointer(obj + !target);
Chris Wilson721d8742016-10-27 11:32:47 +0100137 execbuf.buffer_count = 1 + !!target;
138
139 memset(obj, 0, sizeof(obj));
140 obj[0].handle = target;
141 obj[1].handle = gem_create(fd, 4096);
142 batch = gem_mmap__wc(fd, obj[1].handle, 0, 4096, PROT_WRITE);
143 gem_set_domain(fd, obj[1].handle,
144 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
145
Chris Wilson4de67b22017-01-02 11:05:21 +0000146 obj[1].relocs_ptr = to_user_pointer(reloc);
Chris Wilson721d8742016-10-27 11:32:47 +0100147 obj[1].relocation_count = 1 + !!target;
148 memset(reloc, 0, sizeof(reloc));
149
150 reloc[0].target_handle = obj[1].handle; /* recurse */
151 reloc[0].presumed_offset = 0;
152 reloc[0].offset = sizeof(uint32_t);
153 reloc[0].delta = 0;
154 reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND;
155 reloc[0].write_domain = 0;
156
157 reloc[1].target_handle = target;
158 reloc[1].presumed_offset = 0;
159 reloc[1].offset = 1024;
160 reloc[1].delta = 0;
161 reloc[1].read_domains = I915_GEM_DOMAIN_COMMAND;
162 reloc[1].write_domain = 0;
163
164 i = 0;
165 batch[i] = MI_BATCH_BUFFER_START;
166 if (gen >= 8) {
167 batch[i] |= 1 << 8 | 1;
168 batch[++i] = 0;
169 batch[++i] = 0;
170 } else if (gen >= 6) {
171 batch[i] |= 1 << 8;
172 batch[++i] = 0;
173 } else {
174 batch[i] |= 2 << 6;
175 batch[++i] = 0;
176 if (gen < 4) {
177 batch[i] |= 1;
178 reloc[0].delta = 1;
179 }
180 }
181 i++;
182
183 if (ring != -1) {
184 execbuf.flags = ring;
185 for (int n = 0; n < BUSY_QLEN; n++)
186 gem_execbuf(fd, &execbuf);
187 } else {
188 for_each_engine(fd, ring) {
189 if (ring == 0)
190 continue;
191
192 execbuf.flags = ring;
193 for (int n = 0; n < BUSY_QLEN; n++)
194 gem_execbuf(fd, &execbuf);
195 igt_assert(execbuf.flags == ring);
196 }
197 }
198
199 if (target) {
200 execbuf.flags = 0;
201 reloc[1].write_domain = I915_GEM_DOMAIN_COMMAND;
202 gem_execbuf(fd, &execbuf);
203 }
204
205 gem_close(fd, obj[1].handle);
206
207 return batch;
208}
209
210static void finish_busy(uint32_t *busy)
211{
212 *busy = MI_BATCH_BUFFER_END;
213 munmap(busy, 4096);
214}
215
216struct cork {
217 int device;
218 uint32_t handle;
219 uint32_t fence;
220};
221
222static void plug(int fd, struct cork *c)
223{
224 struct vgem_bo bo;
225 int dmabuf;
226
227 c->device = drm_open_driver(DRIVER_VGEM);
228
229 bo.width = bo.height = 1;
230 bo.bpp = 4;
231 vgem_create(c->device, &bo);
232 c->fence = vgem_fence_attach(c->device, &bo, VGEM_FENCE_WRITE);
233
234 dmabuf = prime_handle_to_fd(c->device, bo.handle);
235 c->handle = prime_fd_to_handle(fd, dmabuf);
236 close(dmabuf);
237}
238
239static void unplug(struct cork *c)
240{
241 vgem_fence_signal(c->device, c->fence);
242 close(c->device);
243}
244
245static void fifo(int fd, unsigned ring)
246{
247 struct cork cork;
248 uint32_t *busy;
249 uint32_t scratch;
250 uint32_t *ptr;
251
252 scratch = gem_create(fd, 4096);
253
254 busy = make_busy(fd, scratch, ring);
255 plug(fd, &cork);
256
257 /* Same priority, same timeline, final result will be the second eb */
258 store_dword(fd, 0, ring, scratch, 0, 1, cork.handle, 0);
259 store_dword(fd, 0, ring, scratch, 0, 2, cork.handle, 0);
260
261 unplug(&cork); /* only now submit our batches */
262 igt_debugfs_dump(fd, "i915_engine_info");
263 finish_busy(busy);
264
265 ptr = gem_mmap__gtt(fd, scratch, 4096, PROT_READ);
266 gem_set_domain(fd, scratch, /* no write hazard lies! */
267 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
268 gem_close(fd, scratch);
269
270 igt_assert_eq_u32(ptr[0], 2);
271 munmap(ptr, 4096);
272}
273
274static void reorder(int fd, unsigned ring, unsigned flags)
275#define EQUAL 1
276{
277 struct cork cork;
278 uint32_t scratch;
279 uint32_t *busy;
280 uint32_t *ptr;
281 uint32_t ctx[2];
282
283 ctx[LO] = gem_context_create(fd);
Chris Wilsonda553ff2017-09-22 10:32:44 +0100284 ctx_set_priority(fd, ctx[LO], MIN_PRIO);
Chris Wilson721d8742016-10-27 11:32:47 +0100285
286 ctx[HI] = gem_context_create(fd);
Chris Wilsonda553ff2017-09-22 10:32:44 +0100287 ctx_set_priority(fd, ctx[HI], flags & EQUAL ? MIN_PRIO : 0);
Chris Wilson721d8742016-10-27 11:32:47 +0100288
289 scratch = gem_create(fd, 4096);
290
291 busy = make_busy(fd, scratch, ring);
292 plug(fd, &cork);
293
294 /* We expect the high priority context to be executed first, and
295 * so the final result will be value from the low priority context.
296 */
297 store_dword(fd, ctx[LO], ring, scratch, 0, ctx[LO], cork.handle, 0);
298 store_dword(fd, ctx[HI], ring, scratch, 0, ctx[HI], cork.handle, 0);
299
300 unplug(&cork); /* only now submit our batches */
301 igt_debugfs_dump(fd, "i915_engine_info");
302 finish_busy(busy);
303
304 gem_context_destroy(fd, ctx[LO]);
305 gem_context_destroy(fd, ctx[HI]);
306
307 ptr = gem_mmap__gtt(fd, scratch, 4096, PROT_READ);
308 gem_set_domain(fd, scratch, /* no write hazard lies! */
309 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
310 gem_close(fd, scratch);
311
312 if (flags & EQUAL) /* equal priority, result will be fifo */
313 igt_assert_eq_u32(ptr[0], ctx[HI]);
314 else
315 igt_assert_eq_u32(ptr[0], ctx[LO]);
316 munmap(ptr, 4096);
317}
318
319static void promotion(int fd, unsigned ring)
320{
321 struct cork cork;
322 uint32_t result, dep;
323 uint32_t *busy;
324 uint32_t *ptr;
325 uint32_t ctx[3];
326
327 ctx[LO] = gem_context_create(fd);
Chris Wilsonda553ff2017-09-22 10:32:44 +0100328 ctx_set_priority(fd, ctx[LO], MIN_PRIO);
Chris Wilson721d8742016-10-27 11:32:47 +0100329
330 ctx[HI] = gem_context_create(fd);
331 ctx_set_priority(fd, ctx[HI], 0);
332
333 ctx[NOISE] = gem_context_create(fd);
Chris Wilsonda553ff2017-09-22 10:32:44 +0100334 ctx_set_priority(fd, ctx[NOISE], MIN_PRIO/2);
Chris Wilson721d8742016-10-27 11:32:47 +0100335
336 result = gem_create(fd, 4096);
337 dep = gem_create(fd, 4096);
338
339 busy = make_busy(fd, result, ring);
340 plug(fd, &cork);
341
342 /* Expect that HI promotes LO, so the order will be LO, HI, NOISE.
343 *
344 * fifo would be NOISE, LO, HI.
345 * strict priority would be HI, NOISE, LO
346 */
347 store_dword(fd, ctx[NOISE], ring, result, 0, ctx[NOISE], cork.handle, 0);
348 store_dword(fd, ctx[LO], ring, result, 0, ctx[LO], cork.handle, 0);
349
350 /* link LO <-> HI via a dependency on another buffer */
351 store_dword(fd, ctx[LO], ring, dep, 0, ctx[LO], 0, I915_GEM_DOMAIN_INSTRUCTION);
352 store_dword(fd, ctx[HI], ring, dep, 0, ctx[HI], 0, 0);
353
354 store_dword(fd, ctx[HI], ring, result, 0, ctx[HI], 0, 0);
355
356 unplug(&cork); /* only now submit our batches */
357 igt_debugfs_dump(fd, "i915_engine_info");
358 finish_busy(busy);
359
360 gem_context_destroy(fd, ctx[NOISE]);
361 gem_context_destroy(fd, ctx[LO]);
362 gem_context_destroy(fd, ctx[HI]);
363
364 ptr = gem_mmap__gtt(fd, dep, 4096, PROT_READ);
365 gem_set_domain(fd, dep, /* no write hazard lies! */
366 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
367 gem_close(fd, dep);
368
369 igt_assert_eq_u32(ptr[0], ctx[HI]);
370 munmap(ptr, 4096);
371
372 ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
373 gem_set_domain(fd, result, /* no write hazard lies! */
374 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
375 gem_close(fd, result);
376
377 igt_assert_eq_u32(ptr[0], ctx[NOISE]);
378 munmap(ptr, 4096);
379}
380
Chris Wilsona3801342017-07-16 16:28:41 +0100381#define NEW_CTX 0x1
382static void preempt(int fd, unsigned ring, unsigned flags)
383{
384 uint32_t result = gem_create(fd, 4096);
385 uint32_t *ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
386 igt_spin_t *spin[16];
387 uint32_t ctx[2];
388
389 ctx[LO] = gem_context_create(fd);
Chris Wilsonda553ff2017-09-22 10:32:44 +0100390 ctx_set_priority(fd, ctx[LO], MIN_PRIO);
Chris Wilsona3801342017-07-16 16:28:41 +0100391
392 ctx[HI] = gem_context_create(fd);
393 ctx_set_priority(fd, ctx[HI], MAX_PRIO);
394
395 for (int n = 0; n < 16; n++) {
396 if (flags & NEW_CTX) {
397 gem_context_destroy(fd, ctx[LO]);
398 ctx[LO] = gem_context_create(fd);
Chris Wilsonda553ff2017-09-22 10:32:44 +0100399 ctx_set_priority(fd, ctx[LO], MIN_PRIO);
Chris Wilsona3801342017-07-16 16:28:41 +0100400 }
401 spin[n] = __igt_spin_batch_new(fd, ctx[LO], ring, 0);
402 igt_debug("spin[%d].handle=%d\n", n, spin[n]->handle);
403
404 store_dword(fd, ctx[HI], ring, result, 0, n + 1, 0, I915_GEM_DOMAIN_RENDER);
405
406 gem_set_domain(fd, result, I915_GEM_DOMAIN_GTT, 0);
407 igt_assert_eq_u32(ptr[0], n + 1);
408 igt_assert(gem_bo_busy(fd, spin[0]->handle));
409 }
410
411 for (int n = 0; n < 16; n++)
412 igt_spin_batch_free(fd, spin[n]);
413
414 gem_context_destroy(fd, ctx[LO]);
415 gem_context_destroy(fd, ctx[HI]);
416
417 munmap(ptr, 4096);
418 gem_close(fd, result);
419}
420
421static void preempt_other(int fd, unsigned ring)
422{
423 uint32_t result = gem_create(fd, 4096);
424 uint32_t *ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
425 igt_spin_t *spin[16];
426 unsigned int other;
427 unsigned int n, i;
428 uint32_t ctx[3];
429
430 /* On each engine, insert
431 * [NOISE] spinner,
432 * [LOW] write
433 *
434 * Then on our target engine do a [HIGH] write which should then
435 * prompt its dependent LOW writes in front of the spinner on
436 * each engine. The purpose of this test is to check that preemption
437 * can cross engines.
438 */
439
440 ctx[LO] = gem_context_create(fd);
Chris Wilsonda553ff2017-09-22 10:32:44 +0100441 ctx_set_priority(fd, ctx[LO], MIN_PRIO);
Chris Wilsona3801342017-07-16 16:28:41 +0100442
443 ctx[NOISE] = gem_context_create(fd);
444
445 ctx[HI] = gem_context_create(fd);
446 ctx_set_priority(fd, ctx[HI], MAX_PRIO);
447
448 n = 0;
449 for_each_engine(fd, other) {
450 spin[n] = __igt_spin_batch_new(fd, ctx[NOISE], other, 0);
451 store_dword(fd, ctx[LO], other,
452 result, (n + 1)*sizeof(uint32_t), n + 1,
453 0, I915_GEM_DOMAIN_RENDER);
454 n++;
455 }
456 store_dword(fd, ctx[HI], ring,
457 result, (n + 1)*sizeof(uint32_t), n + 1,
458 0, I915_GEM_DOMAIN_RENDER);
459
460 gem_set_domain(fd, result, I915_GEM_DOMAIN_GTT, 0);
461
462 for (i = 0; i < n; i++) {
463 igt_assert(gem_bo_busy(fd, spin[i]->handle));
464 igt_spin_batch_free(fd, spin[i]);
465 }
466
467 n++;
468 for (i = 0; i <= n; i++)
469 igt_assert_eq_u32(ptr[i], i);
470
471 gem_context_destroy(fd, ctx[LO]);
472 gem_context_destroy(fd, ctx[NOISE]);
473 gem_context_destroy(fd, ctx[HI]);
474
475 munmap(ptr, 4096);
476 gem_close(fd, result);
477}
478
479static void preempt_self(int fd, unsigned ring)
480{
481 uint32_t result = gem_create(fd, 4096);
482 uint32_t *ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
483 igt_spin_t *spin[16];
484 unsigned int other;
485 unsigned int n, i;
486 uint32_t ctx[3];
487
488 /* On each engine, insert
489 * [NOISE] spinner,
490 * [self/LOW] write
491 *
492 * Then on our target engine do a [self/HIGH] write which should then
493 * preempt its own lower priority task on any engine.
494 */
495
496 ctx[NOISE] = gem_context_create(fd);
497
498 ctx[HI] = gem_context_create(fd);
499
500 n = 0;
Chris Wilsonda553ff2017-09-22 10:32:44 +0100501 ctx_set_priority(fd, ctx[HI], MIN_PRIO);
Chris Wilsona3801342017-07-16 16:28:41 +0100502 for_each_engine(fd, other) {
503 spin[n] = __igt_spin_batch_new(fd, ctx[NOISE], other, 0);
504 store_dword(fd, ctx[HI], other,
505 result, (n + 1)*sizeof(uint32_t), n + 1,
506 0, I915_GEM_DOMAIN_RENDER);
507 n++;
508 }
509 ctx_set_priority(fd, ctx[HI], MAX_PRIO);
510 store_dword(fd, ctx[HI], ring,
511 result, (n + 1)*sizeof(uint32_t), n + 1,
512 0, I915_GEM_DOMAIN_RENDER);
513
514 gem_set_domain(fd, result, I915_GEM_DOMAIN_GTT, 0);
515
516 for (i = 0; i < n; i++) {
517 igt_assert(gem_bo_busy(fd, spin[i]->handle));
518 igt_spin_batch_free(fd, spin[i]);
519 }
520
521 n++;
522 for (i = 0; i <= n; i++)
523 igt_assert_eq_u32(ptr[i], i);
524
525 gem_context_destroy(fd, ctx[NOISE]);
526 gem_context_destroy(fd, ctx[HI]);
527
528 munmap(ptr, 4096);
529 gem_close(fd, result);
530}
531
Chris Wilson721d8742016-10-27 11:32:47 +0100532static void deep(int fd, unsigned ring)
533{
534#define XS 8
535 struct cork cork;
536 uint32_t result, dep[XS];
537 uint32_t *busy;
538 uint32_t *ptr;
539 uint32_t *ctx;
540
541 ctx = malloc(sizeof(*ctx)*(MAX_PRIO + 1));
542 for (int n = 0; n <= MAX_PRIO; n++) {
543 ctx[n] = gem_context_create(fd);
544 ctx_set_priority(fd, ctx[n], n);
545 }
546
547 result = gem_create(fd, 4096);
548 for (int m = 0; m < XS; m ++)
549 dep[m] = gem_create(fd, 4096);
550
551 busy = make_busy(fd, result, ring);
552 plug(fd, &cork);
553
554 /* Create a deep dependency chain, with a few branches */
555 for (int n = 0; n <= MAX_PRIO; n++)
556 for (int m = 0; m < XS; m++)
557 store_dword(fd, ctx[n], ring, dep[m], 4*n, ctx[n], cork.handle, I915_GEM_DOMAIN_INSTRUCTION);
558
559 for (int n = 0; n <= MAX_PRIO; n++) {
560 for (int m = 0; m < XS; m++) {
561 store_dword(fd, ctx[n], ring, result, 4*n, ctx[n], dep[m], 0);
562 store_dword(fd, ctx[n], ring, result, 4*m, ctx[n], 0, I915_GEM_DOMAIN_INSTRUCTION);
563 }
564 }
565
566 igt_assert(gem_bo_busy(fd, result));
567 unplug(&cork); /* only now submit our batches */
568 igt_debugfs_dump(fd, "i915_engine_info");
569 finish_busy(busy);
570
571 for (int n = 0; n <= MAX_PRIO; n++)
572 gem_context_destroy(fd, ctx[n]);
573
574 for (int m = 0; m < XS; m++) {
575 ptr = gem_mmap__gtt(fd, dep[m], 4096, PROT_READ);
576 gem_set_domain(fd, dep[m], /* no write hazard lies! */
577 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
578 gem_close(fd, dep[m]);
579
580 for (int n = 0; n <= MAX_PRIO; n++)
581 igt_assert_eq_u32(ptr[n], ctx[n]);
582 munmap(ptr, 4096);
583 }
584
585 ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
586 gem_set_domain(fd, result, /* no write hazard lies! */
587 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
588 gem_close(fd, result);
589
590 for (int m = 0; m < XS; m++)
591 igt_assert_eq_u32(ptr[m], ctx[MAX_PRIO]);
592 munmap(ptr, 4096);
593
594 free(ctx);
Chris Wilsonf6920752017-04-24 13:20:04 +0100595#undef XS
596}
597
Chris Wilsona19ef052017-07-31 14:15:59 +0100598static void alarm_handler(int sig)
599{
600}
601
602static int __execbuf(int fd, struct drm_i915_gem_execbuffer2 *execbuf)
603{
Chris Wilson976ed7c2017-08-24 12:16:17 +0100604 int err = 0;
605 if (ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, execbuf))
606 err = -errno;
607 return err;
Chris Wilsona19ef052017-07-31 14:15:59 +0100608}
609
610static unsigned int measure_ring_size(int fd, unsigned int ring)
611{
612 struct sigaction sa = { .sa_handler = alarm_handler };
613 struct drm_i915_gem_exec_object2 obj[2];
614 struct drm_i915_gem_execbuffer2 execbuf;
615 const uint32_t bbe = MI_BATCH_BUFFER_END;
616 unsigned int count, last;
617 struct itimerval itv;
618 struct cork c;
619
620 memset(obj, 0, sizeof(obj));
621 obj[1].handle = gem_create(fd, 4096);
622 gem_write(fd, obj[1].handle, 0, &bbe, sizeof(bbe));
623
624 memset(&execbuf, 0, sizeof(execbuf));
625 execbuf.buffers_ptr = to_user_pointer(obj + 1);
626 execbuf.buffer_count = 1;
627 execbuf.flags = ring;
628 gem_execbuf(fd, &execbuf);
629 gem_sync(fd, obj[1].handle);
630
631 plug(fd, &c);
632 obj[0].handle = c.handle;
633
634 execbuf.buffers_ptr = to_user_pointer(obj);
635 execbuf.buffer_count = 2;
636
637 sigaction(SIGALRM, &sa, NULL);
638 itv.it_interval.tv_sec = 0;
639 itv.it_interval.tv_usec = 100;
640 itv.it_value.tv_sec = 0;
641 itv.it_value.tv_usec = 1000;
642 setitimer(ITIMER_REAL, &itv, NULL);
643
644 last = -1;
645 count = 0;
646 do {
647 if (__execbuf(fd, &execbuf) == 0) {
648 count++;
649 continue;
650 }
651
652 if (last == count)
653 break;
654
655 last = count;
656 } while (1);
657
658 memset(&itv, 0, sizeof(itv));
659 setitimer(ITIMER_REAL, &itv, NULL);
660
661 unplug(&c);
662 gem_close(fd, obj[1].handle);
663
664 return count;
665}
666
Chris Wilsonf6920752017-04-24 13:20:04 +0100667static void wide(int fd, unsigned ring)
668{
Chris Wilsonf6920752017-04-24 13:20:04 +0100669#define NCTX 4096
Chris Wilsona19ef052017-07-31 14:15:59 +0100670 struct timespec tv = {};
671 unsigned int ring_size = measure_ring_size(fd, ring);
Chris Wilsonf6920752017-04-24 13:20:04 +0100672
673 struct cork cork;
674 uint32_t result;
675 uint32_t *busy;
676 uint32_t *ptr;
677 uint32_t *ctx;
Chris Wilsona19ef052017-07-31 14:15:59 +0100678 unsigned int count;
Chris Wilsonf6920752017-04-24 13:20:04 +0100679
680 ctx = malloc(sizeof(*ctx)*NCTX);
681 for (int n = 0; n < NCTX; n++)
682 ctx[n] = gem_context_create(fd);
683
Chris Wilsonf6920752017-04-24 13:20:04 +0100684 result = gem_create(fd, 4*NCTX);
685
686 busy = make_busy(fd, result, ring);
687 plug(fd, &cork);
688
689 /* Lots of in-order requests, plugged and submitted simultaneously */
Chris Wilsona19ef052017-07-31 14:15:59 +0100690 for (count = 0;
691 igt_seconds_elapsed(&tv) < 5 && count < ring_size;
692 count++) {
693 for (int n = 0; n < NCTX; n++) {
694 store_dword(fd, ctx[n], ring, result, 4*n, ctx[n], cork.handle, I915_GEM_DOMAIN_INSTRUCTION);
695 }
Chris Wilsonf6920752017-04-24 13:20:04 +0100696 }
Chris Wilsona19ef052017-07-31 14:15:59 +0100697 igt_info("Submitted %d requests over %d contexts in %.1fms\n",
698 count, NCTX, igt_nsec_elapsed(&tv) * 1e-6);
Chris Wilsonf6920752017-04-24 13:20:04 +0100699
700 igt_assert(gem_bo_busy(fd, result));
701 unplug(&cork); /* only now submit our batches */
702 igt_debugfs_dump(fd, "i915_engine_info");
703 finish_busy(busy);
704
705 for (int n = 0; n < NCTX; n++)
706 gem_context_destroy(fd, ctx[n]);
707
708 ptr = gem_mmap__gtt(fd, result, 4*NCTX, PROT_READ);
709 gem_set_domain(fd, result, /* no write hazard lies! */
710 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
711 for (int n = 0; n < NCTX; n++)
712 igt_assert_eq_u32(ptr[n], ctx[n]);
Chris Wilson61f8de72017-07-20 10:08:28 +0100713 munmap(ptr, 4*NCTX);
Chris Wilsonf6920752017-04-24 13:20:04 +0100714
Chris Wilsonf6920752017-04-24 13:20:04 +0100715 gem_close(fd, result);
716 free(ctx);
Chris Wilson61f8de72017-07-20 10:08:28 +0100717#undef NCTX
718}
719
720static void reorder_wide(int fd, unsigned ring)
721{
722 const int gen = intel_gen(intel_get_drm_devid(fd));
723 struct drm_i915_gem_relocation_entry reloc;
724 struct drm_i915_gem_exec_object2 obj[3];
725 struct drm_i915_gem_execbuffer2 execbuf;
726 struct cork cork;
727 uint32_t result, target;
728 uint32_t *busy;
729 uint32_t *r, *t;
730
731 result = gem_create(fd, 4096);
732 target = gem_create(fd, 4096);
733
734 busy = make_busy(fd, result, ring);
735 plug(fd, &cork);
736
737 t = gem_mmap__cpu(fd, target, 0, 4096, PROT_WRITE);
738 gem_set_domain(fd, target, I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
739
740 memset(obj, 0, sizeof(obj));
741 obj[0].handle = cork.handle;
742 obj[1].handle = result;
743 obj[2].relocs_ptr = to_user_pointer(&reloc);
744 obj[2].relocation_count = 1;
745
746 memset(&reloc, 0, sizeof(reloc));
747 reloc.target_handle = result;
748 reloc.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
749 reloc.write_domain = 0; /* lies */
750
751 memset(&execbuf, 0, sizeof(execbuf));
752 execbuf.buffers_ptr = to_user_pointer(obj);
753 execbuf.buffer_count = 3;
754 execbuf.flags = ring;
755 if (gen < 6)
756 execbuf.flags |= I915_EXEC_SECURE;
757
Chris Wilsonda553ff2017-09-22 10:32:44 +0100758 for (int n = MIN_PRIO, x = 1; n <= MAX_PRIO; n++, x++) {
Chris Wilson61f8de72017-07-20 10:08:28 +0100759 uint32_t *batch;
760
761 execbuf.rsvd1 = gem_context_create(fd);
762 ctx_set_priority(fd, execbuf.rsvd1, n);
763
764 obj[2].handle = gem_create(fd, 128 * 64);
765 batch = gem_mmap__gtt(fd, obj[2].handle, 128 * 64, PROT_WRITE);
766 gem_set_domain(fd, obj[2].handle, I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
767
768 for (int m = 0; m < 128; m++) {
769 uint64_t addr;
770 int idx = hars_petruska_f54_1_random_unsafe_max( 1024);
771 int i;
772
773 execbuf.batch_start_offset = m * 64;
774 reloc.offset = execbuf.batch_start_offset + sizeof(uint32_t);
775 reloc.delta = idx * sizeof(uint32_t);
776 addr = reloc.presumed_offset + reloc.delta;
777
778 i = execbuf.batch_start_offset / sizeof(uint32_t);
779 batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
780 if (gen >= 8) {
781 batch[++i] = addr;
782 batch[++i] = addr >> 32;
783 } else if (gen >= 4) {
784 batch[++i] = 0;
785 batch[++i] = addr;
786 reloc.offset += sizeof(uint32_t);
787 } else {
788 batch[i]--;
789 batch[++i] = addr;
790 }
791 batch[++i] = x;
792 batch[++i] = MI_BATCH_BUFFER_END;
793
794 if (!t[idx])
795 t[idx] = x;
796
797 gem_execbuf(fd, &execbuf);
798 }
799
800 munmap(batch, 128 * 64);
801 gem_close(fd, obj[2].handle);
802 gem_context_destroy(fd, execbuf.rsvd1);
803 }
804
805 igt_assert(gem_bo_busy(fd, result));
806 unplug(&cork); /* only now submit our batches */
807 igt_debugfs_dump(fd, "i915_engine_info");
808 finish_busy(busy);
809
810 r = gem_mmap__gtt(fd, result, 4096, PROT_READ);
811 gem_set_domain(fd, result, /* no write hazard lies! */
812 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
813 for (int n = 0; n < 1024; n++)
814 igt_assert_eq_u32(r[n], t[n]);
815 munmap(r, 4096);
816 munmap(t, 4096);
817
818 gem_close(fd, result);
819 gem_close(fd, target);
Chris Wilson721d8742016-10-27 11:32:47 +0100820}
821
Chris Wilson976ed7c2017-08-24 12:16:17 +0100822static void bind_to_cpu(int cpu)
823{
824 const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
825 struct sched_param rt = {.sched_priority = 99 };
826 cpu_set_t allowed;
827
828 igt_assert(sched_setscheduler(getpid(), SCHED_RR | SCHED_RESET_ON_FORK, &rt) == 0);
829
830 CPU_ZERO(&allowed);
831 CPU_SET(cpu % ncpus, &allowed);
832 igt_assert(sched_setaffinity(getpid(), sizeof(cpu_set_t), &allowed) == 0);
833}
834
835static void test_pi_ringfull(int fd, unsigned int engine)
836{
837 const uint32_t bbe = MI_BATCH_BUFFER_END;
838 struct sigaction sa = { .sa_handler = alarm_handler };
839 struct drm_i915_gem_execbuffer2 execbuf;
840 struct drm_i915_gem_exec_object2 obj[2];
841 unsigned int last, count;
842 struct itimerval itv;
843 struct cork c;
844 bool *result;
845
846 result = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
847 igt_assert(result != MAP_FAILED);
848
849 memset(&execbuf, 0, sizeof(execbuf));
850 memset(&obj, 0, sizeof(obj));
851
852 obj[1].handle = gem_create(fd, 4096);
853 gem_write(fd, obj[1].handle, 0, &bbe, sizeof(bbe));
854
855 execbuf.buffers_ptr = to_user_pointer(&obj[1]);
856 execbuf.buffer_count = 1;
857 execbuf.flags = engine;
858 execbuf.rsvd1 = gem_context_create(fd);
Chris Wilsonda553ff2017-09-22 10:32:44 +0100859 ctx_set_priority(fd, execbuf.rsvd1, MIN_PRIO);
Chris Wilson976ed7c2017-08-24 12:16:17 +0100860
861 gem_execbuf(fd, &execbuf);
862 gem_sync(fd, obj[1].handle);
863
864 /* Fill the low-priority ring */
865 plug(fd, &c);
866 obj[0].handle = c.handle;
867
868 execbuf.buffers_ptr = to_user_pointer(obj);
869 execbuf.buffer_count = 2;
870
871 sigaction(SIGALRM, &sa, NULL);
872 itv.it_interval.tv_sec = 0;
873 itv.it_interval.tv_usec = 100;
874 itv.it_value.tv_sec = 0;
875 itv.it_value.tv_usec = 1000;
876 setitimer(ITIMER_REAL, &itv, NULL);
877
878 last = -1;
879 count = 0;
880 do {
881 if (__execbuf(fd, &execbuf) == 0) {
882 count++;
883 continue;
884 }
885
886 if (last == count)
887 break;
888
889 last = count;
890 } while (1);
891 igt_debug("Filled low-priority ring with %d batches\n", count);
892
893 memset(&itv, 0, sizeof(itv));
894 setitimer(ITIMER_REAL, &itv, NULL);
895
896 execbuf.buffers_ptr = to_user_pointer(&obj[1]);
897 execbuf.buffer_count = 1;
898
899 /* both parent + child on the same cpu, only parent is RT */
900 bind_to_cpu(0);
901
902 igt_fork(child, 1) {
903 result[0] = true;
904
905 igt_debug("Creating HP context\n");
906 execbuf.rsvd1 = gem_context_create(fd);
907 ctx_set_priority(fd, execbuf.rsvd1, MAX_PRIO);
908
909 kill(getppid(), SIGALRM);
910 sched_yield();
911 result[1] = true;
912
913 itv.it_value.tv_sec = 0;
914 itv.it_value.tv_usec = 10000;
915 setitimer(ITIMER_REAL, &itv, NULL);
916
917 /* Since we are the high priority task, we expect to be
918 * able to add ourselves to *our* ring without interruption.
919 */
920 igt_debug("HP child executing\n");
921 result[2] = __execbuf(fd, &execbuf) == 0;
922 gem_context_destroy(fd, execbuf.rsvd1);
923 }
924
925 /* Relinquish CPU just to allow child to create a context */
926 sleep(1);
927 igt_assert_f(result[0], "HP context (child) not created");
928 igt_assert_f(!result[1], "Child released too early!\n");
929
930 /* Parent sleeps waiting for ringspace, releasing child */
931 itv.it_value.tv_sec = 0;
932 itv.it_value.tv_usec = 50000;
933 setitimer(ITIMER_REAL, &itv, NULL);
934 igt_debug("LP parent executing\n");
935 igt_assert_eq(__execbuf(fd, &execbuf), -EINTR);
936 igt_assert_f(result[1], "Child was not released!\n");
937 igt_assert_f(result[2],
938 "High priority child unable to submit within 10ms\n");
939
940 unplug(&c);
941 igt_waitchildren();
942
943 gem_context_destroy(fd, execbuf.rsvd1);
944 gem_close(fd, obj[1].handle);
945 gem_close(fd, obj[0].handle);
946 munmap(result, 4096);
947}
948
Chris Wilson721d8742016-10-27 11:32:47 +0100949static bool has_scheduler(int fd)
950{
951 drm_i915_getparam_t gp;
952 int has = -1;
953
954 gp.param = LOCAL_PARAM_HAS_SCHEDULER;
955 gp.value = &has;
956 drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
957
958 return has > 0;
959}
960
Chris Wilson976ed7c2017-08-24 12:16:17 +0100961#define HAVE_EXECLISTS 0x1
962#define HAVE_GUC 0x2
963static unsigned print_welcome(int fd)
964{
965 unsigned flags = 0;
966 bool active;
967 int dir;
968
969 dir = igt_sysfs_open_parameters(fd);
970 if (dir < 0)
971 return 0;
972
973 active = igt_sysfs_get_boolean(dir, "enable_guc_submission");
974 if (active) {
975 igt_info("Using GuC submission\n");
976 flags |= HAVE_GUC | HAVE_EXECLISTS;
977 goto out;
978 }
979
980 active = igt_sysfs_get_boolean(dir, "enable_execlists");
981 if (active) {
982 igt_info("Using Execlists submission\n");
983 flags |= HAVE_EXECLISTS;
984 goto out;
985 }
986
987 active = igt_sysfs_get_boolean(dir, "semaphores");
988 igt_info("Using Legacy submission%s\n",
989 active ? ", with semaphores" : "");
990
991out:
992 close(dir);
993 return flags;
994}
995
Chris Wilson721d8742016-10-27 11:32:47 +0100996igt_main
997{
998 const struct intel_execution_engine *e;
Chris Wilson976ed7c2017-08-24 12:16:17 +0100999 unsigned int caps = 0;
Chris Wilson721d8742016-10-27 11:32:47 +01001000 int fd = -1;
1001
1002 igt_skip_on_simulation();
1003
1004 igt_fixture {
1005 fd = drm_open_driver_master(DRIVER_INTEL);
Chris Wilson976ed7c2017-08-24 12:16:17 +01001006 caps = print_welcome(fd);
Chris Wilson9518cb52017-02-22 15:24:54 +00001007 igt_require_gem(fd);
Chris Wilson721d8742016-10-27 11:32:47 +01001008 gem_require_mmap_wc(fd);
1009 igt_fork_hang_detector(fd);
1010 }
1011
1012 igt_subtest_group {
1013 for (e = intel_execution_engines; e->name; e++) {
1014 /* default exec-id is purely symbolic */
1015 if (e->exec_id == 0)
1016 continue;
1017
1018 igt_subtest_f("fifo-%s", e->name) {
1019 gem_require_ring(fd, e->exec_id | e->flags);
Chris Wilsonbc787762017-05-18 12:11:59 +01001020 igt_require(gem_can_store_dword(fd, e->exec_id) | e->flags);
Chris Wilson721d8742016-10-27 11:32:47 +01001021 fifo(fd, e->exec_id | e->flags);
1022 }
1023 }
1024 }
1025
1026 igt_subtest_group {
1027 igt_fixture {
1028 igt_require(has_scheduler(fd));
Chris Wilsonaf0e1c52017-02-21 18:25:58 +00001029 ctx_has_priority(fd);
Chris Wilson721d8742016-10-27 11:32:47 +01001030 }
1031
1032 for (e = intel_execution_engines; e->name; e++) {
1033 /* default exec-id is purely symbolic */
1034 if (e->exec_id == 0)
1035 continue;
1036
1037 igt_subtest_group {
Chris Wilson073cfd72017-03-17 11:52:51 +00001038 igt_fixture {
Chris Wilson721d8742016-10-27 11:32:47 +01001039 gem_require_ring(fd, e->exec_id | e->flags);
Chris Wilsonbc787762017-05-18 12:11:59 +01001040 igt_require(gem_can_store_dword(fd, e->exec_id) | e->flags);
Chris Wilson073cfd72017-03-17 11:52:51 +00001041 }
Chris Wilson721d8742016-10-27 11:32:47 +01001042
1043 igt_subtest_f("in-order-%s", e->name)
1044 reorder(fd, e->exec_id | e->flags, EQUAL);
1045
1046 igt_subtest_f("out-order-%s", e->name)
1047 reorder(fd, e->exec_id | e->flags, 0);
1048
1049 igt_subtest_f("promotion-%s", e->name)
1050 promotion(fd, e->exec_id | e->flags);
1051
Chris Wilsona3801342017-07-16 16:28:41 +01001052 igt_subtest_f("preempt-%s", e->name)
1053 preempt(fd, e->exec_id | e->flags, 0);
1054
1055 igt_subtest_f("preempt-contexts-%s", e->name)
1056 preempt(fd, e->exec_id | e->flags, NEW_CTX);
1057
1058 igt_subtest_f("preempt-other-%s", e->name)
1059 preempt_other(fd, e->exec_id | e->flags);
1060
1061 igt_subtest_f("preempt-self-%s", e->name)
1062 preempt_self(fd, e->exec_id | e->flags);
1063
Chris Wilson721d8742016-10-27 11:32:47 +01001064 igt_subtest_f("deep-%s", e->name)
1065 deep(fd, e->exec_id | e->flags);
Chris Wilsonf6920752017-04-24 13:20:04 +01001066
1067 igt_subtest_f("wide-%s", e->name)
1068 wide(fd, e->exec_id | e->flags);
Chris Wilson61f8de72017-07-20 10:08:28 +01001069
1070 igt_subtest_f("reorder-wide-%s", e->name)
1071 reorder_wide(fd, e->exec_id | e->flags);
Chris Wilson721d8742016-10-27 11:32:47 +01001072 }
1073 }
1074 }
1075
Chris Wilson976ed7c2017-08-24 12:16:17 +01001076 igt_subtest_group {
1077 igt_fixture {
1078 igt_require(has_scheduler(fd));
1079 ctx_has_priority(fd);
1080
1081 /* need separate rings */
1082 igt_require(caps & HAVE_EXECLISTS);
1083 }
1084
1085 for (e = intel_execution_engines; e->name; e++) {
1086 igt_subtest_group {
1087 igt_fixture {
1088 gem_require_ring(fd, e->exec_id | e->flags);
1089 }
1090
1091 igt_subtest_f("pi-ringfull-%s", e->name)
1092 test_pi_ringfull(fd, e->exec_id | e->flags);
1093 }
1094 }
1095 }
1096
Chris Wilson721d8742016-10-27 11:32:47 +01001097 igt_fixture {
1098 igt_stop_hang_detector();
1099 close(fd);
1100 }
1101}