gem_ctx_create.c 9.62 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 * Copyright © 2012 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * Authors:
 *    Ben Widawsky <ben@bwidawsk.net>
 *
 */

28
#include "igt.h"
29

30 31
#include <stdio.h>
#include <string.h>
32
#include <errno.h>
33
#include <time.h>
34

35 36
#include "igt_rand.h"

37 38 39 40 41
#define LOCAL_I915_EXEC_BSD_SHIFT      (13)
#define LOCAL_I915_EXEC_BSD_MASK       (3 << LOCAL_I915_EXEC_BSD_SHIFT)

#define ENGINE_FLAGS  (I915_EXEC_RING_MASK | LOCAL_I915_EXEC_BSD_MASK)

42 43 44
static unsigned all_engines[16];
static unsigned all_nengine;

45 46 47
static unsigned ppgtt_engines[16];
static unsigned ppgtt_nengine;

48
static int __gem_context_create_local(int fd, struct drm_i915_gem_context_create *arg)
49 50 51 52 53 54 55 56 57 58 59 60 61
{
	int ret = 0;
	if (drmIoctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_CREATE, arg))
		ret = -errno;
	return ret;
}

static double elapsed(const struct timespec *start,
		      const struct timespec *end)
{
	return (end->tv_sec - start->tv_sec) + 1e-9*(end->tv_nsec - start->tv_nsec);
}

62
static void files(int core, int timeout, const int ncpus)
63 64 65 66 67 68 69 70 71 72 73 74
{
	const uint32_t bbe = MI_BATCH_BUFFER_END;
	struct drm_i915_gem_execbuffer2 execbuf;
	struct drm_i915_gem_exec_object2 obj;
	uint32_t batch, name;

	batch = gem_create(core, 4096);
	gem_write(core, batch, 0, &bbe, sizeof(bbe));
	name = gem_flink(core, batch);

	memset(&obj, 0, sizeof(obj));
	memset(&execbuf, 0, sizeof(execbuf));
75
	execbuf.buffers_ptr = to_user_pointer(&obj);
76 77
	execbuf.buffer_count = 1;

78 79 80 81 82
	igt_fork(child, ncpus) {
		struct timespec start, end;
		unsigned count = 0;

		clock_gettime(CLOCK_MONOTONIC, &start);
83
		do {
84 85 86 87 88 89 90 91 92 93 94 95
			do {
				int fd = drm_open_driver(DRIVER_INTEL);
				obj.handle = gem_open(fd, name);
				execbuf.flags &= ~ENGINE_FLAGS;
				execbuf.flags |= ppgtt_engines[count % ppgtt_nengine];
				gem_execbuf(fd, &execbuf);
				close(fd);
			} while (++count & 1023);
			clock_gettime(CLOCK_MONOTONIC, &end);
		} while (elapsed(&start, &end) < timeout);

		gem_sync(core, batch);
96
		clock_gettime(CLOCK_MONOTONIC, &end);
97 98 99 100
		igt_info("[%d] File creation + execution: %.3f us\n",
			 child, elapsed(&start, &end) / count *1e6);
	}
	igt_waitchildren();
101 102 103 104

	gem_close(core, batch);
}

105
static void active(int fd, unsigned engine, int timeout, int ncpus)
106 107 108 109
{
	const uint32_t bbe = MI_BATCH_BUFFER_END;
	struct drm_i915_gem_execbuffer2 execbuf;
	struct drm_i915_gem_exec_object2 obj;
110
	unsigned int nengine, engines[16];
111
	unsigned *shared;
112

113
	if (engine == ALL_ENGINES) {
114 115 116 117 118 119 120 121
		igt_require(all_nengine);
		nengine = all_nengine;
		memcpy(engines, all_engines, sizeof(engines[0])*nengine);
	} else {
		gem_require_ring(fd, engine);
		nengine = 1;
		engines[0] = engine;
	}
122

123 124 125
	shared = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
	igt_assert(shared != MAP_FAILED);

126 127 128 129 130
	memset(&obj, 0, sizeof(obj));
	obj.handle = gem_create(fd, 4096);
	gem_write(fd, obj.handle, 0, &bbe, sizeof(bbe));

	memset(&execbuf, 0, sizeof(execbuf));
131
	execbuf.buffers_ptr = to_user_pointer(&obj);
132 133
	execbuf.buffer_count = 1;

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
	if (ncpus < 0) {
		igt_fork(child, ppgtt_nengine) {
			unsigned long count = 0;

			if (ppgtt_engines[child] == engine)
				continue;

			execbuf.flags = ppgtt_engines[child];

			while (!*(volatile unsigned *)shared) {
				obj.handle = gem_create(fd, 4096 << 10);
				gem_write(fd, obj.handle, 0, &bbe, sizeof(bbe));

				gem_execbuf(fd, &execbuf);
				gem_close(fd, obj.handle);
				count++;
			}

			igt_debug("hog[%d]: cycles=%lu\n", child, count);
		}
		ncpus = -ncpus;
	}

157 158 159 160 161
	igt_fork(child, ncpus) {
		struct timespec start, end;
		unsigned count = 0;

		clock_gettime(CLOCK_MONOTONIC, &start);
162
		do {
163 164
			do {
				execbuf.rsvd1 = gem_context_create(fd);
165 166 167 168
				for (unsigned n = 0; n < nengine; n++) {
					execbuf.flags = engines[n];
					gem_execbuf(fd, &execbuf);
				}
169 170 171 172 173 174
				gem_context_destroy(fd, execbuf.rsvd1);
			} while (++count & 1023);
			clock_gettime(CLOCK_MONOTONIC, &end);
		} while (elapsed(&start, &end) < timeout);

		gem_sync(fd, obj.handle);
175
		clock_gettime(CLOCK_MONOTONIC, &end);
176 177
		igt_info("[%d] Context creation + execution: %.3f us\n",
			 child, elapsed(&start, &end) / count *1e6);
178 179

		shared[0] = 1;
180 181
	}
	igt_waitchildren();
182 183

	gem_close(fd, obj.handle);
184
	munmap(shared, 4096);
185
}
186

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
static void xchg_u32(void *array, unsigned i, unsigned j)
{
	uint32_t *a = array, tmp;

	tmp = a[i];
	a[i] = a[j];
	a[j] = tmp;
}

static unsigned __context_size(int fd)
{
	switch (intel_gen(intel_get_drm_devid(fd))) {
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7: return 17 << 12;
	case 8: return 20 << 12;
	case 9: return 22 << 12;
	default: return 32 << 12;
	}
}

static unsigned context_size(int fd)
{
	uint64_t size;

	size = __context_size(fd);
	if (ppgtt_nengine > 1) {
		size += 4 << 12; /* ringbuffer as well */
		size *= ppgtt_nengine;
	}

	return size;
}

static uint64_t total_avail_mem(unsigned mode)
{
	uint64_t total = intel_get_avail_ram_mb();
	if (mode & CHECK_SWAP)
		total += intel_get_total_swap_mb();
	return total << 20;
}

static void maximum(int fd, int ncpus, unsigned mode)
{
	const uint32_t bbe = MI_BATCH_BUFFER_END;
	struct drm_i915_gem_execbuffer2 execbuf;
	struct drm_i915_gem_exec_object2 obj[2];
	uint64_t avail_mem = total_avail_mem(mode);
	unsigned ctx_size = context_size(fd);
	uint32_t *contexts = NULL;
	unsigned long count = 0;
243
	uint32_t ctx_id;
244 245 246 247 248 249 250 251 252 253 254 255 256

	do {
		int err;

		if ((count & -count) == count) {
			int sz = count ? 2*count : 1;
			contexts = realloc(contexts,
					   sz*sizeof(*contexts));
			igt_assert(contexts);
		}

		err = -ENOMEM;
		if (avail_mem > (count + 1) * ctx_size)
257
			err = __gem_context_create(fd, &ctx_id);
258 259 260 261 262 263
		if (err) {
			igt_info("Created %lu contexts, before failing with '%s' [%d]\n",
				 count, strerror(-err), -err);
			break;
		}

264
		contexts[count++] = ctx_id;
265 266 267 268 269 270 271 272
	} while (1);
	igt_require(count);

	memset(obj, 0, sizeof(obj));
	obj[1].handle = gem_create(fd, 4096);
	gem_write(fd, obj[1].handle, 0, &bbe, sizeof(bbe));

	memset(&execbuf, 0, sizeof(execbuf));
273
	execbuf.buffers_ptr = to_user_pointer(obj);
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
	execbuf.buffer_count = 2;

	igt_fork(child, ncpus) {
		struct timespec start, end;

		hars_petruska_f54_1_random_perturb(child);
		obj[0].handle = gem_create(fd, 4096);

		clock_gettime(CLOCK_MONOTONIC, &start);
		for (int repeat = 0; repeat < 3; repeat++) {
			igt_permute_array(contexts, count, xchg_u32);
			igt_permute_array(all_engines, all_nengine, xchg_u32);

			for (unsigned long i = 0; i < count; i++) {
				execbuf.rsvd1 = contexts[i];
				for (unsigned long j = 0; j < all_nengine; j++) {
					execbuf.flags = all_engines[j];
					gem_execbuf(fd, &execbuf);
				}
			}
		}
		gem_sync(fd, obj[0].handle);
		clock_gettime(CLOCK_MONOTONIC, &end);
		gem_close(fd, obj[0].handle);

		igt_info("[%d] Context execution: %.3f us\n", child,
			 elapsed(&start, &end) / (3 * count * all_nengine) * 1e6);
	}
	igt_waitchildren();

	gem_close(fd, obj[1].handle);

	for (unsigned long i = 0; i < count; i++)
		gem_context_destroy(fd, contexts[i]);
	free(contexts);
}

311
igt_main
312
{
313
	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
314
	struct drm_i915_gem_context_create create;
315
	int fd = -1;
316 317

	igt_fixture {
318 319
		unsigned engine;

320
		fd = drm_open_driver(DRIVER_INTEL);
321
		igt_require_gem(fd);
322
		gem_require_contexts(fd);
323

324
		for_each_physical_engine(fd, engine)
325 326
			all_engines[all_nengine++] = engine;
		igt_require(all_nengine);
327

328 329 330 331 332
		if (gem_uses_full_ppgtt(fd)) {
			ppgtt_nengine = all_nengine;
			memcpy(ppgtt_engines,
			       all_engines,
			       all_nengine * sizeof(all_engines[0]));
333 334
		} else
			ppgtt_engines[ppgtt_nengine++] = 0;
335

336 337
		igt_fork_hang_detector(fd);
	}
338

339
	igt_subtest("basic") {
340
		memset(&create, 0, sizeof(create));
341 342
		create.ctx_id = rand();
		create.pad = 0;
343
		igt_assert_eq(__gem_context_create_local(fd, &create), 0);
344
		igt_assert(create.ctx_id != 0);
345
		gem_context_destroy(fd, create.ctx_id);
346
	}
347

348
	igt_subtest("invalid-pad") {
349
		memset(&create, 0, sizeof(create));
350 351
		create.ctx_id = rand();
		create.pad = 1;
352
		igt_assert_eq(__gem_context_create_local(fd, &create), -EINVAL);
353
	}
354

355 356 357 358 359
	igt_subtest("maximum-mem")
		maximum(fd, ncpus, CHECK_RAM);
	igt_subtest("maximum-swap")
		maximum(fd, ncpus, CHECK_RAM | CHECK_SWAP);

360
	igt_subtest("basic-files")
361 362 363
		files(fd, 5, 1);
	igt_subtest("files")
		files(fd, 150, 1);
364
	igt_subtest("forked-files")
365
		files(fd, 150, ncpus);
366

367
	igt_subtest("active-all")
368
		active(fd, ALL_ENGINES, 120, 1);
369
	igt_subtest("forked-active-all")
370
		active(fd, ALL_ENGINES, 120, ncpus);
371

372
	for (const struct intel_execution_engine *e = intel_execution_engines;
373
	     e->name; e++) {
374
		igt_subtest_f("active-%s", e->name)
375 376 377
			active(fd, e->exec_id | e->flags, 20, 1);
		igt_subtest_f("forked-active-%s", e->name)
			active(fd, e->exec_id | e->flags, 20, ncpus);
378 379 380 381
		if (e->exec_id) {
			igt_subtest_f("hog-%s", e->name)
				active(fd, e->exec_id | e->flags, 20, -1);
		}
382
	}
383

384 385
	igt_fixture {
		igt_stop_hang_detector();
386
		close(fd);
387
	}
388
}