blob: b3f61fea7df5e6a7c76b87ea885669798f94d5a2 [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
24#include <sys/poll.h>
25
26#include "igt.h"
27#include "igt_vgem.h"
28
Chris Wilson49f44c72016-11-14 21:24:52 +000029#define LOCAL_PARAM_HAS_SCHEDULER 41
Chris Wilsonaf0e1c52017-02-21 18:25:58 +000030#define LOCAL_CONTEXT_PARAM_PRIORITY 6
Chris Wilson721d8742016-10-27 11:32:47 +010031
32#define LO 0
33#define HI 1
34#define NOISE 2
35
36#define MAX_PRIO 1023
37
38#define BUSY_QLEN 8
39
40IGT_TEST_DESCRIPTION("Check that we can control the order of execution");
41
Chris Wilsonaf0e1c52017-02-21 18:25:58 +000042static int __ctx_set_priority(int fd, uint32_t ctx, int prio)
Chris Wilson721d8742016-10-27 11:32:47 +010043{
44 struct local_i915_gem_context_param param;
45
46 memset(&param, 0, sizeof(param));
47 param.context = ctx;
48 param.size = 0;
49 param.param = LOCAL_CONTEXT_PARAM_PRIORITY;
50 param.value = prio;
51
Chris Wilsonaf0e1c52017-02-21 18:25:58 +000052 return __gem_context_set_param(fd, &param);
53}
54
55static void ctx_set_priority(int fd, uint32_t ctx, int prio)
56{
57 igt_assert_eq(__ctx_set_priority(fd, ctx, prio), 0);
58}
59
60static void ctx_has_priority(int fd)
61{
62 igt_require(__ctx_set_priority(fd, 0, MAX_PRIO) == 0);
Chris Wilson721d8742016-10-27 11:32:47 +010063}
64
65static void store_dword(int fd, uint32_t ctx, unsigned ring,
66 uint32_t target, uint32_t offset, uint32_t value,
67 uint32_t cork, unsigned write_domain)
68{
69 const int gen = intel_gen(intel_get_drm_devid(fd));
70 struct drm_i915_gem_exec_object2 obj[3];
71 struct drm_i915_gem_relocation_entry reloc;
72 struct drm_i915_gem_execbuffer2 execbuf;
73 uint32_t batch[16];
74 int i;
75
76 memset(&execbuf, 0, sizeof(execbuf));
Chris Wilson4de67b22017-01-02 11:05:21 +000077 execbuf.buffers_ptr = to_user_pointer(obj + !cork);
Chris Wilson721d8742016-10-27 11:32:47 +010078 execbuf.buffer_count = 2 + !!cork;
79 execbuf.flags = ring;
80 if (gen < 6)
81 execbuf.flags |= I915_EXEC_SECURE;
82 execbuf.rsvd1 = ctx;
83
84 memset(obj, 0, sizeof(obj));
85 obj[0].handle = cork;
86 obj[1].handle = target;
87 obj[2].handle = gem_create(fd, 4096);
88
89 memset(&reloc, 0, sizeof(reloc));
90 reloc.target_handle = obj[1].handle;
91 reloc.presumed_offset = 0;
92 reloc.offset = sizeof(uint32_t);
93 reloc.delta = offset;
94 reloc.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
95 reloc.write_domain = write_domain;
Chris Wilson4de67b22017-01-02 11:05:21 +000096 obj[2].relocs_ptr = to_user_pointer(&reloc);
Chris Wilson721d8742016-10-27 11:32:47 +010097 obj[2].relocation_count = 1;
98
99 i = 0;
100 batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
101 if (gen >= 8) {
102 batch[++i] = offset;
103 batch[++i] = 0;
104 } else if (gen >= 4) {
105 batch[++i] = 0;
106 batch[++i] = offset;
107 reloc.offset += sizeof(uint32_t);
108 } else {
109 batch[i]--;
110 batch[++i] = offset;
111 }
112 batch[++i] = value;
113 batch[++i] = MI_BATCH_BUFFER_END;
114 gem_write(fd, obj[2].handle, 0, batch, sizeof(batch));
115 gem_execbuf(fd, &execbuf);
116 gem_close(fd, obj[2].handle);
117}
118
119static uint32_t *make_busy(int fd, uint32_t target, unsigned ring)
120{
121 const int gen = intel_gen(intel_get_drm_devid(fd));
122 struct drm_i915_gem_exec_object2 obj[2];
123 struct drm_i915_gem_relocation_entry reloc[2];
124 struct drm_i915_gem_execbuffer2 execbuf;
125 uint32_t *batch;
126 int i;
127
128 memset(&execbuf, 0, sizeof(execbuf));
Chris Wilson4de67b22017-01-02 11:05:21 +0000129 execbuf.buffers_ptr = to_user_pointer(obj + !target);
Chris Wilson721d8742016-10-27 11:32:47 +0100130 execbuf.buffer_count = 1 + !!target;
131
132 memset(obj, 0, sizeof(obj));
133 obj[0].handle = target;
134 obj[1].handle = gem_create(fd, 4096);
135 batch = gem_mmap__wc(fd, obj[1].handle, 0, 4096, PROT_WRITE);
136 gem_set_domain(fd, obj[1].handle,
137 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
138
Chris Wilson4de67b22017-01-02 11:05:21 +0000139 obj[1].relocs_ptr = to_user_pointer(reloc);
Chris Wilson721d8742016-10-27 11:32:47 +0100140 obj[1].relocation_count = 1 + !!target;
141 memset(reloc, 0, sizeof(reloc));
142
143 reloc[0].target_handle = obj[1].handle; /* recurse */
144 reloc[0].presumed_offset = 0;
145 reloc[0].offset = sizeof(uint32_t);
146 reloc[0].delta = 0;
147 reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND;
148 reloc[0].write_domain = 0;
149
150 reloc[1].target_handle = target;
151 reloc[1].presumed_offset = 0;
152 reloc[1].offset = 1024;
153 reloc[1].delta = 0;
154 reloc[1].read_domains = I915_GEM_DOMAIN_COMMAND;
155 reloc[1].write_domain = 0;
156
157 i = 0;
158 batch[i] = MI_BATCH_BUFFER_START;
159 if (gen >= 8) {
160 batch[i] |= 1 << 8 | 1;
161 batch[++i] = 0;
162 batch[++i] = 0;
163 } else if (gen >= 6) {
164 batch[i] |= 1 << 8;
165 batch[++i] = 0;
166 } else {
167 batch[i] |= 2 << 6;
168 batch[++i] = 0;
169 if (gen < 4) {
170 batch[i] |= 1;
171 reloc[0].delta = 1;
172 }
173 }
174 i++;
175
176 if (ring != -1) {
177 execbuf.flags = ring;
178 for (int n = 0; n < BUSY_QLEN; n++)
179 gem_execbuf(fd, &execbuf);
180 } else {
181 for_each_engine(fd, ring) {
182 if (ring == 0)
183 continue;
184
185 execbuf.flags = ring;
186 for (int n = 0; n < BUSY_QLEN; n++)
187 gem_execbuf(fd, &execbuf);
188 igt_assert(execbuf.flags == ring);
189 }
190 }
191
192 if (target) {
193 execbuf.flags = 0;
194 reloc[1].write_domain = I915_GEM_DOMAIN_COMMAND;
195 gem_execbuf(fd, &execbuf);
196 }
197
198 gem_close(fd, obj[1].handle);
199
200 return batch;
201}
202
203static void finish_busy(uint32_t *busy)
204{
205 *busy = MI_BATCH_BUFFER_END;
206 munmap(busy, 4096);
207}
208
209struct cork {
210 int device;
211 uint32_t handle;
212 uint32_t fence;
213};
214
215static void plug(int fd, struct cork *c)
216{
217 struct vgem_bo bo;
218 int dmabuf;
219
220 c->device = drm_open_driver(DRIVER_VGEM);
221
222 bo.width = bo.height = 1;
223 bo.bpp = 4;
224 vgem_create(c->device, &bo);
225 c->fence = vgem_fence_attach(c->device, &bo, VGEM_FENCE_WRITE);
226
227 dmabuf = prime_handle_to_fd(c->device, bo.handle);
228 c->handle = prime_fd_to_handle(fd, dmabuf);
229 close(dmabuf);
230}
231
232static void unplug(struct cork *c)
233{
234 vgem_fence_signal(c->device, c->fence);
235 close(c->device);
236}
237
238static void fifo(int fd, unsigned ring)
239{
240 struct cork cork;
241 uint32_t *busy;
242 uint32_t scratch;
243 uint32_t *ptr;
244
245 scratch = gem_create(fd, 4096);
246
247 busy = make_busy(fd, scratch, ring);
248 plug(fd, &cork);
249
250 /* Same priority, same timeline, final result will be the second eb */
251 store_dword(fd, 0, ring, scratch, 0, 1, cork.handle, 0);
252 store_dword(fd, 0, ring, scratch, 0, 2, cork.handle, 0);
253
254 unplug(&cork); /* only now submit our batches */
255 igt_debugfs_dump(fd, "i915_engine_info");
256 finish_busy(busy);
257
258 ptr = gem_mmap__gtt(fd, scratch, 4096, PROT_READ);
259 gem_set_domain(fd, scratch, /* no write hazard lies! */
260 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
261 gem_close(fd, scratch);
262
263 igt_assert_eq_u32(ptr[0], 2);
264 munmap(ptr, 4096);
265}
266
267static void reorder(int fd, unsigned ring, unsigned flags)
268#define EQUAL 1
269{
270 struct cork cork;
271 uint32_t scratch;
272 uint32_t *busy;
273 uint32_t *ptr;
274 uint32_t ctx[2];
275
276 ctx[LO] = gem_context_create(fd);
277 ctx_set_priority(fd, ctx[LO], -MAX_PRIO);
278
279 ctx[HI] = gem_context_create(fd);
280 ctx_set_priority(fd, ctx[HI], flags & EQUAL ? -MAX_PRIO : 0);
281
282 scratch = gem_create(fd, 4096);
283
284 busy = make_busy(fd, scratch, ring);
285 plug(fd, &cork);
286
287 /* We expect the high priority context to be executed first, and
288 * so the final result will be value from the low priority context.
289 */
290 store_dword(fd, ctx[LO], ring, scratch, 0, ctx[LO], cork.handle, 0);
291 store_dword(fd, ctx[HI], ring, scratch, 0, ctx[HI], cork.handle, 0);
292
293 unplug(&cork); /* only now submit our batches */
294 igt_debugfs_dump(fd, "i915_engine_info");
295 finish_busy(busy);
296
297 gem_context_destroy(fd, ctx[LO]);
298 gem_context_destroy(fd, ctx[HI]);
299
300 ptr = gem_mmap__gtt(fd, scratch, 4096, PROT_READ);
301 gem_set_domain(fd, scratch, /* no write hazard lies! */
302 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
303 gem_close(fd, scratch);
304
305 if (flags & EQUAL) /* equal priority, result will be fifo */
306 igt_assert_eq_u32(ptr[0], ctx[HI]);
307 else
308 igt_assert_eq_u32(ptr[0], ctx[LO]);
309 munmap(ptr, 4096);
310}
311
312static void promotion(int fd, unsigned ring)
313{
314 struct cork cork;
315 uint32_t result, dep;
316 uint32_t *busy;
317 uint32_t *ptr;
318 uint32_t ctx[3];
319
320 ctx[LO] = gem_context_create(fd);
321 ctx_set_priority(fd, ctx[LO], -MAX_PRIO);
322
323 ctx[HI] = gem_context_create(fd);
324 ctx_set_priority(fd, ctx[HI], 0);
325
326 ctx[NOISE] = gem_context_create(fd);
327 ctx_set_priority(fd, ctx[NOISE], -MAX_PRIO/2);
328
329 result = gem_create(fd, 4096);
330 dep = gem_create(fd, 4096);
331
332 busy = make_busy(fd, result, ring);
333 plug(fd, &cork);
334
335 /* Expect that HI promotes LO, so the order will be LO, HI, NOISE.
336 *
337 * fifo would be NOISE, LO, HI.
338 * strict priority would be HI, NOISE, LO
339 */
340 store_dword(fd, ctx[NOISE], ring, result, 0, ctx[NOISE], cork.handle, 0);
341 store_dword(fd, ctx[LO], ring, result, 0, ctx[LO], cork.handle, 0);
342
343 /* link LO <-> HI via a dependency on another buffer */
344 store_dword(fd, ctx[LO], ring, dep, 0, ctx[LO], 0, I915_GEM_DOMAIN_INSTRUCTION);
345 store_dword(fd, ctx[HI], ring, dep, 0, ctx[HI], 0, 0);
346
347 store_dword(fd, ctx[HI], ring, result, 0, ctx[HI], 0, 0);
348
349 unplug(&cork); /* only now submit our batches */
350 igt_debugfs_dump(fd, "i915_engine_info");
351 finish_busy(busy);
352
353 gem_context_destroy(fd, ctx[NOISE]);
354 gem_context_destroy(fd, ctx[LO]);
355 gem_context_destroy(fd, ctx[HI]);
356
357 ptr = gem_mmap__gtt(fd, dep, 4096, PROT_READ);
358 gem_set_domain(fd, dep, /* no write hazard lies! */
359 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
360 gem_close(fd, dep);
361
362 igt_assert_eq_u32(ptr[0], ctx[HI]);
363 munmap(ptr, 4096);
364
365 ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
366 gem_set_domain(fd, result, /* no write hazard lies! */
367 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
368 gem_close(fd, result);
369
370 igt_assert_eq_u32(ptr[0], ctx[NOISE]);
371 munmap(ptr, 4096);
372}
373
374static void deep(int fd, unsigned ring)
375{
376#define XS 8
377 struct cork cork;
378 uint32_t result, dep[XS];
379 uint32_t *busy;
380 uint32_t *ptr;
381 uint32_t *ctx;
382
383 ctx = malloc(sizeof(*ctx)*(MAX_PRIO + 1));
384 for (int n = 0; n <= MAX_PRIO; n++) {
385 ctx[n] = gem_context_create(fd);
386 ctx_set_priority(fd, ctx[n], n);
387 }
388
389 result = gem_create(fd, 4096);
390 for (int m = 0; m < XS; m ++)
391 dep[m] = gem_create(fd, 4096);
392
393 busy = make_busy(fd, result, ring);
394 plug(fd, &cork);
395
396 /* Create a deep dependency chain, with a few branches */
397 for (int n = 0; n <= MAX_PRIO; n++)
398 for (int m = 0; m < XS; m++)
399 store_dword(fd, ctx[n], ring, dep[m], 4*n, ctx[n], cork.handle, I915_GEM_DOMAIN_INSTRUCTION);
400
401 for (int n = 0; n <= MAX_PRIO; n++) {
402 for (int m = 0; m < XS; m++) {
403 store_dword(fd, ctx[n], ring, result, 4*n, ctx[n], dep[m], 0);
404 store_dword(fd, ctx[n], ring, result, 4*m, ctx[n], 0, I915_GEM_DOMAIN_INSTRUCTION);
405 }
406 }
407
408 igt_assert(gem_bo_busy(fd, result));
409 unplug(&cork); /* only now submit our batches */
410 igt_debugfs_dump(fd, "i915_engine_info");
411 finish_busy(busy);
412
413 for (int n = 0; n <= MAX_PRIO; n++)
414 gem_context_destroy(fd, ctx[n]);
415
416 for (int m = 0; m < XS; m++) {
417 ptr = gem_mmap__gtt(fd, dep[m], 4096, PROT_READ);
418 gem_set_domain(fd, dep[m], /* no write hazard lies! */
419 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
420 gem_close(fd, dep[m]);
421
422 for (int n = 0; n <= MAX_PRIO; n++)
423 igt_assert_eq_u32(ptr[n], ctx[n]);
424 munmap(ptr, 4096);
425 }
426
427 ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
428 gem_set_domain(fd, result, /* no write hazard lies! */
429 I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
430 gem_close(fd, result);
431
432 for (int m = 0; m < XS; m++)
433 igt_assert_eq_u32(ptr[m], ctx[MAX_PRIO]);
434 munmap(ptr, 4096);
435
436 free(ctx);
437}
438
439static bool has_scheduler(int fd)
440{
441 drm_i915_getparam_t gp;
442 int has = -1;
443
444 gp.param = LOCAL_PARAM_HAS_SCHEDULER;
445 gp.value = &has;
446 drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp);
447
448 return has > 0;
449}
450
451igt_main
452{
453 const struct intel_execution_engine *e;
454 int fd = -1;
455
456 igt_skip_on_simulation();
457
458 igt_fixture {
459 fd = drm_open_driver_master(DRIVER_INTEL);
460 gem_require_mmap_wc(fd);
461 igt_fork_hang_detector(fd);
462 }
463
464 igt_subtest_group {
465 for (e = intel_execution_engines; e->name; e++) {
466 /* default exec-id is purely symbolic */
467 if (e->exec_id == 0)
468 continue;
469
470 igt_subtest_f("fifo-%s", e->name) {
471 gem_require_ring(fd, e->exec_id | e->flags);
472 fifo(fd, e->exec_id | e->flags);
473 }
474 }
475 }
476
477 igt_subtest_group {
478 igt_fixture {
479 igt_require(has_scheduler(fd));
Chris Wilsonaf0e1c52017-02-21 18:25:58 +0000480 ctx_has_priority(fd);
Chris Wilson721d8742016-10-27 11:32:47 +0100481 }
482
483 for (e = intel_execution_engines; e->name; e++) {
484 /* default exec-id is purely symbolic */
485 if (e->exec_id == 0)
486 continue;
487
488 igt_subtest_group {
489 igt_fixture
490 gem_require_ring(fd, e->exec_id | e->flags);
491
492 igt_subtest_f("in-order-%s", e->name)
493 reorder(fd, e->exec_id | e->flags, EQUAL);
494
495 igt_subtest_f("out-order-%s", e->name)
496 reorder(fd, e->exec_id | e->flags, 0);
497
498 igt_subtest_f("promotion-%s", e->name)
499 promotion(fd, e->exec_id | e->flags);
500
501 igt_subtest_f("deep-%s", e->name)
502 deep(fd, e->exec_id | e->flags);
503 }
504 }
505 }
506
507 igt_fixture {
508 igt_stop_hang_detector();
509 close(fd);
510 }
511}