xref: /aosp_15_r20/external/igt-gpu-tools/tests/i915/gem_ctx_create.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
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