summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2019-03-28 12:31:28 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2019-03-29 08:28:10 +0000
commitdf371f8cda82d37ddb6beeafb3938d62cc5b9c68 (patch)
treeb95505d16b9a646ae2e8b42e2b81d669d9ca645c
parentdf92af3b58b7806a66c6123c66c92998edfb0508 (diff)
-rw-r--r--include/drm-uapi/ugpu.h70
-rw-r--r--tests/ugpu/meson.build2
-rw-r--r--tests/ugpu/ugpu_basic.c80
-rw-r--r--tests/ugpu/ugpu_latency.c214
4 files changed, 365 insertions, 1 deletions
diff --git a/include/drm-uapi/ugpu.h b/include/drm-uapi/ugpu.h
index 1e713683..bd6d7ce9 100644
--- a/include/drm-uapi/ugpu.h
+++ b/include/drm-uapi/ugpu.h
@@ -17,4 +17,74 @@ struct ugpu_query {
#define UGPU_IOCTL_SUBMIT _IO(UGPU_IOCTL_BASE, 2)
+struct ugpu_cs_hdr {
+ __u8 cmd;
+#define __UGPU_CS_NOP 0
+
+#define __UGPU_CS_MAP 1
+#define __UGPU_CS_UNMAP 2
+
+#define __UGPU_CS_WAIT 3
+#define __UGPU_CS_SIGNAL 4
+#define __UGPU_CS_WAKE 5
+
+#define __UGPU_CS_SUBMIT 6
+ __u8 len;
+ __u16 flags;
+};
+#define __UGPU_CS_HDR(cmd, t, flags) { cmd, sizeof(t) / 4, flags }
+
+#define UGPU_CS_NOP __UGPU_CS_HDR(__UGPU_CS_NOP, struct ugpu_cs_hdr, 0)
+#define UGPU_CS_UNPARK __UGPU_CS_HDR(__UGPU_CS_NOP, struct ugpu_cs_hdr, 1)
+#define UGPU_CS_PARK __UGPU_CS_HDR(__UGPU_CS_NOP, struct ugpu_cs_hdr, 2)
+
+struct ugpu_cs_map {
+ struct ugpu_cs_hdr hdr;
+ __s32 fd;
+ __u64 iova;
+ __u64 offset;
+ __u64 length;
+} __attribute__((packed));
+#define UGPU_CS_MAP __UGPU_CS_HDR(__UGPU_CS_MAP, struct ugpu_cs_map, 0)
+
+struct ugpu_cs_unmap {
+ struct ugpu_cs_hdr hdr;
+ __u64 iova;
+ __u64 length;
+} __attribute__((packed));
+#define UGPU_CS_UNMAP __UGPU_CS_HDR(__UGPU_CS_UNMAP, struct ugpu_cs_unmap, 0)
+
+struct ugpu_cs_wait {
+ struct ugpu_cs_hdr hdr;
+ __u64 pointer;
+ __u64 value;
+} __attribute__((packed));
+#define UGPU_CS_WAIT(flags) \
+ __UGPU_CS_HDR(__UGPU_CS_WAIT, struct ugpu_cs_wait, flags)
+
+struct ugpu_cs_signal {
+ struct ugpu_cs_hdr hdr;
+ __u64 pointer;
+ __u64 value;
+} __attribute__((packed));
+#define UGPU_CS_SIGNAL(flags) \
+ __UGPU_CS_HDR(__UGPU_CS_SIGNAL, struct ugpu_cs_signal, flags)
+
+struct ugpu_cs_wake {
+ struct ugpu_cs_hdr hdr;
+ __u64 iova;
+ __u64 value;
+ __u64 cookie;
+} __attribute__((packed));
+#define UGPU_CS_WAKE(flags) \
+ __UGPU_CS_HDR(__UGPU_CS_WAKE, struct ugpu_cs_wake, flags)
+
+struct ugpu_cs_submit {
+ struct ugpu_cs_hdr hdr;
+ __u64 iova;
+ __u64 extensions;
+} __attribute__((packed));
+#define UGPU_CS_SUBMIT(flags) \
+ __UGPU_CS_HDR(__UGPU_CS_SUBMIT, struct ugpu_cs_submit, flags)
+
#endif
diff --git a/tests/ugpu/meson.build b/tests/ugpu/meson.build
index e9300575..d26734e4 100644
--- a/tests/ugpu/meson.build
+++ b/tests/ugpu/meson.build
@@ -1,6 +1,6 @@
ugpu_deps = test_deps
-ugpu_progs = [ 'ugpu_basic' ]
+ugpu_progs = [ 'ugpu_basic', 'ugpu_latency' ]
foreach prog : ugpu_progs
test_executables += executable(prog, prog + '.c',
diff --git a/tests/ugpu/ugpu_basic.c b/tests/ugpu/ugpu_basic.c
index 6d506b07..d2ea9c2d 100644
--- a/tests/ugpu/ugpu_basic.c
+++ b/tests/ugpu/ugpu_basic.c
@@ -1,3 +1,5 @@
+#include "ugpu.h"
+
#include "igt.h"
#include "igt_core.h"
@@ -6,6 +8,64 @@ static int ugpu_open(int i915)
return igt_ioctl(i915, DRM_IOCTL_I915_UGPU, 0);
}
+static int __ugpu_get(int fd, struct ugpu_query *q)
+{
+ int err;
+
+ err = 0;
+ if (igt_ioctl(fd, UGPU_IOCTL_GET, q)) {
+ err = -errno;
+ igt_assume(err);
+ }
+
+ return err;
+}
+
+static int __ugpu_set(int fd, struct ugpu_query *q)
+{
+ int err;
+
+ err = 0;
+ if (igt_ioctl(fd, UGPU_IOCTL_SET, q)) {
+ err = -errno;
+ igt_assume(err);
+ }
+
+ return err;
+}
+
+static int __ugpu_set_command_ring(int fd, uint32_t sz)
+{
+ struct ugpu_query q = {
+ .property = 0,
+ .value = sz,
+ };
+
+ return __ugpu_set(fd, &q);
+}
+
+static void ugpu_set_command_ring(int fd, uint32_t sz)
+{
+ igt_assert_eq(__ugpu_set_command_ring(fd, sz), 0);
+}
+
+static void igt_ugpu_name(int i915)
+{
+ int fd = ugpu_open(i915);
+ char name[128];
+
+ struct ugpu_query q = {
+ .property = 0,
+ .value = to_user_pointer(name),
+ .length = sizeof(name),
+ };
+ igt_assert_eq(__ugpu_get(fd, &q), 0);
+
+ igt_info("ugpu queue '%s'\n", name);
+
+ close(fd);
+}
+
igt_main {
int i915;
@@ -15,4 +75,24 @@ igt_main {
igt_subtest("openclose")
close(ugpu_open(i915));
+
+ igt_subtest("name")
+ igt_ugpu_name(i915);
+
+ igt_subtest("mmap") {
+ int fd = ugpu_open(i915);
+ void *ctl, *mem;
+
+ ugpu_set_command_ring(fd, 4096);
+
+ ctl = mmap(NULL, 4096, PROT_WRITE, MAP_PRIVATE, fd, 0);
+ igt_assert(ctl != MAP_FAILED);
+ munmap(ctl, 4096);
+
+ mem = mmap(NULL, 4096, PROT_WRITE, MAP_PRIVATE, fd, 4096);
+ igt_assert(mem != MAP_FAILED);
+ munmap(mem, 4096);
+
+ close(fd);
+ }
}
diff --git a/tests/ugpu/ugpu_latency.c b/tests/ugpu/ugpu_latency.c
new file mode 100644
index 00000000..1d01427c
--- /dev/null
+++ b/tests/ugpu/ugpu_latency.c
@@ -0,0 +1,214 @@
+#include "ugpu.h"
+
+#include "igt.h"
+#include "igt_core.h"
+#include "igt_device.h"
+#include "igt_stats.h"
+
+static int ugpu_open(int i915)
+{
+ return igt_ioctl(i915, DRM_IOCTL_I915_UGPU, 0);
+}
+
+struct ugpu_queue {
+ int fd;
+ struct {
+ uint32_t write;
+ uint32_t read;
+ } *ctl;
+ uint32_t *mem;
+ uint32_t size;
+};
+
+static int __ugpu_set(int fd, struct ugpu_query *q)
+{
+ int err;
+
+ err = 0;
+ if (igt_ioctl(fd, UGPU_IOCTL_SET, q)) {
+ err = -errno;
+ igt_assume(err);
+ }
+
+ return err;
+}
+
+static int __ugpu_set_command_ring(int fd, uint32_t sz)
+{
+ struct ugpu_query q = {
+ .property = 0,
+ .value = sz,
+ };
+
+ return __ugpu_set(fd, &q);
+}
+
+static void ugpu_set_command_ring(int fd, uint32_t sz)
+{
+ igt_assert_eq(__ugpu_set_command_ring(fd, sz), 0);
+}
+
+static struct ugpu_queue *ugpu_queue_create(int fd)
+{
+ struct ugpu_queue *q;
+
+ q = malloc(sizeof(*q));
+ if (!q)
+ return NULL;
+
+ q->fd = fd;
+ q->size = 64 << 10;
+
+ ugpu_set_command_ring(fd, q->size);
+
+ q->ctl = mmap(NULL, 4096, PROT_WRITE, MAP_PRIVATE, fd, 0);
+ igt_assert(q->ctl != MAP_FAILED);
+
+ q->mem = mmap(NULL, q->size, PROT_WRITE, MAP_PRIVATE, fd, 4096);
+ igt_assert(q->mem != MAP_FAILED);
+
+ igt_assert_eq(q->ctl->write, 0);
+ igt_assert_eq(q->ctl->read, 0);
+
+ return q;
+}
+
+static void ugpu_queue_destroy(struct ugpu_queue *q)
+{
+ close(q->fd);
+ free(q);
+}
+
+static int i915_allocate_dmabuf(int i915, uint64_t sz)
+{
+ uint32_t handle;
+ int buf;
+
+ handle = gem_create(i915, sz);
+ buf = prime_handle_to_fd_for_mmap(i915, handle);
+ gem_close(i915, handle);
+
+ return buf;
+}
+
+static void ugpu_queue_map(struct ugpu_queue *q, uint64_t addr,
+ int buf, uint64_t offset, uint64_t length)
+{
+ struct ugpu_cs_map pkt = {
+ .hdr = UGPU_CS_MAP,
+ .iova = addr,
+ .offset = offset,
+ .length = length,
+ .fd = buf,
+ };
+ igt_assert(write(q->fd, &pkt, sizeof(pkt)) == sizeof(pkt));
+}
+
+static void ugpu_queue_unpark(struct ugpu_queue *q)
+{
+ struct ugpu_cs_hdr pkt = UGPU_CS_UNPARK;
+ igt_assert(write(q->fd, &pkt, sizeof(pkt)) == sizeof(pkt));
+}
+
+static void ugpu_queue_submit(struct ugpu_queue *q, uint64_t addr)
+{
+ struct ugpu_cs_submit pkt = {
+ .hdr = UGPU_CS_SUBMIT(0),
+ .iova = addr,
+ };
+ igt_assert(write(q->fd, &pkt, sizeof(pkt)) == sizeof(pkt));
+}
+
+static double clockrate(int i915)
+{
+ int freq;
+ drm_i915_getparam_t gp = {
+ .value = &freq,
+ .param = I915_PARAM_CS_TIMESTAMP_FREQUENCY,
+ };
+ igt_require(igt_ioctl(i915, DRM_IOCTL_I915_GETPARAM, &gp) == 0);
+ return freq;
+}
+
+igt_main {
+ int i915;
+
+ igt_fixture {
+ i915 = drm_open_driver(DRIVER_INTEL);
+ };
+
+ igt_subtest("write-execution-latency") {
+ const unsigned int mmio_base = 0x2000;
+ const unsigned int cs_timestamp = mmio_base + 0x358;
+ volatile uint32_t *timestamp;
+ uint32_t *cs, *result;
+ struct igt_mean submit, batch;
+ struct ugpu_queue *q;
+ double rcs_clock;
+ int handle;
+
+ intel_register_access_init(igt_device_get_pci_device(i915), false, i915);
+ timestamp =
+ (volatile uint32_t *)((volatile char *)igt_global_mmio + cs_timestamp);
+
+ rcs_clock = clockrate(i915);
+ igt_info("RCS timestamp clock: %.0fKHz, %.1fns\n",
+ rcs_clock / 1e3, 1e9 / rcs_clock);
+ rcs_clock = 1e9 / rcs_clock;
+
+ q = ugpu_queue_create(ugpu_open(i915));
+
+ handle = i915_allocate_dmabuf(i915, 4096);
+
+ result = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, handle, 0);
+ igt_assert(result != MAP_FAILED);
+
+ cs = result;
+ *cs++ = 0x24 << 23 | 2; /* SRM */
+ *cs++ = cs_timestamp;
+ *cs++ = 4096 - 8;
+ *cs++ = 0;
+ *cs++ = 0xa << 23;
+
+ cs = result + 16;
+ *cs++ = 0x24 << 23 | 2; /* SRM */
+ *cs++ = cs_timestamp;
+ *cs++ = 4096 - 4;
+ *cs++ = 0;
+ *cs++ = 0xa << 23;
+
+ ugpu_queue_map(q, 0, handle, 0, 4096);
+ close(handle);
+
+ igt_mean_init(&submit);
+ igt_mean_init(&batch);
+
+ cs = result + 1024 - 2;
+
+ ugpu_queue_unpark(q);
+ igt_until_timeout(2) {
+ uint32_t now;
+
+ cs[1] = 0;
+
+ now = *timestamp;
+ ugpu_queue_submit(q, 0);
+ ugpu_queue_submit(q, 64);
+
+ while (!((volatile uint32_t *)cs)[1])
+ ;
+
+ igt_mean_add(&submit, (cs[0] - now) * rcs_clock);
+ igt_mean_add(&batch, (cs[1] - cs[0]) * rcs_clock);
+ }
+ ugpu_queue_destroy(q);
+
+ igt_info("Submission latency: %.2f±%.2fus\n",
+ 1e-3 * igt_mean_get(&submit),
+ 1e-3 * sqrt(igt_mean_get_variance(&submit)));
+
+ igt_info("Inter-batch latency: %.2f±%.2fus\n",
+ 1e-3 * igt_mean_get(&batch),
+ 1e-3 * sqrt(igt_mean_get_variance(&batch)));
+ }
+}