summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2018-02-27 16:02:37 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2018-03-15 17:47:41 +0000
commit821f110241d7537a1c873c71ad5fd7cc2cfc756d (patch)
treef3dab30d6cffa738f099d1eeec10be205d97cbfe
parent081586fff7b1b5002ce4410ac3544d262809e39f (diff)
rrulrrul
-rw-r--r--benchmarks/.gitignore1
-rw-r--r--benchmarks/Makefile.am5
-rw-r--r--benchmarks/Makefile.sources1
-rw-r--r--benchmarks/benchmarks.h11
-rw-r--r--benchmarks/calibration.c125
-rw-r--r--benchmarks/calibration.h45
-rw-r--r--benchmarks/gem.c226
-rw-r--r--benchmarks/gem.h53
-rw-r--r--benchmarks/host.c378
-rw-r--r--benchmarks/rrul.c735
-rw-r--r--benchmarks/rrul.yaml17
-rw-r--r--benchmarks/yaml_utils.c113
-rw-r--r--benchmarks/yaml_utils.h14
-rw-r--r--configure.ac1
-rw-r--r--lib/i915/gem_context.c4
15 files changed, 1726 insertions, 3 deletions
diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore
index 56daa1fd..92348229 100644
--- a/benchmarks/.gitignore
+++ b/benchmarks/.gitignore
@@ -20,4 +20,5 @@ intel_upload_blit_large_map
intel_upload_blit_small
kms_vblank
prime_lookup
+rrul
vgem_mmap
diff --git a/benchmarks/Makefile.am b/benchmarks/Makefile.am
index 055e4c3a..458d0e5a 100644
--- a/benchmarks/Makefile.am
+++ b/benchmarks/Makefile.am
@@ -15,6 +15,7 @@ AM_CPPFLAGS = \
AM_CFLAGS = -I$(top_srcdir)/include/drm-uapi \
$(DRM_CFLAGS) $(CWARNFLAGS) $(CAIRO_CFLAGS) $(LIBUNWIND_CFLAGS) \
+ $(YAML_CFLAGS) \
$(WERROR_CFLAGS) -D_GNU_SOURCE
LDADD = $(top_builddir)/lib/libintel_tools.la
@@ -28,6 +29,10 @@ gem_syslatency_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
gem_syslatency_LDADD = $(LDADD) -lpthread -lrt
gem_wsim_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la -lpthread
+rrul_SOURCES = host.c rrul.c calibration.c gem.c yaml_utils.c
+rrul_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
+rrul_LDADD = $(LDADD) $(YAML_LIBS) -lpthread -lrt
+
EXTRA_DIST= \
README \
meson.build \
diff --git a/benchmarks/Makefile.sources b/benchmarks/Makefile.sources
index d150035a..eec586dc 100644
--- a/benchmarks/Makefile.sources
+++ b/benchmarks/Makefile.sources
@@ -17,6 +17,7 @@ benchmarks_prog_list = \
gem_wsim \
kms_vblank \
prime_lookup \
+ rrul \
vgem_mmap \
$(NULL)
diff --git a/benchmarks/benchmarks.h b/benchmarks/benchmarks.h
new file mode 100644
index 00000000..45fe848b
--- /dev/null
+++ b/benchmarks/benchmarks.h
@@ -0,0 +1,11 @@
+#ifndef BENCHMARKS_H
+#define BENCHMARKS_H
+
+#include <stdio.h>
+
+typedef int (*benchmark_fn_t)(int, int);
+
+int rrul(int config, int results);
+
+#endif /* BENCHMARKS_H */
+
diff --git a/benchmarks/calibration.c b/benchmarks/calibration.c
new file mode 100644
index 00000000..fc29e474
--- /dev/null
+++ b/benchmarks/calibration.c
@@ -0,0 +1,125 @@
+#include <assert.h>
+#include <time.h>
+#include <yaml.h>
+
+#include "calibration.h"
+#include "gem.h"
+
+#include "i915_drm.h"
+
+#define MI_ARB_CHECK (0x05 << 23)
+#define MI_BATCH_BUFFER_END (0x0a << 23)
+
+uint32_t calibration_batch(int fd, unsigned long size)
+{
+ uint32_t handle, *batch;
+ unsigned long n;
+
+ size = ALIGN(size, 4096);
+ handle = gem_create(fd, size);
+
+ batch = gem_mmap(fd, handle, 0, size, PROT_WRITE, 0);
+ for (n = 0; n < size/sizeof(*batch) - 1; n++)
+ batch[n] = MI_ARB_CHECK;
+ batch[n] = MI_BATCH_BUFFER_END;
+ munmap(batch, size);
+
+ return handle;
+}
+
+static double elapsed(const struct timespec *start, const struct timespec *end)
+{
+ return (end->tv_sec - start->tv_sec) +
+ (end->tv_nsec - start->tv_nsec) / 1e9;
+}
+
+struct calibration calibrate_nops(int fd,
+ unsigned long target_us,
+ unsigned int tolerance_pct)
+{
+ struct drm_i915_gem_exec_object2 obj = {};
+ struct drm_i915_gem_execbuffer2 eb = {
+ .buffer_count = 1, .buffers_ptr = (uintptr_t)&obj
+ };
+ struct timespec t_0, t_end;
+ unsigned int loops = 17;
+ long size, last_size;
+
+ clock_gettime(CLOCK_MONOTONIC, &t_0);
+
+ size = 256 * 1024;
+ do {
+ struct timespec t_start;
+
+ obj.handle = calibration_batch(fd, size);
+
+ gem_execbuf(fd, &eb);
+ gem_wait(fd, obj.handle, NULL);
+
+ clock_gettime(CLOCK_MONOTONIC, &t_start);
+ for (int loop = 0; loop < loops; loop++)
+ gem_execbuf(fd, &eb);
+ gem_wait(fd, obj.handle, NULL);
+ clock_gettime(CLOCK_MONOTONIC, &t_end);
+
+ gem_close(fd, obj.handle);
+
+ last_size = size;
+ size = loops * size / elapsed(&t_start, &t_end) / 1e6 * target_us;
+ size = ALIGN(size, sizeof(uint32_t));
+ } while (elapsed(&t_0, &t_end) < 5 ||
+ abs(size - last_size) > ((size + last_size) * tolerance_pct / 200));
+
+
+ return (struct calibration){ target_us, (size + last_size)/2 };
+}
+
+static uint64_t node_to_u64(yaml_node_t *node)
+{
+ assert(node->type == YAML_SCALAR_NODE);
+ return strtoull((const char *)node->data.scalar.value, NULL, 0);
+}
+
+int parse_calibration(yaml_document_t *doc,
+ yaml_node_t *node,
+ struct calibration *calibration)
+{
+ calibration->duration = 1000; /* 1ms baseline by default */
+
+ switch (node->type) {
+ default:
+ case YAML_NO_NODE:
+ case YAML_SEQUENCE_NODE:
+ return -1;
+
+ case YAML_SCALAR_NODE:
+ calibration->bytes = node_to_u64(node);
+ break;
+
+ case YAML_MAPPING_NODE:
+ for (yaml_node_pair_t *it = node->data.mapping.pairs.start;
+ it < node->data.mapping.pairs.top;
+ it++) {
+ yaml_node_t *key, *value;
+
+ key = yaml_document_get_node(doc, it->key);
+ value = yaml_document_get_node(doc, it->value);
+
+ if (!strcmp(key->data.scalar.value, "duration")) {
+ calibration->duration = node_to_u64(value);
+ } else if (!strcmp(key->data.scalar.value, "bytes")) {
+ calibration->bytes = node_to_u64(value);
+ } else {
+ printf("Unknown calibration field: %s\n",
+ key->data.scalar.value);
+ return -1;
+ }
+ }
+ break;
+ }
+
+ if (!calibration->bytes || !calibration->duration)
+ return -1;
+
+ return 0;
+}
diff --git a/benchmarks/calibration.h b/benchmarks/calibration.h
new file mode 100644
index 00000000..0e53364e
--- /dev/null
+++ b/benchmarks/calibration.h
@@ -0,0 +1,45 @@
+#ifndef CALIBRATION_H
+#define CALIBRATION_H
+
+#include <stdint.h>
+
+#ifndef ALIGN
+#define ALIGN(x, y) (((x) + (y) - 1) & -(y))
+#endif
+
+struct yaml_document_s;
+struct yaml_node_s;
+
+struct calibration {
+ unsigned long duration;
+ unsigned long bytes;
+};
+
+int parse_calibration(struct yaml_document_s *doc,
+ struct yaml_node_s *node,
+ struct calibration *calibration);
+
+struct calibration
+calibrate_nops(int fd,
+ unsigned long target_us,
+ unsigned int tolerance_pct);
+
+static inline unsigned long
+calibrated_batch_size(const struct calibration *c,
+ unsigned long duration_us)
+{
+ return ALIGN(duration_us * c->bytes / c->duration, sizeof(uint32_t));
+}
+
+static inline unsigned long
+calibrated_batch_offset(const struct calibration *c,
+ unsigned long duration_us,
+ unsigned long batch_size)
+{
+ /* BUG_ON(calibrated_batch_size(c, duration_us) > batch_size); */
+ return (batch_size - calibrated_batch_size(c, duration_us) - 8) & -64;
+}
+
+uint32_t calibration_batch(int fd, unsigned long size);
+
+#endif /* CALIBRATION_H */
diff --git a/benchmarks/gem.c b/benchmarks/gem.c
new file mode 100644
index 00000000..2c162af3
--- /dev/null
+++ b/benchmarks/gem.c
@@ -0,0 +1,226 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <time.h>
+
+#include "gem.h"
+
+#include "i915_drm.h"
+
+#define MSEC_PER_SEC (1000)
+#define USEC_PER_SEC (1000 * MSEC_PER_SEC)
+#define NSEC_PER_SEC (1000 * USEC_PER_SEC)
+
+static inline long __sys_ioctl(int fd, unsigned long cmd, void *arg)
+{
+ long result;
+
+#if defined(__linux__) && defined(__GNUC__) && defined (__x86_64__)
+ __asm__ __volatile__
+ ("syscall"
+ : "=a" (result)
+ : "0" (__NR_ioctl), "D" (fd), "S" (cmd), "d" (arg)
+ : "cc", "rcx", "r11", "memory");
+#else
+ result = ioctl(fd, cmd, arg);
+ if (result == -1)
+ result = -errno;
+#endif
+
+ return result;
+}
+
+static inline long sys_ioctl(int fd, unsigned long cmd, void *arg)
+{
+ long result;
+
+ do {
+ result = __sys_ioctl(fd, cmd, arg);
+ } while (result == -EINTR || result == -EAGAIN);
+
+ return result;
+}
+
+static int __gem_create(int fd, uint64_t size, uint32_t *handle)
+{
+ struct drm_i915_gem_create create = {
+ .size = size,
+ };
+ int err;
+
+ err = sys_ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create);
+ if (err)
+ return err;
+
+ *handle = create.handle;
+ return 0;
+}
+
+uint32_t gem_create(int fd, uint64_t size)
+{
+ uint32_t handle = 0;
+
+ __gem_create(fd, size, &handle);
+
+ return handle;
+}
+
+void *gem_mmap(int fd, uint32_t handle,
+ uint64_t offset, uint64_t size,
+ unsigned prot, unsigned int flags)
+{
+ struct drm_i915_gem_mmap arg = {
+ .handle = handle,
+ .offset = offset,
+ .size = size,
+ .flags = flags,
+ };
+
+ if (sys_ioctl(fd, DRM_IOCTL_I915_GEM_MMAP, &arg))
+ return 0;
+
+ return (void *)(uintptr_t)arg.addr_ptr;
+}
+
+static int __gem_context_create(int fd, uint32_t *ctx)
+{
+ struct drm_i915_gem_context_create create = {};
+ int err;
+
+ err =sys_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_CREATE, &create);
+ if (err)
+ return err;
+
+ *ctx = create.ctx_id;
+ return 0;
+}
+
+uint32_t gem_context_create(int fd)
+{
+ uint32_t ctx = 0;
+
+ __gem_context_create(fd, &ctx);
+
+ return ctx;
+}
+
+static int __gem_context_set_param(int fd, struct drm_i915_gem_context_param *p)
+{
+ return sys_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM, p);
+}
+
+int gem_context_set_priority(int fd, uint32_t ctx, int prio)
+{
+ struct drm_i915_gem_context_param p = {
+ .ctx_id = ctx,
+ .param = I915_CONTEXT_PARAM_PRIORITY,
+ .value = prio,
+ };
+
+ return __gem_context_set_param(fd, &p);
+}
+
+int gem_execbuf(int fd, struct drm_i915_gem_execbuffer2 *execbuf)
+{
+ return sys_ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, execbuf);
+}
+
+int gem_execbuf_wr(int fd, struct drm_i915_gem_execbuffer2 *execbuf)
+{
+ return sys_ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, execbuf);
+}
+
+int gem_wait(int fd, uint32_t handle, int64_t *timeout_ns)
+{
+ struct drm_i915_gem_wait wait = {
+ .bo_handle = handle,
+ .timeout_ns = timeout_ns ? *timeout_ns : -1,
+ };
+ int err;
+
+ err = sys_ioctl(fd, DRM_IOCTL_I915_GEM_WAIT, &wait);
+
+ if (timeout_ns)
+ *timeout_ns = wait.timeout_ns;
+
+ return err;
+}
+
+void gem_close(int fd, uint32_t handle)
+{
+ sys_ioctl(fd, DRM_IOCTL_GEM_CLOSE, &handle);
+}
+
+static int __syncobj_create(int fd, uint32_t *handle, uint32_t flags)
+{
+ struct drm_syncobj_create create = {
+ .flags = flags,
+ };
+ int err;
+
+ err = sys_ioctl(fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);
+ if (err)
+ return err;
+
+ *handle = create.handle;
+ return 0;
+}
+
+struct syncobj *syncobj_create(int fd)
+{
+ struct syncobj *f;
+
+ f = malloc(sizeof(*f));
+ if (!f)
+ return NULL;
+
+ f->ref = 1;
+ if (__syncobj_create(fd, &f->handle, 0)) {
+ free(f);
+ return NULL;
+ }
+
+ return f;
+}
+
+static int __syncobj_wait(int fd, struct drm_syncobj_wait *arg)
+{
+ return sys_ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, arg);
+}
+
+static uint64_t abs_time_ns(long sec, long nsec)
+{
+ struct timespec tv;
+
+ clock_gettime(CLOCK_MONOTONIC, &tv);
+
+ tv.tv_sec += sec;
+ tv.tv_nsec += nsec;
+ if (tv.tv_nsec >= NSEC_PER_SEC) {
+ tv.tv_sec++;
+ tv.tv_nsec -= NSEC_PER_SEC;
+ }
+
+ return (uint64_t)tv.tv_sec * NSEC_PER_SEC + tv.tv_nsec;
+}
+
+int syncobj_wait(int fd, struct syncobj *f)
+{
+ struct drm_syncobj_wait wait = {
+ .handles = (uintptr_t)&f->handle,
+ .count_handles = 1,
+ };
+ int err = 0;
+ if (__syncobj_wait(fd, &wait)) {
+ wait.timeout_nsec = abs_time_ns(120, 0);
+ err = __syncobj_wait(fd, &wait);
+ }
+ return err;
+}
+
+void __syncobj_free(int fd, struct syncobj *f)
+{
+ sys_ioctl(fd, DRM_IOCTL_SYNCOBJ_DESTROY, &f->handle);
+ free(f);
+}
diff --git a/benchmarks/gem.h b/benchmarks/gem.h
new file mode 100644
index 00000000..cf1d35ad
--- /dev/null
+++ b/benchmarks/gem.h
@@ -0,0 +1,53 @@
+#ifndef GEM_H
+#define GEM_H
+
+#include <stdint.h>
+#include <sys/mman.h>
+
+uint32_t gem_create(int fd, uint64_t size);
+
+void *gem_mmap(int fd, uint32_t handle,
+ uint64_t offset, uint64_t size,
+ unsigned prot, unsigned int flags);
+
+uint32_t gem_context_create(int fd);
+int gem_context_set_priority(int fd, uint32_t ctx, int prio);
+
+struct drm_i915_gem_execbuffer2;
+int gem_execbuf(int fd, struct drm_i915_gem_execbuffer2 *execbuf);
+int gem_execbuf_wr(int fd, struct drm_i915_gem_execbuffer2 *execbuf);
+
+int gem_wait(int fd, uint32_t handle, int64_t *timeout_ns);
+
+static inline int gem_sync(int fd, uint32_t handle)
+{
+ return gem_wait(fd, handle, 0);
+}
+
+void gem_close(int fd, uint32_t handle);
+
+struct syncobj {
+ unsigned int ref;
+ uint32_t handle;
+};
+
+struct syncobj *syncobj_create(int fd);
+
+static inline struct syncobj *syncobj_get(struct syncobj *f)
+{
+ __sync_fetch_and_add(&f->ref, 1);
+ return f;
+}
+
+void __syncobj_free(int fd, struct syncobj *f);
+static inline void syncobj_put(int fd, struct syncobj *f)
+{
+ if (__sync_sub_and_fetch(&f->ref, 1))
+ return;
+
+ __syncobj_free(fd, f);
+}
+
+int syncobj_wait(int fd, struct syncobj *f);
+
+#endif /* GEM_H */
diff --git a/benchmarks/host.c b/benchmarks/host.c
new file mode 100644
index 00000000..357002d0
--- /dev/null
+++ b/benchmarks/host.c
@@ -0,0 +1,378 @@
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <wait.h>
+#include <yaml.h>
+
+#include "benchmarks.h"
+#include "yaml_utils.h"
+
+static const unsigned char __machines__[] =
+"# machine layout\n"
+"machine: &bdw\n"
+" address: 192.168.1.216\n"
+" command: hostname\n"
+"\n"
+;
+
+struct host {
+ const char *name;
+ pid_t pid;
+ int config;
+ int results;
+};
+
+struct state {
+ yaml_parser_t parser;
+
+ struct config {
+ struct host *host;
+ unsigned int nhost;
+ } config;
+};
+
+static int parse_config(yaml_document_t *doc, struct config *config)
+{
+ if (!yaml_document_get_root_node(doc))
+ return 0;
+
+ return 0;
+}
+
+static int fanout_write(void *data, unsigned char *buffer, size_t size)
+{
+ struct state *st = data;
+
+ for (unsigned int n = 0; n < st->config.nhost; n++) {
+ if (write(st->config.host[n].config, buffer, size) < 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int fanout(struct state *st)
+{
+ yaml_emitter_t emitter;
+ yaml_event_t ev;
+ int depth = 0;
+
+ yaml_emitter_initialize(&emitter);
+ yaml_emitter_set_output(&emitter, fanout_write, st);
+
+ yaml_stream_start_event_initialize(&ev, YAML_UTF8_ENCODING);
+ if (!yaml_emitter_emit(&emitter, &ev))
+ return -1;
+
+ yaml_document_start_event_initialize(&ev, NULL, NULL, NULL, 0);
+ if (!yaml_emitter_emit(&emitter, &ev))
+ return -1;
+
+ do {
+ if (!yaml_parser_parse(&st->parser, &ev)) {
+ yaml_print_parser_error(&st->parser, stderr);
+ return -1;
+ }
+
+ if (!yaml_emitter_emit(&emitter, &ev))
+ return -1;
+
+ switch (ev.type) {
+ case YAML_NO_EVENT:
+ case YAML_ALIAS_EVENT:
+ case YAML_SCALAR_EVENT:
+ break;
+
+ default:
+ case YAML_STREAM_START_EVENT:
+ case YAML_STREAM_END_EVENT:
+ case YAML_DOCUMENT_START_EVENT:
+ case YAML_DOCUMENT_END_EVENT:
+ abort();
+ break;
+
+ case YAML_SEQUENCE_START_EVENT:
+ case YAML_MAPPING_START_EVENT:
+ depth++;
+ break;
+
+ case YAML_SEQUENCE_END_EVENT:
+ case YAML_MAPPING_END_EVENT:
+ --depth;
+ break;
+ }
+ } while (depth);
+
+ yaml_document_end_event_initialize(&ev, 0);
+ yaml_emitter_emit(&emitter, &ev);
+
+ yaml_stream_end_event_initialize(&ev);
+ yaml_emitter_emit(&emitter, &ev);
+
+ yaml_emitter_flush(&emitter);
+ yaml_emitter_delete(&emitter);
+
+ return 0;
+}
+
+static benchmark_fn_t lookup_benchmark(const char *name)
+{
+ if (!strcmp(name, "rrul"))
+ return rrul;
+
+ return NULL;
+}
+
+static pid_t setup_localhost(struct host *host, benchmark_fn_t fn)
+{
+ int link_config[2], link_results[2];
+ pid_t pid;
+
+ pipe(link_config);
+ pipe(link_results);
+
+ switch ((pid = fork())) {
+ case -1:
+ return -1;
+
+ case 0:
+ close(link_config[1]);
+ close(link_results[0]);
+ exit(fn(link_config[0], link_results[1]));
+
+ default:
+ break;
+ }
+
+ host->config = link_config[1];
+ close(link_config[0]);
+
+ host->results = link_results[0];
+ close(link_results[1]);
+
+ host->pid = pid;
+
+ return pid;
+}
+
+static pid_t setup_remote(struct host *host, const char *test)
+{
+ return -1;
+}
+
+static int setup_test(struct state *st)
+{
+ yaml_event_t ev;
+ benchmark_fn_t fn;
+ const char *s;
+
+ if (!yaml_parser_parse(&st->parser, &ev))
+ return EXIT_FAILURE;
+
+ switch (ev.type) {
+ case YAML_SCALAR_EVENT:
+ break;
+
+ default:
+ return EXIT_FAILURE;
+ }
+
+ s = (const char *)ev.data.scalar.value;
+ fn = lookup_benchmark(s);
+ if (!fn) {
+ fprintf(stderr, "unknown test: %s\n", ev.data.scalar.value);
+ return EXIT_FAILURE;
+ }
+
+ for (unsigned int n = 0; n < st->config.nhost; n++) {
+ struct host *host = &st->config.host[0];
+
+ if (!host->name) {
+ if (setup_localhost(host, fn) < 0)
+ return EXIT_FAILURE;
+ } else {
+ if (setup_remote(host, s) < 0)
+ return EXIT_FAILURE;
+ }
+ }
+
+ yaml_event_delete(&ev);
+ return EXIT_SUCCESS;
+}
+
+static int parse_root(struct state *st)
+{
+ yaml_event_t ev;
+ const char *s;
+ bool done = false;
+ int ret = EXIT_FAILURE;
+
+ do {
+ if (!yaml_parser_parse(&st->parser, &ev))
+ goto err;
+
+ switch (ev.type) {
+ case YAML_MAPPING_END_EVENT:
+ ret = EXIT_SUCCESS;
+ goto out;
+
+ case YAML_SCALAR_EVENT:
+ break;
+
+ default:
+ goto err;
+ }
+
+ if (done)
+ goto err;
+
+ s = (char *)ev.data.scalar.value;
+ if (!strcmp(s, "test")) {
+ if (setup_test(st))
+ goto err;
+ } else if (!strcmp(s, "config")) {
+ if (fanout(st))
+ goto err;
+ done = true;
+ } else if (!strcmp(s, "results")) {
+ } else {
+ fprintf(stderr, "Unknown root key: %s\n", s);
+ goto err;
+ }
+
+ yaml_event_delete(&ev);
+ } while (1);
+
+out:
+ for(unsigned n = 0; n < st->config.nhost; n++)
+ close(st->config.host[n].config);
+
+ /* wait for tests to complete */
+ for(unsigned n = 0; n < st->config.nhost; n++) {
+ int status, err;
+
+ do {
+ err = 0;
+ if (waitpid(st->config.host[n].pid, &status, 0) < 0)
+ err = -errno;
+ } while (err == -EINTR);
+ }
+ return ret;
+
+err:
+ for(unsigned n = 0; n < st->config.nhost; n++)
+ kill(st->config.host[n].pid, SIGKILL);
+ goto out;
+}
+
+static int parse_doc(struct state *st)
+{
+ yaml_event_t ev;
+
+ do {
+ if (!yaml_parser_parse(&st->parser, &ev))
+ return EXIT_FAILURE;
+
+ switch (ev.type) {
+ case YAML_DOCUMENT_END_EVENT:
+ return EXIT_SUCCESS;
+
+ case YAML_MAPPING_START_EVENT:
+ break;
+
+ default:
+ return EXIT_FAILURE;
+ }
+ yaml_event_delete(&ev);
+
+ if (parse_root(st))
+ return EXIT_FAILURE;
+ } while (1);
+}
+
+static int parse_stream(struct state *st)
+{
+ yaml_event_t ev;
+
+ if (!yaml_parser_parse(&st->parser, &ev))
+ return EXIT_FAILURE;
+
+ assert(ev.type == YAML_STREAM_START_EVENT);
+ yaml_event_delete(&ev);
+
+ do {
+ if (!yaml_parser_parse(&st->parser, &ev))
+ return EXIT_FAILURE;
+
+ switch (ev.type) {
+ case YAML_DOCUMENT_START_EVENT:
+ break;
+
+ case YAML_STREAM_END_EVENT:
+ return EXIT_SUCCESS;
+
+ default:
+ return EXIT_FAILURE;
+ }
+ yaml_event_delete(&ev);
+
+ if (parse_doc(st))
+ return EXIT_FAILURE;
+ } while (1);
+
+ return 0;
+}
+
+static int load_machines(struct state *st, const char *filename)
+{
+ yaml_document_t doc;
+ int fd;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return EXIT_FAILURE;
+
+ if (yaml_load_document(fd, &doc))
+ return EXIT_FAILURE;
+
+ if (parse_config(&doc, &st->config))
+ return EXIT_FAILURE;
+
+ yaml_document_delete(&doc);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct state st = {};
+
+ if (argc == 2) {
+ benchmark_fn_t fn;
+
+ fn = lookup_benchmark(argv[1]);
+ if (!fn) {
+ fprintf(stderr, "unknown benchmark: %s\n", argv[1]);
+ return 1;
+ }
+
+ return fn(0, 1);
+ }
+
+ if (0) {
+ if (load_machines(&st, "machines"))
+ return EXIT_FAILURE;
+ }
+
+ if (!st.config.nhost) {
+ st.config.host = calloc(1, sizeof(*st.config.host));
+ st.config.nhost++;
+ }
+
+ yaml_parser_initialize(&st.parser);
+ yaml_parser_set_input(&st.parser, yaml_read_fd, 0);
+
+ exit(parse_stream(&st));
+}
diff --git a/benchmarks/rrul.c b/benchmarks/rrul.c
new file mode 100644
index 00000000..1ea9c611
--- /dev/null
+++ b/benchmarks/rrul.c
@@ -0,0 +1,735 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <pciaccess.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <yaml.h>
+#include <assert.h>
+
+#include "drm.h"
+#include "i915_drm.h"
+
+#include "benchmarks.h"
+#include "calibration.h"
+#include "gem.h"
+#include "yaml_utils.h"
+
+#define MSEC_PER_SEC (1000)
+#define USEC_PER_SEC (1000 * MSEC_PER_SEC)
+#define NSEC_PER_SEC (1000 * USEC_PER_SEC)
+
+#define MI_NOOP (0x00 << 23)
+#define MI_ARB_CHECK (0x05 << 23)
+#define MI_BATCH_BUFFER_END (0x0a << 23)
+#define MI_STORE_DWORD_IMM (0x20 << 23)
+#define MI_STORE_REG_MEM (0x24 << 23)
+#define MI_BATCH_BUFFER_START (0x31 << 23)
+
+#define RCS_TIMESTAMP (0x2000 + 0x358)
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+#define READ_ONCE(x) (*(volatile typeof(x) *)(&(x)))
+#define ALIGN(x, y) (((x) + (y) - 1) & -(y))
+
+struct probe_thread {
+ pthread_t thread;
+ struct probe_thread *next;
+
+ int i915;
+ int priority;
+
+ unsigned long clk_freq;
+ void *mmio;
+};
+
+struct client_thread {
+ pthread_t thread;
+ struct client_thread *next;
+
+ int i915;
+ int id;
+
+ int priority;
+
+ uint32_t work;
+ unsigned long duration;
+
+ struct flip_thread *flip;
+ enum vsync { NOFLIP, ASYNC, DOUBLE, TRIPLE } vsync;
+};
+
+struct flip_thread {
+ pthread_t thread;
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+
+ int i915;
+ uint32_t work;
+ unsigned long duration;
+ int priority;
+
+ int nclients;
+ struct syncobj **syncobj[2];
+ struct drm_i915_gem_exec_fence *array;
+ unsigned int serial;
+};
+
+struct state {
+ const char *device;
+ struct calibration calibration;
+
+ unsigned int n_clients;
+ unsigned long max_duration;
+
+ struct probe_thread *probes;
+ struct client_thread *clients;
+ struct flip_thread *flip;
+};
+
+static double elapsed(const struct timespec *start, const struct timespec *end)
+{
+ return (end->tv_sec - start->tv_sec) +
+ (end->tv_nsec - start->tv_nsec) / 1e9;
+}
+
+static int pageflip(int fd, uint32_t fence)
+{
+ union drm_wait_vblank vbl = {};
+
+ vbl.request.type = _DRM_VBLANK_RELATIVE;
+ vbl.request.type |= _DRM_VBLANK_NEXTONMISS;
+ vbl.request.type |= _DRM_VBLANK_SIGNAL;
+
+ return ioctl(fd, DRM_IOCTL_WAIT_VBLANK, &vbl);
+}
+
+static void *flip_thread(void *arg)
+{
+ struct flip_thread *f = arg;
+ struct drm_i915_gem_exec_object2 obj = {
+ .handle = f->work,
+ };
+ struct drm_i915_gem_execbuffer2 eb = {
+ .batch_start_offset = f->duration,
+ .buffers_ptr = (uintptr_t)&obj,
+ .buffer_count = 1,
+ .flags = I915_EXEC_NO_RELOC,
+ .rsvd1 = gem_context_create(f->i915),
+ };
+ struct timespec t[2];
+
+ /*
+ * Once every frame, gather up all the completed work,
+ * pretend to copy the object into our backbuffer and queue
+ * a flip.
+ */
+
+ eb.cliprects_ptr = (uintptr_t)f->array;
+ eb.flags |= I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT;
+ gem_context_set_priority(f->i915, eb.rsvd1, f->priority);
+
+ clock_gettime(CLOCK_MONOTONIC, &t[1]);
+ do {
+ struct syncobj **tmp;
+ char events[1024];
+ int fence;
+ int ret;
+
+ pthread_mutex_lock(&f->lock);
+
+ tmp = f->syncobj[0];
+ f->syncobj[0] = f->syncobj[1];
+ f->syncobj[1] = tmp;
+
+ f->serial++;
+ pthread_cond_broadcast(&f->cond);
+
+ pthread_mutex_unlock(&f->lock);
+
+ eb.num_cliprects = 0;
+ for (int n = 0; n < f->nclients; n++) {
+ if (!tmp[n])
+ continue;
+
+ f->array[eb.num_cliprects++].handle = tmp[n]->handle;
+ }
+ gem_execbuf_wr(f->i915, &eb);
+
+ fence = eb.rsvd2 >> 32;
+
+ ret = pageflip(f->i915, fence);
+ close(fence);
+
+ for (int n = 0; n < f->nclients; n++) {
+ if (!tmp[n])
+ continue;
+
+ syncobj_put(f->i915, tmp[n]);
+ tmp[n] = NULL;
+ }
+
+ if (ret == 0) {
+ read(f->i915, events, sizeof(events));
+ } else {
+ struct timespec tv;
+ int remaining_us;
+
+ gem_sync(f->i915, f->work);
+
+ clock_gettime(CLOCK_MONOTONIC, &tv);
+ remaining_us = 16*1000 - elapsed(&t[1], &tv)*1e6;
+ while (remaining_us < 0)
+ remaining_us += 16*1000;
+ usleep(remaining_us);
+ }
+
+ t[0] = t[1];
+ clock_gettime(CLOCK_MONOTONIC, &t[1]);
+
+ printf("Flip elapsed: %.2fms\n", elapsed(&t[0], &t[1])*1e3);
+ } while (1);
+
+ return NULL;
+}
+
+static void start_flip_thread(struct flip_thread *f,
+ int i915, int nclients,
+ uint32_t work, unsigned long size,
+ struct calibration *calibration)
+{
+ f->i915 = i915;
+ f->nclients = nclients;
+
+ f->work = work;
+ f->duration = calibrated_batch_offset(calibration, f->duration, size);
+
+ f->syncobj[0] = calloc(nclients, sizeof(*f->syncobj[0]));
+ f->syncobj[1] = calloc(nclients, sizeof(*f->syncobj[1]));
+
+ f->array = malloc(nclients * sizeof(*f->array));
+ for (int n = 0; n < nclients; n++)
+ f->array[n].flags = I915_EXEC_FENCE_WAIT;
+
+ pthread_create(&f->thread, NULL, flip_thread, f);
+}
+
+static unsigned int wait_for_flip(struct flip_thread *flip)
+{
+ unsigned int serial;
+
+ serial = flip->serial;
+ do
+ pthread_cond_wait(&flip->cond, &flip->lock);
+ while (serial == flip->serial);
+
+ return flip->serial;
+}
+
+static void *client_thread(void *arg)
+{
+ struct client_thread *c = arg;
+ struct drm_i915_gem_exec_fence fence_array = {
+ .flags = I915_EXEC_FENCE_SIGNAL,
+ };
+ struct drm_i915_gem_exec_object2 obj = {
+ .handle = c->work,
+ };
+ struct drm_i915_gem_execbuffer2 eb = {
+ .buffers_ptr = (uintptr_t)&obj,
+ .buffer_count = 1,
+ .batch_start_offset = c->duration,
+ .flags = I915_EXEC_FENCE_ARRAY | I915_EXEC_NO_RELOC,
+ .cliprects_ptr = (uintptr_t)&fence_array,
+ .num_cliprects = 1,
+ .rsvd1 = gem_context_create(c->i915),
+ };
+ struct flip_thread *flip = c->flip;
+ const enum vsync vsync = flip ? c->vsync : NOFLIP;
+ struct syncobj *syncobj[2] = {};
+ unsigned int seqno = 0;
+ int i915 = c->i915;
+
+ gem_context_set_priority(i915, eb.rsvd1, c->priority);
+
+ while (1) {
+ struct syncobj *f = syncobj_create(i915), *old;
+
+ fence_array.handle = f->handle;
+ gem_execbuf(i915, &eb);
+
+ if (vsync != NOFLIP) {
+ pthread_mutex_lock(&flip->lock);
+ old = flip->syncobj[0][c->id];
+ while (vsync == TRIPLE && old) {
+ wait_for_flip(flip);
+ old = flip->syncobj[0][c->id];
+ }
+ if (!old)
+ flip->syncobj[0][c->id] = syncobj_get(f);
+ if (vsync == DOUBLE)
+ wait_for_flip(flip);
+ pthread_mutex_unlock(&flip->lock);
+ }
+
+ old = syncobj[seqno % ARRAY_SIZE(syncobj)];
+ syncobj[seqno % ARRAY_SIZE(syncobj)] = f;
+ seqno++;
+
+ if (old) {
+ syncobj_wait(i915, old);
+ syncobj_put(i915, old);
+ }
+ }
+
+ return NULL;
+}
+
+static void start_client_thread(struct client_thread *c,
+ int i915, struct flip_thread *flip,
+ uint32_t work, unsigned long size,
+ struct calibration *calibration)
+{
+ c->i915 = i915;
+ c->flip = flip;
+
+ c->work = work;
+ c->duration = calibrated_batch_offset(calibration, c->duration, size);
+
+ pthread_create(&c->thread, NULL, client_thread, c);
+}
+
+static int wakeref_get(void)
+{
+ return open("/sys/kernel/debug/dri/0/i915_forcewake_user", O_RDONLY);
+}
+
+static void wakeref_put(int fw)
+{
+ close(fw);
+}
+
+static void *probe_thread(void *arg)
+{
+ struct probe_thread *probe = arg;
+ struct drm_i915_gem_exec_object2 obj = {
+ .handle = gem_create(probe->i915, 4096),
+ };
+ struct drm_i915_gem_execbuffer2 eb = {
+ .buffers_ptr = (uintptr_t)&obj,
+ .buffer_count = 1,
+ .flags = I915_EXEC_NO_RELOC,
+ .rsvd1 = gem_context_create(probe->i915),
+ };
+ uint32_t *timestamp = (uint32_t *)((char *)probe->mmio + RCS_TIMESTAMP);
+ uint32_t *batch;
+
+ gem_context_set_priority(probe->i915, eb.rsvd1, probe->priority);
+
+ batch = gem_mmap(probe->i915, obj.handle,
+ 0, 4096, PROT_WRITE, I915_MMAP_WC);
+ *batch = MI_BATCH_BUFFER_END;
+ gem_execbuf(probe->i915, &eb);
+
+ batch[16] = MI_STORE_REG_MEM | 2;
+ batch[17] = RCS_TIMESTAMP;
+ batch[18] = obj.offset;
+ batch[19] = obj.offset >> 32;
+ batch[20] = MI_BATCH_BUFFER_END;
+
+ obj.flags = EXEC_OBJECT_PINNED;
+ eb.batch_start_offset = 16 * sizeof(*batch);
+
+ do {
+ unsigned latency;
+ int fw;
+
+ fw = wakeref_get();
+ latency = -READ_ONCE(*timestamp);
+ gem_execbuf(probe->i915, &eb);
+ wakeref_put(fw);
+
+ gem_sync(probe->i915, obj.handle);
+
+ latency += READ_ONCE(batch[0]);
+ printf("Probe latency: %dus\n",
+ (int)((uint64_t)latency * USEC_PER_SEC / probe->clk_freq));
+
+ usleep(100000);
+ } while (1);
+
+ return NULL;
+}
+
+static void rtprio(pthread_attr_t *attr, int prio)
+{
+#ifdef PTHREAD_EXPLICIT_SCHED
+ struct sched_param param = { .sched_priority = 99 };
+
+ pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED);
+ pthread_attr_setschedpolicy(attr, SCHED_FIFO);
+ pthread_attr_setschedparam(attr, &param);
+#endif
+}
+
+static void start_probe_thread(struct probe_thread *probe,
+ int i915, void *mmio, unsigned long clk_freq)
+{
+ pthread_attr_t attr;
+
+ probe->i915 = i915;
+ probe->mmio = mmio;
+ probe->clk_freq = clk_freq;
+
+ pthread_attr_init(&attr);
+ rtprio(&attr, 99);
+
+ pthread_create(&probe->thread, NULL, probe_thread, probe);
+}
+
+static void *ioremap(int i915)
+{
+ struct pci_device *pci_dev;
+ void *mmio = NULL;
+
+ pci_system_init();
+
+ pci_dev = pci_device_find_by_slot(0, 0, 2, 0);
+ if (!pci_dev)
+ return NULL;
+
+ pci_device_probe(pci_dev);
+ pci_device_map_range(pci_dev,
+ pci_dev->regions[0].base_addr,
+ 2 << 20,
+ PCI_DEV_MAP_FLAG_WRITABLE,
+ &mmio);
+
+ return mmio;
+}
+
+static unsigned long measure_clk_freq(void *mmio)
+{
+ uint32_t *timestamp = (uint32_t *)((char *)mmio + RCS_TIMESTAMP);
+ struct timespec t_0, t_1;
+ unsigned long c_0, c_1;
+ int fw;
+
+ fw = wakeref_get();
+
+ c_0 = READ_ONCE(*timestamp);
+ clock_gettime(CLOCK_MONOTONIC, &t_0);
+ c_0 += READ_ONCE(*timestamp);
+
+ usleep(10000);
+
+ c_1 = READ_ONCE(*timestamp);
+ clock_gettime(CLOCK_MONOTONIC, &t_1);
+ c_1 += READ_ONCE(*timestamp);
+
+ wakeref_put(fw);
+
+ return (c_1 - c_0) / (2 * elapsed(&t_0, &t_1));
+}
+
+struct keymap {
+ const char *key;
+ int (*fn)(yaml_document_t *, yaml_node_t *, struct state *);
+};
+
+static int set_device(yaml_document_t *doc,
+ yaml_node_t *node,
+ struct state *st)
+{
+ assert(node->type == YAML_SCALAR_NODE);
+ st->device = node->data.scalar.value;
+ return 0;
+}
+
+static int set_calibration(yaml_document_t *doc,
+ yaml_node_t *node,
+ struct state *st)
+{
+ return parse_calibration(doc, node, &st->calibration);
+}
+
+static uint64_t node_to_u64(yaml_node_t *node)
+{
+ assert(node->type == YAML_SCALAR_NODE);
+ return strtoull((const char *)node->data.scalar.value, NULL, 0);
+}
+
+static int32_t node_to_s32(yaml_node_t *node)
+{
+ assert(node->type == YAML_SCALAR_NODE);
+ return strtol((const char *)node->data.scalar.value, NULL, 0);
+}
+
+static int add_probe(yaml_document_t *doc,
+ yaml_node_t *node,
+ struct state *st)
+{
+ struct probe_thread *probe;
+
+ probe = calloc(1, sizeof(*probe));
+ if (!probe)
+ return -ENOMEM;
+
+ switch (node->type) {
+ default:
+ case YAML_SEQUENCE_NODE:
+ return -1;
+
+ case YAML_SCALAR_NODE:
+ case YAML_NO_NODE:
+ break;
+
+ case YAML_MAPPING_NODE:
+ for (yaml_node_pair_t *it = node->data.mapping.pairs.start;
+ it < node->data.mapping.pairs.top;
+ it++) {
+ yaml_node_t *key, *value;
+
+ key = yaml_document_get_node(doc, it->key);
+ value = yaml_document_get_node(doc, it->value);
+
+ if (!strcmp(key->data.scalar.value, "priority")) {
+ probe->priority = node_to_s32(value);
+ } else {
+ printf("Unknown probe field: %s, value %s\n",
+ key->data.scalar.value,
+ value->data.scalar.value);
+ return -ENOENT;
+ }
+ }
+ break;
+ }
+
+ probe->next = st->probes;
+ st->probes = probe;
+
+ return 0;
+}
+
+static int set_flip(yaml_document_t *doc,
+ yaml_node_t *node,
+ struct state *st)
+{
+ struct flip_thread *flip;
+
+ if (st->flip)
+ return -EBUSY;
+
+ flip = calloc(1, sizeof(*flip));
+ if (!flip)
+ return -ENOMEM;
+
+ pthread_mutex_init(&flip->lock, NULL);
+ pthread_cond_init(&flip->cond, NULL);
+
+ switch (node->type) {
+ default:
+ case YAML_SEQUENCE_NODE:
+ return -1;
+
+ case YAML_SCALAR_NODE:
+ case YAML_NO_NODE:
+ break;
+
+ case YAML_MAPPING_NODE:
+ for (yaml_node_pair_t *it = node->data.mapping.pairs.start;
+ it < node->data.mapping.pairs.top;
+ it++) {
+ yaml_node_t *key, *value;
+
+ key = yaml_document_get_node(doc, it->key);
+ value = yaml_document_get_node(doc, it->value);
+
+ if (!strcmp(key->data.scalar.value, "priority")) {
+ flip->priority = node_to_s32(value);
+ } else if (!strcmp(key->data.scalar.value, "duration")) {
+ flip->duration = node_to_u64(value);
+ } else {
+ printf("Unknown flip field: %s, value %s\n",
+ key->data.scalar.value,
+ value->data.scalar.value);
+ return -ENOENT;
+ }
+ }
+ break;
+ }
+
+ st->flip = flip;
+ if (flip->duration > st->max_duration)
+ st->max_duration = flip->duration;
+
+ return 0;
+}
+
+static int add_clients(yaml_document_t *doc,
+ yaml_node_t *node,
+ struct state *st)
+{
+ unsigned long count = 1;
+ uint64_t duration = 0;
+ unsigned int vsync = 0;
+ int priority = 0;
+
+ switch (node->type) {
+ default:
+ case YAML_SEQUENCE_NODE:
+ for (yaml_node_item_t *it = node->data.sequence.items.start;
+ it < node->data.sequence.items.top;
+ it++)
+ add_clients(doc, yaml_document_get_node(doc, *it), st);
+ return 0;
+
+ case YAML_SCALAR_NODE:
+ case YAML_NO_NODE:
+ return 0;
+
+ case YAML_MAPPING_NODE:
+ for (yaml_node_pair_t *it = node->data.mapping.pairs.start;
+ it < node->data.mapping.pairs.top;
+ it++) {
+ yaml_node_t *key, *value;
+
+ key = yaml_document_get_node(doc, it->key);
+ value = yaml_document_get_node(doc, it->value);
+
+ if (!strcmp(key->data.scalar.value, "priority")) {
+ priority = node_to_s32(value);
+ } else if (!strcmp(key->data.scalar.value, "duration")) {
+ duration = node_to_u64(value);
+ } else if (!strcmp(key->data.scalar.value, "count")) {
+ count = node_to_u64(value);
+ } else if (!strcmp(key->data.scalar.value, "vsync")) {
+ vsync = node_to_u64(value);
+ } else {
+ printf("Unknown client field: %s, value %s\n",
+ key->data.scalar.value,
+ value->data.scalar.value);
+ return -ENOENT;
+ }
+ }
+ break;
+ }
+
+ for (unsigned long n = 0; n < count; n++) {
+ struct client_thread *client;
+
+ client = calloc(1, sizeof(*client));
+ if (!client)
+ return -ENOMEM;
+
+ client->duration = duration;
+ client->priority = priority;
+ client->vsync = vsync;
+
+ client->next = st->clients;
+ st->clients = client;
+ client->id = st->n_clients++;
+ }
+
+ if (duration > st->max_duration)
+ st->max_duration = duration;
+
+ return 0;
+}
+
+static int parse_config(yaml_document_t *doc, struct state *st)
+{
+ static const struct keymap keymap[] = {
+ { "calibration", set_calibration },
+ { "clients", add_clients },
+ { "flip", set_flip },
+ { "device", set_device },
+ { "probe", add_probe },
+ };
+ yaml_node_t *node = yaml_document_get_root_node(doc);
+
+ if (!node)
+ return 0;
+
+ if (node->type != YAML_MAPPING_NODE)
+ return -1;
+
+ for (yaml_node_pair_t *it = node->data.mapping.pairs.start;
+ it < node->data.mapping.pairs.top; it++) {
+ yaml_node_t *key = yaml_document_get_node(doc, it->key);
+ yaml_node_t *value = yaml_document_get_node(doc, it->value);
+ for (int n = 0; n < ARRAY_SIZE(keymap); n++) {
+ const struct keymap *k = &keymap[n];
+
+ if (strcmp(k->key, key->data.scalar.value))
+ continue;
+
+ k->fn(doc, value, st);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int execute(struct state *st)
+{
+ unsigned long size;
+ unsigned long clk_freq;
+ uint32_t work;
+ void *mmio;
+ int i915;
+
+ i915 = open(st->device, O_RDWR);
+ if (i915 < 0)
+ return 77;
+
+ if (st->calibration.bytes == 0)
+ st->calibration = calibrate_nops(i915, 1000, 1);
+
+ size = calibrated_batch_size(&st->calibration, st->max_duration);
+ size = size ? ALIGN(size, 4096) : 4096;
+ work = calibration_batch(i915, size);
+
+ mmio = ioremap(i915);
+ if (!mmio) {
+ fprintf(stderr, "No mmio :(\n");
+ return 77;
+ }
+ clk_freq = measure_clk_freq(mmio);
+
+ if (st->flip)
+ start_flip_thread(st->flip, i915, st->n_clients,
+ work, size, &st->calibration);
+
+ for (struct probe_thread *p = st->probes; p; p = p->next)
+ start_probe_thread(p, i915, mmio, clk_freq);
+
+ for (struct client_thread *c = st->clients; c; c = c->next)
+ start_client_thread(c, i915, st->flip,
+ work, size, &st->calibration);
+
+ pause();
+ return 0;
+}
+
+int rrul(int config, int results)
+{
+ struct state st = { .device = "/dev/dri/card0" };
+ yaml_document_t doc;
+
+ if (yaml_load_document(config, &doc))
+ return EXIT_FAILURE;
+
+ if (parse_config(&doc, &st))
+ return EXIT_FAILURE;
+
+ return execute(&st);
+}
diff --git a/benchmarks/rrul.yaml b/benchmarks/rrul.yaml
new file mode 100644
index 00000000..ae96a98a
--- /dev/null
+++ b/benchmarks/rrul.yaml
@@ -0,0 +1,17 @@
+test: rrul
+config:
+ device: /dev/dri/card0
+ calibration: {duration: 1000, bytes: 704878}
+ probe:
+ clients:
+ - duration: 2000 # microseconds, single background task
+ - duration: 200 # 5000fps in microseconds
+ vsync: 3 # triple buffered
+ count: 64
+ - duration: 200
+ vsync: 1 # async flips
+ flip:
+ duration: 250 # 400fps in microseconds (approx. fullscreen composite)
+#results:
+# - machine:
+# date:
diff --git a/benchmarks/yaml_utils.c b/benchmarks/yaml_utils.c
new file mode 100644
index 00000000..a1a0a0d4
--- /dev/null
+++ b/benchmarks/yaml_utils.c
@@ -0,0 +1,113 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "yaml_utils.h"
+
+void yaml_print_parser_error(yaml_parser_t *parser, FILE *stream)
+{
+ switch (parser->error) {
+ case YAML_MEMORY_ERROR:
+ fprintf(stderr, "Memory error: Not enough memory for parsing\n");
+ break;
+
+ case YAML_READER_ERROR:
+ if (parser->problem_value != -1) {
+ fprintf(stderr, "Reader error: %s: #%X at %zd\n", parser->problem,
+ parser->problem_value, parser->problem_offset);
+ } else {
+ fprintf(stderr, "Reader error: %s at %zd\n", parser->problem,
+ parser->problem_offset);
+ }
+ break;
+
+ case YAML_SCANNER_ERROR:
+ if (parser->context) {
+ fprintf(stderr, "Scanner error: %s at line %lu, column %lu\n"
+ "%s at line %lu, column %lu\n", parser->context,
+ parser->context_mark.line+1, parser->context_mark.column+1,
+ parser->problem, parser->problem_mark.line+1,
+ parser->problem_mark.column+1);
+ } else {
+ fprintf(stderr, "Scanner error: %s at line %lu, column %lu\n",
+ parser->problem, parser->problem_mark.line+1,
+ parser->problem_mark.column+1);
+ }
+ break;
+
+ case YAML_PARSER_ERROR:
+ if (parser->context) {
+ fprintf(stderr, "Parser error: %s at line %lu, column %lu\n"
+ "%s at line %lu, column %lu\n", parser->context,
+ parser->context_mark.line+1, parser->context_mark.column+1,
+ parser->problem, parser->problem_mark.line+1,
+ parser->problem_mark.column+1);
+ } else {
+ fprintf(stderr, "Parser error: %s at line %lu, column %lu\n",
+ parser->problem, parser->problem_mark.line+1,
+ parser->problem_mark.column+1);
+ }
+ break;
+
+ default:
+ /* Couldn't happen. */
+ fprintf(stderr, "Internal error\n");
+ break;
+ }
+}
+
+int yaml_read_fd(void *data, unsigned char *buffer, size_t size, size_t *out)
+{
+ int fd = (intptr_t)data;
+ ssize_t ret;
+
+ ret = read(fd, buffer, size);
+ if (ret < 0)
+ return 0;
+
+ *out = ret;
+ return 1;
+}
+
+int yaml_write_fd(void *data, unsigned char *buffer, size_t size)
+{
+ int fd = (intptr_t)data;
+ size_t total = 0;
+ ssize_t ret;
+
+ do {
+ ret = write(fd, buffer + total, size - total);
+ if (ret < 0)
+ ret = -errno;
+ if (ret == -EINTR || ret == -EAGAIN)
+ continue;
+ if (ret <= 0)
+ return 0;
+
+ total += ret;
+ } while (total != size);
+
+ return 1;
+}
+
+int yaml_load_document(int fd, yaml_document_t *doc)
+{
+ yaml_parser_t parser;
+ int err = 0;
+
+ if (!yaml_parser_initialize(&parser)) {
+ fprintf(stderr, "Failed to initialize YAML parser\n");
+ return -1;
+ }
+
+ yaml_parser_set_input(&parser, yaml_read_fd, (void *)(intptr_t)fd);
+
+ if (!yaml_parser_load(&parser, doc)) {
+ yaml_print_parser_error(&parser, stderr);
+ err = -1;
+ }
+
+ yaml_parser_delete(&parser);
+ return err;
+}
diff --git a/benchmarks/yaml_utils.h b/benchmarks/yaml_utils.h
new file mode 100644
index 00000000..19d0be81
--- /dev/null
+++ b/benchmarks/yaml_utils.h
@@ -0,0 +1,14 @@
+#ifndef YAML_UTILS_H
+#define YAML_UTILS_H
+
+#include <stdio.h>
+#include <yaml.h>
+
+void yaml_print_parser_error(yaml_parser_t *parser, FILE *stream);
+
+int yaml_read_fd(void *data, unsigned char *buffer, size_t size, size_t *out);
+int yaml_write_fd(void *data, unsigned char *buffer, size_t size);
+
+int yaml_load_document(int fd, yaml_document_t *doc);
+
+#endif /* YAML_UTILS_H */
diff --git a/configure.ac b/configure.ac
index c5ee78bb..88da8eed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -126,6 +126,7 @@ PKG_CHECK_MODULES(KMOD, [libkmod])
PKG_CHECK_MODULES(PROCPS, [libprocps])
PKG_CHECK_MODULES(LIBUNWIND, [libunwind])
PKG_CHECK_MODULES(VALGRIND, [valgrind], [have_valgrind=yes], [have_valgrind=no])
+PKG_CHECK_MODULES(YAML, [yaml-0.1])
if test x$have_valgrind = xyes; then
AC_DEFINE(HAVE_VALGRIND, 1, [Enable valgrind annotation support.])
diff --git a/lib/i915/gem_context.c b/lib/i915/gem_context.c
index 02cf2ccf..afba5075 100644
--- a/lib/i915/gem_context.c
+++ b/lib/i915/gem_context.c
@@ -234,8 +234,6 @@ void gem_context_require_bannable(int fd)
igt_require(has_ban_period || has_bannable);
}
-#define DRM_I915_CONTEXT_PARAM_PRIORITY 0x6
-
/**
* __gem_context_set_priority:
* @fd: open i915 drm file descriptor
@@ -255,7 +253,7 @@ int __gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
memset(&p, 0, sizeof(p));
p.ctx_id = ctx_id;
p.size = 0;
- p.param = DRM_I915_CONTEXT_PARAM_PRIORITY;
+ p.param = I915_CONTEXT_PARAM_PRIORITY;
p.value = prio;
return __gem_context_set_param(fd, &p);