1 /*
2 * Copyright © 2012 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 * Authors:
24 * Ben Widawsky <[email protected]>
25 *
26 */
27
28 #include "igt.h"
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <time.h>
34
35 #include "igt_rand.h"
36 #include "sw_sync.h"
37
38 #define LOCAL_I915_EXEC_BSD_SHIFT (13)
39 #define LOCAL_I915_EXEC_BSD_MASK (3 << LOCAL_I915_EXEC_BSD_SHIFT)
40
41 #define ENGINE_FLAGS (I915_EXEC_RING_MASK | LOCAL_I915_EXEC_BSD_MASK)
42
43 static unsigned all_engines[16];
44 static unsigned all_nengine;
45
46 static unsigned ppgtt_engines[16];
47 static unsigned ppgtt_nengine;
48
create_ioctl(int fd,struct drm_i915_gem_context_create * arg)49 static int create_ioctl(int fd, struct drm_i915_gem_context_create *arg)
50 {
51 int err;
52
53 err = 0;
54 if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_CREATE, arg)) {
55 err = -errno;
56 igt_assume(err);
57 }
58
59 errno = 0;
60 return err;
61 }
62
create_ext_ioctl(int i915,struct drm_i915_gem_context_create_ext * arg)63 static int create_ext_ioctl(int i915,
64 struct drm_i915_gem_context_create_ext *arg)
65 {
66 int err;
67
68 err = 0;
69 if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT, arg)) {
70 err = -errno;
71 igt_assume(err);
72 }
73
74 errno = 0;
75 return err;
76 }
77
elapsed(const struct timespec * start,const struct timespec * end)78 static double elapsed(const struct timespec *start,
79 const struct timespec *end)
80 {
81 return (end->tv_sec - start->tv_sec) + 1e-9*(end->tv_nsec - start->tv_nsec);
82 }
83
files(int core,int timeout,const int ncpus)84 static void files(int core, int timeout, const int ncpus)
85 {
86 const uint32_t bbe = MI_BATCH_BUFFER_END;
87 struct drm_i915_gem_execbuffer2 execbuf;
88 struct drm_i915_gem_exec_object2 obj;
89 uint32_t batch, name;
90
91 batch = gem_create(core, 4096);
92 gem_write(core, batch, 0, &bbe, sizeof(bbe));
93 name = gem_flink(core, batch);
94
95 memset(&obj, 0, sizeof(obj));
96 memset(&execbuf, 0, sizeof(execbuf));
97 execbuf.buffers_ptr = to_user_pointer(&obj);
98 execbuf.buffer_count = 1;
99
100 igt_fork(child, ncpus) {
101 struct timespec start, end;
102 unsigned count = 0;
103
104 clock_gettime(CLOCK_MONOTONIC, &start);
105 do {
106 do {
107 int fd = drm_open_driver(DRIVER_INTEL);
108 obj.handle = gem_open(fd, name);
109 execbuf.flags &= ~ENGINE_FLAGS;
110 execbuf.flags |= ppgtt_engines[count % ppgtt_nengine];
111 gem_execbuf(fd, &execbuf);
112 close(fd);
113 } while (++count & 1023);
114 clock_gettime(CLOCK_MONOTONIC, &end);
115 } while (elapsed(&start, &end) < timeout);
116
117 gem_sync(core, batch);
118 clock_gettime(CLOCK_MONOTONIC, &end);
119 igt_info("[%d] File creation + execution: %.3f us\n",
120 child, elapsed(&start, &end) / count *1e6);
121 }
122 igt_waitchildren();
123
124 gem_close(core, batch);
125 }
126
active(int fd,unsigned engine,int timeout,int ncpus)127 static void active(int fd, unsigned engine, int timeout, int ncpus)
128 {
129 const uint32_t bbe = MI_BATCH_BUFFER_END;
130 struct drm_i915_gem_execbuffer2 execbuf;
131 struct drm_i915_gem_exec_object2 obj;
132 unsigned int nengine, engines[16];
133 unsigned *shared;
134
135 if (engine == ALL_ENGINES) {
136 igt_require(all_nengine);
137 nengine = all_nengine;
138 memcpy(engines, all_engines, sizeof(engines[0])*nengine);
139 } else {
140 gem_require_ring(fd, engine);
141 nengine = 1;
142 engines[0] = engine;
143 }
144
145 shared = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
146 igt_assert(shared != MAP_FAILED);
147
148 memset(&obj, 0, sizeof(obj));
149 obj.handle = gem_create(fd, 4096);
150 gem_write(fd, obj.handle, 0, &bbe, sizeof(bbe));
151
152 memset(&execbuf, 0, sizeof(execbuf));
153 execbuf.buffers_ptr = to_user_pointer(&obj);
154 execbuf.buffer_count = 1;
155
156 if (ncpus < 0) {
157 igt_fork(child, ppgtt_nengine) {
158 unsigned long count = 0;
159
160 if (ppgtt_engines[child] == engine)
161 continue;
162
163 execbuf.flags = ppgtt_engines[child];
164
165 while (!*(volatile unsigned *)shared) {
166 obj.handle = gem_create(fd, 4096 << 10);
167 gem_write(fd, obj.handle, 0, &bbe, sizeof(bbe));
168
169 gem_execbuf(fd, &execbuf);
170 gem_close(fd, obj.handle);
171 count++;
172 }
173
174 igt_debug("hog[%d]: cycles=%lu\n", child, count);
175 }
176 ncpus = -ncpus;
177 }
178
179 igt_fork(child, ncpus) {
180 struct timespec start, end;
181 unsigned count = 0;
182
183 clock_gettime(CLOCK_MONOTONIC, &start);
184 do {
185 do {
186 execbuf.rsvd1 = gem_context_create(fd);
187 for (unsigned n = 0; n < nengine; n++) {
188 execbuf.flags = engines[n];
189 gem_execbuf(fd, &execbuf);
190 }
191 gem_context_destroy(fd, execbuf.rsvd1);
192 } while (++count & 1023);
193 clock_gettime(CLOCK_MONOTONIC, &end);
194 } while (elapsed(&start, &end) < timeout);
195
196 gem_sync(fd, obj.handle);
197 clock_gettime(CLOCK_MONOTONIC, &end);
198 igt_info("[%d] Context creation + execution: %.3f us\n",
199 child, elapsed(&start, &end) / count *1e6);
200
201 shared[0] = 1;
202 }
203 igt_waitchildren();
204
205 gem_close(fd, obj.handle);
206 munmap(shared, 4096);
207 }
208
xchg_u32(void * array,unsigned i,unsigned j)209 static void xchg_u32(void *array, unsigned i, unsigned j)
210 {
211 uint32_t *a = array, tmp;
212
213 tmp = a[i];
214 a[i] = a[j];
215 a[j] = tmp;
216 }
217
__context_size(int fd)218 static unsigned __context_size(int fd)
219 {
220 switch (intel_gen(intel_get_drm_devid(fd))) {
221 case 0:
222 case 1:
223 case 2:
224 case 3:
225 case 4:
226 case 5:
227 case 6:
228 case 7: return 17 << 12;
229 case 8: return 20 << 12;
230 case 9: return 22 << 12;
231 default: return 32 << 12;
232 }
233 }
234
context_size(int fd)235 static unsigned context_size(int fd)
236 {
237 uint64_t size;
238
239 size = __context_size(fd);
240 if (ppgtt_nengine > 1) {
241 size += 4 << 12; /* ringbuffer as well */
242 size *= ppgtt_nengine;
243 }
244
245 return size;
246 }
247
total_avail_mem(unsigned mode)248 static uint64_t total_avail_mem(unsigned mode)
249 {
250 uint64_t total = intel_get_avail_ram_mb();
251 if (mode & CHECK_SWAP)
252 total += intel_get_total_swap_mb();
253 return total << 20;
254 }
255
maximum(int fd,int ncpus,unsigned mode)256 static void maximum(int fd, int ncpus, unsigned mode)
257 {
258 const uint32_t bbe = MI_BATCH_BUFFER_END;
259 struct drm_i915_gem_execbuffer2 execbuf;
260 struct drm_i915_gem_exec_object2 obj[2];
261 uint64_t avail_mem = total_avail_mem(mode);
262 unsigned ctx_size = context_size(fd);
263 uint32_t *contexts = NULL;
264 unsigned long count = 0;
265 uint32_t ctx_id;
266
267 do {
268 int err;
269
270 if ((count & -count) == count) {
271 int sz = count ? 2*count : 1;
272 contexts = realloc(contexts,
273 sz*sizeof(*contexts));
274 igt_assert(contexts);
275 }
276
277 err = -ENOMEM;
278 if (avail_mem > (count + 1) * ctx_size)
279 err = __gem_context_create(fd, &ctx_id);
280 if (err) {
281 igt_info("Created %lu contexts, before failing with '%s' [%d]\n",
282 count, strerror(-err), -err);
283 break;
284 }
285
286 contexts[count++] = ctx_id;
287 } while (1);
288 igt_require(count);
289
290 memset(obj, 0, sizeof(obj));
291 obj[1].handle = gem_create(fd, 4096);
292 gem_write(fd, obj[1].handle, 0, &bbe, sizeof(bbe));
293
294 memset(&execbuf, 0, sizeof(execbuf));
295 execbuf.buffers_ptr = to_user_pointer(obj);
296 execbuf.buffer_count = 2;
297
298 igt_fork(child, ncpus) {
299 struct timespec start, end;
300
301 hars_petruska_f54_1_random_perturb(child);
302 obj[0].handle = gem_create(fd, 4096);
303
304 clock_gettime(CLOCK_MONOTONIC, &start);
305 for (int repeat = 0; repeat < 3; repeat++) {
306 igt_permute_array(contexts, count, xchg_u32);
307 igt_permute_array(all_engines, all_nengine, xchg_u32);
308
309 for (unsigned long i = 0; i < count; i++) {
310 execbuf.rsvd1 = contexts[i];
311 for (unsigned long j = 0; j < all_nengine; j++) {
312 execbuf.flags = all_engines[j];
313 gem_execbuf(fd, &execbuf);
314 }
315 }
316 }
317 gem_sync(fd, obj[0].handle);
318 clock_gettime(CLOCK_MONOTONIC, &end);
319 gem_close(fd, obj[0].handle);
320
321 igt_info("[%d] Context execution: %.3f us\n", child,
322 elapsed(&start, &end) / (3 * count * all_nengine) * 1e6);
323 }
324 igt_waitchildren();
325
326 gem_close(fd, obj[1].handle);
327
328 for (unsigned long i = 0; i < count; i++)
329 gem_context_destroy(fd, contexts[i]);
330 free(contexts);
331 }
332
basic_ext_param(int i915)333 static void basic_ext_param(int i915)
334 {
335 struct drm_i915_gem_context_create_ext_setparam ext = {
336 { .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
337 };
338 struct drm_i915_gem_context_create_ext create = {
339 .flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS
340 };
341 struct drm_i915_gem_context_param get;
342
343 igt_require(create_ext_ioctl(i915, &create) == 0);
344 gem_context_destroy(i915, create.ctx_id);
345
346 create.extensions = -1ull;
347 igt_assert_eq(create_ext_ioctl(i915, &create), -EFAULT);
348
349 create.extensions = to_user_pointer(&ext);
350 igt_assert_eq(create_ext_ioctl(i915, &create), -EINVAL);
351
352 ext.param.param = I915_CONTEXT_PARAM_PRIORITY;
353 if (create_ext_ioctl(i915, &create) != -ENODEV) {
354 gem_context_destroy(i915, create.ctx_id);
355
356 ext.base.next_extension = -1ull;
357 igt_assert_eq(create_ext_ioctl(i915, &create), -EFAULT);
358 ext.base.next_extension = to_user_pointer(&ext);
359 igt_assert_eq(create_ext_ioctl(i915, &create), -E2BIG);
360 ext.base.next_extension = 0;
361
362 ext.param.value = 32;
363 igt_assert_eq(create_ext_ioctl(i915, &create), 0);
364
365 memset(&get, 0, sizeof(get));
366 get.ctx_id = create.ctx_id;
367 get.param = I915_CONTEXT_PARAM_PRIORITY;
368 gem_context_get_param(i915, &get);
369 igt_assert_eq(get.value, ext.param.value);
370
371 gem_context_destroy(i915, create.ctx_id);
372
373 /* Having demonstrated a valid setup, check a few invalids */
374 ext.param.ctx_id = 1;
375 igt_assert_eq(create_ext_ioctl(i915, &create), -EINVAL);
376 ext.param.ctx_id = create.ctx_id;
377 igt_assert_eq(create_ext_ioctl(i915, &create), -EINVAL);
378 ext.param.ctx_id = -1;
379 igt_assert_eq(create_ext_ioctl(i915, &create), -EINVAL);
380 ext.param.ctx_id = 0;
381 }
382 }
383
check_single_timeline(int i915,uint32_t ctx,int num_engines)384 static void check_single_timeline(int i915, uint32_t ctx, int num_engines)
385 {
386 #define RCS_TIMESTAMP (0x2000 + 0x358)
387 const int gen = intel_gen(intel_get_drm_devid(i915));
388 const int has_64bit_reloc = gen >= 8;
389 struct drm_i915_gem_exec_object2 results = { .handle = gem_create(i915, 4096) };
390 const uint32_t bbe = MI_BATCH_BUFFER_END;
391 int timeline = sw_sync_timeline_create();
392 uint32_t last, *map;
393
394 {
395 struct drm_i915_gem_execbuffer2 execbuf = {
396 .buffers_ptr = to_user_pointer(&results),
397 .buffer_count = 1,
398 .rsvd1 = ctx,
399 };
400 gem_write(i915, results.handle, 0, &bbe, sizeof(bbe));
401 gem_execbuf(i915, &execbuf);
402 results.flags = EXEC_OBJECT_PINNED;
403 }
404
405 for (int i = 0; i < num_engines; i++) {
406 struct drm_i915_gem_exec_object2 obj[2] = {
407 results, /* write hazard lies! */
408 { .handle = gem_create(i915, 4096) },
409 };
410 struct drm_i915_gem_execbuffer2 execbuf = {
411 .buffers_ptr = to_user_pointer(obj),
412 .buffer_count = 2,
413 .rsvd1 = ctx,
414 .rsvd2 = sw_sync_timeline_create_fence(timeline, num_engines - i),
415 .flags = i | I915_EXEC_FENCE_IN,
416 };
417 uint64_t offset = results.offset + 4 * i;
418 uint32_t *cs;
419 int j = 0;
420
421 cs = gem_mmap__cpu(i915, obj[1].handle, 0, 4096, PROT_WRITE);
422
423 cs[j] = 0x24 << 23 | 1; /* SRM */
424 if (has_64bit_reloc)
425 cs[j]++;
426 j++;
427 cs[j++] = RCS_TIMESTAMP;
428 cs[j++] = offset;
429 if (has_64bit_reloc)
430 cs[j++] = offset >> 32;
431 cs[j++] = MI_BATCH_BUFFER_END;
432
433 munmap(cs, 4096);
434
435 gem_execbuf(i915, &execbuf);
436 gem_close(i915, obj[1].handle);
437 close(execbuf.rsvd2);
438 }
439 close(timeline);
440 gem_sync(i915, results.handle);
441
442 map = gem_mmap__cpu(i915, results.handle, 0, 4096, PROT_READ);
443 gem_set_domain(i915, results.handle, I915_GEM_DOMAIN_CPU, 0);
444 gem_close(i915, results.handle);
445
446 last = map[0];
447 for (int i = 1; i < num_engines; i++) {
448 igt_assert_f((map[i] - last) > 0,
449 "Engine instance [%d] executed too early: this:%x, last:%x\n",
450 i, map[i], last);
451 last = map[i];
452 }
453 munmap(map, 4096);
454 }
455
iris_pipeline(int i915)456 static void iris_pipeline(int i915)
457 {
458 #ifdef I915_DEFINE_CONTEXT_PARAM_ENGINES
459 #define RCS0 {0, 0}
460 I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, 2) = {
461 .engines = { RCS0, RCS0 }
462 };
463 struct drm_i915_gem_context_create_ext_setparam p_engines = {
464 .base = {
465 .name = I915_CONTEXT_CREATE_EXT_SETPARAM,
466 .next_extension = 0, /* end of chain */
467 },
468 .param = {
469 .param = I915_CONTEXT_PARAM_ENGINES,
470 .value = to_user_pointer(&engines),
471 .size = sizeof(engines),
472 },
473 };
474 struct drm_i915_gem_context_create_ext_setparam p_recover = {
475 .base = {
476 .name =I915_CONTEXT_CREATE_EXT_SETPARAM,
477 .next_extension = to_user_pointer(&p_engines),
478 },
479 .param = {
480 .param = I915_CONTEXT_PARAM_RECOVERABLE,
481 .value = 0,
482 },
483 };
484 struct drm_i915_gem_context_create_ext_setparam p_prio = {
485 .base = {
486 .name =I915_CONTEXT_CREATE_EXT_SETPARAM,
487 .next_extension = to_user_pointer(&p_recover),
488 },
489 .param = {
490 .param = I915_CONTEXT_PARAM_PRIORITY,
491 .value = 768,
492 },
493 };
494 struct drm_i915_gem_context_create_ext create = {
495 .flags = (I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE |
496 I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS),
497 };
498 struct drm_i915_gem_context_param get;
499
500 igt_require(create_ext_ioctl(i915, &create) == 0);
501
502 create.extensions = to_user_pointer(&p_prio);
503 igt_assert_eq(create_ext_ioctl(i915, &create), 0);
504
505 memset(&get, 0, sizeof(get));
506 get.ctx_id = create.ctx_id;
507 get.param = I915_CONTEXT_PARAM_PRIORITY;
508 gem_context_get_param(i915, &get);
509 igt_assert_eq(get.value, p_prio.param.value);
510
511 memset(&get, 0, sizeof(get));
512 get.ctx_id = create.ctx_id;
513 get.param = I915_CONTEXT_PARAM_RECOVERABLE;
514 gem_context_get_param(i915, &get);
515 igt_assert_eq(get.value, 0);
516
517 check_single_timeline(i915, create.ctx_id, 2);
518
519 gem_context_destroy(i915, create.ctx_id);
520 #endif /* I915_DEFINE_CONTEXT_PARAM_ENGINES */
521 }
522
523 igt_main
524 {
525 const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
526 struct drm_i915_gem_context_create create;
527 int fd = -1;
528
529 igt_fixture {
530 unsigned engine;
531
532 fd = drm_open_driver(DRIVER_INTEL);
533 igt_require_gem(fd);
534 gem_require_contexts(fd);
535
536 for_each_physical_engine(fd, engine)
537 all_engines[all_nengine++] = engine;
538 igt_require(all_nengine);
539
540 if (gem_uses_full_ppgtt(fd)) {
541 ppgtt_nengine = all_nengine;
542 memcpy(ppgtt_engines,
543 all_engines,
544 all_nengine * sizeof(all_engines[0]));
545 } else
546 ppgtt_engines[ppgtt_nengine++] = 0;
547
548 igt_fork_hang_detector(fd);
549 }
550
551 igt_subtest("basic") {
552 memset(&create, 0, sizeof(create));
553 create.ctx_id = rand();
554 create.pad = 0;
555 igt_assert_eq(create_ioctl(fd, &create), 0);
556 igt_assert(create.ctx_id != 0);
557 gem_context_destroy(fd, create.ctx_id);
558 }
559
560 igt_subtest("ext-param")
561 basic_ext_param(fd);
562 igt_subtest("iris-pipeline")
563 iris_pipeline(fd);
564
565 igt_subtest("maximum-mem")
566 maximum(fd, ncpus, CHECK_RAM);
567 igt_subtest("maximum-swap")
568 maximum(fd, ncpus, CHECK_RAM | CHECK_SWAP);
569
570 igt_subtest("basic-files")
571 files(fd, 5, 1);
572 igt_subtest("files")
573 files(fd, 150, 1);
574 igt_subtest("forked-files")
575 files(fd, 150, ncpus);
576
577 igt_subtest("active-all")
578 active(fd, ALL_ENGINES, 120, 1);
579 igt_subtest("forked-active-all")
580 active(fd, ALL_ENGINES, 120, ncpus);
581
582 for (const struct intel_execution_engine *e = intel_execution_engines;
583 e->name; e++) {
584 igt_subtest_f("active-%s", e->name)
585 active(fd, e->exec_id | e->flags, 20, 1);
586 igt_subtest_f("forked-active-%s", e->name)
587 active(fd, e->exec_id | e->flags, 20, ncpus);
588 if (e->exec_id) {
589 igt_subtest_f("hog-%s", e->name)
590 active(fd, e->exec_id | e->flags, 20, -1);
591 }
592 }
593
594 igt_fixture {
595 igt_stop_hang_detector();
596 close(fd);
597 }
598 }
599