[igt-dev] [PATCH i-g-t] tests/kms_vblank: add hotplug detection

Juha-Pekka Heikkila juhapekka.heikkila at gmail.com
Sun Aug 30 11:58:50 UTC 2020


In case there's hpd during test execution try to restart
the test once before giving up. Here fixed vblank_query and vblank_wait
as they are ones affected by hpd's

needs_retry_after_link_reset() along with comments is direct cut'n'paste
from Imre's code in kms_flip for same issue.

Signed-off-by: Juha-Pekka Heikkila <juhapekka.heikkila at gmail.com>
---
 tests/kms_vblank.c | 86 +++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 73 insertions(+), 13 deletions(-)

diff --git a/tests/kms_vblank.c b/tests/kms_vblank.c
index be001312..f81cb8ff 100644
--- a/tests/kms_vblank.c
+++ b/tests/kms_vblank.c
@@ -59,8 +59,33 @@ typedef struct {
 #define DPMS	0x20
 #define SUSPEND 0x40
 #define RPM	0x80
+	struct udev_monitor *mon;
+	bool retried;
+	bool escape;
 } data_t;
 
+/*
+ * Some monitors with odd behavior signal a bad link after waking from a power
+ * saving state and the subsequent (successful) modeset. This will result in a
+ * link-retraining (DP) or async modeset (HDMI), which in turn makes the test
+ * miss vblank/flip events and fail.  Work around this by retrying the test
+ * once in case of such a link reset event, which the driver signals with a
+ * hotplug event.
+ */
+static bool needs_retry_after_link_reset(struct udev_monitor *mon)
+{
+	bool hotplug_detected;
+
+	igt_suspend_signal_helper();
+	hotplug_detected = igt_hotplug_detected(mon, 3);
+	igt_resume_signal_helper();
+
+	if (hotplug_detected)
+		igt_debug("Retrying after a hotplug event\n");
+
+	return hotplug_detected;
+}
+
 static double elapsed(const struct timespec *start,
 		      const struct timespec *end,
 		      int loop)
@@ -119,7 +144,12 @@ static void run_test(data_t *data, void (*testfunc)(data_t *, int, int))
 	int fd = display->drm_fd;
 	igt_hang_t hang;
 
+	data->retried = false;
+	data->mon = igt_watch_uevents();
+retry:
 	prepare_crtc(data, fd, output);
+	igt_flush_uevents(data->mon);
+	data->escape = false;
 
 	if (data->flags & RPM)
 		igt_require(igt_setup_runtime_pm(fd));
@@ -153,7 +183,7 @@ static void run_test(data_t *data, void (*testfunc)(data_t *, int, int))
 	} else
 		testfunc(data, fd, 1);
 
-	if (data->flags & BUSY) {
+	if (data->flags & BUSY && !data->escape) {
 		struct drm_event_vblank buf;
 		igt_assert_eq(read(fd, &buf, sizeof(buf)), sizeof(buf));
 	}
@@ -163,11 +193,22 @@ static void run_test(data_t *data, void (*testfunc)(data_t *, int, int))
 	if (!(data->flags & NOHANG))
 		igt_post_hang_ring(fd, hang);
 
-	igt_info("\n%s on pipe %s, connector %s: PASSED\n\n",
-		 igt_subtest_name(), kmstest_pipe_name(data->pipe), igt_output_name(output));
-
 	/* cleanup what prepare_crtc() has done */
+
 	cleanup_crtc(data, fd, output);
+
+	if (data->escape)
+	{
+		igt_assert(!data->retried &&
+			   needs_retry_after_link_reset(data->mon));
+		data->retried = true;
+		goto retry;
+	}
+	igt_info("\n%s on pipe %s, connector %s: PASSED\n\n",
+		igt_subtest_name(), kmstest_pipe_name(data->pipe), igt_output_name(output));
+
+	data->retried = false;
+	igt_cleanup_uevents(data->mon);
 }
 
 static void crtc_id_subtest(data_t *data, int fd)
@@ -279,10 +320,15 @@ static void vblank_query(data_t *data, int fd, int nchildren)
 
 	clock_gettime(CLOCK_MONOTONIC, &start);
 	do {
+		int rval;
 		vbl.request.type = DRM_VBLANK_RELATIVE;
 		vbl.request.type |= pipe_id_flag;
 		vbl.request.sequence = 0;
-		igt_assert_eq(wait_vblank(fd, &vbl), 0);
+		rval = wait_vblank(fd, &vbl);
+		if (rval) {
+			data->escape = true;
+			return;
+		}
 		count++;
 	} while ((vbl.reply.sequence - sq) <= 120);
 	clock_gettime(CLOCK_MONOTONIC, &end);
@@ -308,10 +354,15 @@ static void vblank_wait(data_t *data, int fd, int nchildren)
 
 	clock_gettime(CLOCK_MONOTONIC, &start);
 	do {
+		int rval;
 		vbl.request.type = DRM_VBLANK_RELATIVE;
 		vbl.request.type |= pipe_id_flag;
 		vbl.request.sequence = 1;
-		igt_assert_eq(wait_vblank(fd, &vbl), 0);
+		rval = wait_vblank(fd, &vbl);
+		if (rval) {
+			data->escape = true;
+			return;
+		}
 		count++;
 	} while ((vbl.reply.sequence - sq) <= 120);
 	clock_gettime(CLOCK_MONOTONIC, &end);
@@ -514,22 +565,31 @@ static void invalid_subtest(data_t *data, int fd)
 igt_main
 {
 	int fd;
-	data_t data;
+	data_t *data;
 
 	igt_fixture {
+		// mmap data so forked tests can talk back.
+		data = mmap(NULL, sizeof(data_t), PROT_READ | PROT_WRITE,
+			    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+		memset(data, 0, sizeof(data_t));
+
 		fd = drm_open_driver_master(DRIVER_ANY);
 		kmstest_set_vt_graphics_mode();
-		igt_display_require(&data.display, fd);
-		igt_display_require_output(&data.display);
+		igt_display_require(&data->display, fd);
+		igt_display_require_output(&data->display);
 	}
 
 	igt_subtest("invalid")
-		invalid_subtest(&data, fd);
+		invalid_subtest(data, fd);
 
 	igt_subtest("crtc-id")
-		crtc_id_subtest(&data, fd);
+		crtc_id_subtest(data, fd);
 
-	for_each_pipe_static(data.pipe)
+	for_each_pipe_static(data->pipe)
 		igt_subtest_group
-			run_subtests_for_pipe(&data);
+			run_subtests_for_pipe(data);
+
+	igt_fixture {
+		munmap(data, sizeof(data_t));
+	}
 }
-- 
2.26.0



More information about the igt-dev mailing list