xref: /aosp_15_r20/external/igt-gpu-tools/tests/i915/gem_ctx_shared.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1 /*
2  * Copyright © 2017-2019 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 
25 #include "igt.h"
26 
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <inttypes.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36 #include <sys/ioctl.h>
37 #include <sys/time.h>
38 
39 #include <drm.h>
40 
41 #include "igt_rand.h"
42 #include "igt_vgem.h"
43 #include "sync_file.h"
44 
45 #define LO 0
46 #define HI 1
47 #define NOISE 2
48 
49 #define MAX_PRIO LOCAL_I915_CONTEXT_MAX_USER_PRIORITY
50 #define MIN_PRIO LOCAL_I915_CONTEXT_MIN_USER_PRIORITY
51 
52 static int priorities[] = {
53 	[LO] = MIN_PRIO / 2,
54 	[HI] = MAX_PRIO / 2,
55 };
56 
57 #define MAX_ELSP_QLEN 16
58 
59 IGT_TEST_DESCRIPTION("Test shared contexts.");
60 
create_shared_gtt(int i915,unsigned int flags)61 static void create_shared_gtt(int i915, unsigned int flags)
62 #define DETACHED 0x1
63 {
64 	const uint32_t bbe = MI_BATCH_BUFFER_END;
65 	struct drm_i915_gem_exec_object2 obj = {
66 		.handle = gem_create(i915, 4096),
67 	};
68 	struct drm_i915_gem_execbuffer2 execbuf = {
69 		.buffers_ptr = to_user_pointer(&obj),
70 		.buffer_count = 1,
71 	};
72 	uint32_t parent, child;
73 
74 	gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
75 	gem_execbuf(i915, &execbuf);
76 	gem_sync(i915, obj.handle);
77 
78 	child = flags & DETACHED ? gem_context_create(i915) : 0;
79 	igt_until_timeout(2) {
80 		parent = flags & DETACHED ? child : 0;
81 		child = gem_context_clone(i915,
82 					  parent, I915_CONTEXT_CLONE_VM,
83 					  0);
84 		execbuf.rsvd1 = child;
85 		gem_execbuf(i915, &execbuf);
86 
87 		if (flags & DETACHED) {
88 			gem_context_destroy(i915, parent);
89 			gem_execbuf(i915, &execbuf);
90 		} else {
91 			parent = child;
92 			gem_context_destroy(i915, parent);
93 		}
94 
95 		execbuf.rsvd1 = parent;
96 		igt_assert_eq(__gem_execbuf(i915, &execbuf), -ENOENT);
97 		igt_assert_eq(__gem_context_clone(i915,
98 						  parent, I915_CONTEXT_CLONE_VM,
99 						  0, &parent), -ENOENT);
100 	}
101 	if (flags & DETACHED)
102 		gem_context_destroy(i915, child);
103 
104 	gem_sync(i915, obj.handle);
105 	gem_close(i915, obj.handle);
106 }
107 
disjoint_timelines(int i915)108 static void disjoint_timelines(int i915)
109 {
110 	IGT_CORK_HANDLE(cork);
111 	igt_spin_t *spin[2];
112 	uint32_t plug, child;
113 
114 	igt_require(gem_has_execlists(i915));
115 
116 	/*
117 	 * Each context, although they share a vm, are expected to be
118 	 * distinct timelines. A request queued to one context should be
119 	 * independent of any shared contexts.
120 	 */
121 	child = gem_context_clone(i915, 0, I915_CONTEXT_CLONE_VM, 0);
122 	plug = igt_cork_plug(&cork, i915);
123 
124 	spin[0] = __igt_spin_new(i915, .ctx = 0, .dependency = plug);
125 	spin[1] = __igt_spin_new(i915, .ctx = child);
126 
127 	/* Wait for the second spinner, will hang if stuck behind the first */
128 	igt_spin_end(spin[1]);
129 	gem_sync(i915, spin[1]->handle);
130 
131 	igt_cork_unplug(&cork);
132 
133 	igt_spin_free(i915, spin[1]);
134 	igt_spin_free(i915, spin[0]);
135 }
136 
exhaust_shared_gtt(int i915,unsigned int flags)137 static void exhaust_shared_gtt(int i915, unsigned int flags)
138 #define EXHAUST_LRC 0x1
139 {
140 	i915 = gem_reopen_driver(i915);
141 
142 	igt_fork(pid, 1) {
143 		const uint32_t bbe = MI_BATCH_BUFFER_END;
144 		struct drm_i915_gem_exec_object2 obj = {
145 			.handle = gem_create(i915, 4096)
146 		};
147 		struct drm_i915_gem_execbuffer2 execbuf = {
148 			.buffers_ptr = to_user_pointer(&obj),
149 			.buffer_count = 1,
150 		};
151 		uint32_t parent, child;
152 		unsigned long count = 0;
153 		int err;
154 
155 		gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
156 
157 		child = 0;
158 		for (;;) {
159 			parent = child;
160 			err = __gem_context_clone(i915,
161 						  parent, I915_CONTEXT_CLONE_VM,
162 						  0, &child);
163 			if (err)
164 				break;
165 
166 			if (flags & EXHAUST_LRC) {
167 				execbuf.rsvd1 = child;
168 				err = __gem_execbuf(i915, &execbuf);
169 				if (err)
170 					break;
171 			}
172 
173 			count++;
174 		}
175 		gem_sync(i915, obj.handle);
176 
177 		igt_info("Created %lu shared contexts, before %d (%s)\n",
178 			 count, err, strerror(-err));
179 	}
180 	close(i915);
181 	igt_waitchildren();
182 }
183 
exec_shared_gtt(int i915,unsigned int ring)184 static void exec_shared_gtt(int i915, unsigned int ring)
185 {
186 	const int gen = intel_gen(intel_get_drm_devid(i915));
187 	const uint32_t bbe = MI_BATCH_BUFFER_END;
188 	struct drm_i915_gem_exec_object2 obj = {};
189 	struct drm_i915_gem_execbuffer2 execbuf = {
190 		.buffers_ptr = to_user_pointer(&obj),
191 		.buffer_count = 1,
192 		.flags = ring,
193 	};
194 	uint32_t scratch, *s;
195 	uint32_t batch, cs[16];
196 	uint64_t offset;
197 	int i;
198 
199 	gem_require_ring(i915, ring);
200 	igt_require(gem_can_store_dword(i915, ring));
201 
202 	/* Find a hole big enough for both objects later */
203 	scratch = gem_create(i915, 16384);
204 	gem_write(i915, scratch, 0, &bbe, sizeof(bbe));
205 	obj.handle = scratch;
206 	gem_execbuf(i915, &execbuf);
207 	gem_close(i915, scratch);
208 	obj.flags |= EXEC_OBJECT_PINNED; /* reuse this address */
209 
210 	scratch = gem_create(i915, 4096);
211 	s = gem_mmap__wc(i915, scratch, 0, 4096, PROT_WRITE);
212 
213 	gem_set_domain(i915, scratch, I915_GEM_DOMAIN_WC, I915_GEM_DOMAIN_WC);
214 	s[0] = bbe;
215 	s[64] = bbe;
216 
217 	/* Load object into place in the GTT */
218 	obj.handle = scratch;
219 	gem_execbuf(i915, &execbuf);
220 	offset = obj.offset;
221 
222 	/* Presume nothing causes an eviction in the meantime! */
223 
224 	batch = gem_create(i915, 4096);
225 
226 	i = 0;
227 	cs[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
228 	if (gen >= 8) {
229 		cs[++i] = obj.offset;
230 		cs[++i] = obj.offset >> 32;
231 	} else if (gen >= 4) {
232 		cs[++i] = 0;
233 		cs[++i] = obj.offset;
234 	} else {
235 		cs[i]--;
236 		cs[++i] = obj.offset;
237 	}
238 	cs[++i] = 0xc0ffee;
239 	cs[++i] = bbe;
240 	gem_write(i915, batch, 0, cs, sizeof(cs));
241 
242 	obj.handle = batch;
243 	obj.offset += 8192; /* make sure we don't cause an eviction! */
244 	execbuf.rsvd1 = gem_context_clone(i915, 0, I915_CONTEXT_CLONE_VM, 0);
245 	if (gen > 3 && gen < 6)
246 		execbuf.flags |= I915_EXEC_SECURE;
247 	gem_execbuf(i915, &execbuf);
248 
249 	/* Check the scratch didn't move */
250 	obj.handle = scratch;
251 	obj.offset = -1;
252 	obj.flags &= ~EXEC_OBJECT_PINNED;
253 	execbuf.batch_start_offset = 64 * sizeof(s[0]);
254 	gem_execbuf(i915, &execbuf);
255 	igt_assert_eq_u64(obj.offset, offset);
256 	gem_context_destroy(i915, execbuf.rsvd1);
257 
258 	gem_sync(i915, batch); /* write hazard lies */
259 	gem_close(i915, batch);
260 
261 	/*
262 	 * If we created the new context with the old GTT, the write
263 	 * into the stale location of scratch will have landed in the right
264 	 * object. Otherwise, it should read the previous value of
265 	 * MI_BATCH_BUFFER_END.
266 	 */
267 	igt_assert_eq_u32(*s, 0xc0ffee);
268 
269 	munmap(s, 4096);
270 	gem_close(i915, scratch);
271 }
272 
nop_sync(int i915,uint32_t ctx,unsigned int ring,int64_t timeout)273 static int nop_sync(int i915, uint32_t ctx, unsigned int ring, int64_t timeout)
274 {
275 	const uint32_t bbe = MI_BATCH_BUFFER_END;
276 	struct drm_i915_gem_exec_object2 obj = {
277 		.handle = gem_create(i915, 4096),
278 	};
279 	struct drm_i915_gem_execbuffer2 execbuf = {
280 		.buffers_ptr = to_user_pointer(&obj),
281 		.buffer_count = 1,
282 		.flags = ring,
283 		.rsvd1 = ctx,
284 	};
285 	int err;
286 
287 	gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
288 	gem_execbuf(i915, &execbuf);
289 	err = gem_wait(i915, obj.handle, &timeout);
290 	gem_close(i915, obj.handle);
291 
292 	return err;
293 }
294 
has_single_timeline(int i915)295 static bool has_single_timeline(int i915)
296 {
297 	uint32_t ctx;
298 
299 	__gem_context_clone(i915, 0, 0,
300 			    I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE,
301 			    &ctx);
302 	if (ctx)
303 		gem_context_destroy(i915, ctx);
304 
305 	return ctx != 0;
306 }
307 
single_timeline(int i915)308 static void single_timeline(int i915)
309 {
310 	const uint32_t bbe = MI_BATCH_BUFFER_END;
311 	struct drm_i915_gem_exec_object2 obj = {
312 		.handle = gem_create(i915, 4096),
313 	};
314 	struct drm_i915_gem_execbuffer2 execbuf = {
315 		.buffers_ptr = to_user_pointer(&obj),
316 		.buffer_count = 1,
317 	};
318 	struct sync_fence_info rings[16];
319 	struct sync_file_info sync_file_info = {
320 		.num_fences = 1,
321 	};
322 	unsigned int engine;
323 	int n;
324 
325 	igt_require(has_single_timeline(i915));
326 
327 	gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
328 	gem_execbuf(i915, &execbuf);
329 	gem_sync(i915, obj.handle);
330 
331 	/*
332 	 * For a "single timeline" context, each ring is on the common
333 	 * timeline, unlike a normal context where each ring has an
334 	 * independent timeline. That is no matter which engine we submit
335 	 * to, it reports the same timeline name and fence context. However,
336 	 * the fence context is not reported through the sync_fence_info.
337 	 */
338 	execbuf.rsvd1 =
339 		gem_context_clone(i915, 0, 0,
340 				  I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE);
341 	execbuf.flags = I915_EXEC_FENCE_OUT;
342 	n = 0;
343 	for_each_engine(i915, engine) {
344 		gem_execbuf_wr(i915, &execbuf);
345 		sync_file_info.sync_fence_info = to_user_pointer(&rings[n]);
346 		do_ioctl(execbuf.rsvd2 >> 32, SYNC_IOC_FILE_INFO, &sync_file_info);
347 		close(execbuf.rsvd2 >> 32);
348 
349 		igt_info("ring[%d] fence: %s %s\n",
350 			 n, rings[n].driver_name, rings[n].obj_name);
351 		n++;
352 	}
353 	gem_sync(i915, obj.handle);
354 	gem_close(i915, obj.handle);
355 
356 	for (int i = 1; i < n; i++) {
357 		igt_assert(!strcmp(rings[0].driver_name, rings[i].driver_name));
358 		igt_assert(!strcmp(rings[0].obj_name, rings[i].obj_name));
359 	}
360 }
361 
exec_single_timeline(int i915,unsigned int engine)362 static void exec_single_timeline(int i915, unsigned int engine)
363 {
364 	unsigned int other;
365 	igt_spin_t *spin;
366 	uint32_t ctx;
367 
368 	igt_require(gem_ring_has_physical_engine(i915, engine));
369 	igt_require(has_single_timeline(i915));
370 
371 	/*
372 	 * On an ordinary context, a blockage on one engine doesn't prevent
373 	 * execution on an other.
374 	 */
375 	ctx = 0;
376 	spin = NULL;
377 	for_each_physical_engine(i915, other) {
378 		if (other == engine)
379 			continue;
380 
381 		if (spin == NULL) {
382 			spin = __igt_spin_new(i915, .ctx = ctx, .engine = other);
383 		} else {
384 			struct drm_i915_gem_execbuffer2 execbuf = {
385 				.buffers_ptr = spin->execbuf.buffers_ptr,
386 				.buffer_count = spin->execbuf.buffer_count,
387 				.flags = other,
388 				.rsvd1 = ctx,
389 			};
390 			gem_execbuf(i915, &execbuf);
391 		}
392 	}
393 	igt_require(spin);
394 	igt_assert_eq(nop_sync(i915, ctx, engine, NSEC_PER_SEC), 0);
395 	igt_spin_free(i915, spin);
396 
397 	/*
398 	 * But if we create a context with just a single shared timeline,
399 	 * then it will block waiting for the earlier requests on the
400 	 * other engines.
401 	 */
402 	ctx = gem_context_clone(i915, 0, 0,
403 				I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE);
404 	spin = NULL;
405 	for_each_physical_engine(i915, other) {
406 		if (other == engine)
407 			continue;
408 
409 		if (spin == NULL) {
410 			spin = __igt_spin_new(i915, .ctx = ctx, .engine = other);
411 		} else {
412 			struct drm_i915_gem_execbuffer2 execbuf = {
413 				.buffers_ptr = spin->execbuf.buffers_ptr,
414 				.buffer_count = spin->execbuf.buffer_count,
415 				.flags = other,
416 				.rsvd1 = ctx,
417 			};
418 			gem_execbuf(i915, &execbuf);
419 		}
420 	}
421 	igt_assert(spin);
422 	igt_assert_eq(nop_sync(i915, ctx, engine, NSEC_PER_SEC), -ETIME);
423 	igt_spin_free(i915, spin);
424 }
425 
store_dword(int i915,uint32_t ctx,unsigned ring,uint32_t target,uint32_t offset,uint32_t value,uint32_t cork,unsigned write_domain)426 static void store_dword(int i915, uint32_t ctx, unsigned ring,
427 			uint32_t target, uint32_t offset, uint32_t value,
428 			uint32_t cork, unsigned write_domain)
429 {
430 	const int gen = intel_gen(intel_get_drm_devid(i915));
431 	struct drm_i915_gem_exec_object2 obj[3];
432 	struct drm_i915_gem_relocation_entry reloc;
433 	struct drm_i915_gem_execbuffer2 execbuf;
434 	uint32_t batch[16];
435 	int i;
436 
437 	memset(&execbuf, 0, sizeof(execbuf));
438 	execbuf.buffers_ptr = to_user_pointer(obj + !cork);
439 	execbuf.buffer_count = 2 + !!cork;
440 	execbuf.flags = ring;
441 	if (gen < 6)
442 		execbuf.flags |= I915_EXEC_SECURE;
443 	execbuf.rsvd1 = ctx;
444 
445 	memset(obj, 0, sizeof(obj));
446 	obj[0].handle = cork;
447 	obj[1].handle = target;
448 	obj[2].handle = gem_create(i915, 4096);
449 
450 	memset(&reloc, 0, sizeof(reloc));
451 	reloc.target_handle = obj[1].handle;
452 	reloc.presumed_offset = 0;
453 	reloc.offset = sizeof(uint32_t);
454 	reloc.delta = offset;
455 	reloc.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
456 	reloc.write_domain = write_domain;
457 	obj[2].relocs_ptr = to_user_pointer(&reloc);
458 	obj[2].relocation_count = 1;
459 
460 	i = 0;
461 	batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
462 	if (gen >= 8) {
463 		batch[++i] = offset;
464 		batch[++i] = 0;
465 	} else if (gen >= 4) {
466 		batch[++i] = 0;
467 		batch[++i] = offset;
468 		reloc.offset += sizeof(uint32_t);
469 	} else {
470 		batch[i]--;
471 		batch[++i] = offset;
472 	}
473 	batch[++i] = value;
474 	batch[++i] = MI_BATCH_BUFFER_END;
475 	gem_write(i915, obj[2].handle, 0, batch, sizeof(batch));
476 	gem_execbuf(i915, &execbuf);
477 	gem_close(i915, obj[2].handle);
478 }
479 
create_highest_priority(int i915)480 static uint32_t create_highest_priority(int i915)
481 {
482 	uint32_t ctx = gem_context_create(i915);
483 
484 	/*
485 	 * If there is no priority support, all contexts will have equal
486 	 * priority (and therefore the max user priority), so no context
487 	 * can overtake us, and we effectively can form a plug.
488 	 */
489 	__gem_context_set_priority(i915, ctx, MAX_PRIO);
490 
491 	return ctx;
492 }
493 
unplug_show_queue(int i915,struct igt_cork * c,unsigned int engine)494 static void unplug_show_queue(int i915, struct igt_cork *c, unsigned int engine)
495 {
496 	igt_spin_t *spin[MAX_ELSP_QLEN];
497 
498 	for (int n = 0; n < ARRAY_SIZE(spin); n++) {
499 		const struct igt_spin_factory opts = {
500 			.ctx = create_highest_priority(i915),
501 			.engine = engine,
502 		};
503 		spin[n] = __igt_spin_factory(i915, &opts);
504 		gem_context_destroy(i915, opts.ctx);
505 	}
506 
507 	igt_cork_unplug(c); /* batches will now be queued on the engine */
508 	igt_debugfs_dump(i915, "i915_engine_info");
509 
510 	for (int n = 0; n < ARRAY_SIZE(spin); n++)
511 		igt_spin_free(i915, spin[n]);
512 }
513 
store_timestamp(int i915,uint32_t ctx,unsigned ring,unsigned mmio_base,int offset)514 static uint32_t store_timestamp(int i915,
515 				uint32_t ctx, unsigned ring,
516 				unsigned mmio_base,
517 				int offset)
518 {
519 	const bool r64b = intel_gen(intel_get_drm_devid(i915)) >= 8;
520 	struct drm_i915_gem_exec_object2 obj = {
521 		.handle = gem_create(i915, 4096),
522 		.relocation_count = 1,
523 	};
524 	struct drm_i915_gem_relocation_entry reloc = {
525 		.target_handle = obj.handle,
526 		.offset = 2 * sizeof(uint32_t),
527 		.delta = offset * sizeof(uint32_t),
528 		.read_domains = I915_GEM_DOMAIN_INSTRUCTION,
529 	};
530 	struct drm_i915_gem_execbuffer2 execbuf = {
531 		.buffers_ptr = to_user_pointer(&obj),
532 		.buffer_count = 1,
533 		.flags = ring,
534 		.rsvd1 = ctx,
535 	};
536 	uint32_t batch[] = {
537 		0x24 << 23 | (1 + r64b), /* SRM */
538 		mmio_base + 0x358,
539 		offset * sizeof(uint32_t),
540 		0,
541 		MI_BATCH_BUFFER_END
542 	};
543 
544 	igt_require(intel_gen(intel_get_drm_devid(i915)) >= 7);
545 
546 	gem_write(i915, obj.handle, 0, batch, sizeof(batch));
547 	obj.relocs_ptr = to_user_pointer(&reloc);
548 
549 	gem_execbuf(i915, &execbuf);
550 
551 	return obj.handle;
552 }
553 
independent(int i915,unsigned ring,unsigned flags)554 static void independent(int i915, unsigned ring, unsigned flags)
555 {
556 	const int TIMESTAMP = 1023;
557 	uint32_t handle[ARRAY_SIZE(priorities)];
558 	igt_spin_t *spin[MAX_ELSP_QLEN];
559 	unsigned int mmio_base;
560 
561 	/* XXX i915_query()! */
562 	switch (ring) {
563 	case I915_EXEC_DEFAULT:
564 	case I915_EXEC_RENDER:
565 		mmio_base = 0x2000;
566 		break;
567 #if 0
568 	case I915_EXEC_BSD:
569 		mmio_base = 0x12000;
570 		break;
571 #endif
572 	case I915_EXEC_BLT:
573 		mmio_base = 0x22000;
574 		break;
575 
576 #define GEN11_VECS0_BASE 0x1c8000
577 #define GEN11_VECS1_BASE 0x1d8000
578 	case I915_EXEC_VEBOX:
579 		if (intel_gen(intel_get_drm_devid(i915)) >= 11)
580 			mmio_base = GEN11_VECS0_BASE;
581 		else
582 			mmio_base = 0x1a000;
583 		break;
584 
585 	default:
586 		igt_skip("mmio base not known\n");
587 	}
588 
589 	for (int n = 0; n < ARRAY_SIZE(spin); n++) {
590 		const struct igt_spin_factory opts = {
591 			.ctx = create_highest_priority(i915),
592 			.engine = ring,
593 		};
594 		spin[n] = __igt_spin_factory(i915, &opts);
595 		gem_context_destroy(i915, opts.ctx);
596 	}
597 
598 	for (int i = 0; i < ARRAY_SIZE(priorities); i++) {
599 		uint32_t ctx = gem_queue_create(i915);
600 		gem_context_set_priority(i915, ctx, priorities[i]);
601 		handle[i] = store_timestamp(i915, ctx, ring, mmio_base, TIMESTAMP);
602 		gem_context_destroy(i915, ctx);
603 	}
604 
605 	for (int n = 0; n < ARRAY_SIZE(spin); n++)
606 		igt_spin_free(i915, spin[n]);
607 
608 	for (int i = 0; i < ARRAY_SIZE(priorities); i++) {
609 		uint32_t *ptr;
610 
611 		ptr = gem_mmap__gtt(i915, handle[i], 4096, PROT_READ);
612 		gem_set_domain(i915, handle[i], /* no write hazard lies! */
613 			       I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
614 		gem_close(i915, handle[i]);
615 
616 		handle[i] = ptr[TIMESTAMP];
617 		munmap(ptr, 4096);
618 
619 		igt_debug("ctx[%d] .prio=%d, timestamp=%u\n",
620 			  i, priorities[i], handle[i]);
621 	}
622 
623 	igt_assert((int32_t)(handle[HI] - handle[LO]) < 0);
624 }
625 
reorder(int i915,unsigned ring,unsigned flags)626 static void reorder(int i915, unsigned ring, unsigned flags)
627 #define EQUAL 1
628 {
629 	IGT_CORK_HANDLE(cork);
630 	uint32_t scratch;
631 	uint32_t *ptr;
632 	uint32_t ctx[2];
633 	uint32_t plug;
634 
635 	ctx[LO] = gem_queue_create(i915);
636 	gem_context_set_priority(i915, ctx[LO], MIN_PRIO);
637 
638 	ctx[HI] = gem_queue_create(i915);
639 	gem_context_set_priority(i915, ctx[HI], flags & EQUAL ? MIN_PRIO : 0);
640 
641 	scratch = gem_create(i915, 4096);
642 	plug = igt_cork_plug(&cork, i915);
643 
644 	/* We expect the high priority context to be executed first, and
645 	 * so the final result will be value from the low priority context.
646 	 */
647 	store_dword(i915, ctx[LO], ring, scratch, 0, ctx[LO], plug, 0);
648 	store_dword(i915, ctx[HI], ring, scratch, 0, ctx[HI], plug, 0);
649 
650 	unplug_show_queue(i915, &cork, ring);
651 	gem_close(i915, plug);
652 
653 	gem_context_destroy(i915, ctx[LO]);
654 	gem_context_destroy(i915, ctx[HI]);
655 
656 	ptr = gem_mmap__gtt(i915, scratch, 4096, PROT_READ);
657 	gem_set_domain(i915, scratch, /* no write hazard lies! */
658 		       I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
659 	gem_close(i915, scratch);
660 
661 	if (flags & EQUAL) /* equal priority, result will be fifo */
662 		igt_assert_eq_u32(ptr[0], ctx[HI]);
663 	else
664 		igt_assert_eq_u32(ptr[0], ctx[LO]);
665 	munmap(ptr, 4096);
666 }
667 
promotion(int i915,unsigned ring)668 static void promotion(int i915, unsigned ring)
669 {
670 	IGT_CORK_HANDLE(cork);
671 	uint32_t result, dep;
672 	uint32_t *ptr;
673 	uint32_t ctx[3];
674 	uint32_t plug;
675 
676 	ctx[LO] = gem_queue_create(i915);
677 	gem_context_set_priority(i915, ctx[LO], MIN_PRIO);
678 
679 	ctx[HI] = gem_queue_create(i915);
680 	gem_context_set_priority(i915, ctx[HI], 0);
681 
682 	ctx[NOISE] = gem_queue_create(i915);
683 	gem_context_set_priority(i915, ctx[NOISE], MIN_PRIO/2);
684 
685 	result = gem_create(i915, 4096);
686 	dep = gem_create(i915, 4096);
687 
688 	plug = igt_cork_plug(&cork, i915);
689 
690 	/* Expect that HI promotes LO, so the order will be LO, HI, NOISE.
691 	 *
692 	 * fifo would be NOISE, LO, HI.
693 	 * strict priority would be  HI, NOISE, LO
694 	 */
695 	store_dword(i915, ctx[NOISE], ring, result, 0, ctx[NOISE], plug, 0);
696 	store_dword(i915, ctx[LO], ring, result, 0, ctx[LO], plug, 0);
697 
698 	/* link LO <-> HI via a dependency on another buffer */
699 	store_dword(i915, ctx[LO], ring, dep, 0, ctx[LO], 0, I915_GEM_DOMAIN_INSTRUCTION);
700 	store_dword(i915, ctx[HI], ring, dep, 0, ctx[HI], 0, 0);
701 
702 	store_dword(i915, ctx[HI], ring, result, 0, ctx[HI], 0, 0);
703 
704 	unplug_show_queue(i915, &cork, ring);
705 	gem_close(i915, plug);
706 
707 	gem_context_destroy(i915, ctx[NOISE]);
708 	gem_context_destroy(i915, ctx[LO]);
709 	gem_context_destroy(i915, ctx[HI]);
710 
711 	ptr = gem_mmap__gtt(i915, dep, 4096, PROT_READ);
712 	gem_set_domain(i915, dep, /* no write hazard lies! */
713 			I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
714 	gem_close(i915, dep);
715 
716 	igt_assert_eq_u32(ptr[0], ctx[HI]);
717 	munmap(ptr, 4096);
718 
719 	ptr = gem_mmap__gtt(i915, result, 4096, PROT_READ);
720 	gem_set_domain(i915, result, /* no write hazard lies! */
721 			I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
722 	gem_close(i915, result);
723 
724 	igt_assert_eq_u32(ptr[0], ctx[NOISE]);
725 	munmap(ptr, 4096);
726 }
727 
smoketest(int i915,unsigned ring,unsigned timeout)728 static void smoketest(int i915, unsigned ring, unsigned timeout)
729 {
730 	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
731 	unsigned engines[16];
732 	unsigned nengine;
733 	unsigned engine;
734 	uint32_t scratch;
735 	uint32_t *ptr;
736 
737 	nengine = 0;
738 	for_each_physical_engine(i915, engine)
739 		engines[nengine++] = engine;
740 	igt_require(nengine);
741 
742 	scratch = gem_create(i915, 4096);
743 	igt_fork(child, ncpus) {
744 		unsigned long count = 0;
745 		uint32_t ctx;
746 
747 		hars_petruska_f54_1_random_perturb(child);
748 
749 		ctx = gem_queue_create(i915);
750 		igt_until_timeout(timeout) {
751 			int prio;
752 
753 			prio = hars_petruska_f54_1_random_unsafe_max(MAX_PRIO - MIN_PRIO) + MIN_PRIO;
754 			gem_context_set_priority(i915, ctx, prio);
755 
756 			engine = engines[hars_petruska_f54_1_random_unsafe_max(nengine)];
757 			store_dword(i915, ctx, engine, scratch,
758 				    8*child + 0, ~child,
759 				    0, 0);
760 			for (unsigned int step = 0; step < 8; step++)
761 				store_dword(i915, ctx, engine, scratch,
762 					    8*child + 4, count++,
763 					    0, 0);
764 		}
765 		gem_context_destroy(i915, ctx);
766 	}
767 	igt_waitchildren();
768 
769 	ptr = gem_mmap__gtt(i915, scratch, 4096, PROT_READ);
770 	gem_set_domain(i915, scratch, /* no write hazard lies! */
771 			I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
772 	gem_close(i915, scratch);
773 
774 	for (unsigned n = 0; n < ncpus; n++) {
775 		igt_assert_eq_u32(ptr[2*n], ~n);
776 		/*
777 		 * Note this count is approximate due to unconstrained
778 		 * ordering of the dword writes between engines.
779 		 *
780 		 * Take the result with a pinch of salt.
781 		 */
782 		igt_info("Child[%d] completed %u cycles\n",  n, ptr[2*n+1]);
783 	}
784 	munmap(ptr, 4096);
785 }
786 
787 igt_main
788 {
789 	const struct intel_execution_engine *e;
790 	int i915 = -1;
791 
792 	igt_fixture {
793 		i915 = drm_open_driver(DRIVER_INTEL);
794 		igt_require_gem(i915);
795 	}
796 
797 	igt_subtest_group {
798 		igt_fixture {
799 			igt_require(gem_contexts_has_shared_gtt(i915));
800 			igt_fork_hang_detector(i915);
801 		}
802 
803 		igt_subtest("create-shared-gtt")
804 			create_shared_gtt(i915, 0);
805 
806 		igt_subtest("detached-shared-gtt")
807 			create_shared_gtt(i915, DETACHED);
808 
809 		igt_subtest("disjoint-timelines")
810 			disjoint_timelines(i915);
811 
812 		igt_subtest("single-timeline")
813 			single_timeline(i915);
814 
815 		igt_subtest("exhaust-shared-gtt")
816 			exhaust_shared_gtt(i915, 0);
817 
818 		igt_subtest("exhaust-shared-gtt-lrc")
819 			exhaust_shared_gtt(i915, EXHAUST_LRC);
820 
821 		for (e = intel_execution_engines; e->name; e++) {
822 			igt_subtest_f("exec-shared-gtt-%s", e->name)
823 				exec_shared_gtt(i915, e->exec_id | e->flags);
824 
825 			igt_subtest_f("exec-single-timeline-%s", e->name)
826 				exec_single_timeline(i915,
827 						     e->exec_id | e->flags);
828 
829 			/*
830 			 * Check that the shared contexts operate independently,
831 			 * that is requests on one ("queue") can be scheduled
832 			 * around another queue. We only check the basics here,
833 			 * enough to reduce the queue into just another context,
834 			 * and so rely on gem_exec_schedule to prove the rest.
835 			 */
836 			igt_subtest_group {
837 				igt_fixture {
838 					gem_require_ring(i915, e->exec_id | e->flags);
839 					igt_require(gem_can_store_dword(i915, e->exec_id | e->flags));
840 					igt_require(gem_scheduler_enabled(i915));
841 					igt_require(gem_scheduler_has_ctx_priority(i915));
842 				}
843 
844 				igt_subtest_f("Q-independent-%s", e->name)
845 					independent(i915, e->exec_id | e->flags, 0);
846 
847 				igt_subtest_f("Q-in-order-%s", e->name)
848 					reorder(i915, e->exec_id | e->flags, EQUAL);
849 
850 				igt_subtest_f("Q-out-order-%s", e->name)
851 					reorder(i915, e->exec_id | e->flags, 0);
852 
853 				igt_subtest_f("Q-promotion-%s", e->name)
854 					promotion(i915, e->exec_id | e->flags);
855 
856 				igt_subtest_f("Q-smoketest-%s", e->name)
857 					smoketest(i915, e->exec_id | e->flags, 5);
858 			}
859 		}
860 
861 		igt_subtest("Q-smoketest-all") {
862 			igt_require(gem_scheduler_enabled(i915));
863 			igt_require(gem_scheduler_has_ctx_priority(i915));
864 			smoketest(i915, -1, 30);
865 		}
866 
867 		igt_fixture {
868 			igt_stop_hang_detector();
869 		}
870 	}
871 }
872