* [PATCH i-g-t 0/6] Queued/runnable/running engine stats
@ 2018-06-06 12:49 Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 1/6] include: i915 uAPI headers Tvrtko Ursulin
` (5 more replies)
0 siblings, 6 replies; 10+ messages in thread
From: Tvrtko Ursulin @ 2018-06-06 12:49 UTC (permalink / raw)
To: igt-dev; +Cc: intel-gfx
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Tests for new PMU counters, new query and adding support to intel_gpu_top and
intel_gpu_overlay to show queue depths and GPU load average metric.
Tvrtko Ursulin (6):
include: i915 uAPI headers
intel-gpu-overlay: Add engine queue stats
intel-gpu-overlay: Show 1s, 30s and 15m GPU load
tests/perf_pmu: Add tests for engine queued/runnable/running stats
intel-gpu-top: Add queue depths and load average
tests/i915_query: Engine queues tests
include/drm-uapi/i915_drm.h | 19 +-
overlay/gpu-top.c | 81 ++++++-
overlay/gpu-top.h | 22 +-
overlay/overlay.c | 35 ++-
tests/i915_query.c | 442 ++++++++++++++++++++++++++++++++++++
tests/perf_pmu.c | 258 +++++++++++++++++++++
tools/Makefile.am | 2 +-
tools/intel_gpu_top.c | 122 ++++++++--
tools/meson.build | 2 +-
9 files changed, 958 insertions(+), 25 deletions(-)
--
2.17.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH i-g-t 1/6] include: i915 uAPI headers
2018-06-06 12:49 [PATCH i-g-t 0/6] Queued/runnable/running engine stats Tvrtko Ursulin
@ 2018-06-06 12:49 ` Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 2/6] intel-gpu-overlay: Add engine queue stats Tvrtko Ursulin
` (4 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Tvrtko Ursulin @ 2018-06-06 12:49 UTC (permalink / raw)
To: igt-dev; +Cc: intel-gfx
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Temporary up to date uAPI headers.
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
include/drm-uapi/i915_drm.h | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index 16e452aa12d4..14c7e790f6ed 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -110,9 +110,17 @@ enum drm_i915_gem_engine_class {
enum drm_i915_pmu_engine_sample {
I915_SAMPLE_BUSY = 0,
I915_SAMPLE_WAIT = 1,
- I915_SAMPLE_SEMA = 2
+ I915_SAMPLE_SEMA = 2,
+ I915_SAMPLE_QUEUED = 3,
+ I915_SAMPLE_RUNNABLE = 4,
+ I915_SAMPLE_RUNNING = 5,
};
+ /* Divide counter value by divisor to get the real value. */
+#define I915_SAMPLE_QUEUED_DIVISOR (1024)
+#define I915_SAMPLE_RUNNABLE_DIVISOR (1024)
+#define I915_SAMPLE_RUNNING_DIVISOR (1024)
+
#define I915_PMU_SAMPLE_BITS (4)
#define I915_PMU_SAMPLE_MASK (0xf)
#define I915_PMU_SAMPLE_INSTANCE_BITS (8)
@@ -133,6 +141,15 @@ enum drm_i915_pmu_engine_sample {
#define I915_PMU_ENGINE_SEMA(class, instance) \
__I915_PMU_ENGINE(class, instance, I915_SAMPLE_SEMA)
+#define I915_PMU_ENGINE_QUEUED(class, instance) \
+ __I915_PMU_ENGINE(class, instance, I915_SAMPLE_QUEUED)
+
+#define I915_PMU_ENGINE_RUNNABLE(class, instance) \
+ __I915_PMU_ENGINE(class, instance, I915_SAMPLE_RUNNABLE)
+
+#define I915_PMU_ENGINE_RUNNING(class, instance) \
+ __I915_PMU_ENGINE(class, instance, I915_SAMPLE_RUNNING)
+
#define __I915_PMU_OTHER(x) (__I915_PMU_ENGINE(0xff, 0xff, 0xf) + 1 + (x))
#define I915_PMU_ACTUAL_FREQUENCY __I915_PMU_OTHER(0)
--
2.17.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t 2/6] intel-gpu-overlay: Add engine queue stats
2018-06-06 12:49 [PATCH i-g-t 0/6] Queued/runnable/running engine stats Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 1/6] include: i915 uAPI headers Tvrtko Ursulin
@ 2018-06-06 12:49 ` Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 3/6] intel-gpu-overlay: Show 1s, 30s and 15m GPU load Tvrtko Ursulin
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Tvrtko Ursulin @ 2018-06-06 12:49 UTC (permalink / raw)
To: igt-dev; +Cc: intel-gfx
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Use new PMU engine queue stats (queued, runnable and running) and display
them per engine.
v2:
* Compact per engine stats. (Chris Wilson)
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
overlay/gpu-top.c | 42 ++++++++++++++++++++++++++++++++++++++++++
overlay/gpu-top.h | 11 +++++++++++
overlay/overlay.c | 7 +++++++
3 files changed, 60 insertions(+)
diff --git a/overlay/gpu-top.c b/overlay/gpu-top.c
index 61b8f62fd78c..22e9badb22c1 100644
--- a/overlay/gpu-top.c
+++ b/overlay/gpu-top.c
@@ -72,6 +72,18 @@ static int perf_init(struct gpu_top *gt)
gt->fd) >= 0)
gt->have_sema = 1;
+ if (perf_i915_open_group(I915_PMU_ENGINE_QUEUED(d->class, d->inst),
+ gt->fd) >= 0)
+ gt->have_queued = 1;
+
+ if (perf_i915_open_group(I915_PMU_ENGINE_RUNNABLE(d->class, d->inst),
+ gt->fd) >= 0)
+ gt->have_runnable = 1;
+
+ if (perf_i915_open_group(I915_PMU_ENGINE_RUNNING(d->class, d->inst),
+ gt->fd) >= 0)
+ gt->have_running = 1;
+
gt->ring[0].name = d->name;
gt->num_rings = 1;
@@ -93,6 +105,24 @@ static int perf_init(struct gpu_top *gt)
gt->fd) < 0)
return -1;
+ if (gt->have_queued &&
+ perf_i915_open_group(I915_PMU_ENGINE_QUEUED(d->class,
+ d->inst),
+ gt->fd) < 0)
+ return -1;
+
+ if (gt->have_runnable &&
+ perf_i915_open_group(I915_PMU_ENGINE_RUNNABLE(d->class,
+ d->inst),
+ gt->fd) < 0)
+ return -1;
+
+ if (gt->have_running &&
+ perf_i915_open_group(I915_PMU_ENGINE_RUNNING(d->class,
+ d->inst),
+ gt->fd) < 0)
+ return -1;
+
gt->ring[gt->num_rings++].name = d->name;
}
@@ -298,6 +328,12 @@ int gpu_top_update(struct gpu_top *gt)
s->wait[n] = sample[m++];
if (gt->have_sema)
s->sema[n] = sample[m++];
+ if (gt->have_queued)
+ s->queued[n] = sample[m++];
+ if (gt->have_runnable)
+ s->runnable[n] = sample[m++];
+ if (gt->have_running)
+ s->running[n] = sample[m++];
}
if (gt->count == 1)
@@ -310,6 +346,12 @@ int gpu_top_update(struct gpu_top *gt)
gt->ring[n].u.u.wait = (100 * (s->wait[n] - d->wait[n]) + d_time/2) / d_time;
if (gt->have_sema)
gt->ring[n].u.u.sema = (100 * (s->sema[n] - d->sema[n]) + d_time/2) / d_time;
+ if (gt->have_queued)
+ gt->ring[n].queued = (double)((s->queued[n] - d->queued[n])) / I915_SAMPLE_QUEUED_DIVISOR * 1e9 / d_time;
+ if (gt->have_runnable)
+ gt->ring[n].runnable = (double)((s->runnable[n] - d->runnable[n])) / I915_SAMPLE_RUNNABLE_DIVISOR * 1e9 / d_time;
+ if (gt->have_running)
+ gt->ring[n].running = (double)((s->running[n] - d->running[n])) / I915_SAMPLE_RUNNING_DIVISOR * 1e9 / d_time;
/* in case of rounding + sampling errors, fudge */
if (gt->ring[n].u.u.busy > 100)
diff --git a/overlay/gpu-top.h b/overlay/gpu-top.h
index d3cdd779760f..cb4310c82a94 100644
--- a/overlay/gpu-top.h
+++ b/overlay/gpu-top.h
@@ -36,6 +36,9 @@ struct gpu_top {
int num_rings;
int have_wait;
int have_sema;
+ int have_queued;
+ int have_runnable;
+ int have_running;
struct gpu_top_ring {
const char *name;
@@ -47,6 +50,10 @@ struct gpu_top {
} u;
uint32_t payload;
} u;
+
+ double queued;
+ double runnable;
+ double running;
} ring[MAX_RINGS];
struct gpu_top_stat {
@@ -54,7 +61,11 @@ struct gpu_top {
uint64_t busy[MAX_RINGS];
uint64_t wait[MAX_RINGS];
uint64_t sema[MAX_RINGS];
+ uint64_t queued[MAX_RINGS];
+ uint64_t runnable[MAX_RINGS];
+ uint64_t running[MAX_RINGS];
} stat[2];
+
int count;
};
diff --git a/overlay/overlay.c b/overlay/overlay.c
index eae5ddfa8823..7087d5e5b24e 100644
--- a/overlay/overlay.c
+++ b/overlay/overlay.c
@@ -256,6 +256,13 @@ static void show_gpu_top(struct overlay_context *ctx, struct overlay_gpu_top *gt
len = sprintf(txt, "%s: %3d%% busy",
gt->gpu_top.ring[n].name,
gt->gpu_top.ring[n].u.u.busy);
+ if (gt->gpu_top.have_queued &&
+ gt->gpu_top.have_runnable &&
+ gt->gpu_top.have_running)
+ len += sprintf(txt + len, " (%.2f / %.2f / %.2f)",
+ gt->gpu_top.ring[n].queued,
+ gt->gpu_top.ring[n].runnable,
+ gt->gpu_top.ring[n].running);
if (gt->gpu_top.ring[n].u.u.wait)
len += sprintf(txt + len, ", %d%% wait",
gt->gpu_top.ring[n].u.u.wait);
--
2.17.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t 3/6] intel-gpu-overlay: Show 1s, 30s and 15m GPU load
2018-06-06 12:49 [PATCH i-g-t 0/6] Queued/runnable/running engine stats Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 1/6] include: i915 uAPI headers Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 2/6] intel-gpu-overlay: Add engine queue stats Tvrtko Ursulin
@ 2018-06-06 12:49 ` Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 4/6] tests/perf_pmu: Add tests for engine queued/runnable/running stats Tvrtko Ursulin
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Tvrtko Ursulin @ 2018-06-06 12:49 UTC (permalink / raw)
To: igt-dev; +Cc: intel-gfx
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Show total GPU loads in the window banner.
Engine load is defined as total of runnable and running requests on an
engine.
Total, non-normalized, load is display. In other words if N engines are
busy with exactly one request, the load will be shown as N.
v2:
* Different flavour of load avg. (Chris Wilson)
* Simplify code. (Chris Wilson)
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
overlay/gpu-top.c | 39 ++++++++++++++++++++++++++++++++++++++-
overlay/gpu-top.h | 11 ++++++++++-
overlay/overlay.c | 28 ++++++++++++++++++++++------
3 files changed, 70 insertions(+), 8 deletions(-)
diff --git a/overlay/gpu-top.c b/overlay/gpu-top.c
index 22e9badb22c1..501429b86379 100644
--- a/overlay/gpu-top.c
+++ b/overlay/gpu-top.c
@@ -28,6 +28,7 @@
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
+#include <math.h>
#include <errno.h>
#include <assert.h>
@@ -126,6 +127,10 @@ static int perf_init(struct gpu_top *gt)
gt->ring[gt->num_rings++].name = d->name;
}
+ gt->have_load_avg = gt->have_queued &&
+ gt->have_runnable &&
+ gt->have_running;
+
return 0;
}
@@ -290,17 +295,32 @@ static void mmio_init(struct gpu_top *gt)
}
}
-void gpu_top_init(struct gpu_top *gt)
+void gpu_top_init(struct gpu_top *gt, unsigned int period_us)
{
+ const double period = (double)period_us / 1e6;
+ const double load_period[NUM_LOADS] = { 1.0, 30.0, 900.0 };
+ const char *load_names[NUM_LOADS] = { "1s", "30s", "15m" };
+ unsigned int i;
+
memset(gt, 0, sizeof(*gt));
gt->fd = -1;
+ for (i = 0; i < NUM_LOADS; i++) {
+ gt->load_name[i] = load_names[i];
+ gt->exp[i] = exp(-period / load_period[i]);
+ }
+
if (perf_init(gt) == 0)
return;
mmio_init(gt);
}
+static double update_load(double load, double exp, double val)
+{
+ return val + exp * (load - val);
+}
+
int gpu_top_update(struct gpu_top *gt)
{
uint32_t data[1024];
@@ -313,6 +333,8 @@ int gpu_top_update(struct gpu_top *gt)
struct gpu_top_stat *s = >->stat[gt->count++&1];
struct gpu_top_stat *d = >->stat[gt->count&1];
uint64_t *sample, d_time;
+ double gpu_qd = 0.0;
+ unsigned int i;
int n, m;
len = read(gt->fd, data, sizeof(data));
@@ -341,6 +363,8 @@ int gpu_top_update(struct gpu_top *gt)
d_time = s->time - d->time;
for (n = 0; n < gt->num_rings; n++) {
+ double qd = 0.0;
+
gt->ring[n].u.u.busy = (100 * (s->busy[n] - d->busy[n]) + d_time/2) / d_time;
if (gt->have_wait)
gt->ring[n].u.u.wait = (100 * (s->wait[n] - d->wait[n]) + d_time/2) / d_time;
@@ -353,6 +377,14 @@ int gpu_top_update(struct gpu_top *gt)
if (gt->have_running)
gt->ring[n].running = (double)((s->running[n] - d->running[n])) / I915_SAMPLE_RUNNING_DIVISOR * 1e9 / d_time;
+ qd = gt->ring[n].runnable + gt->ring[n].running;
+ gpu_qd += qd;
+
+ for (i = 0; i < NUM_LOADS; i++)
+ gt->ring[n].load[i] =
+ update_load(gt->ring[n].load[i],
+ gt->exp[i], qd);
+
/* in case of rounding + sampling errors, fudge */
if (gt->ring[n].u.u.busy > 100)
gt->ring[n].u.u.busy = 100;
@@ -362,6 +394,11 @@ int gpu_top_update(struct gpu_top *gt)
gt->ring[n].u.u.sema = 100;
}
+ for (i = 0; i < NUM_LOADS; i++) {
+ gt->load[i] = update_load(gt->load[i], gt->exp[i],
+ gpu_qd);
+ gt->norm_load[i] = gt->load[i] / gt->num_rings;
+ }
update = 1;
} else {
while ((len = read(gt->fd, data, sizeof(data))) > 0) {
diff --git a/overlay/gpu-top.h b/overlay/gpu-top.h
index cb4310c82a94..115ce8c482c1 100644
--- a/overlay/gpu-top.h
+++ b/overlay/gpu-top.h
@@ -26,6 +26,7 @@
#define GPU_TOP_H
#define MAX_RINGS 16
+#define NUM_LOADS 3
#include <stdint.h>
@@ -39,6 +40,12 @@ struct gpu_top {
int have_queued;
int have_runnable;
int have_running;
+ int have_load_avg;
+
+ double exp[NUM_LOADS];
+ double load[NUM_LOADS];
+ double norm_load[NUM_LOADS];
+ const char *load_name[NUM_LOADS];
struct gpu_top_ring {
const char *name;
@@ -54,6 +61,8 @@ struct gpu_top {
double queued;
double runnable;
double running;
+
+ double load[NUM_LOADS];
} ring[MAX_RINGS];
struct gpu_top_stat {
@@ -69,7 +78,7 @@ struct gpu_top {
int count;
};
-void gpu_top_init(struct gpu_top *gt);
+void gpu_top_init(struct gpu_top *gt, unsigned int period_us);
int gpu_top_update(struct gpu_top *gt);
#endif /* GPU_TOP_H */
diff --git a/overlay/overlay.c b/overlay/overlay.c
index 7087d5e5b24e..1a1d01db082b 100644
--- a/overlay/overlay.c
+++ b/overlay/overlay.c
@@ -141,7 +141,8 @@ struct overlay_context {
};
static void init_gpu_top(struct overlay_context *ctx,
- struct overlay_gpu_top *gt)
+ struct overlay_gpu_top *gt,
+ unsigned int period_us)
{
const double rgba[][4] = {
{ 1, 0.25, 0.25, 1 },
@@ -153,7 +154,7 @@ static void init_gpu_top(struct overlay_context *ctx,
int n;
cpu_top_init(>->cpu_top);
- gpu_top_init(>->gpu_top);
+ gpu_top_init(>->gpu_top, period_us);
chart_init(>->cpu, "CPU", 120);
chart_set_position(>->cpu, PAD, PAD);
@@ -928,13 +929,13 @@ int main(int argc, char **argv)
debugfs_init();
- init_gpu_top(&ctx, &ctx.gpu_top);
+ sample_period = get_sample_period(&config);
+
+ init_gpu_top(&ctx, &ctx.gpu_top, sample_period);
init_gpu_perf(&ctx, &ctx.gpu_perf);
init_gpu_freq(&ctx, &ctx.gpu_freq);
init_gem_objects(&ctx, &ctx.gem_objects);
- sample_period = get_sample_period(&config);
-
i = 0;
while (1) {
ctx.time = time(NULL);
@@ -950,9 +951,24 @@ int main(int argc, char **argv)
show_gem_objects(&ctx, &ctx.gem_objects);
{
- char buf[80];
+ struct gpu_top *gt = &ctx.gpu_top.gpu_top;
cairo_text_extents_t extents;
+ char buf[256];
+
gethostname(buf, sizeof(buf));
+
+ if (gt->have_load_avg) {
+ int len = strlen(buf);
+
+ snprintf(buf + len, sizeof(buf) - len,
+ "%s; %u engines; load %s %.2f, %s %.2f, %s %.2f",
+ buf,
+ gt->num_rings,
+ gt->load_name[0], gt->load[0],
+ gt->load_name[1], gt->load[1],
+ gt->load_name[2], gt->load[2]);
+ }
+
cairo_set_source_rgb(ctx.cr, .5, .5, .5);
cairo_set_font_size(ctx.cr, PAD-2);
cairo_text_extents(ctx.cr, buf, &extents);
--
2.17.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t 4/6] tests/perf_pmu: Add tests for engine queued/runnable/running stats
2018-06-06 12:49 [PATCH i-g-t 0/6] Queued/runnable/running engine stats Tvrtko Ursulin
` (2 preceding siblings ...)
2018-06-06 12:49 ` [PATCH i-g-t 3/6] intel-gpu-overlay: Show 1s, 30s and 15m GPU load Tvrtko Ursulin
@ 2018-06-06 12:49 ` Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 5/6] intel-gpu-top: Add queue depths and load average Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 6/6] tests/i915_query: Engine queues tests Tvrtko Ursulin
5 siblings, 0 replies; 10+ messages in thread
From: Tvrtko Ursulin @ 2018-06-06 12:49 UTC (permalink / raw)
To: igt-dev; +Cc: intel-gfx
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Simple tests to check reported queue depths are correct.
v2:
* Improvements similar to ones from i915_query.c.
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
tests/perf_pmu.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 258 insertions(+)
diff --git a/tests/perf_pmu.c b/tests/perf_pmu.c
index 4570f926d7fe..788ea09cc6a0 100644
--- a/tests/perf_pmu.c
+++ b/tests/perf_pmu.c
@@ -169,6 +169,7 @@ static unsigned int e2ring(int gem_fd, const struct intel_execution_engine2 *e)
#define TEST_RUNTIME_PM (8)
#define FLAG_LONG (16)
#define FLAG_HANG (32)
+#define TEST_CONTEXTS (64)
static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
{
@@ -952,6 +953,223 @@ multi_client(int gem_fd, const struct intel_execution_engine2 *e)
assert_within_epsilon(val[1], perf_slept[1], tolerance);
}
+static double calc_queued(uint64_t d_val, uint64_t d_ns)
+{
+ return (double)d_val * 1e9 / I915_SAMPLE_QUEUED_DIVISOR / d_ns;
+}
+
+static void
+queued(int gem_fd, const struct intel_execution_engine2 *e, unsigned int flags)
+{
+ const unsigned long engine = e2ring(gem_fd, e);
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ const unsigned int max_rq = 10;
+ double queued[max_rq + 1];
+ unsigned int n, i;
+ uint64_t val[2];
+ uint64_t ts[2];
+ uint32_t bo;
+ int fd;
+
+ igt_require_sw_sync();
+ if (flags & TEST_CONTEXTS)
+ gem_require_contexts(gem_fd);
+
+ memset(queued, 0, sizeof(queued));
+
+ bo = gem_create(gem_fd, 4096);
+ gem_write(gem_fd, bo, 4092, &bbe, sizeof(bbe));
+
+ fd = open_pmu(I915_PMU_ENGINE_QUEUED(e->class, e->instance));
+
+ for (n = 0; n <= max_rq; n++) {
+ IGT_CORK_FENCE(cork);
+ int fence = -1;
+
+ gem_quiescent_gpu(gem_fd);
+
+ if (n)
+ fence = igt_cork_plug(&cork, -1);
+
+ for (i = 0; i < n; i++) {
+ struct drm_i915_gem_exec_object2 obj = { };
+ struct drm_i915_gem_execbuffer2 eb = { };
+
+ obj.handle = bo;
+
+ eb.buffer_count = 1;
+ eb.buffers_ptr = to_user_pointer(&obj);
+
+ eb.flags = engine | I915_EXEC_FENCE_IN;
+ if (flags & TEST_CONTEXTS)
+ eb.rsvd1 = gem_context_create(gem_fd);
+ eb.rsvd2 = fence;
+
+ gem_execbuf(gem_fd, &eb);
+
+ if (flags & TEST_CONTEXTS)
+ gem_context_destroy(gem_fd, eb.rsvd1);
+ }
+
+ val[0] = __pmu_read_single(fd, &ts[0]);
+ usleep(batch_duration_ns / 1000);
+ val[1] = __pmu_read_single(fd, &ts[1]);
+
+ queued[n] = calc_queued(val[1] - val[0], ts[1] - ts[0]);
+ igt_info("n=%u queued=%.2f\n", n, queued[n]);
+
+ if (fence >= 0)
+ igt_cork_unplug(&cork);
+
+ for (i = 0; i < n; i++)
+ gem_sync(gem_fd, bo);
+ }
+
+ close(fd);
+
+ gem_close(gem_fd, bo);
+
+ for (i = 0; i <= max_rq; i++)
+ assert_within_epsilon(queued[i], i, tolerance);
+}
+
+static unsigned long __query_wait(igt_spin_t *spin, unsigned int n)
+{
+ struct timespec ts = { };
+ unsigned long t;
+
+ igt_nsec_elapsed(&ts);
+
+ if (spin->running) {
+ igt_spin_busywait_until_running(spin);
+ } else {
+ igt_debug("__spin_wait - usleep mode\n");
+ usleep(500e3); /* Better than nothing! */
+ }
+
+ t = igt_nsec_elapsed(&ts);
+
+ return spin->running ? t : 500e6 / n;
+}
+
+static void
+runnable(int gem_fd, const struct intel_execution_engine2 *e)
+{
+ const unsigned long engine = e2ring(gem_fd, e);
+ bool contexts = gem_has_contexts(gem_fd);
+ const unsigned int max_rq = 10;
+ igt_spin_t *spin[max_rq + 1];
+ double runnable[max_rq + 1];
+ uint32_t ctx[max_rq];
+ unsigned int n, i;
+ uint64_t val[2];
+ uint64_t ts[2];
+ int fd;
+
+ memset(runnable, 0, sizeof(runnable));
+
+ if (contexts) {
+ for (i = 0; i < max_rq; i++)
+ ctx[i] = gem_context_create(gem_fd);
+ }
+
+ fd = open_pmu(I915_PMU_ENGINE_RUNNABLE(e->class, e->instance));
+
+ for (n = 0; n <= max_rq; n++) {
+ gem_quiescent_gpu(gem_fd);
+
+ for (i = 0; i < n; i++) {
+ uint32_t ctx_ = contexts ? ctx[i] : 0;
+
+ if (i == 0)
+ spin[i] = __spin_poll(gem_fd, ctx_, engine);
+ else
+ spin[i] = __igt_spin_batch_new(gem_fd, ctx_,
+ engine, 0);
+ }
+
+ if (n)
+ usleep(__query_wait(spin[0], n) * n);
+
+ val[0] = __pmu_read_single(fd, &ts[0]);
+ usleep(batch_duration_ns / 1000);
+ val[1] = __pmu_read_single(fd, &ts[1]);
+
+ runnable[n] = calc_queued(val[1] - val[0], ts[1] - ts[0]);
+ igt_info("n=%u runnable=%.2f\n", n, runnable[n]);
+
+ for (i = 0; i < n; i++) {
+ end_spin(gem_fd, spin[i], FLAG_SYNC);
+ igt_spin_batch_free(gem_fd, spin[i]);
+ }
+ }
+
+ if (contexts) {
+ for (i = 0; i < max_rq; i++)
+ gem_context_destroy(gem_fd, ctx[i]);
+ }
+
+ close(fd);
+
+ assert_within_epsilon(runnable[0], 0, tolerance);
+ igt_assert(runnable[max_rq] > 0.0);
+
+ if (contexts)
+ assert_within_epsilon(runnable[max_rq] - runnable[max_rq - 1],
+ 1, tolerance);
+}
+
+static void
+running(int gem_fd, const struct intel_execution_engine2 *e)
+{
+ const unsigned long engine = e2ring(gem_fd, e);
+ const unsigned int max_rq = 10;
+ igt_spin_t *spin[max_rq + 1];
+ double running[max_rq + 1];
+ unsigned int n, i;
+ uint64_t val[2];
+ uint64_t ts[2];
+ int fd;
+
+ memset(running, 0, sizeof(running));
+ memset(spin, 0, sizeof(spin));
+
+ fd = open_pmu(I915_PMU_ENGINE_RUNNING(e->class, e->instance));
+
+ for (n = 0; n <= max_rq; n++) {
+ gem_quiescent_gpu(gem_fd);
+
+ for (i = 0; i < n; i++) {
+ if (i == 0)
+ spin[i] = __spin_poll(gem_fd, 0, engine);
+ else
+ spin[i] = __igt_spin_batch_new(gem_fd, 0,
+ engine, 0);
+ }
+
+ if (n)
+ usleep(__query_wait(spin[0], n) * n);
+
+ val[0] = __pmu_read_single(fd, &ts[0]);
+ usleep(batch_duration_ns / 1000);
+ val[1] = __pmu_read_single(fd, &ts[1]);
+
+ running[n] = calc_queued(val[1] - val[0], ts[1] - ts[0]);
+ igt_info("n=%u running=%.2f\n", n, running[n]);
+
+ for (i = 0; i < n; i++) {
+ end_spin(gem_fd, spin[i], FLAG_SYNC);
+ igt_spin_batch_free(gem_fd, spin[i]);
+ }
+ }
+
+ close(fd);
+
+ assert_within_epsilon(running[0], 0, tolerance);
+ for (i = 1; i <= max_rq; i++)
+ igt_assert(running[i] > 0);
+}
+
/**
* Tests that i915 PMU corectly errors out in invalid initialization.
* i915 PMU is uncore PMU, thus:
@@ -1684,6 +1902,15 @@ igt_main
igt_subtest_f("init-sema-%s", e->name)
init(fd, e, I915_SAMPLE_SEMA);
+ igt_subtest_f("init-queued-%s", e->name)
+ init(fd, e, I915_SAMPLE_QUEUED);
+
+ igt_subtest_f("init-runnable-%s", e->name)
+ init(fd, e, I915_SAMPLE_RUNNABLE);
+
+ igt_subtest_f("init-running-%s", e->name)
+ init(fd, e, I915_SAMPLE_RUNNING);
+
igt_subtest_group {
igt_fixture {
gem_require_engine(fd, e->class, e->instance);
@@ -1789,6 +2016,27 @@ igt_main
igt_subtest_f("busy-hang-%s", e->name)
single(fd, e, TEST_BUSY | FLAG_HANG);
+
+ /**
+ * Test that queued metric works.
+ */
+ igt_subtest_f("queued-%s", e->name)
+ queued(fd, e, 0);
+
+ igt_subtest_f("queued-contexts-%s", e->name)
+ queued(fd, e, TEST_CONTEXTS);
+
+ /**
+ * Test that runnable metric works.
+ */
+ igt_subtest_f("runnable-%s", e->name)
+ runnable(fd, e);
+
+ /**
+ * Test that running metric works.
+ */
+ igt_subtest_f("running-%s", e->name)
+ running(fd, e);
}
/**
@@ -1881,6 +2129,16 @@ igt_main
e->name)
single(render_fd, e,
TEST_BUSY | TEST_TRAILING_IDLE);
+ igt_subtest_f("render-node-queued-%s", e->name)
+ queued(render_fd, e, 0);
+ igt_subtest_f("render-node-queued-contexts-%s",
+ e->name)
+ queued(render_fd, e, TEST_CONTEXTS);
+ igt_subtest_f("render-node-runnable-%s",
+ e->name)
+ runnable(render_fd, e);
+ igt_subtest_f("render-node-running-%s", e->name)
+ running(render_fd, e);
}
}
--
2.17.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t 5/6] intel-gpu-top: Add queue depths and load average
2018-06-06 12:49 [PATCH i-g-t 0/6] Queued/runnable/running engine stats Tvrtko Ursulin
` (3 preceding siblings ...)
2018-06-06 12:49 ` [PATCH i-g-t 4/6] tests/perf_pmu: Add tests for engine queued/runnable/running stats Tvrtko Ursulin
@ 2018-06-06 12:49 ` Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 6/6] tests/i915_query: Engine queues tests Tvrtko Ursulin
5 siblings, 0 replies; 10+ messages in thread
From: Tvrtko Ursulin @ 2018-06-06 12:49 UTC (permalink / raw)
To: igt-dev; +Cc: intel-gfx
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
With the driver now exporting various request queue depths for each
engine, we can display this information here. Both as raw counters,
and by adding a load average like metrics composed from number of
runnable and running requests in a given time period. 1s, 30s and 5m
periods are used for load average.
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
tools/Makefile.am | 2 +-
tools/intel_gpu_top.c | 122 +++++++++++++++++++++++++++++++++++++-----
tools/meson.build | 2 +-
3 files changed, 110 insertions(+), 16 deletions(-)
diff --git a/tools/Makefile.am b/tools/Makefile.am
index a0b016ddd7ff..e1d5f1bdc89a 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -28,7 +28,7 @@ intel_aubdump_la_LDFLAGS = -module -avoid-version -no-undefined
intel_aubdump_la_SOURCES = aubdump.c
intel_aubdump_la_LIBADD = $(top_builddir)/lib/libintel_tools.la -ldl
-intel_gpu_top_LDADD = $(top_builddir)/lib/libigt_perf.la
+intel_gpu_top_LDADD = $(top_builddir)/lib/libigt_perf.la -lm
bin_SCRIPTS = intel_aubdump
CLEANFILES = $(bin_SCRIPTS)
diff --git a/tools/intel_gpu_top.c b/tools/intel_gpu_top.c
index b923c3cfbe97..8990ef17b771 100644
--- a/tools/intel_gpu_top.c
+++ b/tools/intel_gpu_top.c
@@ -56,6 +56,10 @@ struct pmu_counter {
struct pmu_pair val;
};
+#define NUM_QUEUES (3)
+
+#define NUM_LOADS (3)
+
struct engine {
const char *name;
const char *display_name;
@@ -65,9 +69,13 @@ struct engine {
unsigned int num_counters;
+ double qd[3];
+ double load_avg[NUM_LOADS];
+
struct pmu_counter busy;
struct pmu_counter wait;
struct pmu_counter sema;
+ struct pmu_counter queue[NUM_QUEUES];
};
struct engines {
@@ -95,6 +103,11 @@ struct engines {
struct pmu_counter imc_reads;
struct pmu_counter imc_writes;
+ double qd_scale;
+
+ double load_exp[NUM_LOADS];
+ double load_avg[NUM_LOADS];
+
struct engine engine;
};
@@ -320,6 +333,13 @@ static double filename_to_double(const char *filename)
return v;
}
+#define I915_EVENT "/sys/devices/i915/events/"
+
+static double i915_qd_scale(void)
+{
+ return filename_to_double(I915_EVENT "rcs0-queued.scale");
+}
+
#define RAPL_ROOT "/sys/devices/power/"
#define RAPL_EVENT "/sys/devices/power/events/"
@@ -453,6 +473,8 @@ static int pmu_init(struct engines *engines)
engines->rc6.config = I915_PMU_RC6_RESIDENCY;
_open_pmu(engines->num_counters, &engines->rc6, engines->fd);
+ engines->qd_scale = i915_qd_scale();
+
for (i = 0; i < engines->num_engines; i++) {
struct engine *engine = engine_ptr(engines, i);
struct {
@@ -462,6 +484,9 @@ static int pmu_init(struct engines *engines)
{ .pmu = &engine->busy, .counter = "busy" },
{ .pmu = &engine->wait, .counter = "wait" },
{ .pmu = &engine->sema, .counter = "sema" },
+ { .pmu = &engine->queue[0], .counter = "queued" },
+ { .pmu = &engine->queue[1], .counter = "runnable" },
+ { .pmu = &engine->queue[2], .counter = "running" },
{ .pmu = NULL, .counter = NULL },
};
@@ -576,12 +601,11 @@ static void fill_str(char *buf, unsigned int bufsz, char c, unsigned int num)
*buf = 0;
}
-static void pmu_calc(struct pmu_counter *cnt,
- char *buf, unsigned int bufsz,
- unsigned int width, unsigned width_dec,
- double d, double t, double s)
+static void _pmu_calc(struct pmu_counter *cnt,
+ char *buf, unsigned int bufsz,
+ unsigned int width, unsigned width_dec,
+ double val)
{
- double val;
int len;
assert(bufsz >= (width + width_dec + 1));
@@ -591,8 +615,6 @@ static void pmu_calc(struct pmu_counter *cnt,
return;
}
- val = __pmu_calc(&cnt->val, d, t, s);
-
len = snprintf(buf, bufsz, "%*.*f", width + width_dec, width_dec, val);
if (len < 0 || len == bufsz) {
fill_str(buf, bufsz, 'X', width + width_dec);
@@ -600,6 +622,16 @@ static void pmu_calc(struct pmu_counter *cnt,
}
}
+static void pmu_calc(struct pmu_counter *cnt,
+ char *buf, unsigned int bufsz,
+ unsigned int width, unsigned width_dec,
+ double d, double t, double s)
+{
+ double val = __pmu_calc(&cnt->val, d, t, s);
+
+ _pmu_calc(cnt, buf, bufsz, width, width_dec, val);
+}
+
static uint64_t __pmu_read_single(int fd, uint64_t *ts)
{
uint64_t data[2] = { };
@@ -658,10 +690,14 @@ static void pmu_sample(struct engines *engines)
for (i = 0; i < engines->num_engines; i++) {
struct engine *engine = engine_ptr(engines, i);
+ unsigned int j;
update_sample(&engine->busy, val);
update_sample(&engine->sema, val);
update_sample(&engine->wait, val);
+
+ for (j = 0; j < NUM_QUEUES; j++)
+ update_sample(&engine->queue[j], val);
}
}
@@ -702,12 +738,19 @@ usage(const char *appname)
appname, DEFAULT_PERIOD_MS);
}
+static double update_load(double load, double exp, double val)
+{
+ return val + exp * (load - val);
+}
+
int main(int argc, char **argv)
{
unsigned int period_us = DEFAULT_PERIOD_MS * 1000;
+ const double load_period[NUM_LOADS] = { 1.0, 30.0, 900.0 };
int con_w = -1, con_h = -1;
struct engines *engines;
unsigned int i;
+ double period;
int ret, ch;
/* Parse options */
@@ -741,10 +784,15 @@ int main(int argc, char **argv)
return 1;
}
+ /* Load average setup. */
+ period = (double)period_us / 1e6;
+ for (i = 0; i < NUM_LOADS; i++)
+ engines->load_exp[i] = exp(-period / load_period[i]);
+
pmu_sample(engines);
for (;;) {
- double t;
+ double t, qd = 0;
#define BUFSZ 16
char freq[BUFSZ];
char fact[BUFSZ];
@@ -754,6 +802,7 @@ int main(int argc, char **argv)
char reads[BUFSZ];
char writes[BUFSZ];
struct winsize ws;
+ unsigned int j;
int lines = 0;
/* Update terminal size. */
@@ -778,8 +827,44 @@ int main(int argc, char **argv)
pmu_calc(&engines->imc_writes, writes, BUFSZ, 6, 0, 1.0, t,
engines->imc_writes_scale);
+ for (i = 0; i < engines->num_engines; i++) {
+ struct engine *engine = engine_ptr(engines, i);
+
+ if (!engine->num_counters)
+ continue;
+
+ for (j = 0; j < NUM_QUEUES; j++) {
+ if (!engine->queue[j].present)
+ continue;
+
+ engine->qd[j] =
+ __pmu_calc(&engine->queue[j].val, 1, t,
+ engines->qd_scale);
+ }
+
+ qd += engine->qd[1] + engine->qd[2];
+
+ for (j = 0; j < NUM_LOADS; j++) {
+ engine->load_avg[j] =
+ update_load(engine->load_avg[j],
+ engines->load_exp[j],
+ engine->qd[1] +
+ engine->qd[2]);
+ }
+ }
+
+ for (j = 0; j < NUM_LOADS; j++) {
+ engines->load_avg[j] =
+ update_load(engines->load_avg[j],
+ engines->load_exp[j],
+ qd);
+ }
+
if (lines++ < con_h)
- printf("intel-gpu-top - %s/%s MHz; %s%% RC6; %s %s; %s irqs/s\n",
+ printf("intel-gpu-top - load avg %5.2f, %5.2f, %5.2f; %s/%s MHz; %s%% RC6; %s %s; %s irqs/s\n",
+ engines->load_avg[0],
+ engines->load_avg[1],
+ engines->load_avg[2],
fact, freq, rc6, power, engines->rapl_unit, irq);
if (lines++ < con_h)
@@ -803,7 +888,7 @@ int main(int argc, char **argv)
if (engine->num_counters && lines < con_h) {
const char *a = " ENGINE BUSY ";
- const char *b = " MI_SEMA MI_WAIT";
+ const char *b = "Q r R MI_SEMA MI_WAIT";
printf("\033[7m%s%*s%s\033[0m\n",
a,
@@ -817,6 +902,7 @@ int main(int argc, char **argv)
for (i = 0; i < engines->num_engines && lines < con_h; i++) {
struct engine *engine = engine_ptr(engines, i);
unsigned int max_w = con_w - 1;
+ char qdbuf[NUM_LOADS][BUFSZ];
unsigned int len;
char sema[BUFSZ];
char wait[BUFSZ];
@@ -827,14 +913,22 @@ int main(int argc, char **argv)
if (!engine->num_counters)
continue;
+ for (j = 0; j < NUM_QUEUES; j++)
+ _pmu_calc(&engine->queue[j], qdbuf[j], BUFSZ,
+ 3, 0, engine->qd[j]);
+
pmu_calc(&engine->sema, sema, BUFSZ, 3, 0, 1e9, t, 100);
pmu_calc(&engine->wait, wait, BUFSZ, 3, 0, 1e9, t, 100);
- len = snprintf(buf, sizeof(buf), " %s%% %s%%",
+
+ len = snprintf(buf, sizeof(buf),
+ " %s %s %s %s%% %s%%",
+ qdbuf[0], qdbuf[1], qdbuf[2],
sema, wait);
- pmu_calc(&engine->busy, busy, BUFSZ, 6, 2, 1e9, t,
- 100);
- len += printf("%16s %s%% ", engine->display_name, busy);
+ pmu_calc(&engine->busy, busy, BUFSZ, 6, 2, 1e9, t, 100);
+
+ len += printf("%16s %s%% ",
+ engine->display_name, busy);
val = __pmu_calc(&engine->busy.val, 1e9, t, 100);
print_percentage_bar(val, max_w - len);
diff --git a/tools/meson.build b/tools/meson.build
index 8ed1ccf48d6e..18abb396c56f 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -119,7 +119,7 @@ shared_library('intel_aubdump', 'aubdump.c',
executable('intel_gpu_top', 'intel_gpu_top.c',
install : true,
install_rpath : rpathdir,
- dependencies : tool_deps + [ lib_igt_perf ])
+ dependencies : tool_deps + [ lib_igt_perf, math ])
conf_data = configuration_data()
conf_data.set('prefix', prefix)
--
2.17.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t 6/6] tests/i915_query: Engine queues tests
2018-06-06 12:49 [PATCH i-g-t 0/6] Queued/runnable/running engine stats Tvrtko Ursulin
` (4 preceding siblings ...)
2018-06-06 12:49 ` [PATCH i-g-t 5/6] intel-gpu-top: Add queue depths and load average Tvrtko Ursulin
@ 2018-06-06 12:49 ` Tvrtko Ursulin
2018-06-06 14:33 ` Lionel Landwerlin
5 siblings, 1 reply; 10+ messages in thread
From: Tvrtko Ursulin @ 2018-06-06 12:49 UTC (permalink / raw)
To: igt-dev; +Cc: intel-gfx
From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Basic tests to cover engine queued/runnable/running metric as reported
by the DRM_I915_QUERY_ENGINE_QUEUES query.
v2:
* Update ABI for i915 changes.
* Use igt_spin_busywait_until_running.
* Support no hardware contexts.
* More comments. (Lionel Landwerlin)
Chris Wilson:
* Check for sw sync support.
* Multiple contexts queued test.
* Simplify context and bb allocation.
* Fix asserts in the running subtest.
v3:
* Update for uAPI change.
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
tests/i915_query.c | 442 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 442 insertions(+)
diff --git a/tests/i915_query.c b/tests/i915_query.c
index c7de8cbd8371..588428749c50 100644
--- a/tests/i915_query.c
+++ b/tests/i915_query.c
@@ -22,6 +22,7 @@
*/
#include "igt.h"
+#include "sw_sync.h"
#include <limits.h>
@@ -477,8 +478,414 @@ test_query_topology_known_pci_ids(int fd, int devid)
free(topo_info);
}
+#define DRM_I915_QUERY_ENGINE_QUEUES 2
+
+struct drm_i915_query_engine_queues {
+ /** Engine class as in enum drm_i915_gem_engine_class. */
+ __u16 class;
+
+ /** Engine instance number. */
+ __u16 instance;
+
+ /** Number of requests with unresolved fences and dependencies. */
+ __u32 queued;
+
+ /** Number of ready requests waiting on a slot on GPU. */
+ __u32 runnable;
+
+ /** Number of requests executing on the GPU. */
+ __u32 running;
+
+ __u32 rsvd[6];
+};
+
+static bool query_engine_queues_supported(int fd)
+{
+ struct drm_i915_query_item item = {
+ .query_id = DRM_I915_QUERY_ENGINE_QUEUES,
+ };
+
+ return __i915_query_items(fd, &item, 1) == 0 && item.length > 0;
+}
+
+static void engine_queues_invalid(int fd)
+{
+ struct drm_i915_query_engine_queues queues;
+ struct drm_i915_query_item item;
+ unsigned int len;
+ unsigned int i;
+
+ /* Flags is MBZ. */
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.flags = 1;
+ i915_query_items(fd, &item, 1);
+ igt_assert_eq(item.length, -EINVAL);
+
+ /* Length not zero and not greater or equal required size. */
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.length = 1;
+ i915_query_items(fd, &item, 1);
+ igt_assert_eq(item.length, -EINVAL);
+
+ /* Query correct length. */
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ i915_query_items(fd, &item, 1);
+ igt_assert(item.length >= 0);
+ len = item.length;
+
+ /* Ivalid pointer. */
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.length = len;
+ i915_query_items(fd, &item, 1);
+ igt_assert_eq(item.length, -EFAULT);
+
+ /* Reserved fields are MBZ. */
+
+ for (i = 0; i < ARRAY_SIZE(queues.rsvd); i++) {
+ memset(&queues, 0, sizeof(queues));
+ queues.rsvd[i] = 1;
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.length = len;
+ item.data_ptr = to_user_pointer(&queues);
+ i915_query_items(fd, &item, 1);
+ igt_assert_eq(item.length, -EINVAL);
+ }
+
+ memset(&queues, 0, sizeof(queues));
+ queues.class = -1;
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.length = len;
+ item.data_ptr = to_user_pointer(&queues);
+ i915_query_items(fd, &item, 1);
+ igt_assert_eq(item.length, -ENOENT);
+
+ memset(&queues, 0, sizeof(queues));
+ queues.instance = -1;
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.length = len;
+ item.data_ptr = to_user_pointer(&queues);
+ i915_query_items(fd, &item, 1);
+ igt_assert_eq(item.length, -ENOENT);
+}
+
+static void engine_queues(int fd, const struct intel_execution_engine2 *e)
+{
+ struct drm_i915_query_engine_queues queues;
+ struct drm_i915_query_item item;
+ unsigned int len;
+
+ /* Query required buffer length. */
+ memset(&queues, 0, sizeof(queues));
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.data_ptr = to_user_pointer(&queues);
+ i915_query_items(fd, &item, 1);
+ igt_assert(item.length >= 0);
+ igt_assert(item.length <= sizeof(queues));
+ len = item.length;
+
+ /* Check length larger than required works and reports same length. */
+ memset(&queues, 0, sizeof(queues));
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.data_ptr = to_user_pointer(&queues);
+ item.length = len + 1;
+ i915_query_items(fd, &item, 1);
+ igt_assert_eq(item.length, len);
+
+ /* Actual query. */
+ memset(&queues, 0, sizeof(queues));
+ queues.class = e->class;
+ queues.instance = e->instance;
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.data_ptr = to_user_pointer(&queues);
+ item.length = len;
+ i915_query_items(fd, &item, 1);
+ igt_assert_eq(item.length, len);
+}
+
+static unsigned int e2ring(int gem_fd, const struct intel_execution_engine2 *e)
+{
+ return gem_class_instance_to_eb_flags(gem_fd, e->class, e->instance);
+}
+
+static void
+__query_queues(int fd, const struct intel_execution_engine2 *e,
+ struct drm_i915_query_engine_queues *queues)
+{
+ struct drm_i915_query_item item;
+
+ memset(queues, 0, sizeof(*queues));
+ queues->class = e->class;
+ queues->instance = e->instance;
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
+ item.data_ptr = to_user_pointer(queues);
+ item.length = sizeof(*queues);
+ i915_query_items(fd, &item, 1);
+ igt_assert_eq(item.length, sizeof(*queues));
+}
+
+#define TEST_CONTEXTS (1 << 0)
+
+/*
+ * Test that the reported number of queued (not ready for execution due fences
+ * or dependencies) requests on an engine is correct.
+ */
+static void
+engine_queued(int gem_fd, const struct intel_execution_engine2 *e,
+ unsigned int flags)
+{
+ const unsigned long engine = e2ring(gem_fd, e);
+ struct drm_i915_query_engine_queues queues;
+ const uint32_t bbe = MI_BATCH_BUFFER_END;
+ const unsigned int max_rq = 10;
+ uint32_t queued[max_rq + 1];
+ unsigned int n, i;
+ uint32_t bo;
+
+ igt_require_sw_sync();
+ if (flags & TEST_CONTEXTS)
+ gem_require_contexts(gem_fd);
+
+ memset(queued, 0, sizeof(queued));
+
+ bo = gem_create(gem_fd, 4096);
+ gem_write(gem_fd, bo, 4092, &bbe, sizeof(bbe));
+
+ /* Create a specific queue depth of unready requests. */
+ for (n = 0; n <= max_rq; n++) {
+ int fence = -1;
+ IGT_CORK_FENCE(cork);
+
+ gem_quiescent_gpu(gem_fd);
+
+ /* Create a cork so we can create a dependency chain. */
+ if (n)
+ fence = igt_cork_plug(&cork, -1);
+
+ /* Submit n unready requests depending on the cork. */
+ for (i = 0; i < n; i++) {
+ struct drm_i915_gem_exec_object2 obj = { };
+ struct drm_i915_gem_execbuffer2 eb = { };
+
+ obj.handle = bo;
+
+ eb.buffer_count = 1;
+ eb.buffers_ptr = to_user_pointer(&obj);
+
+ eb.flags = engine | I915_EXEC_FENCE_IN;
+
+ /*
+ * In context mode each submission is on a separate
+ * context.
+ */
+ if (flags & TEST_CONTEXTS)
+ eb.rsvd1 = gem_context_create(gem_fd);
+
+ eb.rsvd2 = fence;
+
+ gem_execbuf(gem_fd, &eb);
+
+ if (flags & TEST_CONTEXTS)
+ gem_context_destroy(gem_fd, eb.rsvd1);
+ }
+
+ /* Store reported queue depth to assert against later. */
+ __query_queues(gem_fd, e, &queues);
+ queued[n] = queues.queued;
+ igt_info("n=%u queued=%u\n", n, queued[n]);
+
+ /* Unplug the queue and proceed to the next queue depth. */
+ if (fence >= 0)
+ igt_cork_unplug(&cork);
+
+ gem_sync(gem_fd, bo);
+ }
+
+ gem_close(gem_fd, bo);
+
+ for (i = 0; i <= max_rq; i++)
+ igt_assert_eq(queued[i], i);
+}
+
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+ if (gem_can_store_dword(fd, flags))
+ return __igt_spin_batch_new_poll(fd, ctx, flags);
+ else
+ return __igt_spin_batch_new(fd, ctx, flags, 0);
+}
+
+static unsigned long __spin_wait(igt_spin_t *spin, unsigned int n)
+{
+ struct timespec ts = { };
+ unsigned long t;
+
+ igt_nsec_elapsed(&ts);
+
+ if (spin->running) {
+ igt_spin_busywait_until_running(spin);
+ } else {
+ igt_debug("__spin_wait - usleep mode\n");
+ usleep(500e3); /* Better than nothing! */
+ }
+
+ t = igt_nsec_elapsed(&ts);
+
+ return spin->running ? t : 500e6 / n;
+}
+
+/*
+ * Test that the number of requests ready for execution but waiting on space on
+ * GPU is correctly reported.
+ */
+static void
+engine_runnable(int gem_fd, const struct intel_execution_engine2 *e)
+{
+ const unsigned long engine = e2ring(gem_fd, e);
+ struct drm_i915_query_engine_queues queues;
+ bool contexts = gem_has_contexts(gem_fd);
+ const unsigned int max_rq = 10;
+ igt_spin_t *spin[max_rq + 1];
+ uint32_t runnable[max_rq + 1];
+ uint32_t ctx[max_rq];
+ unsigned int n, i;
+
+ memset(runnable, 0, sizeof(runnable));
+
+ if (contexts) {
+ for (i = 0; i < max_rq; i++)
+ ctx[i] = gem_context_create(gem_fd);
+ }
+
+ /*
+ * Submit different number of requests, potentially against different
+ * contexts, in order to provoke engine runnable metric returning
+ * different numbers.
+ */
+ for (n = 0; n <= max_rq; n++) {
+ gem_quiescent_gpu(gem_fd);
+
+ for (i = 0; i < n; i++) {
+ uint32_t ctx_ = contexts ? ctx[i] : 0;
+
+ if (i == 0)
+ spin[i] = __spin_poll(gem_fd, ctx_, engine);
+ else
+ spin[i] = __igt_spin_batch_new(gem_fd, ctx_,
+ engine, 0);
+ }
+
+ if (n)
+ usleep(__spin_wait(spin[0], n) * n);
+
+ /* Query and store for later checking. */
+ __query_queues(gem_fd, e, &queues);
+ runnable[n] = queues.runnable;
+ igt_info("n=%u runnable=%u\n", n, runnable[n]);
+
+ for (i = 0; i < n; i++) {
+ igt_spin_batch_end(spin[i]);
+ gem_sync(gem_fd, spin[i]->handle);
+ igt_spin_batch_free(gem_fd, spin[i]);
+ }
+ }
+
+ if (contexts) {
+ for (i = 0; i < max_rq; i++)
+ gem_context_destroy(gem_fd, ctx[i]);
+ }
+
+ /*
+ * Check that the runnable metric is zero when nothing is submitted,
+ * and that it is greater than zero on the maximum queue depth.
+ *
+ * We cannot assert the exact value since we do not know how many
+ * requests can the submission backend consume.
+ */
+ igt_assert_eq(runnable[0], 0);
+ igt_assert(runnable[max_rq] > 0);
+
+ /*
+ * We can only test that the runnable metric is growing by one if we
+ * have context support.
+ */
+ if (contexts)
+ igt_assert_eq(runnable[max_rq] - runnable[max_rq - 1], 1);
+}
+
+/*
+ * Test that the number of requests currently executing on the GPU is correctly
+ * reported.
+ */
+static void
+engine_running(int gem_fd, const struct intel_execution_engine2 *e)
+{
+ const unsigned long engine = e2ring(gem_fd, e);
+ struct drm_i915_query_engine_queues queues;
+ const unsigned int max_rq = 10;
+ igt_spin_t *spin[max_rq + 1];
+ uint32_t running[max_rq + 1];
+ unsigned int n, i;
+
+ memset(running, 0, sizeof(running));
+ memset(spin, 0, sizeof(spin));
+
+ /*
+ * Create various queue depths of requests against the same context to
+ * try and get submission backed execute one or more on the GPU.
+ */
+ for (n = 0; n <= max_rq; n++) {
+ gem_quiescent_gpu(gem_fd);
+
+ for (i = 0; i < n; i++) {
+ if (i == 0)
+ spin[i] = __spin_poll(gem_fd, 0, engine);
+ else
+ spin[i] = __igt_spin_batch_new(gem_fd, 0,
+ engine, 0);
+ }
+
+ if (n)
+ usleep(__spin_wait(spin[0], n) * n);
+
+ /* Query and store for later checking. */
+ __query_queues(gem_fd, e, &queues);
+ running[n] = queues.running;
+ igt_info("n=%u running=%u\n", n, running[n]);
+
+ for (i = 0; i < n; i++) {
+ igt_spin_batch_end(spin[i]);
+ gem_sync(gem_fd, spin[i]->handle);
+ igt_spin_batch_free(gem_fd, spin[i]);
+ }
+ }
+
+ /*
+ * Check that the running metric is zero when nothing is submitted,
+ * one when one request is submitted, and at least one for any greater
+ * queue depth.
+ *
+ * We cannot assert the exact value since we do not know how many
+ * requests can the submission backend consume.
+ */
+ igt_assert_eq(running[0], 0);
+ for (i = 1; i <= max_rq; i++)
+ igt_assert(running[i] > 0);
+}
+
igt_main
{
+ const struct intel_execution_engine2 *e;
int fd = -1;
int devid;
@@ -524,6 +931,41 @@ igt_main
test_query_topology_known_pci_ids(fd, devid);
}
+ igt_subtest_group {
+ igt_fixture {
+ igt_require(query_engine_queues_supported(fd));
+ }
+
+ igt_subtest("engine-queues-invalid")
+ engine_queues_invalid(fd);
+
+ __for_each_engine_class_instance(fd, e) {
+ igt_subtest_group {
+ igt_fixture {
+ gem_require_engine(fd,
+ e->class,
+ e->instance);
+ }
+
+ igt_subtest_f("engine-queues-%s", e->name)
+ engine_queues(fd, e);
+
+ igt_subtest_f("engine-queued-%s", e->name)
+ engine_queued(fd, e, 0);
+
+ igt_subtest_f("engine-queued-contexts-%s",
+ e->name)
+ engine_queued(fd, e, TEST_CONTEXTS);
+
+ igt_subtest_f("engine-runnable-%s", e->name)
+ engine_runnable(fd, e);
+
+ igt_subtest_f("engine-running-%s", e->name)
+ engine_running(fd, e);
+ }
+ }
+ }
+
igt_fixture {
close(fd);
}
--
2.17.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH i-g-t 6/6] tests/i915_query: Engine queues tests
2018-06-06 12:49 ` [PATCH i-g-t 6/6] tests/i915_query: Engine queues tests Tvrtko Ursulin
@ 2018-06-06 14:33 ` Lionel Landwerlin
2018-06-08 10:02 ` Tvrtko Ursulin
0 siblings, 1 reply; 10+ messages in thread
From: Lionel Landwerlin @ 2018-06-06 14:33 UTC (permalink / raw)
To: Tvrtko Ursulin, igt-dev; +Cc: intel-gfx
Just a few suggestions below. Otherwise looks good to me.
On 06/06/18 13:49, Tvrtko Ursulin wrote:
> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>
> Basic tests to cover engine queued/runnable/running metric as reported
> by the DRM_I915_QUERY_ENGINE_QUEUES query.
>
> v2:
> * Update ABI for i915 changes.
> * Use igt_spin_busywait_until_running.
> * Support no hardware contexts.
> * More comments. (Lionel Landwerlin)
> Chris Wilson:
> * Check for sw sync support.
> * Multiple contexts queued test.
> * Simplify context and bb allocation.
> * Fix asserts in the running subtest.
>
> v3:
> * Update for uAPI change.
>
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> ---
> tests/i915_query.c | 442 +++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 442 insertions(+)
>
> diff --git a/tests/i915_query.c b/tests/i915_query.c
> index c7de8cbd8371..588428749c50 100644
> --- a/tests/i915_query.c
> +++ b/tests/i915_query.c
> @@ -22,6 +22,7 @@
> */
>
> #include "igt.h"
> +#include "sw_sync.h"
>
> #include <limits.h>
>
> @@ -477,8 +478,414 @@ test_query_topology_known_pci_ids(int fd, int devid)
> free(topo_info);
> }
>
> +#define DRM_I915_QUERY_ENGINE_QUEUES 2
> +
> +struct drm_i915_query_engine_queues {
> + /** Engine class as in enum drm_i915_gem_engine_class. */
> + __u16 class;
> +
> + /** Engine instance number. */
> + __u16 instance;
> +
> + /** Number of requests with unresolved fences and dependencies. */
> + __u32 queued;
> +
> + /** Number of ready requests waiting on a slot on GPU. */
> + __u32 runnable;
> +
> + /** Number of requests executing on the GPU. */
> + __u32 running;
> +
> + __u32 rsvd[6];
> +};
I suppose this above would be in the updated i915_drm.h.
I thought you would have put it there as part of your first commit.
> +
> +static bool query_engine_queues_supported(int fd)
> +{
> + struct drm_i915_query_item item = {
> + .query_id = DRM_I915_QUERY_ENGINE_QUEUES,
> + };
> +
> + return __i915_query_items(fd, &item, 1) == 0 && item.length > 0;
> +}
> +
> +static void engine_queues_invalid(int fd)
> +{
> + struct drm_i915_query_engine_queues queues;
> + struct drm_i915_query_item item;
> + unsigned int len;
> + unsigned int i;
> +
> + /* Flags is MBZ. */
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.flags = 1;
> + i915_query_items(fd, &item, 1);
> + igt_assert_eq(item.length, -EINVAL);
> +
> + /* Length not zero and not greater or equal required size. */
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.length = 1;
> + i915_query_items(fd, &item, 1);
> + igt_assert_eq(item.length, -EINVAL);
> +
> + /* Query correct length. */
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + i915_query_items(fd, &item, 1);
> + igt_assert(item.length >= 0);
Could be item.length > 0?
> + len = item.length;
> +
> + /* Ivalid pointer. */
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.length = len;
> + i915_query_items(fd, &item, 1);
> + igt_assert_eq(item.length, -EFAULT);
Maybe verify ULONG_MAX too?
> +
> + /* Reserved fields are MBZ. */
> +
> + for (i = 0; i < ARRAY_SIZE(queues.rsvd); i++) {
> + memset(&queues, 0, sizeof(queues));
> + queues.rsvd[i] = 1;
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.length = len;
> + item.data_ptr = to_user_pointer(&queues);
> + i915_query_items(fd, &item, 1);
> + igt_assert_eq(item.length, -EINVAL);
> + }
> +
> + memset(&queues, 0, sizeof(queues));
> + queues.class = -1;
class = I915_ENGINE_CLASS_INVALID ?
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.length = len;
> + item.data_ptr = to_user_pointer(&queues);
> + i915_query_items(fd, &item, 1);
> + igt_assert_eq(item.length, -ENOENT);
> +
> + memset(&queues, 0, sizeof(queues));
> + queues.instance = -1;
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.length = len;
> + item.data_ptr = to_user_pointer(&queues);
> + i915_query_items(fd, &item, 1);
> + igt_assert_eq(item.length, -ENOENT);
> +}
> +
> +static void engine_queues(int fd, const struct intel_execution_engine2 *e)
> +{
> + struct drm_i915_query_engine_queues queues;
> + struct drm_i915_query_item item;
> + unsigned int len;
> +
> + /* Query required buffer length. */
> + memset(&queues, 0, sizeof(queues));
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.data_ptr = to_user_pointer(&queues);
> + i915_query_items(fd, &item, 1);
> + igt_assert(item.length >= 0);
item.length > 0?
> + igt_assert(item.length <= sizeof(queues));
I guess we can expect item.length == sizeof(queues) here.
> + len = item.length;
> +
> + /* Check length larger than required works and reports same length. */
> + memset(&queues, 0, sizeof(queues));
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.data_ptr = to_user_pointer(&queues);
> + item.length = len + 1;
> + i915_query_items(fd, &item, 1);
> + igt_assert_eq(item.length, len);
> +
> + /* Actual query. */
> + memset(&queues, 0, sizeof(queues));
> + queues.class = e->class;
> + queues.instance = e->instance;
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.data_ptr = to_user_pointer(&queues);
> + item.length = len;
> + i915_query_items(fd, &item, 1);
> + igt_assert_eq(item.length, len);
> +}
> +
> +static unsigned int e2ring(int gem_fd, const struct intel_execution_engine2 *e)
> +{
> + return gem_class_instance_to_eb_flags(gem_fd, e->class, e->instance);
> +}
> +
> +static void
> +__query_queues(int fd, const struct intel_execution_engine2 *e,
> + struct drm_i915_query_engine_queues *queues)
> +{
> + struct drm_i915_query_item item;
> +
> + memset(queues, 0, sizeof(*queues));
> + queues->class = e->class;
> + queues->instance = e->instance;
> + memset(&item, 0, sizeof(item));
> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
> + item.data_ptr = to_user_pointer(queues);
> + item.length = sizeof(*queues);
> + i915_query_items(fd, &item, 1);
> + igt_assert_eq(item.length, sizeof(*queues));
> +}
> +
> +#define TEST_CONTEXTS (1 << 0)
> +
> +/*
> + * Test that the reported number of queued (not ready for execution due fences
> + * or dependencies) requests on an engine is correct.
> + */
> +static void
> +engine_queued(int gem_fd, const struct intel_execution_engine2 *e,
> + unsigned int flags)
> +{
> + const unsigned long engine = e2ring(gem_fd, e);
> + struct drm_i915_query_engine_queues queues;
> + const uint32_t bbe = MI_BATCH_BUFFER_END;
> + const unsigned int max_rq = 10;
> + uint32_t queued[max_rq + 1];
> + unsigned int n, i;
> + uint32_t bo;
> +
> + igt_require_sw_sync();
> + if (flags & TEST_CONTEXTS)
> + gem_require_contexts(gem_fd);
> +
> + memset(queued, 0, sizeof(queued));
> +
> + bo = gem_create(gem_fd, 4096);
> + gem_write(gem_fd, bo, 4092, &bbe, sizeof(bbe));
> +
> + /* Create a specific queue depth of unready requests. */
> + for (n = 0; n <= max_rq; n++) {
> + int fence = -1;
> + IGT_CORK_FENCE(cork);
> +
> + gem_quiescent_gpu(gem_fd);
> +
> + /* Create a cork so we can create a dependency chain. */
> + if (n)
> + fence = igt_cork_plug(&cork, -1);
> +
> + /* Submit n unready requests depending on the cork. */
> + for (i = 0; i < n; i++) {
> + struct drm_i915_gem_exec_object2 obj = { };
> + struct drm_i915_gem_execbuffer2 eb = { };
> +
> + obj.handle = bo;
> +
> + eb.buffer_count = 1;
> + eb.buffers_ptr = to_user_pointer(&obj);
> +
> + eb.flags = engine | I915_EXEC_FENCE_IN;
> +
> + /*
> + * In context mode each submission is on a separate
> + * context.
> + */
> + if (flags & TEST_CONTEXTS)
> + eb.rsvd1 = gem_context_create(gem_fd);
> +
> + eb.rsvd2 = fence;
> +
> + gem_execbuf(gem_fd, &eb);
> +
> + if (flags & TEST_CONTEXTS)
> + gem_context_destroy(gem_fd, eb.rsvd1);
> + }
> +
> + /* Store reported queue depth to assert against later. */
> + __query_queues(gem_fd, e, &queues);
> + queued[n] = queues.queued;
> + igt_info("n=%u queued=%u\n", n, queued[n]);
> +
> + /* Unplug the queue and proceed to the next queue depth. */
> + if (fence >= 0)
> + igt_cork_unplug(&cork);
> +
> + gem_sync(gem_fd, bo);
> + }
> +
> + gem_close(gem_fd, bo);
> +
> + for (i = 0; i <= max_rq; i++)
> + igt_assert_eq(queued[i], i);
> +}
> +
> +static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
> +{
> + if (gem_can_store_dword(fd, flags))
> + return __igt_spin_batch_new_poll(fd, ctx, flags);
> + else
> + return __igt_spin_batch_new(fd, ctx, flags, 0);
> +}
> +
> +static unsigned long __spin_wait(igt_spin_t *spin, unsigned int n)
> +{
> + struct timespec ts = { };
> + unsigned long t;
> +
> + igt_nsec_elapsed(&ts);
> +
> + if (spin->running) {
> + igt_spin_busywait_until_running(spin);
> + } else {
> + igt_debug("__spin_wait - usleep mode\n");
> + usleep(500e3); /* Better than nothing! */
> + }
> +
> + t = igt_nsec_elapsed(&ts);
> +
> + return spin->running ? t : 500e6 / n;
> +}
> +
> +/*
> + * Test that the number of requests ready for execution but waiting on space on
> + * GPU is correctly reported.
> + */
> +static void
> +engine_runnable(int gem_fd, const struct intel_execution_engine2 *e)
> +{
> + const unsigned long engine = e2ring(gem_fd, e);
> + struct drm_i915_query_engine_queues queues;
> + bool contexts = gem_has_contexts(gem_fd);
> + const unsigned int max_rq = 10;
> + igt_spin_t *spin[max_rq + 1];
> + uint32_t runnable[max_rq + 1];
> + uint32_t ctx[max_rq];
> + unsigned int n, i;
> +
> + memset(runnable, 0, sizeof(runnable));
> +
> + if (contexts) {
> + for (i = 0; i < max_rq; i++)
> + ctx[i] = gem_context_create(gem_fd);
> + }
> +
> + /*
> + * Submit different number of requests, potentially against different
> + * contexts, in order to provoke engine runnable metric returning
> + * different numbers.
> + */
> + for (n = 0; n <= max_rq; n++) {
> + gem_quiescent_gpu(gem_fd);
> +
> + for (i = 0; i < n; i++) {
> + uint32_t ctx_ = contexts ? ctx[i] : 0;
> +
> + if (i == 0)
> + spin[i] = __spin_poll(gem_fd, ctx_, engine);
> + else
> + spin[i] = __igt_spin_batch_new(gem_fd, ctx_,
> + engine, 0);
> + }
> +
> + if (n)
> + usleep(__spin_wait(spin[0], n) * n);
> +
> + /* Query and store for later checking. */
> + __query_queues(gem_fd, e, &queues);
> + runnable[n] = queues.runnable;
> + igt_info("n=%u runnable=%u\n", n, runnable[n]);
> +
> + for (i = 0; i < n; i++) {
> + igt_spin_batch_end(spin[i]);
> + gem_sync(gem_fd, spin[i]->handle);
> + igt_spin_batch_free(gem_fd, spin[i]);
> + }
> + }
> +
> + if (contexts) {
> + for (i = 0; i < max_rq; i++)
> + gem_context_destroy(gem_fd, ctx[i]);
> + }
> +
> + /*
> + * Check that the runnable metric is zero when nothing is submitted,
> + * and that it is greater than zero on the maximum queue depth.
> + *
> + * We cannot assert the exact value since we do not know how many
> + * requests can the submission backend consume.
> + */
> + igt_assert_eq(runnable[0], 0);
> + igt_assert(runnable[max_rq] > 0);
> +
> + /*
> + * We can only test that the runnable metric is growing by one if we
> + * have context support.
> + */
> + if (contexts)
> + igt_assert_eq(runnable[max_rq] - runnable[max_rq - 1], 1);
> +}
> +
> +/*
> + * Test that the number of requests currently executing on the GPU is correctly
> + * reported.
> + */
> +static void
> +engine_running(int gem_fd, const struct intel_execution_engine2 *e)
> +{
> + const unsigned long engine = e2ring(gem_fd, e);
> + struct drm_i915_query_engine_queues queues;
> + const unsigned int max_rq = 10;
> + igt_spin_t *spin[max_rq + 1];
> + uint32_t running[max_rq + 1];
> + unsigned int n, i;
> +
> + memset(running, 0, sizeof(running));
> + memset(spin, 0, sizeof(spin));
> +
> + /*
> + * Create various queue depths of requests against the same context to
> + * try and get submission backed execute one or more on the GPU.
> + */
> + for (n = 0; n <= max_rq; n++) {
> + gem_quiescent_gpu(gem_fd);
> +
> + for (i = 0; i < n; i++) {
> + if (i == 0)
> + spin[i] = __spin_poll(gem_fd, 0, engine);
> + else
> + spin[i] = __igt_spin_batch_new(gem_fd, 0,
> + engine, 0);
> + }
> +
> + if (n)
> + usleep(__spin_wait(spin[0], n) * n);
> +
> + /* Query and store for later checking. */
> + __query_queues(gem_fd, e, &queues);
> + running[n] = queues.running;
> + igt_info("n=%u running=%u\n", n, running[n]);
> +
> + for (i = 0; i < n; i++) {
> + igt_spin_batch_end(spin[i]);
> + gem_sync(gem_fd, spin[i]->handle);
> + igt_spin_batch_free(gem_fd, spin[i]);
> + }
> + }
> +
> + /*
> + * Check that the running metric is zero when nothing is submitted,
> + * one when one request is submitted, and at least one for any greater
> + * queue depth.
> + *
> + * We cannot assert the exact value since we do not know how many
> + * requests can the submission backend consume.
> + */
> + igt_assert_eq(running[0], 0);
> + for (i = 1; i <= max_rq; i++)
> + igt_assert(running[i] > 0);
> +}
> +
> igt_main
> {
> + const struct intel_execution_engine2 *e;
> int fd = -1;
> int devid;
>
> @@ -524,6 +931,41 @@ igt_main
> test_query_topology_known_pci_ids(fd, devid);
> }
>
> + igt_subtest_group {
> + igt_fixture {
> + igt_require(query_engine_queues_supported(fd));
> + }
> +
> + igt_subtest("engine-queues-invalid")
> + engine_queues_invalid(fd);
> +
> + __for_each_engine_class_instance(fd, e) {
> + igt_subtest_group {
> + igt_fixture {
> + gem_require_engine(fd,
> + e->class,
> + e->instance);
> + }
> +
> + igt_subtest_f("engine-queues-%s", e->name)
> + engine_queues(fd, e);
> +
> + igt_subtest_f("engine-queued-%s", e->name)
> + engine_queued(fd, e, 0);
> +
> + igt_subtest_f("engine-queued-contexts-%s",
> + e->name)
> + engine_queued(fd, e, TEST_CONTEXTS);
> +
> + igt_subtest_f("engine-runnable-%s", e->name)
> + engine_runnable(fd, e);
> +
> + igt_subtest_f("engine-running-%s", e->name)
> + engine_running(fd, e);
> + }
> + }
> + }
> +
> igt_fixture {
> close(fd);
> }
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH i-g-t 6/6] tests/i915_query: Engine queues tests
2018-06-06 14:33 ` Lionel Landwerlin
@ 2018-06-08 10:02 ` Tvrtko Ursulin
2018-06-08 10:13 ` Lionel Landwerlin
0 siblings, 1 reply; 10+ messages in thread
From: Tvrtko Ursulin @ 2018-06-08 10:02 UTC (permalink / raw)
To: Lionel Landwerlin, Tvrtko Ursulin, igt-dev; +Cc: intel-gfx
On 06/06/2018 15:33, Lionel Landwerlin wrote:
> Just a few suggestions below. Otherwise looks good to me.
>
> On 06/06/18 13:49, Tvrtko Ursulin wrote:
>> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>>
>> Basic tests to cover engine queued/runnable/running metric as reported
>> by the DRM_I915_QUERY_ENGINE_QUEUES query.
>>
>> v2:
>> * Update ABI for i915 changes.
>> * Use igt_spin_busywait_until_running.
>> * Support no hardware contexts.
>> * More comments. (Lionel Landwerlin)
>> Chris Wilson:
>> * Check for sw sync support.
>> * Multiple contexts queued test.
>> * Simplify context and bb allocation.
>> * Fix asserts in the running subtest.
>>
>> v3:
>> * Update for uAPI change.
>>
>> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>> ---
>> tests/i915_query.c | 442 +++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 442 insertions(+)
>>
>> diff --git a/tests/i915_query.c b/tests/i915_query.c
>> index c7de8cbd8371..588428749c50 100644
>> --- a/tests/i915_query.c
>> +++ b/tests/i915_query.c
>> @@ -22,6 +22,7 @@
>> */
>> #include "igt.h"
>> +#include "sw_sync.h"
>> #include <limits.h>
>> @@ -477,8 +478,414 @@ test_query_topology_known_pci_ids(int fd, int
>> devid)
>> free(topo_info);
>> }
>> +#define DRM_I915_QUERY_ENGINE_QUEUES 2
>> +
>> +struct drm_i915_query_engine_queues {
>> + /** Engine class as in enum drm_i915_gem_engine_class. */
>> + __u16 class;
>> +
>> + /** Engine instance number. */
>> + __u16 instance;
>> +
>> + /** Number of requests with unresolved fences and dependencies. */
>> + __u32 queued;
>> +
>> + /** Number of ready requests waiting on a slot on GPU. */
>> + __u32 runnable;
>> +
>> + /** Number of requests executing on the GPU. */
>> + __u32 running;
>> +
>> + __u32 rsvd[6];
>> +};
>
> I suppose this above would be in the updated i915_drm.h.
> I thought you would have put it there as part of your first commit.
Yes, or I should prefix the copy with local_ as we usually do.
>> +
>> +static bool query_engine_queues_supported(int fd)
>> +{
>> + struct drm_i915_query_item item = {
>> + .query_id = DRM_I915_QUERY_ENGINE_QUEUES,
>> + };
>> +
>> + return __i915_query_items(fd, &item, 1) == 0 && item.length > 0;
>> +}
>> +
>> +static void engine_queues_invalid(int fd)
>> +{
>> + struct drm_i915_query_engine_queues queues;
>> + struct drm_i915_query_item item;
>> + unsigned int len;
>> + unsigned int i;
>> +
>> + /* Flags is MBZ. */
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.flags = 1;
>> + i915_query_items(fd, &item, 1);
>> + igt_assert_eq(item.length, -EINVAL);
>> +
>> + /* Length not zero and not greater or equal required size. */
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.length = 1;
>> + i915_query_items(fd, &item, 1);
>> + igt_assert_eq(item.length, -EINVAL);
>> +
>> + /* Query correct length. */
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + i915_query_items(fd, &item, 1);
>> + igt_assert(item.length >= 0);
>
> Could be item.length > 0?
True, should be even. :)
>
>> + len = item.length;
>> +
>> + /* Ivalid pointer. */
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.length = len;
>> + i915_query_items(fd, &item, 1);
>> + igt_assert_eq(item.length, -EFAULT);
>
> Maybe verify ULONG_MAX too?
item.data_ptr = ULONG_MAX? Can do.
>
>> +
>> + /* Reserved fields are MBZ. */
>> +
>> + for (i = 0; i < ARRAY_SIZE(queues.rsvd); i++) {
>> + memset(&queues, 0, sizeof(queues));
>> + queues.rsvd[i] = 1;
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.length = len;
>> + item.data_ptr = to_user_pointer(&queues);
>> + i915_query_items(fd, &item, 1);
>> + igt_assert_eq(item.length, -EINVAL);
>> + }
>> +
>> + memset(&queues, 0, sizeof(queues));
>> + queues.class = -1;
>
> class = I915_ENGINE_CLASS_INVALID ?
Yes thanks!
>
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.length = len;
>> + item.data_ptr = to_user_pointer(&queues);
>> + i915_query_items(fd, &item, 1);
>> + igt_assert_eq(item.length, -ENOENT);
>> +
>> + memset(&queues, 0, sizeof(queues));
>> + queues.instance = -1;
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.length = len;
>> + item.data_ptr = to_user_pointer(&queues);
>> + i915_query_items(fd, &item, 1);
>> + igt_assert_eq(item.length, -ENOENT);
>> +}
>> +
>> +static void engine_queues(int fd, const struct
>> intel_execution_engine2 *e)
>> +{
>> + struct drm_i915_query_engine_queues queues;
>> + struct drm_i915_query_item item;
>> + unsigned int len;
>> +
>> + /* Query required buffer length. */
>> + memset(&queues, 0, sizeof(queues));
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.data_ptr = to_user_pointer(&queues);
>> + i915_query_items(fd, &item, 1);
>> + igt_assert(item.length >= 0);
>
> item.length > 0?
Marking on TODO..
>
>> + igt_assert(item.length <= sizeof(queues));
>
> I guess we can expect item.length == sizeof(queues) here.
Hmm yes, no idea what I was thinking there. :I
>
>
>> + len = item.length;
>> +
>> + /* Check length larger than required works and reports same
>> length. */
>> + memset(&queues, 0, sizeof(queues));
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.data_ptr = to_user_pointer(&queues);
>> + item.length = len + 1;
>> + i915_query_items(fd, &item, 1);
>> + igt_assert_eq(item.length, len);
>> +
>> + /* Actual query. */
>> + memset(&queues, 0, sizeof(queues));
>> + queues.class = e->class;
>> + queues.instance = e->instance;
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.data_ptr = to_user_pointer(&queues);
>> + item.length = len;
>> + i915_query_items(fd, &item, 1);
>> + igt_assert_eq(item.length, len);
>> +}
>> +
>> +static unsigned int e2ring(int gem_fd, const struct
>> intel_execution_engine2 *e)
>> +{
>> + return gem_class_instance_to_eb_flags(gem_fd, e->class,
>> e->instance);
>> +}
>> +
>> +static void
>> +__query_queues(int fd, const struct intel_execution_engine2 *e,
>> + struct drm_i915_query_engine_queues *queues)
>> +{
>> + struct drm_i915_query_item item;
>> +
>> + memset(queues, 0, sizeof(*queues));
>> + queues->class = e->class;
>> + queues->instance = e->instance;
>> + memset(&item, 0, sizeof(item));
>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>> + item.data_ptr = to_user_pointer(queues);
>> + item.length = sizeof(*queues);
>> + i915_query_items(fd, &item, 1);
>> + igt_assert_eq(item.length, sizeof(*queues));
>> +}
>> +
>> +#define TEST_CONTEXTS (1 << 0)
>> +
>> +/*
>> + * Test that the reported number of queued (not ready for execution
>> due fences
>> + * or dependencies) requests on an engine is correct.
>> + */
>> +static void
>> +engine_queued(int gem_fd, const struct intel_execution_engine2 *e,
>> + unsigned int flags)
>> +{
>> + const unsigned long engine = e2ring(gem_fd, e);
>> + struct drm_i915_query_engine_queues queues;
>> + const uint32_t bbe = MI_BATCH_BUFFER_END;
>> + const unsigned int max_rq = 10;
>> + uint32_t queued[max_rq + 1];
>> + unsigned int n, i;
>> + uint32_t bo;
>> +
>> + igt_require_sw_sync();
>> + if (flags & TEST_CONTEXTS)
>> + gem_require_contexts(gem_fd);
>> +
>> + memset(queued, 0, sizeof(queued));
>> +
>> + bo = gem_create(gem_fd, 4096);
>> + gem_write(gem_fd, bo, 4092, &bbe, sizeof(bbe));
>> +
>> + /* Create a specific queue depth of unready requests. */
>> + for (n = 0; n <= max_rq; n++) {
>> + int fence = -1;
>> + IGT_CORK_FENCE(cork);
>> +
>> + gem_quiescent_gpu(gem_fd);
>> +
>> + /* Create a cork so we can create a dependency chain. */
>> + if (n)
>> + fence = igt_cork_plug(&cork, -1);
>> +
>> + /* Submit n unready requests depending on the cork. */
>> + for (i = 0; i < n; i++) {
>> + struct drm_i915_gem_exec_object2 obj = { };
>> + struct drm_i915_gem_execbuffer2 eb = { };
>> +
>> + obj.handle = bo;
>> +
>> + eb.buffer_count = 1;
>> + eb.buffers_ptr = to_user_pointer(&obj);
>> +
>> + eb.flags = engine | I915_EXEC_FENCE_IN;
>> +
>> + /*
>> + * In context mode each submission is on a separate
>> + * context.
>> + */
>> + if (flags & TEST_CONTEXTS)
>> + eb.rsvd1 = gem_context_create(gem_fd);
>> +
>> + eb.rsvd2 = fence;
>> +
>> + gem_execbuf(gem_fd, &eb);
>> +
>> + if (flags & TEST_CONTEXTS)
>> + gem_context_destroy(gem_fd, eb.rsvd1);
>> + }
>> +
>> + /* Store reported queue depth to assert against later. */
>> + __query_queues(gem_fd, e, &queues);
>> + queued[n] = queues.queued;
>> + igt_info("n=%u queued=%u\n", n, queued[n]);
>> +
>> + /* Unplug the queue and proceed to the next queue depth. */
>> + if (fence >= 0)
>> + igt_cork_unplug(&cork);
>> +
>> + gem_sync(gem_fd, bo);
>> + }
>> +
>> + gem_close(gem_fd, bo);
>> +
>> + for (i = 0; i <= max_rq; i++)
>> + igt_assert_eq(queued[i], i);
>> +}
>> +
>> +static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long
>> flags)
>> +{
>> + if (gem_can_store_dword(fd, flags))
>> + return __igt_spin_batch_new_poll(fd, ctx, flags);
>> + else
>> + return __igt_spin_batch_new(fd, ctx, flags, 0);
>> +}
>> +
>> +static unsigned long __spin_wait(igt_spin_t *spin, unsigned int n)
>> +{
>> + struct timespec ts = { };
>> + unsigned long t;
>> +
>> + igt_nsec_elapsed(&ts);
>> +
>> + if (spin->running) {
>> + igt_spin_busywait_until_running(spin);
>> + } else {
>> + igt_debug("__spin_wait - usleep mode\n");
>> + usleep(500e3); /* Better than nothing! */
>> + }
>> +
>> + t = igt_nsec_elapsed(&ts);
>> +
>> + return spin->running ? t : 500e6 / n;
>> +}
>> +
>> +/*
>> + * Test that the number of requests ready for execution but waiting
>> on space on
>> + * GPU is correctly reported.
>> + */
>> +static void
>> +engine_runnable(int gem_fd, const struct intel_execution_engine2 *e)
>> +{
>> + const unsigned long engine = e2ring(gem_fd, e);
>> + struct drm_i915_query_engine_queues queues;
>> + bool contexts = gem_has_contexts(gem_fd);
>> + const unsigned int max_rq = 10;
>> + igt_spin_t *spin[max_rq + 1];
>> + uint32_t runnable[max_rq + 1];
>> + uint32_t ctx[max_rq];
>> + unsigned int n, i;
>> +
>> + memset(runnable, 0, sizeof(runnable));
>> +
>> + if (contexts) {
>> + for (i = 0; i < max_rq; i++)
>> + ctx[i] = gem_context_create(gem_fd);
>> + }
>> +
>> + /*
>> + * Submit different number of requests, potentially against
>> different
>> + * contexts, in order to provoke engine runnable metric returning
>> + * different numbers.
>> + */
>> + for (n = 0; n <= max_rq; n++) {
>> + gem_quiescent_gpu(gem_fd);
>> +
>> + for (i = 0; i < n; i++) {
>> + uint32_t ctx_ = contexts ? ctx[i] : 0;
>> +
>> + if (i == 0)
>> + spin[i] = __spin_poll(gem_fd, ctx_, engine);
>> + else
>> + spin[i] = __igt_spin_batch_new(gem_fd, ctx_,
>> + engine, 0);
>> + }
>> +
>> + if (n)
>> + usleep(__spin_wait(spin[0], n) * n);
>> +
>> + /* Query and store for later checking. */
>> + __query_queues(gem_fd, e, &queues);
>> + runnable[n] = queues.runnable;
>> + igt_info("n=%u runnable=%u\n", n, runnable[n]);
>> +
>> + for (i = 0; i < n; i++) {
>> + igt_spin_batch_end(spin[i]);
>> + gem_sync(gem_fd, spin[i]->handle);
>> + igt_spin_batch_free(gem_fd, spin[i]);
>> + }
>> + }
>> +
>> + if (contexts) {
>> + for (i = 0; i < max_rq; i++)
>> + gem_context_destroy(gem_fd, ctx[i]);
>> + }
>> +
>> + /*
>> + * Check that the runnable metric is zero when nothing is submitted,
>> + * and that it is greater than zero on the maximum queue depth.
>> + *
>> + * We cannot assert the exact value since we do not know how many
>> + * requests can the submission backend consume.
>> + */
>> + igt_assert_eq(runnable[0], 0);
>> + igt_assert(runnable[max_rq] > 0);
>> +
>> + /*
>> + * We can only test that the runnable metric is growing by one if we
>> + * have context support.
>> + */
>> + if (contexts)
>> + igt_assert_eq(runnable[max_rq] - runnable[max_rq - 1], 1);
>> +}
>> +
>> +/*
>> + * Test that the number of requests currently executing on the GPU is
>> correctly
>> + * reported.
>> + */
>> +static void
>> +engine_running(int gem_fd, const struct intel_execution_engine2 *e)
>> +{
>> + const unsigned long engine = e2ring(gem_fd, e);
>> + struct drm_i915_query_engine_queues queues;
>> + const unsigned int max_rq = 10;
>> + igt_spin_t *spin[max_rq + 1];
>> + uint32_t running[max_rq + 1];
>> + unsigned int n, i;
>> +
>> + memset(running, 0, sizeof(running));
>> + memset(spin, 0, sizeof(spin));
>> +
>> + /*
>> + * Create various queue depths of requests against the same
>> context to
>> + * try and get submission backed execute one or more on the GPU.
>> + */
>> + for (n = 0; n <= max_rq; n++) {
>> + gem_quiescent_gpu(gem_fd);
>> +
>> + for (i = 0; i < n; i++) {
>> + if (i == 0)
>> + spin[i] = __spin_poll(gem_fd, 0, engine);
>> + else
>> + spin[i] = __igt_spin_batch_new(gem_fd, 0,
>> + engine, 0);
>> + }
>> +
>> + if (n)
>> + usleep(__spin_wait(spin[0], n) * n);
>> +
>> + /* Query and store for later checking. */
>> + __query_queues(gem_fd, e, &queues);
>> + running[n] = queues.running;
>> + igt_info("n=%u running=%u\n", n, running[n]);
>> +
>> + for (i = 0; i < n; i++) {
>> + igt_spin_batch_end(spin[i]);
>> + gem_sync(gem_fd, spin[i]->handle);
>> + igt_spin_batch_free(gem_fd, spin[i]);
>> + }
>> + }
>> +
>> + /*
>> + * Check that the running metric is zero when nothing is submitted,
>> + * one when one request is submitted, and at least one for any
>> greater
>> + * queue depth.
>> + *
>> + * We cannot assert the exact value since we do not know how many
>> + * requests can the submission backend consume.
>> + */
>> + igt_assert_eq(running[0], 0);
>> + for (i = 1; i <= max_rq; i++)
>> + igt_assert(running[i] > 0);
>> +}
>> +
>> igt_main
>> {
>> + const struct intel_execution_engine2 *e;
>> int fd = -1;
>> int devid;
>> @@ -524,6 +931,41 @@ igt_main
>> test_query_topology_known_pci_ids(fd, devid);
>> }
>> + igt_subtest_group {
>> + igt_fixture {
>> + igt_require(query_engine_queues_supported(fd));
>> + }
>> +
>> + igt_subtest("engine-queues-invalid")
>> + engine_queues_invalid(fd);
>> +
>> + __for_each_engine_class_instance(fd, e) {
>> + igt_subtest_group {
>> + igt_fixture {
>> + gem_require_engine(fd,
>> + e->class,
>> + e->instance);
>> + }
>> +
>> + igt_subtest_f("engine-queues-%s", e->name)
>> + engine_queues(fd, e);
>> +
>> + igt_subtest_f("engine-queued-%s", e->name)
>> + engine_queued(fd, e, 0);
>> +
>> + igt_subtest_f("engine-queued-contexts-%s",
>> + e->name)
>> + engine_queued(fd, e, TEST_CONTEXTS);
>> +
>> + igt_subtest_f("engine-runnable-%s", e->name)
>> + engine_runnable(fd, e);
>> +
>> + igt_subtest_f("engine-running-%s", e->name)
>> + engine_running(fd, e);
>> + }
>> + }
>> + }
>> +
>> igt_fixture {
>> close(fd);
>> }
Thanks for reading it through. I have marked the tweaks for the next
resend. Problem with this query is that userspace is AWOL so until it
materializes it will be just for reference.
Regards,
Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH i-g-t 6/6] tests/i915_query: Engine queues tests
2018-06-08 10:02 ` Tvrtko Ursulin
@ 2018-06-08 10:13 ` Lionel Landwerlin
0 siblings, 0 replies; 10+ messages in thread
From: Lionel Landwerlin @ 2018-06-08 10:13 UTC (permalink / raw)
To: Tvrtko Ursulin, Tvrtko Ursulin, igt-dev; +Cc: intel-gfx
On 08/06/18 11:02, Tvrtko Ursulin wrote:
>
> On 06/06/2018 15:33, Lionel Landwerlin wrote:
>> Just a few suggestions below. Otherwise looks good to me.
>>
>> On 06/06/18 13:49, Tvrtko Ursulin wrote:
>>> From: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>>>
>>> Basic tests to cover engine queued/runnable/running metric as reported
>>> by the DRM_I915_QUERY_ENGINE_QUEUES query.
>>>
>>> v2:
>>> * Update ABI for i915 changes.
>>> * Use igt_spin_busywait_until_running.
>>> * Support no hardware contexts.
>>> * More comments. (Lionel Landwerlin)
>>> Chris Wilson:
>>> * Check for sw sync support.
>>> * Multiple contexts queued test.
>>> * Simplify context and bb allocation.
>>> * Fix asserts in the running subtest.
>>>
>>> v3:
>>> * Update for uAPI change.
>>>
>>> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
>>> ---
>>> tests/i915_query.c | 442
>>> +++++++++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 442 insertions(+)
>>>
>>> diff --git a/tests/i915_query.c b/tests/i915_query.c
>>> index c7de8cbd8371..588428749c50 100644
>>> --- a/tests/i915_query.c
>>> +++ b/tests/i915_query.c
>>> @@ -22,6 +22,7 @@
>>> */
>>> #include "igt.h"
>>> +#include "sw_sync.h"
>>> #include <limits.h>
>>> @@ -477,8 +478,414 @@ test_query_topology_known_pci_ids(int fd, int
>>> devid)
>>> free(topo_info);
>>> }
>>> +#define DRM_I915_QUERY_ENGINE_QUEUES 2
>>> +
>>> +struct drm_i915_query_engine_queues {
>>> + /** Engine class as in enum drm_i915_gem_engine_class. */
>>> + __u16 class;
>>> +
>>> + /** Engine instance number. */
>>> + __u16 instance;
>>> +
>>> + /** Number of requests with unresolved fences and dependencies. */
>>> + __u32 queued;
>>> +
>>> + /** Number of ready requests waiting on a slot on GPU. */
>>> + __u32 runnable;
>>> +
>>> + /** Number of requests executing on the GPU. */
>>> + __u32 running;
>>> +
>>> + __u32 rsvd[6];
>>> +};
>>
>> I suppose this above would be in the updated i915_drm.h.
>> I thought you would have put it there as part of your first commit.
>
> Yes, or I should prefix the copy with local_ as we usually do.
I think the point of copying the headers locally was to get rid of the
local_ etc...
>
>>> +
>>> +static bool query_engine_queues_supported(int fd)
>>> +{
>>> + struct drm_i915_query_item item = {
>>> + .query_id = DRM_I915_QUERY_ENGINE_QUEUES,
>>> + };
>>> +
>>> + return __i915_query_items(fd, &item, 1) == 0 && item.length > 0;
>>> +}
>>> +
>>> +static void engine_queues_invalid(int fd)
>>> +{
>>> + struct drm_i915_query_engine_queues queues;
>>> + struct drm_i915_query_item item;
>>> + unsigned int len;
>>> + unsigned int i;
>>> +
>>> + /* Flags is MBZ. */
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.flags = 1;
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert_eq(item.length, -EINVAL);
>>> +
>>> + /* Length not zero and not greater or equal required size. */
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.length = 1;
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert_eq(item.length, -EINVAL);
>>> +
>>> + /* Query correct length. */
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert(item.length >= 0);
>>
>> Could be item.length > 0?
>
> True, should be even. :)
>
>>
>>> + len = item.length;
>>> +
>>> + /* Ivalid pointer. */
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.length = len;
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert_eq(item.length, -EFAULT);
>>
>> Maybe verify ULONG_MAX too?
>
> item.data_ptr = ULONG_MAX? Can do.
>
>>
>>> +
>>> + /* Reserved fields are MBZ. */
>>> +
>>> + for (i = 0; i < ARRAY_SIZE(queues.rsvd); i++) {
>>> + memset(&queues, 0, sizeof(queues));
>>> + queues.rsvd[i] = 1;
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.length = len;
>>> + item.data_ptr = to_user_pointer(&queues);
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert_eq(item.length, -EINVAL);
>>> + }
>>> +
>>> + memset(&queues, 0, sizeof(queues));
>>> + queues.class = -1;
>>
>> class = I915_ENGINE_CLASS_INVALID ?
>
> Yes thanks!
>
>>
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.length = len;
>>> + item.data_ptr = to_user_pointer(&queues);
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert_eq(item.length, -ENOENT);
>>> +
>>> + memset(&queues, 0, sizeof(queues));
>>> + queues.instance = -1;
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.length = len;
>>> + item.data_ptr = to_user_pointer(&queues);
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert_eq(item.length, -ENOENT);
>>> +}
>>> +
>>> +static void engine_queues(int fd, const struct
>>> intel_execution_engine2 *e)
>>> +{
>>> + struct drm_i915_query_engine_queues queues;
>>> + struct drm_i915_query_item item;
>>> + unsigned int len;
>>> +
>>> + /* Query required buffer length. */
>>> + memset(&queues, 0, sizeof(queues));
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.data_ptr = to_user_pointer(&queues);
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert(item.length >= 0);
>>
>> item.length > 0?
>
> Marking on TODO..
>
>>
>>> + igt_assert(item.length <= sizeof(queues));
>>
>> I guess we can expect item.length == sizeof(queues) here.
>
> Hmm yes, no idea what I was thinking there. :I
>
>>
>>
>>> + len = item.length;
>>> +
>>> + /* Check length larger than required works and reports same
>>> length. */
>>> + memset(&queues, 0, sizeof(queues));
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.data_ptr = to_user_pointer(&queues);
>>> + item.length = len + 1;
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert_eq(item.length, len);
>>> +
>>> + /* Actual query. */
>>> + memset(&queues, 0, sizeof(queues));
>>> + queues.class = e->class;
>>> + queues.instance = e->instance;
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.data_ptr = to_user_pointer(&queues);
>>> + item.length = len;
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert_eq(item.length, len);
>>> +}
>>> +
>>> +static unsigned int e2ring(int gem_fd, const struct
>>> intel_execution_engine2 *e)
>>> +{
>>> + return gem_class_instance_to_eb_flags(gem_fd, e->class,
>>> e->instance);
>>> +}
>>> +
>>> +static void
>>> +__query_queues(int fd, const struct intel_execution_engine2 *e,
>>> + struct drm_i915_query_engine_queues *queues)
>>> +{
>>> + struct drm_i915_query_item item;
>>> +
>>> + memset(queues, 0, sizeof(*queues));
>>> + queues->class = e->class;
>>> + queues->instance = e->instance;
>>> + memset(&item, 0, sizeof(item));
>>> + item.query_id = DRM_I915_QUERY_ENGINE_QUEUES;
>>> + item.data_ptr = to_user_pointer(queues);
>>> + item.length = sizeof(*queues);
>>> + i915_query_items(fd, &item, 1);
>>> + igt_assert_eq(item.length, sizeof(*queues));
>>> +}
>>> +
>>> +#define TEST_CONTEXTS (1 << 0)
>>> +
>>> +/*
>>> + * Test that the reported number of queued (not ready for execution
>>> due fences
>>> + * or dependencies) requests on an engine is correct.
>>> + */
>>> +static void
>>> +engine_queued(int gem_fd, const struct intel_execution_engine2 *e,
>>> + unsigned int flags)
>>> +{
>>> + const unsigned long engine = e2ring(gem_fd, e);
>>> + struct drm_i915_query_engine_queues queues;
>>> + const uint32_t bbe = MI_BATCH_BUFFER_END;
>>> + const unsigned int max_rq = 10;
>>> + uint32_t queued[max_rq + 1];
>>> + unsigned int n, i;
>>> + uint32_t bo;
>>> +
>>> + igt_require_sw_sync();
>>> + if (flags & TEST_CONTEXTS)
>>> + gem_require_contexts(gem_fd);
>>> +
>>> + memset(queued, 0, sizeof(queued));
>>> +
>>> + bo = gem_create(gem_fd, 4096);
>>> + gem_write(gem_fd, bo, 4092, &bbe, sizeof(bbe));
>>> +
>>> + /* Create a specific queue depth of unready requests. */
>>> + for (n = 0; n <= max_rq; n++) {
>>> + int fence = -1;
>>> + IGT_CORK_FENCE(cork);
>>> +
>>> + gem_quiescent_gpu(gem_fd);
>>> +
>>> + /* Create a cork so we can create a dependency chain. */
>>> + if (n)
>>> + fence = igt_cork_plug(&cork, -1);
>>> +
>>> + /* Submit n unready requests depending on the cork. */
>>> + for (i = 0; i < n; i++) {
>>> + struct drm_i915_gem_exec_object2 obj = { };
>>> + struct drm_i915_gem_execbuffer2 eb = { };
>>> +
>>> + obj.handle = bo;
>>> +
>>> + eb.buffer_count = 1;
>>> + eb.buffers_ptr = to_user_pointer(&obj);
>>> +
>>> + eb.flags = engine | I915_EXEC_FENCE_IN;
>>> +
>>> + /*
>>> + * In context mode each submission is on a separate
>>> + * context.
>>> + */
>>> + if (flags & TEST_CONTEXTS)
>>> + eb.rsvd1 = gem_context_create(gem_fd);
>>> +
>>> + eb.rsvd2 = fence;
>>> +
>>> + gem_execbuf(gem_fd, &eb);
>>> +
>>> + if (flags & TEST_CONTEXTS)
>>> + gem_context_destroy(gem_fd, eb.rsvd1);
>>> + }
>>> +
>>> + /* Store reported queue depth to assert against later. */
>>> + __query_queues(gem_fd, e, &queues);
>>> + queued[n] = queues.queued;
>>> + igt_info("n=%u queued=%u\n", n, queued[n]);
>>> +
>>> + /* Unplug the queue and proceed to the next queue depth. */
>>> + if (fence >= 0)
>>> + igt_cork_unplug(&cork);
>>> +
>>> + gem_sync(gem_fd, bo);
>>> + }
>>> +
>>> + gem_close(gem_fd, bo);
>>> +
>>> + for (i = 0; i <= max_rq; i++)
>>> + igt_assert_eq(queued[i], i);
>>> +}
>>> +
>>> +static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long
>>> flags)
>>> +{
>>> + if (gem_can_store_dword(fd, flags))
>>> + return __igt_spin_batch_new_poll(fd, ctx, flags);
>>> + else
>>> + return __igt_spin_batch_new(fd, ctx, flags, 0);
>>> +}
>>> +
>>> +static unsigned long __spin_wait(igt_spin_t *spin, unsigned int n)
>>> +{
>>> + struct timespec ts = { };
>>> + unsigned long t;
>>> +
>>> + igt_nsec_elapsed(&ts);
>>> +
>>> + if (spin->running) {
>>> + igt_spin_busywait_until_running(spin);
>>> + } else {
>>> + igt_debug("__spin_wait - usleep mode\n");
>>> + usleep(500e3); /* Better than nothing! */
>>> + }
>>> +
>>> + t = igt_nsec_elapsed(&ts);
>>> +
>>> + return spin->running ? t : 500e6 / n;
>>> +}
>>> +
>>> +/*
>>> + * Test that the number of requests ready for execution but waiting
>>> on space on
>>> + * GPU is correctly reported.
>>> + */
>>> +static void
>>> +engine_runnable(int gem_fd, const struct intel_execution_engine2 *e)
>>> +{
>>> + const unsigned long engine = e2ring(gem_fd, e);
>>> + struct drm_i915_query_engine_queues queues;
>>> + bool contexts = gem_has_contexts(gem_fd);
>>> + const unsigned int max_rq = 10;
>>> + igt_spin_t *spin[max_rq + 1];
>>> + uint32_t runnable[max_rq + 1];
>>> + uint32_t ctx[max_rq];
>>> + unsigned int n, i;
>>> +
>>> + memset(runnable, 0, sizeof(runnable));
>>> +
>>> + if (contexts) {
>>> + for (i = 0; i < max_rq; i++)
>>> + ctx[i] = gem_context_create(gem_fd);
>>> + }
>>> +
>>> + /*
>>> + * Submit different number of requests, potentially against
>>> different
>>> + * contexts, in order to provoke engine runnable metric returning
>>> + * different numbers.
>>> + */
>>> + for (n = 0; n <= max_rq; n++) {
>>> + gem_quiescent_gpu(gem_fd);
>>> +
>>> + for (i = 0; i < n; i++) {
>>> + uint32_t ctx_ = contexts ? ctx[i] : 0;
>>> +
>>> + if (i == 0)
>>> + spin[i] = __spin_poll(gem_fd, ctx_, engine);
>>> + else
>>> + spin[i] = __igt_spin_batch_new(gem_fd, ctx_,
>>> + engine, 0);
>>> + }
>>> +
>>> + if (n)
>>> + usleep(__spin_wait(spin[0], n) * n);
>>> +
>>> + /* Query and store for later checking. */
>>> + __query_queues(gem_fd, e, &queues);
>>> + runnable[n] = queues.runnable;
>>> + igt_info("n=%u runnable=%u\n", n, runnable[n]);
>>> +
>>> + for (i = 0; i < n; i++) {
>>> + igt_spin_batch_end(spin[i]);
>>> + gem_sync(gem_fd, spin[i]->handle);
>>> + igt_spin_batch_free(gem_fd, spin[i]);
>>> + }
>>> + }
>>> +
>>> + if (contexts) {
>>> + for (i = 0; i < max_rq; i++)
>>> + gem_context_destroy(gem_fd, ctx[i]);
>>> + }
>>> +
>>> + /*
>>> + * Check that the runnable metric is zero when nothing is
>>> submitted,
>>> + * and that it is greater than zero on the maximum queue depth.
>>> + *
>>> + * We cannot assert the exact value since we do not know how many
>>> + * requests can the submission backend consume.
>>> + */
>>> + igt_assert_eq(runnable[0], 0);
>>> + igt_assert(runnable[max_rq] > 0);
>>> +
>>> + /*
>>> + * We can only test that the runnable metric is growing by one
>>> if we
>>> + * have context support.
>>> + */
>>> + if (contexts)
>>> + igt_assert_eq(runnable[max_rq] - runnable[max_rq - 1], 1);
>>> +}
>>> +
>>> +/*
>>> + * Test that the number of requests currently executing on the GPU
>>> is correctly
>>> + * reported.
>>> + */
>>> +static void
>>> +engine_running(int gem_fd, const struct intel_execution_engine2 *e)
>>> +{
>>> + const unsigned long engine = e2ring(gem_fd, e);
>>> + struct drm_i915_query_engine_queues queues;
>>> + const unsigned int max_rq = 10;
>>> + igt_spin_t *spin[max_rq + 1];
>>> + uint32_t running[max_rq + 1];
>>> + unsigned int n, i;
>>> +
>>> + memset(running, 0, sizeof(running));
>>> + memset(spin, 0, sizeof(spin));
>>> +
>>> + /*
>>> + * Create various queue depths of requests against the same
>>> context to
>>> + * try and get submission backed execute one or more on the GPU.
>>> + */
>>> + for (n = 0; n <= max_rq; n++) {
>>> + gem_quiescent_gpu(gem_fd);
>>> +
>>> + for (i = 0; i < n; i++) {
>>> + if (i == 0)
>>> + spin[i] = __spin_poll(gem_fd, 0, engine);
>>> + else
>>> + spin[i] = __igt_spin_batch_new(gem_fd, 0,
>>> + engine, 0);
>>> + }
>>> +
>>> + if (n)
>>> + usleep(__spin_wait(spin[0], n) * n);
>>> +
>>> + /* Query and store for later checking. */
>>> + __query_queues(gem_fd, e, &queues);
>>> + running[n] = queues.running;
>>> + igt_info("n=%u running=%u\n", n, running[n]);
>>> +
>>> + for (i = 0; i < n; i++) {
>>> + igt_spin_batch_end(spin[i]);
>>> + gem_sync(gem_fd, spin[i]->handle);
>>> + igt_spin_batch_free(gem_fd, spin[i]);
>>> + }
>>> + }
>>> +
>>> + /*
>>> + * Check that the running metric is zero when nothing is
>>> submitted,
>>> + * one when one request is submitted, and at least one for any
>>> greater
>>> + * queue depth.
>>> + *
>>> + * We cannot assert the exact value since we do not know how many
>>> + * requests can the submission backend consume.
>>> + */
>>> + igt_assert_eq(running[0], 0);
>>> + for (i = 1; i <= max_rq; i++)
>>> + igt_assert(running[i] > 0);
>>> +}
>>> +
>>> igt_main
>>> {
>>> + const struct intel_execution_engine2 *e;
>>> int fd = -1;
>>> int devid;
>>> @@ -524,6 +931,41 @@ igt_main
>>> test_query_topology_known_pci_ids(fd, devid);
>>> }
>>> + igt_subtest_group {
>>> + igt_fixture {
>>> + igt_require(query_engine_queues_supported(fd));
>>> + }
>>> +
>>> + igt_subtest("engine-queues-invalid")
>>> + engine_queues_invalid(fd);
>>> +
>>> + __for_each_engine_class_instance(fd, e) {
>>> + igt_subtest_group {
>>> + igt_fixture {
>>> + gem_require_engine(fd,
>>> + e->class,
>>> + e->instance);
>>> + }
>>> +
>>> + igt_subtest_f("engine-queues-%s", e->name)
>>> + engine_queues(fd, e);
>>> +
>>> + igt_subtest_f("engine-queued-%s", e->name)
>>> + engine_queued(fd, e, 0);
>>> +
>>> + igt_subtest_f("engine-queued-contexts-%s",
>>> + e->name)
>>> + engine_queued(fd, e, TEST_CONTEXTS);
>>> +
>>> + igt_subtest_f("engine-runnable-%s", e->name)
>>> + engine_runnable(fd, e);
>>> +
>>> + igt_subtest_f("engine-running-%s", e->name)
>>> + engine_running(fd, e);
>>> + }
>>> + }
>>> + }
>>> +
>>> igt_fixture {
>>> close(fd);
>>> }
>
> Thanks for reading it through. I have marked the tweaks for the next
> resend. Problem with this query is that userspace is AWOL so until it
> materializes it will be just for reference.
>
> Regards,
>
> Tvrtko
>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2018-06-08 10:13 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-06-06 12:49 [PATCH i-g-t 0/6] Queued/runnable/running engine stats Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 1/6] include: i915 uAPI headers Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 2/6] intel-gpu-overlay: Add engine queue stats Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 3/6] intel-gpu-overlay: Show 1s, 30s and 15m GPU load Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 4/6] tests/perf_pmu: Add tests for engine queued/runnable/running stats Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 5/6] intel-gpu-top: Add queue depths and load average Tvrtko Ursulin
2018-06-06 12:49 ` [PATCH i-g-t 6/6] tests/i915_query: Engine queues tests Tvrtko Ursulin
2018-06-06 14:33 ` Lionel Landwerlin
2018-06-08 10:02 ` Tvrtko Ursulin
2018-06-08 10:13 ` Lionel Landwerlin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox