blob: 69379ecefd9b50938a8d99ec9e2d678c72d2b051 [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
43
44#define BUSY_QLEN 8
45
46IGT_TEST_DESCRIPTION("Check that we can control the order of execution");
47
Chris Wilsonaf0e1c52017-02-21 18:25:58 +000048static int __ctx_set_priority(int fd, uint32_t ctx, int prio)
Chris Wilson721d8742016-10-27 11:32:47 +010049{
50 struct local_i915_gem_context_param param;
51
52 memset(&param, 0, sizeof(param));
53 param.context = ctx;
54 param.size = 0;
55 param.param = LOCAL_CONTEXT_PARAM_PRIORITY;
56 param.value = prio;
57
Chris Wilsonaf0e1c52017-02-21 18:25:58 +000058 return __gem_context_set_param(fd, &param);
59}
60
61static void ctx_set_priority(int fd, uint32_t ctx, int prio)
62{
63 igt_assert_eq(__ctx_set_priority(fd, ctx, prio), 0);
64}
65
66static void ctx_has_priority(int fd)
67{
68 igt_require(__ctx_set_priority(fd, 0, MAX_PRIO) == 0);
Chris Wilson721d8742016-10-27 11:32:47 +010069}
70
71static void store_dword(int fd, uint32_t ctx, unsigned ring,
72 uint32_t target, uint32_t offset, uint32_t value,
73 uint32_t cork, unsigned write_domain)
74{
75 const int gen = intel_gen(intel_get_drm_devid(fd));
76 struct drm_i915_gem_exec_object2 obj[3];
77 struct drm_i915_gem_relocation_entry reloc;
78 struct drm_i915_gem_execbuffer2 execbuf;
79 uint32_t batch[16];
80 int i;
81
82 memset(&execbuf, 0, sizeof(execbuf));
Chris Wilson4de67b22017-01-02 11:05:21 +000083 execbuf.buffers_ptr = to_user_pointer(obj + !cork);
Chris Wilson721d8742016-10-27 11:32:47 +010084 execbuf.buffer_count = 2 + !!cork;
85 execbuf.flags = ring;
86 if (gen < 6)
87 execbuf.flags |= I915_EXEC_SECURE;
88 execbuf.rsvd1 = ctx;
89
90 memset(obj, 0, sizeof(obj));
91 obj[0].handle = cork;
92 obj[1].handle = target;
93 obj[2].handle = gem_create(fd, 4096);
94
95 memset(&reloc, 0, sizeof(reloc));
96 reloc.target_handle = obj[1].handle;
97 reloc.presumed_offset = 0;
98 reloc.offset = sizeof(uint32_t);
99 reloc.delta = offset;
100 reloc.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
101 reloc.write_domain = write_domain;
Chris Wilson4de67b22017-01-02 11:05:21 +0000102 obj[2].relocs_ptr = to_user_pointer(&reloc);
Chris Wilson721d8742016-10-27 11:32:47 +0100103 obj[2].relocation_count = 1;
104
105 i = 0;
106 batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
107 if (gen >= 8) {
108 batch[++i] = offset;
109 batch[++i] = 0;
110 } else if (gen >= 4) {
111 batch[++i] = 0;
112 batch[++i] = offset;
113 reloc.offset += sizeof(uint32_t);
114 } else {
115 batch[i]--;
116 batch[++i] = offset;
117 }
118 batch[++i] = value;
119 batch[++i] = MI_BATCH_BUFFER_END;
120 gem_write(fd, obj[2].handle, 0, batch, sizeof(batch));
121 gem_execbuf(fd, &execbuf);
122 gem_close(fd, obj[2].handle);
123}
124
125static uint32_t *make_busy(int fd, uint32_t target, unsigned ring)
126{
127 const int gen = intel_gen(intel_get_drm_devid(fd));
128 struct drm_i915_gem_exec_object2 obj[2];
129 struct drm_i915_gem_relocation_entry reloc[2];
130 struct drm_i915_gem_execbuffer2 execbuf;
131 uint32_t *batch;
132 int i;
133
134 memset(&execbuf, 0, sizeof(execbuf));
Chris Wilson4de67b22017-01-02 11:05:21 +0000135 execbuf.buffers_ptr = to_user_pointer(obj + !target);
Chris Wilson721d8742016-10-27 11:32:47 +0100136 execbuf.buffer_count = 1 + !!target;
137
138 memset(obj, 0, sizeof(obj));
139 obj[0].handle = target;
140 obj[1].handle = gem_create(fd, 4096);
141 batch = gem_mmap__wc(fd, obj[1].handle, 0, 4096, PROT_WRITE);
142 gem_set_domain(fd, obj[1].handle,
143 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
144
Chris Wilson4de67b22017-01-02 11:05:21 +0000145 obj[1].relocs_ptr = to_user_pointer(reloc);
Chris Wilson721d8742016-10-27 11:32:47 +0100146 obj[1].relocation_count = 1 + !!target;
147 memset(reloc, 0, sizeof(reloc));
148
149 reloc[0].target_handle = obj[1].handle; /* recurse */
150 reloc[0].presumed_offset = 0;
151 reloc[0].offset = sizeof(uint32_t);
152 reloc[0].delta = 0;
153 reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND;
154 reloc[0].write_domain = 0;
155
156 reloc[1].target_handle = target;
157 reloc[1].presumed_offset = 0;
158 reloc[1].offset = 1024;
159 reloc[1].delta = 0;
160 reloc[1].read_domains = I915_GEM_DOMAIN_COMMAND;
161 reloc[1].write_domain = 0;
162
163 i = 0;
164 batch[i] = MI_BATCH_BUFFER_START;
165 if (gen >= 8) {
166 batch[i] |= 1 << 8 | 1;
167 batch[++i] = 0;
168 batch[++i] = 0;
169 } else if (gen >= 6) {
170 batch[i] |= 1 << 8;
171 batch[++i] = 0;
172 } else {
173 batch[i] |= 2 << 6;
174 batch[++i] = 0;
175 if (gen < 4) {
176 batch[i] |= 1;
177 reloc[0].delta = 1;
178 }
179 }
180 i++;
181
182 if (ring != -1) {
183 execbuf.flags = ring;
184 for (int n = 0; n < BUSY_QLEN; n++)
185 gem_execbuf(fd, &execbuf);
186 } else {
187 for_each_engine(fd, ring) {
188 if (ring == 0)
189 continue;
190
191 execbuf.flags = ring;
192 for (int n = 0; n < BUSY_QLEN; n++)
193 gem_execbuf(fd, &execbuf);
194 igt_assert(execbuf.flags == ring);
195 }
196 }
197
198 if (target) {
199 execbuf.flags = 0;
200 reloc[1].write_domain = I915_GEM_DOMAIN_COMMAND;
201 gem_execbuf(fd, &execbuf);
202 }
203
204 gem_close(fd, obj[1].handle);
205
206 return batch;
207}
208
209static void finish_busy(uint32_t *busy)
210{
211 *busy = MI_BATCH_BUFFER_END;
212 munmap(busy, 4096);
213}
214
215struct cork {
216 int device;
217 uint32_t handle;
218 uint32_t fence;
219};
220
221static void plug(int fd, struct cork *c)
222{
223 struct vgem_bo bo;
224 int dmabuf;
225
226 c->device = drm_open_driver(DRIVER_VGEM);
227
228 bo.width = bo.height = 1;
229 bo.bpp = 4;
230 vgem_create(c->device, &bo);
231 c->fence = vgem_fence_attach(c->device, &bo, VGEM_FENCE_WRITE);
232
233 dmabuf = prime_handle_to_fd(c->device, bo.handle);
234 c->handle = prime_fd_to_handle(fd, dmabuf);
235 close(dmabuf);
236}
237
238static void unplug(struct cork *c)
239{
240 vgem_fence_signal(c->device, c->fence);
241 close(c->device);
242}
243
244static void fifo(int fd, unsigned ring)
245{
246 struct cork cork;
247 uint32_t *busy;
248 uint32_t scratch;
249 uint32_t *ptr;
250
251 scratch = gem_create(fd, 4096);
252
253 busy = make_busy(fd, scratch, ring);
254 plug(fd, &cork);
255
256 /* Same priority, same timeline, final result will be the second eb */
257 store_dword(fd, 0, ring, scratch, 0, 1, cork.handle, 0);
258 store_dword(fd, 0, ring, scratch, 0, 2, cork.handle, 0);
259
260 unplug(&cork); /* only now submit our batches */
261 igt_debugfs_dump(fd, "i915_engine_info");
262 finish_busy(busy);
263
264 ptr = gem_mmap__gtt(fd, scratch, 4096, PROT_READ);
265 gem_set_domain(fd, scratch, /* no write hazard lies! */
266 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
267 gem_close(fd, scratch);
268
269 igt_assert_eq_u32(ptr[0], 2);
270 munmap(ptr, 4096);
271}
272
273static void reorder(int fd, unsigned ring, unsigned flags)
274#define EQUAL 1
275{
276 struct cork cork;
277 uint32_t scratch;
278 uint32_t *busy;
279 uint32_t *ptr;
280 uint32_t ctx[2];
281
282 ctx[LO] = gem_context_create(fd);
283 ctx_set_priority(fd, ctx[LO], -MAX_PRIO);
284
285 ctx[HI] = gem_context_create(fd);
286 ctx_set_priority(fd, ctx[HI], flags & EQUAL ? -MAX_PRIO : 0);
287
288 scratch = gem_create(fd, 4096);
289
290 busy = make_busy(fd, scratch, ring);
291 plug(fd, &cork);
292
293 /* We expect the high priority context to be executed first, and
294 * so the final result will be value from the low priority context.
295 */
296 store_dword(fd, ctx[LO], ring, scratch, 0, ctx[LO], cork.handle, 0);
297 store_dword(fd, ctx[HI], ring, scratch, 0, ctx[HI], cork.handle, 0);
298
299 unplug(&cork); /* only now submit our batches */
300 igt_debugfs_dump(fd, "i915_engine_info");
301 finish_busy(busy);
302
303 gem_context_destroy(fd, ctx[LO]);
304 gem_context_destroy(fd, ctx[HI]);
305
306 ptr = gem_mmap__gtt(fd, scratch, 4096, PROT_READ);
307 gem_set_domain(fd, scratch, /* no write hazard lies! */
308 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
309 gem_close(fd, scratch);
310
311 if (flags & EQUAL) /* equal priority, result will be fifo */
312 igt_assert_eq_u32(ptr[0], ctx[HI]);
313 else
314 igt_assert_eq_u32(ptr[0], ctx[LO]);
315 munmap(ptr, 4096);
316}
317
318static void promotion(int fd, unsigned ring)
319{
320 struct cork cork;
321 uint32_t result, dep;
322 uint32_t *busy;
323 uint32_t *ptr;
324 uint32_t ctx[3];
325
326 ctx[LO] = gem_context_create(fd);
327 ctx_set_priority(fd, ctx[LO], -MAX_PRIO);
328
329 ctx[HI] = gem_context_create(fd);
330 ctx_set_priority(fd, ctx[HI], 0);
331
332 ctx[NOISE] = gem_context_create(fd);
333 ctx_set_priority(fd, ctx[NOISE], -MAX_PRIO/2);
334
335 result = gem_create(fd, 4096);
336 dep = gem_create(fd, 4096);
337
338 busy = make_busy(fd, result, ring);
339 plug(fd, &cork);
340
341 /* Expect that HI promotes LO, so the order will be LO, HI, NOISE.
342 *
343 * fifo would be NOISE, LO, HI.
344 * strict priority would be HI, NOISE, LO
345 */
346 store_dword(fd, ctx[NOISE], ring, result, 0, ctx[NOISE], cork.handle, 0);
347 store_dword(fd, ctx[LO], ring, result, 0, ctx[LO], cork.handle, 0);
348
349 /* link LO <-> HI via a dependency on another buffer */
350 store_dword(fd, ctx[LO], ring, dep, 0, ctx[LO], 0, I915_GEM_DOMAIN_INSTRUCTION);
351 store_dword(fd, ctx[HI], ring, dep, 0, ctx[HI], 0, 0);
352
353 store_dword(fd, ctx[HI], ring, result, 0, ctx[HI], 0, 0);
354
355 unplug(&cork); /* only now submit our batches */
356 igt_debugfs_dump(fd, "i915_engine_info");
357 finish_busy(busy);
358
359 gem_context_destroy(fd, ctx[NOISE]);
360 gem_context_destroy(fd, ctx[LO]);
361 gem_context_destroy(fd, ctx[HI]);
362
363 ptr = gem_mmap__gtt(fd, dep, 4096, PROT_READ);
364 gem_set_domain(fd, dep, /* no write hazard lies! */
365 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
366 gem_close(fd, dep);
367
368 igt_assert_eq_u32(ptr[0], ctx[HI]);
369 munmap(ptr, 4096);
370
371 ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
372 gem_set_domain(fd, result, /* no write hazard lies! */
373 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
374 gem_close(fd, result);
375
376 igt_assert_eq_u32(ptr[0], ctx[NOISE]);
377 munmap(ptr, 4096);
378}
379
Chris Wilsona3801342017-07-16 16:28:41 +0100380#define NEW_CTX 0x1
381static void preempt(int fd, unsigned ring, unsigned flags)
382{
383 uint32_t result = gem_create(fd, 4096);
384 uint32_t *ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
385 igt_spin_t *spin[16];
386 uint32_t ctx[2];
387
388 ctx[LO] = gem_context_create(fd);
389 ctx_set_priority(fd, ctx[LO], -MAX_PRIO);
390
391 ctx[HI] = gem_context_create(fd);
392 ctx_set_priority(fd, ctx[HI], MAX_PRIO);
393
394 for (int n = 0; n < 16; n++) {
395 if (flags & NEW_CTX) {
396 gem_context_destroy(fd, ctx[LO]);
397 ctx[LO] = gem_context_create(fd);
398 ctx_set_priority(fd, ctx[LO], -MAX_PRIO);
399 }
400 spin[n] = __igt_spin_batch_new(fd, ctx[LO], ring, 0);
401 igt_debug("spin[%d].handle=%d\n", n, spin[n]->handle);
402
403 store_dword(fd, ctx[HI], ring, result, 0, n + 1, 0, I915_GEM_DOMAIN_RENDER);
404
405 gem_set_domain(fd, result, I915_GEM_DOMAIN_GTT, 0);
406 igt_assert_eq_u32(ptr[0], n + 1);
407 igt_assert(gem_bo_busy(fd, spin[0]->handle));
408 }
409
410 for (int n = 0; n < 16; n++)
411 igt_spin_batch_free(fd, spin[n]);
412
413 gem_context_destroy(fd, ctx[LO]);
414 gem_context_destroy(fd, ctx[HI]);
415
416 munmap(ptr, 4096);
417 gem_close(fd, result);
418}
419
420static void preempt_other(int fd, unsigned ring)
421{
422 uint32_t result = gem_create(fd, 4096);
423 uint32_t *ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
424 igt_spin_t *spin[16];
425 unsigned int other;
426 unsigned int n, i;
427 uint32_t ctx[3];
428
429 /* On each engine, insert
430 * [NOISE] spinner,
431 * [LOW] write
432 *
433 * Then on our target engine do a [HIGH] write which should then
434 * prompt its dependent LOW writes in front of the spinner on
435 * each engine. The purpose of this test is to check that preemption
436 * can cross engines.
437 */
438
439 ctx[LO] = gem_context_create(fd);
440 ctx_set_priority(fd, ctx[LO], -MAX_PRIO);
441
442 ctx[NOISE] = gem_context_create(fd);
443
444 ctx[HI] = gem_context_create(fd);
445 ctx_set_priority(fd, ctx[HI], MAX_PRIO);
446
447 n = 0;
448 for_each_engine(fd, other) {
449 spin[n] = __igt_spin_batch_new(fd, ctx[NOISE], other, 0);
450 store_dword(fd, ctx[LO], other,
451 result, (n + 1)*sizeof(uint32_t), n + 1,
452 0, I915_GEM_DOMAIN_RENDER);
453 n++;
454 }
455 store_dword(fd, ctx[HI], ring,
456 result, (n + 1)*sizeof(uint32_t), n + 1,
457 0, I915_GEM_DOMAIN_RENDER);
458
459 gem_set_domain(fd, result, I915_GEM_DOMAIN_GTT, 0);
460
461 for (i = 0; i < n; i++) {
462 igt_assert(gem_bo_busy(fd, spin[i]->handle));
463 igt_spin_batch_free(fd, spin[i]);
464 }
465
466 n++;
467 for (i = 0; i <= n; i++)
468 igt_assert_eq_u32(ptr[i], i);
469
470 gem_context_destroy(fd, ctx[LO]);
471 gem_context_destroy(fd, ctx[NOISE]);
472 gem_context_destroy(fd, ctx[HI]);
473
474 munmap(ptr, 4096);
475 gem_close(fd, result);
476}
477
478static void preempt_self(int fd, unsigned ring)
479{
480 uint32_t result = gem_create(fd, 4096);
481 uint32_t *ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
482 igt_spin_t *spin[16];
483 unsigned int other;
484 unsigned int n, i;
485 uint32_t ctx[3];
486
487 /* On each engine, insert
488 * [NOISE] spinner,
489 * [self/LOW] write
490 *
491 * Then on our target engine do a [self/HIGH] write which should then
492 * preempt its own lower priority task on any engine.
493 */
494
495 ctx[NOISE] = gem_context_create(fd);
496
497 ctx[HI] = gem_context_create(fd);
498
499 n = 0;
500 ctx_set_priority(fd, ctx[HI], -MAX_PRIO);
501 for_each_engine(fd, other) {
502 spin[n] = __igt_spin_batch_new(fd, ctx[NOISE], other, 0);
503 store_dword(fd, ctx[HI], other,
504 result, (n + 1)*sizeof(uint32_t), n + 1,
505 0, I915_GEM_DOMAIN_RENDER);
506 n++;
507 }
508 ctx_set_priority(fd, ctx[HI], MAX_PRIO);
509 store_dword(fd, ctx[HI], ring,
510 result, (n + 1)*sizeof(uint32_t), n + 1,
511 0, I915_GEM_DOMAIN_RENDER);
512
513 gem_set_domain(fd, result, I915_GEM_DOMAIN_GTT, 0);
514
515 for (i = 0; i < n; i++) {
516 igt_assert(gem_bo_busy(fd, spin[i]->handle));
517 igt_spin_batch_free(fd, spin[i]);
518 }
519
520 n++;
521 for (i = 0; i <= n; i++)
522 igt_assert_eq_u32(ptr[i], i);
523
524 gem_context_destroy(fd, ctx[NOISE]);
525 gem_context_destroy(fd, ctx[HI]);
526
527 munmap(ptr, 4096);
528 gem_close(fd, result);
529}
530
Chris Wilson721d8742016-10-27 11:32:47 +0100531static void deep(int fd, unsigned ring)
532{
533#define XS 8
534 struct cork cork;
535 uint32_t result, dep[XS];
536 uint32_t *busy;
537 uint32_t *ptr;
538 uint32_t *ctx;
539
540 ctx = malloc(sizeof(*ctx)*(MAX_PRIO + 1));
541 for (int n = 0; n <= MAX_PRIO; n++) {
542 ctx[n] = gem_context_create(fd);
543 ctx_set_priority(fd, ctx[n], n);
544 }
545
546 result = gem_create(fd, 4096);
547 for (int m = 0; m < XS; m ++)
548 dep[m] = gem_create(fd, 4096);
549
550 busy = make_busy(fd, result, ring);
551 plug(fd, &cork);
552
553 /* Create a deep dependency chain, with a few branches */
554 for (int n = 0; n <= MAX_PRIO; n++)
555 for (int m = 0; m < XS; m++)
556 store_dword(fd, ctx[n], ring, dep[m], 4*n, ctx[n], cork.handle, I915_GEM_DOMAIN_INSTRUCTION);
557
558 for (int n = 0; n <= MAX_PRIO; n++) {
559 for (int m = 0; m < XS; m++) {
560 store_dword(fd, ctx[n], ring, result, 4*n, ctx[n], dep[m], 0);
561 store_dword(fd, ctx[n], ring, result, 4*m, ctx[n], 0, I915_GEM_DOMAIN_INSTRUCTION);
562 }
563 }
564
565 igt_assert(gem_bo_busy(fd, result));
566 unplug(&cork); /* only now submit our batches */
567 igt_debugfs_dump(fd, "i915_engine_info");
568 finish_busy(busy);
569
570 for (int n = 0; n <= MAX_PRIO; n++)
571 gem_context_destroy(fd, ctx[n]);
572
573 for (int m = 0; m < XS; m++) {
574 ptr = gem_mmap__gtt(fd, dep[m], 4096, PROT_READ);
575 gem_set_domain(fd, dep[m], /* no write hazard lies! */
576 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
577 gem_close(fd, dep[m]);
578
579 for (int n = 0; n <= MAX_PRIO; n++)
580 igt_assert_eq_u32(ptr[n], ctx[n]);
581 munmap(ptr, 4096);
582 }
583
584 ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
585 gem_set_domain(fd, result, /* no write hazard lies! */
586 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
587 gem_close(fd, result);
588
589 for (int m = 0; m < XS; m++)
590 igt_assert_eq_u32(ptr[m], ctx[MAX_PRIO]);
591 munmap(ptr, 4096);
592
593 free(ctx);
Chris Wilsonf6920752017-04-24 13:20:04 +0100594#undef XS
595}
596
Chris Wilsona19ef052017-07-31 14:15:59 +0100597static void alarm_handler(int sig)
598{
599}
600
601static int __execbuf(int fd, struct drm_i915_gem_execbuffer2 *execbuf)
602{
Chris Wilson976ed7c2017-08-24 12:16:17 +0100603 int err = 0;
604 if (ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, execbuf))
605 err = -errno;
606 return err;
Chris Wilsona19ef052017-07-31 14:15:59 +0100607}
608
609static unsigned int measure_ring_size(int fd, unsigned int ring)
610{
611 struct sigaction sa = { .sa_handler = alarm_handler };
612 struct drm_i915_gem_exec_object2 obj[2];
613 struct drm_i915_gem_execbuffer2 execbuf;
614 const uint32_t bbe = MI_BATCH_BUFFER_END;
615 unsigned int count, last;
616 struct itimerval itv;
617 struct cork c;
618
619 memset(obj, 0, sizeof(obj));
620 obj[1].handle = gem_create(fd, 4096);
621 gem_write(fd, obj[1].handle, 0, &bbe, sizeof(bbe));
622
623 memset(&execbuf, 0, sizeof(execbuf));
624 execbuf.buffers_ptr = to_user_pointer(obj + 1);
625 execbuf.buffer_count = 1;
626 execbuf.flags = ring;
627 gem_execbuf(fd, &execbuf);
628 gem_sync(fd, obj[1].handle);
629
630 plug(fd, &c);
631 obj[0].handle = c.handle;
632
633 execbuf.buffers_ptr = to_user_pointer(obj);
634 execbuf.buffer_count = 2;
635
636 sigaction(SIGALRM, &sa, NULL);
637 itv.it_interval.tv_sec = 0;
638 itv.it_interval.tv_usec = 100;
639 itv.it_value.tv_sec = 0;
640 itv.it_value.tv_usec = 1000;
641 setitimer(ITIMER_REAL, &itv, NULL);
642
643 last = -1;
644 count = 0;
645 do {
646 if (__execbuf(fd, &execbuf) == 0) {
647 count++;
648 continue;
649 }
650
651 if (last == count)
652 break;
653
654 last = count;
655 } while (1);
656
657 memset(&itv, 0, sizeof(itv));
658 setitimer(ITIMER_REAL, &itv, NULL);
659
660 unplug(&c);
661 gem_close(fd, obj[1].handle);
662
663 return count;
664}
665
Chris Wilsonf6920752017-04-24 13:20:04 +0100666static void wide(int fd, unsigned ring)
667{
Chris Wilsonf6920752017-04-24 13:20:04 +0100668#define NCTX 4096
Chris Wilsona19ef052017-07-31 14:15:59 +0100669 struct timespec tv = {};
670 unsigned int ring_size = measure_ring_size(fd, ring);
Chris Wilsonf6920752017-04-24 13:20:04 +0100671
672 struct cork cork;
673 uint32_t result;
674 uint32_t *busy;
675 uint32_t *ptr;
676 uint32_t *ctx;
Chris Wilsona19ef052017-07-31 14:15:59 +0100677 unsigned int count;
Chris Wilsonf6920752017-04-24 13:20:04 +0100678
679 ctx = malloc(sizeof(*ctx)*NCTX);
680 for (int n = 0; n < NCTX; n++)
681 ctx[n] = gem_context_create(fd);
682
Chris Wilsonf6920752017-04-24 13:20:04 +0100683 result = gem_create(fd, 4*NCTX);
684
685 busy = make_busy(fd, result, ring);
686 plug(fd, &cork);
687
688 /* Lots of in-order requests, plugged and submitted simultaneously */
Chris Wilsona19ef052017-07-31 14:15:59 +0100689 for (count = 0;
690 igt_seconds_elapsed(&tv) < 5 && count < ring_size;
691 count++) {
692 for (int n = 0; n < NCTX; n++) {
693 store_dword(fd, ctx[n], ring, result, 4*n, ctx[n], cork.handle, I915_GEM_DOMAIN_INSTRUCTION);
694 }
Chris Wilsonf6920752017-04-24 13:20:04 +0100695 }
Chris Wilsona19ef052017-07-31 14:15:59 +0100696 igt_info("Submitted %d requests over %d contexts in %.1fms\n",
697 count, NCTX, igt_nsec_elapsed(&tv) * 1e-6);
Chris Wilsonf6920752017-04-24 13:20:04 +0100698
699 igt_assert(gem_bo_busy(fd, result));
700 unplug(&cork); /* only now submit our batches */
701 igt_debugfs_dump(fd, "i915_engine_info");
702 finish_busy(busy);
703
704 for (int n = 0; n < NCTX; n++)
705 gem_context_destroy(fd, ctx[n]);
706
707 ptr = gem_mmap__gtt(fd, result, 4*NCTX, PROT_READ);
708 gem_set_domain(fd, result, /* no write hazard lies! */
709 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
710 for (int n = 0; n < NCTX; n++)
711 igt_assert_eq_u32(ptr[n], ctx[n]);
Chris Wilson61f8de72017-07-20 10:08:28 +0100712 munmap(ptr, 4*NCTX);
Chris Wilsonf6920752017-04-24 13:20:04 +0100713
Chris Wilsonf6920752017-04-24 13:20:04 +0100714 gem_close(fd, result);
715 free(ctx);
Chris Wilson61f8de72017-07-20 10:08:28 +0100716#undef NCTX
717}
718
719static void reorder_wide(int fd, unsigned ring)
720{
721 const int gen = intel_gen(intel_get_drm_devid(fd));
722 struct drm_i915_gem_relocation_entry reloc;
723 struct drm_i915_gem_exec_object2 obj[3];
724 struct drm_i915_gem_execbuffer2 execbuf;
725 struct cork cork;
726 uint32_t result, target;
727 uint32_t *busy;
728 uint32_t *r, *t;
729
730 result = gem_create(fd, 4096);
731 target = gem_create(fd, 4096);
732
733 busy = make_busy(fd, result, ring);
734 plug(fd, &cork);
735
736 t = gem_mmap__cpu(fd, target, 0, 4096, PROT_WRITE);
737 gem_set_domain(fd, target, I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
738
739 memset(obj, 0, sizeof(obj));
740 obj[0].handle = cork.handle;
741 obj[1].handle = result;
742 obj[2].relocs_ptr = to_user_pointer(&reloc);
743 obj[2].relocation_count = 1;
744
745 memset(&reloc, 0, sizeof(reloc));
746 reloc.target_handle = result;
747 reloc.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
748 reloc.write_domain = 0; /* lies */
749
750 memset(&execbuf, 0, sizeof(execbuf));
751 execbuf.buffers_ptr = to_user_pointer(obj);
752 execbuf.buffer_count = 3;
753 execbuf.flags = ring;
754 if (gen < 6)
755 execbuf.flags |= I915_EXEC_SECURE;
756
757 for (int n = -MAX_PRIO, x = 1; n <= MAX_PRIO; n++, x++) {
758 uint32_t *batch;
759
760 execbuf.rsvd1 = gem_context_create(fd);
761 ctx_set_priority(fd, execbuf.rsvd1, n);
762
763 obj[2].handle = gem_create(fd, 128 * 64);
764 batch = gem_mmap__gtt(fd, obj[2].handle, 128 * 64, PROT_WRITE);
765 gem_set_domain(fd, obj[2].handle, I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
766
767 for (int m = 0; m < 128; m++) {
768 uint64_t addr;
769 int idx = hars_petruska_f54_1_random_unsafe_max( 1024);
770 int i;
771
772 execbuf.batch_start_offset = m * 64;
773 reloc.offset = execbuf.batch_start_offset + sizeof(uint32_t);
774 reloc.delta = idx * sizeof(uint32_t);
775 addr = reloc.presumed_offset + reloc.delta;
776
777 i = execbuf.batch_start_offset / sizeof(uint32_t);
778 batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
779 if (gen >= 8) {
780 batch[++i] = addr;
781 batch[++i] = addr >> 32;
782 } else if (gen >= 4) {
783 batch[++i] = 0;
784 batch[++i] = addr;
785 reloc.offset += sizeof(uint32_t);
786 } else {
787 batch[i]--;
788 batch[++i] = addr;
789 }
790 batch[++i] = x;
791 batch[++i] = MI_BATCH_BUFFER_END;
792
793 if (!t[idx])
794 t[idx] = x;
795
796 gem_execbuf(fd, &execbuf);
797 }
798
799 munmap(batch, 128 * 64);
800 gem_close(fd, obj[2].handle);
801 gem_context_destroy(fd, execbuf.rsvd1);
802 }
803
804 igt_assert(gem_bo_busy(fd, result));
805 unplug(&cork); /* only now submit our batches */
806 igt_debugfs_dump(fd, "i915_engine_info");
807 finish_busy(busy);
808
809 r = gem_mmap__gtt(fd, result, 4096, PROT_READ);
810 gem_set_domain(fd, result, /* no write hazard lies! */
811 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
812 for (int n = 0; n < 1024; n++)
813 igt_assert_eq_u32(r[n], t[n]);
814 munmap(r, 4096);
815 munmap(t, 4096);
816
817 gem_close(fd, result);
818 gem_close(fd, target);
Chris Wilson721d8742016-10-27 11:32:47 +0100819}
820
Chris Wilson976ed7c2017-08-24 12:16:17 +0100821static void bind_to_cpu(int cpu)
822{
823 const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
824 struct sched_param rt = {.sched_priority = 99 };
825 cpu_set_t allowed;
826
827 igt_assert(sched_setscheduler(getpid(), SCHED_RR | SCHED_RESET_ON_FORK, &rt) == 0);
828
829 CPU_ZERO(&allowed);
830 CPU_SET(cpu % ncpus, &allowed);
831 igt_assert(sched_setaffinity(getpid(), sizeof(cpu_set_t), &allowed) == 0);
832}
833
834static void test_pi_ringfull(int fd, unsigned int engine)
835{
836 const uint32_t bbe = MI_BATCH_BUFFER_END;
837 struct sigaction sa = { .sa_handler = alarm_handler };
838 struct drm_i915_gem_execbuffer2 execbuf;
839 struct drm_i915_gem_exec_object2 obj[2];
840 unsigned int last, count;
841 struct itimerval itv;
842 struct cork c;
843 bool *result;
844
845 result = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
846 igt_assert(result != MAP_FAILED);
847
848 memset(&execbuf, 0, sizeof(execbuf));
849 memset(&obj, 0, sizeof(obj));
850
851 obj[1].handle = gem_create(fd, 4096);
852 gem_write(fd, obj[1].handle, 0, &bbe, sizeof(bbe));
853
854 execbuf.buffers_ptr = to_user_pointer(&obj[1]);
855 execbuf.buffer_count = 1;
856 execbuf.flags = engine;
857 execbuf.rsvd1 = gem_context_create(fd);
858 ctx_set_priority(fd, execbuf.rsvd1, -MAX_PRIO);
859
860 gem_execbuf(fd, &execbuf);
861 gem_sync(fd, obj[1].handle);
862
863 /* Fill the low-priority ring */
864 plug(fd, &c);
865 obj[0].handle = c.handle;
866
867 execbuf.buffers_ptr = to_user_pointer(obj);
868 execbuf.buffer_count = 2;
869
870 sigaction(SIGALRM, &sa, NULL);
871 itv.it_interval.tv_sec = 0;
872 itv.it_interval.tv_usec = 100;
873 itv.it_value.tv_sec = 0;
874 itv.it_value.tv_usec = 1000;
875 setitimer(ITIMER_REAL, &itv, NULL);
876
877 last = -1;
878 count = 0;
879 do {
880 if (__execbuf(fd, &execbuf) == 0) {
881 count++;
882 continue;
883 }
884
885 if (last == count)
886 break;
887
888 last = count;
889 } while (1);
890 igt_debug("Filled low-priority ring with %d batches\n", count);
891
892 memset(&itv, 0, sizeof(itv));
893 setitimer(ITIMER_REAL, &itv, NULL);
894
895 execbuf.buffers_ptr = to_user_pointer(&obj[1]);
896 execbuf.buffer_count = 1;
897
898 /* both parent + child on the same cpu, only parent is RT */
899 bind_to_cpu(0);
900
901 igt_fork(child, 1) {
902 result[0] = true;
903
904 igt_debug("Creating HP context\n");
905 execbuf.rsvd1 = gem_context_create(fd);
906 ctx_set_priority(fd, execbuf.rsvd1, MAX_PRIO);
907
908 kill(getppid(), SIGALRM);
909 sched_yield();
910 result[1] = true;
911
912 itv.it_value.tv_sec = 0;
913 itv.it_value.tv_usec = 10000;
914 setitimer(ITIMER_REAL, &itv, NULL);
915
916 /* Since we are the high priority task, we expect to be
917 * able to add ourselves to *our* ring without interruption.
918 */
919 igt_debug("HP child executing\n");
920 result[2] = __execbuf(fd, &execbuf) == 0;
921 gem_context_destroy(fd, execbuf.rsvd1);
922 }
923
924 /* Relinquish CPU just to allow child to create a context */
925 sleep(1);
926 igt_assert_f(result[0], "HP context (child) not created");
927 igt_assert_f(!result[1], "Child released too early!\n");
928
929 /* Parent sleeps waiting for ringspace, releasing child */
930 itv.it_value.tv_sec = 0;
931 itv.it_value.tv_usec = 50000;
932 setitimer(ITIMER_REAL, &itv, NULL);
933 igt_debug("LP parent executing\n");
934 igt_assert_eq(__execbuf(fd, &execbuf), -EINTR);
935 igt_assert_f(result[1], "Child was not released!\n");
936 igt_assert_f(result[2],
937 "High priority child unable to submit within 10ms\n");
938
939 unplug(&c);
940 igt_waitchildren();
941
942 gem_context_destroy(fd, execbuf.rsvd1);
943 gem_close(fd, obj[1].handle);
944 gem_close(fd, obj[0].handle);
945 munmap(result, 4096);
946}
947
Chris Wilson721d8742016-10-27 11:32:47 +0100948static bool has_scheduler(int fd)
949{
950 drm_i915_getparam_t gp;
951 int has = -1;
952
953 gp.param = LOCAL_PARAM_HAS_SCHEDULER;
954 gp.value = &has;
955 drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
956
957 return has > 0;
958}
959
Chris Wilson976ed7c2017-08-24 12:16:17 +0100960#define HAVE_EXECLISTS 0x1
961#define HAVE_GUC 0x2
962static unsigned print_welcome(int fd)
963{
964 unsigned flags = 0;
965 bool active;
966 int dir;
967
968 dir = igt_sysfs_open_parameters(fd);
969 if (dir < 0)
970 return 0;
971
972 active = igt_sysfs_get_boolean(dir, "enable_guc_submission");
973 if (active) {
974 igt_info("Using GuC submission\n");
975 flags |= HAVE_GUC | HAVE_EXECLISTS;
976 goto out;
977 }
978
979 active = igt_sysfs_get_boolean(dir, "enable_execlists");
980 if (active) {
981 igt_info("Using Execlists submission\n");
982 flags |= HAVE_EXECLISTS;
983 goto out;
984 }
985
986 active = igt_sysfs_get_boolean(dir, "semaphores");
987 igt_info("Using Legacy submission%s\n",
988 active ? ", with semaphores" : "");
989
990out:
991 close(dir);
992 return flags;
993}
994
Chris Wilson721d8742016-10-27 11:32:47 +0100995igt_main
996{
997 const struct intel_execution_engine *e;
Chris Wilson976ed7c2017-08-24 12:16:17 +0100998 unsigned int caps = 0;
Chris Wilson721d8742016-10-27 11:32:47 +0100999 int fd = -1;
1000
1001 igt_skip_on_simulation();
1002
1003 igt_fixture {
1004 fd = drm_open_driver_master(DRIVER_INTEL);
Chris Wilson976ed7c2017-08-24 12:16:17 +01001005 caps = print_welcome(fd);
Chris Wilson9518cb52017-02-22 15:24:54 +00001006 igt_require_gem(fd);
Chris Wilson721d8742016-10-27 11:32:47 +01001007 gem_require_mmap_wc(fd);
1008 igt_fork_hang_detector(fd);
1009 }
1010
1011 igt_subtest_group {
1012 for (e = intel_execution_engines; e->name; e++) {
1013 /* default exec-id is purely symbolic */
1014 if (e->exec_id == 0)
1015 continue;
1016
1017 igt_subtest_f("fifo-%s", e->name) {
1018 gem_require_ring(fd, e->exec_id | e->flags);
Chris Wilsonbc787762017-05-18 12:11:59 +01001019 igt_require(gem_can_store_dword(fd, e->exec_id) | e->flags);
Chris Wilson721d8742016-10-27 11:32:47 +01001020 fifo(fd, e->exec_id | e->flags);
1021 }
1022 }
1023 }
1024
1025 igt_subtest_group {
1026 igt_fixture {
1027 igt_require(has_scheduler(fd));
Chris Wilsonaf0e1c52017-02-21 18:25:58 +00001028 ctx_has_priority(fd);
Chris Wilson721d8742016-10-27 11:32:47 +01001029 }
1030
1031 for (e = intel_execution_engines; e->name; e++) {
1032 /* default exec-id is purely symbolic */
1033 if (e->exec_id == 0)
1034 continue;
1035
1036 igt_subtest_group {
Chris Wilson073cfd72017-03-17 11:52:51 +00001037 igt_fixture {
Chris Wilson721d8742016-10-27 11:32:47 +01001038 gem_require_ring(fd, e->exec_id | e->flags);
Chris Wilsonbc787762017-05-18 12:11:59 +01001039 igt_require(gem_can_store_dword(fd, e->exec_id) | e->flags);
Chris Wilson073cfd72017-03-17 11:52:51 +00001040 }
Chris Wilson721d8742016-10-27 11:32:47 +01001041
1042 igt_subtest_f("in-order-%s", e->name)
1043 reorder(fd, e->exec_id | e->flags, EQUAL);
1044
1045 igt_subtest_f("out-order-%s", e->name)
1046 reorder(fd, e->exec_id | e->flags, 0);
1047
1048 igt_subtest_f("promotion-%s", e->name)
1049 promotion(fd, e->exec_id | e->flags);
1050
Chris Wilsona3801342017-07-16 16:28:41 +01001051 igt_subtest_f("preempt-%s", e->name)
1052 preempt(fd, e->exec_id | e->flags, 0);
1053
1054 igt_subtest_f("preempt-contexts-%s", e->name)
1055 preempt(fd, e->exec_id | e->flags, NEW_CTX);
1056
1057 igt_subtest_f("preempt-other-%s", e->name)
1058 preempt_other(fd, e->exec_id | e->flags);
1059
1060 igt_subtest_f("preempt-self-%s", e->name)
1061 preempt_self(fd, e->exec_id | e->flags);
1062
Chris Wilson721d8742016-10-27 11:32:47 +01001063 igt_subtest_f("deep-%s", e->name)
1064 deep(fd, e->exec_id | e->flags);
Chris Wilsonf6920752017-04-24 13:20:04 +01001065
1066 igt_subtest_f("wide-%s", e->name)
1067 wide(fd, e->exec_id | e->flags);
Chris Wilson61f8de72017-07-20 10:08:28 +01001068
1069 igt_subtest_f("reorder-wide-%s", e->name)
1070 reorder_wide(fd, e->exec_id | e->flags);
Chris Wilson721d8742016-10-27 11:32:47 +01001071 }
1072 }
1073 }
1074
Chris Wilson976ed7c2017-08-24 12:16:17 +01001075 igt_subtest_group {
1076 igt_fixture {
1077 igt_require(has_scheduler(fd));
1078 ctx_has_priority(fd);
1079
1080 /* need separate rings */
1081 igt_require(caps & HAVE_EXECLISTS);
1082 }
1083
1084 for (e = intel_execution_engines; e->name; e++) {
1085 igt_subtest_group {
1086 igt_fixture {
1087 gem_require_ring(fd, e->exec_id | e->flags);
1088 }
1089
1090 igt_subtest_f("pi-ringfull-%s", e->name)
1091 test_pi_ringfull(fd, e->exec_id | e->flags);
1092 }
1093 }
1094 }
1095
Chris Wilson721d8742016-10-27 11:32:47 +01001096 igt_fixture {
1097 igt_stop_hang_detector();
1098 close(fd);
1099 }
1100}