blob: 125776498b3eeece41150cb4ee1a557aa3a7d8cd [file] [log] [blame]
Chris Wilson0393e722015-08-09 17:39:41 +01001/*
2 * Copyright © 2011 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 * Chris Wilson <chris@chris-wilson.co.uk>
25 *
26 */
27
28#include <unistd.h>
29#include <stdlib.h>
30#include <stdint.h>
31#include <stdio.h>
32#include <string.h>
33#include <fcntl.h>
34#include <inttypes.h>
35#include <errno.h>
36#include <sys/stat.h>
37#include <sys/ioctl.h>
38#include <sys/time.h>
39#include <time.h>
Chris Wilson005502f2017-03-28 16:05:28 +010040#include <assert.h>
Chris Wilson0393e722015-08-09 17:39:41 +010041
42#include "drm.h"
43#include "ioctl_wrappers.h"
44#include "drmtest.h"
45#include "intel_io.h"
46#include "igt_stats.h"
47
48enum {
49 ADD_BO = 0,
50 DEL_BO,
Chris Wilson49e09e72017-03-28 11:26:39 +010051 ADD_CTX,
52 DEL_CTX,
Chris Wilson0393e722015-08-09 17:39:41 +010053 EXEC,
Chris Wilson4bf3ca82017-03-28 13:12:24 +010054 WAIT,
Chris Wilson0393e722015-08-09 17:39:41 +010055};
56
57struct trace_add_bo {
58 uint32_t handle;
59 uint64_t size;
60} __attribute__((packed));
61
62struct trace_del_bo {
63 uint32_t handle;
64} __attribute__((packed));
65
Chris Wilson49e09e72017-03-28 11:26:39 +010066struct trace_add_ctx {
67 uint32_t handle;
68} __attribute__((packed));
69
70struct trace_del_ctx {
71 uint32_t handle;
72} __attribute__((packed));
73
Chris Wilson0393e722015-08-09 17:39:41 +010074struct trace_exec {
75 uint32_t object_count;
76 uint64_t flags;
Chris Wilson49e09e72017-03-28 11:26:39 +010077 uint32_t context;
78}__attribute__((packed));
79
Chris Wilson0393e722015-08-09 17:39:41 +010080struct trace_exec_object {
81 uint32_t handle;
82 uint32_t relocation_count;
83 uint64_t alignment;
Chris Wilson49e09e72017-03-28 11:26:39 +010084 uint64_t offset;
Chris Wilson0393e722015-08-09 17:39:41 +010085 uint64_t flags;
86 uint64_t rsvd1;
87 uint64_t rsvd2;
Chris Wilson49e09e72017-03-28 11:26:39 +010088}__attribute__((packed));
Chris Wilson0393e722015-08-09 17:39:41 +010089
Chris Wilson4bf3ca82017-03-28 13:12:24 +010090struct trace_wait {
91 uint32_t handle;
92} __attribute__((packed));
93
Chris Wilsonff9c6d72017-03-29 18:28:09 +010094static uint32_t hars_petruska_f54_1_random(void)
95{
96 static uint32_t state = 0x12345678;
97
98#define rol(x,k) ((x << k) | (x >> (32-k)))
99 return state = (state ^ rol (state, 5) ^ rol (state, 24)) + 0x37798849;
100#undef rol
101}
102
Chris Wilson0393e722015-08-09 17:39:41 +0100103static double elapsed(const struct timespec *start, const struct timespec *end)
104{
105 return 1e3*(end->tv_sec - start->tv_sec) + 1e-6*(end->tv_nsec - start->tv_nsec);
106}
107
Chris Wilson49e09e72017-03-28 11:26:39 +0100108static uint32_t __gem_context_create(int fd)
109{
110 struct drm_i915_gem_context_create arg = {};
111 drmIoctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_CREATE, &arg);
112 return arg.ctx_id;
113}
114
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100115static double replay(const char *filename, long nop, long range)
Chris Wilson0393e722015-08-09 17:39:41 +0100116{
117 struct timespec t_start, t_end;
Chris Wilson77b8af22015-08-14 20:35:18 +0100118 struct drm_i915_gem_execbuffer2 eb = {};
Chris Wilson49e09e72017-03-28 11:26:39 +0100119 const struct trace_version {
120 uint32_t magic;
121 uint32_t version;
122 } *tv;
Chris Wilson005502f2017-03-28 16:05:28 +0100123 const uint32_t bbe = 0xa << 23;
Chris Wilson77b8af22015-08-14 20:35:18 +0100124 struct drm_i915_gem_exec_object2 *exec_objects = NULL;
Chris Wilson49e09e72017-03-28 11:26:39 +0100125 uint32_t *bo, *ctx;
126 int num_bo, num_ctx;
Chris Wilson77b8af22015-08-14 20:35:18 +0100127 int max_objects = 0;
Chris Wilson0393e722015-08-09 17:39:41 +0100128 struct stat st;
129 uint8_t *ptr, *end;
Chris Wilson77b8af22015-08-14 20:35:18 +0100130 int fd;
Chris Wilson0393e722015-08-09 17:39:41 +0100131
132 fd = open(filename, O_RDONLY);
133 if (fd < 0)
Chris Wilson49e09e72017-03-28 11:26:39 +0100134 return -1;
Chris Wilson0393e722015-08-09 17:39:41 +0100135
Chris Wilson005502f2017-03-28 16:05:28 +0100136 if (fstat(fd, &st) < 0) {
137 close(fd);
Chris Wilson49e09e72017-03-28 11:26:39 +0100138 return -1;
Chris Wilson005502f2017-03-28 16:05:28 +0100139 }
Chris Wilson49e09e72017-03-28 11:26:39 +0100140
Chris Wilson005502f2017-03-28 16:05:28 +0100141 ptr = mmap(0, st.st_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
Chris Wilson0393e722015-08-09 17:39:41 +0100142 close(fd);
143
Chris Wilsonb483e682015-08-10 18:45:00 +0100144 if (ptr == MAP_FAILED)
Chris Wilson49e09e72017-03-28 11:26:39 +0100145 return -1;
Chris Wilsonb483e682015-08-10 18:45:00 +0100146
147 madvise(ptr, st.st_size, MADV_SEQUENTIAL);
Chris Wilson0393e722015-08-09 17:39:41 +0100148 end = ptr + st.st_size;
Chris Wilson0393e722015-08-09 17:39:41 +0100149
Chris Wilson49e09e72017-03-28 11:26:39 +0100150 tv = (struct trace_version *)ptr;
151 if (tv->magic != 0xdeadbeef) {
152 fprintf(stderr, "%s: invalid magic\n", filename);
153 return -1;
154 }
155 if (tv->version != 1) {
156 fprintf(stderr, "%s: unhandled version %d\n",
157 filename, tv->version);
158 return -1;
159 }
160 ptr = (void *)(tv + 1);
161
Chris Wilson005502f2017-03-28 16:05:28 +0100162 ctx = calloc(1024, sizeof(*ctx));
163 num_ctx = 1024;
164
165 bo = calloc(4096, sizeof(*bo));
166 num_bo = 4096;
167
168 fd = drm_open_driver(DRIVER_INTEL);
Chris Wilsondc43fb92017-03-29 01:31:10 +0100169 if (nop > 0) {
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100170 bo[0] = gem_create(fd, nop + range);
171 gem_write(fd, bo[0], nop + range - sizeof(bbe),
172 &bbe, sizeof(bbe));
173 range *= 2;
174 range -= 64;
Chris Wilsondc43fb92017-03-29 01:31:10 +0100175 } else {
176 bo[0] = gem_create(fd, 4096);
177 gem_write(fd, bo[0], 0, &bbe, sizeof(bbe));
178 }
Chris Wilson005502f2017-03-28 16:05:28 +0100179
Chris Wilson0393e722015-08-09 17:39:41 +0100180 clock_gettime(CLOCK_MONOTONIC, &t_start);
Chris Wilson49e09e72017-03-28 11:26:39 +0100181 do switch (*ptr++) {
182 case ADD_BO:
183 {
Chris Wilson49e09e72017-03-28 11:26:39 +0100184 struct trace_add_bo *t = (void *)ptr;
185 ptr = (void *)(t + 1);
Chris Wilson77b8af22015-08-14 20:35:18 +0100186
Chris Wilson49e09e72017-03-28 11:26:39 +0100187 if (t->handle >= num_bo) {
188 int new_bo = ALIGN(t->handle, 4096);
189 bo = realloc(bo, sizeof(*bo)*new_bo);
190 memset(bo + num_bo, 0, sizeof(*bo)*(new_bo - num_bo));
191 num_bo = new_bo;
192 }
Chris Wilson77b8af22015-08-14 20:35:18 +0100193
Chris Wilson49e09e72017-03-28 11:26:39 +0100194 bo[t->handle] = gem_create(fd, t->size);
Chris Wilson49e09e72017-03-28 11:26:39 +0100195 break;
Chris Wilson0393e722015-08-09 17:39:41 +0100196 }
Chris Wilson49e09e72017-03-28 11:26:39 +0100197 case DEL_BO:
198 {
199 struct trace_del_bo *t = (void *)ptr;
200 ptr = (void *)(t + 1);
201
Chris Wilson005502f2017-03-28 16:05:28 +0100202 assert(t->handle && t->handle < num_bo && bo[t->handle]);
Chris Wilson49e09e72017-03-28 11:26:39 +0100203 gem_close(fd, bo[t->handle]);
204 bo[t->handle] = 0;
205 break;
206 }
207 case ADD_CTX:
208 {
209 struct trace_add_ctx *t = (void *)ptr;
210 ptr = (void *)(t + 1);
211
212 if (t->handle >= num_ctx) {
213 int new_ctx = ALIGN(t->handle, 1024);
214 ctx = realloc(ctx, sizeof(*ctx)*new_ctx);
215 memset(ctx + num_ctx, 0, sizeof(*ctx)*(new_ctx - num_ctx));
216 num_ctx = new_ctx;
217 }
218
219 ctx[t->handle] = __gem_context_create(fd);
220 break;
221 }
222 case DEL_CTX:
223 {
224 struct trace_del_ctx *t = (void *)ptr;
225 ptr = (void *)(t + 1);
226
Chris Wilson005502f2017-03-28 16:05:28 +0100227 assert(t->handle < num_ctx && ctx[t->handle]);
228 gem_context_destroy(fd, ctx[t->handle]);
229 ctx[t->handle] = 0;
Chris Wilson49e09e72017-03-28 11:26:39 +0100230 break;
231 }
232 case EXEC:
233 {
234 struct trace_exec *t = (void *)ptr;
235 ptr = (void *)(t + 1);
236
237 eb.buffer_count = t->object_count;
238 eb.flags = t->flags;
239 eb.rsvd1 = ctx[t->context];
240
Chris Wilson005502f2017-03-28 16:05:28 +0100241 if (eb.buffer_count >= max_objects) {
Chris Wilson49e09e72017-03-28 11:26:39 +0100242 free(exec_objects);
243
Chris Wilson005502f2017-03-28 16:05:28 +0100244 max_objects = ALIGN(eb.buffer_count + 1, 4096);
Chris Wilson49e09e72017-03-28 11:26:39 +0100245
246 exec_objects = malloc(max_objects*sizeof(*exec_objects));
247 eb.buffers_ptr = (uintptr_t)exec_objects;
248 }
249
250 for (uint32_t i = 0; i < eb.buffer_count; i++) {
251 struct trace_exec_object *to = (void *)ptr;
252 ptr = (void *)(to + 1);
253
254 exec_objects[i].handle = bo[to->handle];
255 exec_objects[i].alignment = to->alignment;
256 exec_objects[i].offset = to->offset;
257 exec_objects[i].flags = to->flags;
258 exec_objects[i].rsvd1 = to->rsvd1;
259 exec_objects[i].rsvd2 = to->rsvd2;
260
261 exec_objects[i].relocation_count = to->relocation_count;
262 exec_objects[i].relocs_ptr = (uintptr_t)ptr;
Chris Wilson005502f2017-03-28 16:05:28 +0100263
264 if (!(eb.flags & I915_EXEC_HANDLE_LUT)) {
265 struct drm_i915_gem_relocation_entry *relocs =
266 (struct drm_i915_gem_relocation_entry *)ptr;
267 for (uint32_t j = 0; j < to->relocation_count; j++)
268 relocs[j].target_handle = bo[relocs[j].target_handle];
269 }
270
Chris Wilson49e09e72017-03-28 11:26:39 +0100271 ptr += sizeof(struct drm_i915_gem_relocation_entry) * to->relocation_count;
272 }
273
Chris Wilson005502f2017-03-28 16:05:28 +0100274 ((struct drm_i915_gem_exec_object2 *)
275 memset(&exec_objects[eb.buffer_count++], 0,
276 sizeof(*exec_objects)))->handle = bo[0];
277
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100278 if (nop > 0) {
279 eb.batch_start_offset = hars_petruska_f54_1_random();
280 eb.batch_start_offset =
281 ((uint64_t)eb.batch_start_offset * range) >> 32;
282 eb.batch_start_offset = ALIGN(eb.batch_start_offset, 64);
283 }
Chris Wilson49e09e72017-03-28 11:26:39 +0100284 gem_execbuf(fd, &eb);
285 break;
286 }
287
Chris Wilson4bf3ca82017-03-28 13:12:24 +0100288 case WAIT:
289 {
290 struct trace_wait *t = (void *)ptr;
291 ptr = (void *)(t + 1);
292
Chris Wilson005502f2017-03-28 16:05:28 +0100293 assert(t->handle && t->handle < num_bo && bo[t->handle]);
294 gem_wait(fd, bo[t->handle], NULL);
Chris Wilson4bf3ca82017-03-28 13:12:24 +0100295 break;
296 }
297
Chris Wilson49e09e72017-03-28 11:26:39 +0100298 default:
299 fprintf(stderr, "Unknown cmd: %x\n", *ptr);
300 return -1;
Chris Wilson0393e722015-08-09 17:39:41 +0100301 } while (ptr < end);
302 clock_gettime(CLOCK_MONOTONIC, &t_end);
Chris Wilson0393e722015-08-09 17:39:41 +0100303
Chris Wilson49e09e72017-03-28 11:26:39 +0100304 return elapsed(&t_start, &t_end);
Chris Wilson0393e722015-08-09 17:39:41 +0100305}
306
Chris Wilsondc43fb92017-03-29 01:31:10 +0100307static long calibrate_nop(int usecs)
308{
309 const uint32_t bbe = 0xa << 23;
310 int fd = drm_open_driver(DRIVER_INTEL);
311 struct drm_i915_gem_exec_object2 obj = {};
312 struct drm_i915_gem_execbuffer2 eb = { .buffer_count = 1, .buffers_ptr = (uintptr_t)&obj};
313 unsigned long size, last_size;
314
315 size = 256*1024;
316 do {
317 struct timespec t_start, t_end;
318
319 obj.handle = gem_create(fd, size);
320 gem_write(fd, obj.handle, size - sizeof(bbe), &bbe, sizeof(bbe));
321 gem_execbuf(fd, &eb);
322 gem_sync(fd, obj.handle);
323
324 clock_gettime(CLOCK_MONOTONIC, &t_start);
325 for (int loop = 0; loop < 9; loop++)
326 gem_execbuf(fd, &eb);
327 gem_sync(fd, obj.handle);
328 clock_gettime(CLOCK_MONOTONIC, &t_end);
329
330 gem_close(fd, obj.handle);
331
332 last_size = size;
333 size = 9e-3*usecs / elapsed(&t_start, &t_end) * size;
334 size = ALIGN(size, 4096);
335 } while (size != last_size);
336
337 close(fd);
338 return size;
339}
340
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100341static int measure_nop(long nop)
342{
343 const uint32_t bbe = 0xa << 23;
344 int fd = drm_open_driver(DRIVER_INTEL);
345 struct drm_i915_gem_exec_object2 obj = {};
346 struct drm_i915_gem_execbuffer2 eb = { .buffer_count = 1, .buffers_ptr = (uintptr_t)&obj};
347 struct timespec t_start, t_end;
348
349 obj.handle = gem_create(fd, nop);
350 gem_write(fd, obj.handle, nop - sizeof(bbe), &bbe, sizeof(bbe));
351 gem_execbuf(fd, &eb);
352 gem_sync(fd, obj.handle);
353
354 clock_gettime(CLOCK_MONOTONIC, &t_start);
355 for (int loop = 0; loop < 9; loop++)
356 gem_execbuf(fd, &eb);
357 gem_sync(fd, obj.handle);
358 clock_gettime(CLOCK_MONOTONIC, &t_end);
359
360 gem_close(fd, obj.handle);
361
362 close(fd);
363 return 1e3*elapsed(&t_start, &t_end) / 9;
364}
365
Chris Wilson0393e722015-08-09 17:39:41 +0100366int main(int argc, char **argv)
367{
Chris Wilsondc43fb92017-03-29 01:31:10 +0100368 int delay = 1000;
Chris Wilson49e09e72017-03-28 11:26:39 +0100369 double *results;
Chris Wilsondc43fb92017-03-29 01:31:10 +0100370 long nop = 0;
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100371 long range = 0;
Chris Wilsondc43fb92017-03-29 01:31:10 +0100372 int i, c;
Chris Wilson0393e722015-08-09 17:39:41 +0100373
Chris Wilson49e09e72017-03-28 11:26:39 +0100374 results = mmap(NULL, ALIGN(argc*sizeof(double), 4096),
375 PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
376
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100377 while ((c = getopt(argc, argv, "d:n:r:")) != -1) {
Chris Wilsondc43fb92017-03-29 01:31:10 +0100378 switch (c) {
379 case 'd':
380 delay = atoi(optarg);
381 break;
382 case 'n':
383 nop = strtol(optarg, NULL, 0);
384 if (nop > 0)
385 nop = ALIGN(nop, 4096);
386 break;
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100387 case 'r':
388 range = strtol(optarg, NULL, 0);
389 if (range > 0)
390 range = ALIGN(range, 4096);
391 break;
Chris Wilsondc43fb92017-03-29 01:31:10 +0100392 default:
393 break;
394 }
395 }
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100396
Chris Wilsondc43fb92017-03-29 01:31:10 +0100397 if (!nop)
398 nop = calibrate_nop(delay);
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100399 if (!range)
400 range = nop / 2;
401 if (nop > 0) {
402 delay = measure_nop(nop);
403 printf("Using %lu nop batch for ~%dus delay, range %lu [%dus]\n",
404 nop, delay,
405 range, (int)(delay * range / nop));
406 }
Chris Wilsondc43fb92017-03-29 01:31:10 +0100407
408 igt_fork(child, argc-optind)
Chris Wilsonff9c6d72017-03-29 18:28:09 +0100409 results[child] = replay(argv[child + optind], nop, range);
Chris Wilson49e09e72017-03-28 11:26:39 +0100410 igt_waitchildren();
411
Chris Wilsondc43fb92017-03-29 01:31:10 +0100412 for (i = 0; i < argc - optind; i++) {
Chris Wilson49e09e72017-03-28 11:26:39 +0100413 double t = results[i];
414 if (t < 0)
Chris Wilsondc43fb92017-03-29 01:31:10 +0100415 printf("%s: failed\n", argv[optind + i]);
Chris Wilson49e09e72017-03-28 11:26:39 +0100416 else
Chris Wilsondc43fb92017-03-29 01:31:10 +0100417 printf("%s: %.3f\n", argv[optind + i], t);
Chris Wilson49e09e72017-03-28 11:26:39 +0100418 }
Chris Wilson0393e722015-08-09 17:39:41 +0100419
420 return 0;
421}