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