[igt-dev] [PATCH i-g-t 3/4] tests/perf: new tests for OA interrupt

Lionel Landwerlin lionel.g.landwerlin at intel.com
Wed Jan 16 15:37:31 UTC 2019


Those tests verify that the interrupt wakes up userspace waiting on
the perf stream either with poll() or with read().

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin at intel.com>
---
 tests/perf.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 218 insertions(+)

diff --git a/tests/perf.c b/tests/perf.c
index 1a773455..3effc34d 100644
--- a/tests/perf.c
+++ b/tests/perf.c
@@ -442,6 +442,20 @@ oa_exponent_to_ns(int exponent)
        return 1000000000ULL * (2ULL << exponent) / timestamp_frequency;
 }
 
+static int
+find_oa_exponent_for_buffer_fill_time(size_t oa_buf_size, size_t report_size, uint64_t fill_time_ns)
+{
+       size_t n_reports = oa_buf_size / report_size;
+
+       for (int e = 1; e < 32; e++) {
+               if (fill_time_ns < oa_exponent_to_ns(e) * n_reports)
+                       return e;
+       }
+
+       igt_assert(!"reached");
+       return -1;
+}
+
 static bool
 oa_report_is_periodic(uint32_t oa_exponent, const uint32_t *report)
 {
@@ -2354,6 +2368,111 @@ test_polling(uint64_t requested_oa_period, bool set_kernel_hrtimer, uint64_t ker
 	__perf_close(stream_fd);
 }
 
+static void
+test_interrupt(uint64_t oa_exponent,
+	       uint64_t kernel_oa_poll_delay,
+	       bool use_interrupt,
+	       bool expect_buffer_lost,
+	       bool use_polling,
+	       uint32_t expect_min_reports)
+{
+	uint64_t properties[] = {
+		/* Include OA reports in samples */
+		DRM_I915_PERF_PROP_SAMPLE_OA, true,
+
+		/* OA unit configuration */
+		DRM_I915_PERF_PROP_OA_METRICS_SET, test_metric_set_id,
+		DRM_I915_PERF_PROP_OA_FORMAT, test_oa_format,
+		DRM_I915_PERF_PROP_OA_EXPONENT, oa_exponent,
+		DRM_I915_PERF_PROP_OA_ENABLE_INTERRUPT, use_interrupt,
+
+		/* Kernel configuration */
+		DRM_I915_PERF_PROP_POLL_OA_DELAY, kernel_oa_poll_delay,
+	};
+	struct drm_i915_perf_open_param param = {
+		.flags = I915_PERF_FLAG_FD_CLOEXEC |
+			I915_PERF_FLAG_DISABLED |
+			(use_polling ? I915_PERF_FLAG_FD_NONBLOCK : 0),
+		.num_properties = ARRAY_SIZE(properties) / 2,
+		.properties_ptr = to_user_pointer(properties),
+	};
+	struct pollfd pollfd = { .events = POLLIN };
+	struct drm_i915_perf_record_header *header;
+	uint32_t n_reports = 0;
+	bool buffer_lost = false;
+	uint8_t *buf = malloc(MAX_OA_BUF_SIZE);
+	int ret;
+
+	stream_fd = __perf_open(drm_fd, &param, true /* prevent_pm */);
+	pollfd.fd = stream_fd;
+
+	igt_debug("OA period = %s, ",
+		  pretty_print_oa_period(oa_exponent_to_ns(oa_exponent)));
+	igt_debug("OA poll delay = %s, use interrupt = %i, "
+		  "expected min report = %u\n",
+		  pretty_print_oa_period(kernel_oa_poll_delay),
+		  use_interrupt, expect_min_reports);
+
+	do_ioctl(stream_fd, I915_PERF_IOCTL_ENABLE, 0);
+
+	if (use_polling) {
+		while ((ret = poll(&pollfd, 1, -1)) < 0 &&
+		       errno == EINTR)
+			;
+		igt_assert_eq(ret, 1);
+		igt_assert(pollfd.revents & POLLIN);
+	}
+
+	while ((ret = read(stream_fd, buf, MAX_OA_BUF_SIZE)) < 0 &&
+	       errno == EINTR)
+		;
+
+	if (ret < 0)
+		igt_debug("Unexpected error when reading after poll = %d\n", errno);
+	igt_assert_neq(ret, -1);
+
+	__perf_close(stream_fd);
+
+	/* For Haswell reports don't contain a well defined reason
+	 * field we so assume all reports to be 'periodic'. For gen8+
+	 * we want to to consider that the HW automatically writes some
+	 * non periodic reports (e.g. on context switch) which might
+	 * lead to more successful read()s than expected due to
+	 * periodic sampling and we don't want these extra reads to
+	 * cause the test to fail...
+	 */
+	for (int offset = 0; offset < ret; offset += header->size) {
+		header = (void *)(buf + offset);
+
+		switch (header->type) {
+		case DRM_I915_PERF_RECORD_SAMPLE:
+			n_reports++;
+			break;
+		case DRM_I915_PERF_RECORD_OA_BUFFER_LOST:
+			buffer_lost = true;
+			break;
+		}
+	}
+
+	igt_debug("Got %i report(s)\n", n_reports);
+
+	igt_assert_eq(buffer_lost, expect_buffer_lost);
+
+	/*
+	 * Leave a 5% error margin for 2 reasons :
+	 *
+	 * - the tail pointer race condition might remove a couple of
+	 *   reports because things have not yet landed in memory.
+	 *
+	 * - the OA unit sometimes drop a writing a report here and
+	 *   there, the algorithm is linked to pressure on memory
+	 *   controller but undocumented.
+	 */
+	igt_assert_lte(expect_min_reports * 0.95, n_reports);
+
+	free(buf);
+}
+
 static void
 test_buffer_fill(void)
 {
@@ -4227,6 +4346,56 @@ igt_main
 			      500 * 1000 /* default 500us hrtimer */);
 	}
 
+	igt_subtest("blocking-with-interrupt") {
+		uint64_t target_fill_time = /* 1000ms */ 1000 * 1000 * 1000ul;
+		size_t report_size = get_oa_format(test_oa_format).size;
+		uint32_t max_reports = MAX_OA_BUF_SIZE / report_size;
+		int oa_exponent =
+			find_oa_exponent_for_buffer_fill_time(MAX_OA_BUF_SIZE,
+							      report_size, target_fill_time);
+		uint64_t fill_time = oa_exponent_to_ns(oa_exponent) *
+			(MAX_OA_BUF_SIZE / report_size);
+
+		igt_require(kernel_supports_open_option(drm_fd,
+							DRM_I915_PERF_PROP_POLL_OA_DELAY,
+							target_fill_time));
+		igt_require(kernel_supports_open_option(drm_fd,
+							DRM_I915_PERF_PROP_OA_ENABLE_INTERRUPT,
+							true));
+
+		/*
+		 * We should be waken up by the HR timer but too late,
+		 * so we'll loose reports.
+		 */
+		test_interrupt(oa_exponent,
+			       fill_time + fill_time / 2,
+			       false /* interrupt */, true /* loss */, false /* use_polling */,
+			       0);
+
+		/*
+		 * We should get woken up by the HR timer and get the
+		 * appropriate number of report.
+		 */
+		test_interrupt(oa_exponent,
+			       /* 500us */ 500 * 1000,
+			       false /* interrupt */, false /* no loss */, false /* use_polling */,
+			       (500 * 1000 * max_reports) / fill_time);
+
+
+		/* We should be waken up by the interrupt first. */
+		test_interrupt(oa_exponent,
+			       2 * fill_time,
+			       true /* interrupt */, false /* no loss */, false /* use_polling */,
+			       max_reports / 2);
+
+		/* We should be waken up by the HR timer first. */
+		test_interrupt(oa_exponent,
+			       fill_time / 4,
+			       true /* interrupt */, false /* no loss */, false /* use_polling */,
+			       (fill_time / 4) * max_reports / fill_time);
+	}
+
+
 	igt_subtest("polling") {
 		test_polling(40 * 1000 * 1000 /* 40ms oa period */,
 			     false /* set_kernel_hrtimer */,
@@ -4246,6 +4415,55 @@ igt_main
 			     500 * 1000 /* default 500us hrtimer */);
 	}
 
+	igt_subtest("polling-with-interrupt") {
+		uint64_t target_fill_time = /* 1000ms */ 1000 * 1000 * 1000ul;
+		size_t report_size = get_oa_format(test_oa_format).size;
+		uint32_t max_reports = MAX_OA_BUF_SIZE / report_size;
+		int oa_exponent =
+			find_oa_exponent_for_buffer_fill_time(MAX_OA_BUF_SIZE,
+							      report_size, target_fill_time);
+		uint64_t fill_time = oa_exponent_to_ns(oa_exponent) *
+			(MAX_OA_BUF_SIZE / report_size);
+
+		igt_require(kernel_supports_open_option(drm_fd,
+							DRM_I915_PERF_PROP_POLL_OA_DELAY,
+							target_fill_time));
+		igt_require(kernel_supports_open_option(drm_fd,
+							DRM_I915_PERF_PROP_OA_ENABLE_INTERRUPT,
+							true));
+
+		/*
+		 * We should be waken up by the HR timer but too late,
+		 * so we'll loose reports.
+		 */
+		test_interrupt(oa_exponent,
+			       fill_time + fill_time / 2,
+			       false /* interrupt */, true /* loss */, true /* use_polling */,
+			       0);
+
+		/*
+		 * We should get woken up by the HR timer and get the
+		 * appropriate number of report.
+		 */
+		test_interrupt(oa_exponent,
+			       /* 500us */ 500 * 1000,
+			       false /* interrupt */, false /* no loss */, true /* use_polling */,
+			       (500 * 1000 * max_reports) / fill_time);
+
+
+		/* We should be waken up by the interrupt first. */
+		test_interrupt(oa_exponent,
+			       2 * fill_time,
+			       true /* interrupt */, false /* no loss */, true /* use_polling */,
+			       max_reports / 2);
+
+		/* We should be waken up by the HR timer first. */
+		test_interrupt(oa_exponent,
+			       fill_time / 4,
+			       true /* interrupt */, false /* no loss */, true /* use_polling */,
+			       (fill_time / 4) * max_reports / fill_time);
+	}
+
 	igt_subtest("short-reads")
 		test_short_reads();
 
-- 
2.20.1



More information about the igt-dev mailing list