[igt-dev] [PATCH i-g-t] tests: Add a new test to stress PSR

Hogander, Jouni jouni.hogander at intel.com
Mon Mar 28 08:01:49 UTC 2022


On Thu, 2022-03-24 at 11:20 -0700, José Roberto de Souza wrote:
> This tests is intended to reproduce a issue found in real world but
> the software enviroment to reproduce it was not easy to duplicate, so
> this test was written to make it easy to reproduce it, debug and fix.
> 
> To make sure we don't regress it in the future here the test.
> 
> It mixes page flips and frontbuffer writes in primary and overlay
> planes and expects that PSR stays active after each subtest without
> any warnings or underruns.

Reviewed-by: Jouni Högander <jouni.hogander at intel.com>
> 
> Cc: Mika Kahola <mika.kahola at intel.com>
> Cc: Jouni Högander <jouni.hogander at intel.com>
> Signed-off-by: José Roberto de Souza <jose.souza at intel.com>
> ---
>  lib/igt_psr.c                    |  29 +++
>  lib/igt_psr.h                    |   2 +
>  tests/i915/kms_psr_stress_test.c | 377
> +++++++++++++++++++++++++++++++
>  tests/meson.build                |   1 +
>  4 files changed, 409 insertions(+)
>  create mode 100644 tests/i915/kms_psr_stress_test.c
> 
> diff --git a/lib/igt_psr.c b/lib/igt_psr.c
> index 2b73e80954..a2d8803126 100644
> --- a/lib/igt_psr.c
> +++ b/lib/igt_psr.c
> @@ -330,3 +330,32 @@ void i915_psr2_sel_fetch_restore(int drm_fd)
>  	psr_set(drm_fd, debugfs_fd, PSR_MODE_2_SEL_FETCH);
>  	close(debugfs_fd);
>  }
> +
> +/**
> + * psr_get_mode
> + *
> + * Return the current PSR mode.
> + */
> +enum psr_mode psr_get_mode(int debugfs_fd)
> +{
> +	char buf[PSR_STATUS_MAX_LEN];
> +	int ret;
> +
> +
> +	ret = igt_debugfs_simple_read(debugfs_fd,
> "i915_edp_psr_status", buf,
> +				      sizeof(buf));
> +	if (ret < 0) {
> +		igt_info("Could not read i915_edp_psr_status: %s\n",
> +			 strerror(-ret));
> +		return PSR_DISABLED;
> +	}
> +
> +	if (strstr(buf, "PSR2 selective fetch: enabled"))
> +		return PSR_MODE_2_SEL_FETCH;
> +	else if (strstr(buf, "PSR2 enabled"))
> +		return PSR_MODE_2;
> +	else if (strstr(buf, "PSR1 enabled"))
> +		return PSR_MODE_1;
> +
> +	return PSR_DISABLED;
> +}
> diff --git a/lib/igt_psr.h b/lib/igt_psr.h
> index 76cd26c657..12ffc9d6cb 100644
> --- a/lib/igt_psr.h
> +++ b/lib/igt_psr.h
> @@ -34,6 +34,7 @@ enum psr_mode {
>  	PSR_MODE_1,
>  	PSR_MODE_2,
>  	PSR_MODE_2_SEL_FETCH,
> +	PSR_DISABLED,
>  };
>  
>  bool psr_disabled_check(int debugfs_fd);
> @@ -46,6 +47,7 @@ bool psr_disable(int device, int debugfs_fd);
>  bool psr_sink_support(int device, int debugfs_fd, enum psr_mode
> mode);
>  bool psr2_wait_su(int debugfs_fd, uint16_t *num_su_blocks);
>  void psr_print_debugfs(int debugfs_fd);
> +enum psr_mode psr_get_mode(int debugfs_fd);
>  
>  bool i915_psr2_selective_fetch_check(int drm_fd);
>  
> diff --git a/tests/i915/kms_psr_stress_test.c
> b/tests/i915/kms_psr_stress_test.c
> new file mode 100644
> index 0000000000..daac41d44c
> --- /dev/null
> +++ b/tests/i915/kms_psr_stress_test.c
> @@ -0,0 +1,377 @@
> +#include "igt.h"
> +#include "igt_sysfs.h"
> +#include "igt_psr.h"
> +#include <errno.h>
> +#include <poll.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/timerfd.h>
> +
> +#define INVALIDATES_PER_SEC 15
> +#define FLIPS_PER_SEC 30
> +#define SECS_TO_COMPLETE_TEST 10
> +
> +#define OVERLAY_SIZE 500
> +#define OVERLAY_POSITION 250
> +
> +#define FRAMEBUFFERS_LEN 60
> +
> +#define DRAW_METHOD IGT_DRAW_BLT
> +
> +typedef struct {
> +	int drm_fd;
> +	int debugfs_fd;
> +	struct buf_ops *bops;
> +	igt_display_t display;
> +	drmModeModeInfo *mode;
> +	igt_output_t *output;
> +
> +	struct igt_fb primary_fb[FRAMEBUFFERS_LEN];
> +	struct igt_fb overlay_fb[FRAMEBUFFERS_LEN];
> +
> +	uint8_t flip_fb_in_use;
> +	uint8_t invalidate_progress;
> +
> +	int invalidate_timerfd;
> +	int flip_timerfd;
> +	int completed_timerfd;
> +
> +	/*
> +	 * There is 2 subtest, one that flips primary and invalidates
> overlay
> +	 * and other that invalidates primary and flips overlay.
> +	 */
> +	bool flip_primary;
> +
> +	enum psr_mode initial_state;
> +} data_t;
> +
> +struct color {
> +	union {
> +		uint32_t val;
> +		struct {
> +			uint8_t b;
> +			uint8_t g;
> +			uint8_t r;
> +			uint8_t a;
> +		};
> +	};
> +};
> +
> +static void setup_output(data_t *data)
> +{
> +	igt_display_t *display = &data->display;
> +	igt_output_t *output;
> +	enum pipe pipe;
> +
> +	igt_display_require(&data->display, data->drm_fd);
> +
> +	for_each_pipe_with_valid_output(display, pipe, output) {
> +		drmModeConnectorPtr c = output->config.connector;
> +
> +		if (c->connector_type != DRM_MODE_CONNECTOR_eDP)
> +			continue;
> +
> +		igt_output_set_pipe(output, pipe);
> +		data->output = output;
> +		data->mode = igt_output_get_mode(output);
> +
> +		return;
> +	}
> +
> +	igt_require(data->output);
> +}
> +
> +static void primary_draw(data_t *data, struct igt_fb *fb, uint8_t i)
> +{
> +	uint32_t x, y, w, h;
> +	struct color cl;
> +
> +	x = 0;
> +	y = 500;
> +	w = data->mode->hdisplay / FRAMEBUFFERS_LEN;
> +	w *= i;
> +	h = OVERLAY_SIZE;
> +
> +	cl.a = 0xff;
> +
> +	if (w == 0) {
> +		cl.r = cl.g = cl.b = 128;
> +		y = 0;
> +		w = data->mode->hdisplay;
> +		h = data->mode->vdisplay;
> +	} else {
> +		cl.r = cl.b = 0x00;
> +		cl.g = 0xff;
> +	}
> +
> +	igt_draw_rect_fb(data->drm_fd, data->bops, 0, fb, DRAW_METHOD,
> x, y,
> +			 w, h, cl.val);
> +}
> +
> +static void overlay_draw(data_t *data, struct igt_fb *fb, uint8_t i)
> +{
> +	uint32_t x, y, w, h;
> +	struct color cl;
> +
> +	x = 0;
> +	y = 0;
> +	w = OVERLAY_SIZE;
> +	h = OVERLAY_SIZE / FRAMEBUFFERS_LEN;
> +	h *= i;
> +
> +	cl.a = 0xff;
> +
> +	if (h == 0) {
> +		cl.r = cl.g = cl.b = 0xff;
> +		h = OVERLAY_SIZE;
> +	} else {
> +		cl.r = 0xff;
> +		cl.g = cl.b = 0x0;
> +	}
> +
> +	igt_draw_rect_fb(data->drm_fd, data->bops, 0, fb, DRAW_METHOD,
> x, y,
> +			 w, h, cl.val);
> +}
> +
> +static void prepare(data_t *data)
> +{
> +	struct itimerspec interval;
> +	igt_plane_t *plane;
> +	int r, i;
> +
> +	if (data->flip_primary) {
> +		for (i = 0; i < ARRAY_SIZE(data->primary_fb); i++) {
> +			igt_create_color_fb(data->drm_fd, data->mode-
> >hdisplay,
> +					    data->mode->vdisplay,
> +					    DRM_FORMAT_XRGB8888,
> +					    DRM_FORMAT_MOD_LINEAR, 0.0,
> 0.0,
> +					    0.0, &data->primary_fb[i]);
> +			primary_draw(data, &data->primary_fb[i], 0);
> +			primary_draw(data, &data->primary_fb[i], i);
> +		}
> +
> +		igt_create_color_fb(data->drm_fd, OVERLAY_SIZE,
> OVERLAY_SIZE,
> +				    DRM_FORMAT_XRGB8888,
> DRM_FORMAT_MOD_LINEAR,
> +				    0.0, 0.0, 0.0, &data-
> >overlay_fb[0]);
> +		overlay_draw(data, &data->overlay_fb[0], 0);
> +	} else {
> +		igt_create_color_fb(data->drm_fd, data->mode->hdisplay,
> +				    data->mode->vdisplay,
> DRM_FORMAT_XRGB8888,
> +				    DRM_FORMAT_MOD_LINEAR, 0.0, 0.0,
> 0.0,
> +				    &data->primary_fb[0]);
> +		primary_draw(data, &data->primary_fb[0], 0);
> +
> +		for (i = 0; i < ARRAY_SIZE(data->overlay_fb); i++) {
> +			igt_create_color_fb(data->drm_fd, OVERLAY_SIZE,
> +					    OVERLAY_SIZE,
> DRM_FORMAT_XRGB8888,
> +					    DRM_FORMAT_MOD_LINEAR,
> 0.0f, 0.0f,
> +					    0.0f, &data-
> >overlay_fb[i]);
> +			overlay_draw(data, &data->overlay_fb[i], 0);
> +			overlay_draw(data, &data->overlay_fb[i], i);
> +		}
> +	}
> +
> +	plane = igt_output_get_plane_type(data->output,
> DRM_PLANE_TYPE_PRIMARY);
> +	igt_plane_set_fb(plane, &data->primary_fb[0]);
> +
> +	plane = igt_output_get_plane_type(data->output,
> DRM_PLANE_TYPE_OVERLAY);
> +	igt_plane_set_fb(plane, &data->overlay_fb[0]);
> +	igt_plane_set_position(plane, -(OVERLAY_SIZE / 2), 350);
> +
> +	igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +	data->flip_fb_in_use = data->invalidate_progress = 0;
> +
> +	/* Arm timers */
> +	interval.it_value.tv_nsec = NSEC_PER_SEC / INVALIDATES_PER_SEC;
> +	interval.it_value.tv_sec = 0;
> +	interval.it_interval.tv_nsec = interval.it_value.tv_nsec;
> +	interval.it_interval.tv_sec = interval.it_value.tv_sec;
> +	r = timerfd_settime(data->invalidate_timerfd, 0, &interval,
> NULL);
> +	igt_require_f(r != -1, "Error setting invalidate_timerfd\n");
> +
> +	interval.it_value.tv_nsec = NSEC_PER_SEC / FLIPS_PER_SEC;
> +	interval.it_value.tv_sec = 0;
> +	interval.it_interval.tv_nsec = interval.it_value.tv_nsec;
> +	interval.it_interval.tv_sec = interval.it_value.tv_sec;
> +	r = timerfd_settime(data->flip_timerfd, 0, &interval, NULL);
> +	igt_require_f(r != -1, "Error setting flip_timerfd\n");
> +
> +	interval.it_value.tv_nsec = 0;
> +	interval.it_value.tv_sec = SECS_TO_COMPLETE_TEST;
> +	interval.it_interval.tv_nsec = interval.it_value.tv_nsec;
> +	interval.it_interval.tv_sec = interval.it_value.tv_sec;
> +	r = timerfd_settime(data->completed_timerfd, 0, &interval,
> NULL);
> +	igt_require_f(r != -1, "Error setting completed_timerfd\n");
> +
> +	data->initial_state = psr_get_mode(data->debugfs_fd);
> +	igt_require(data->initial_state != PSR_DISABLED);
> +	igt_require(psr_wait_entry(data->debugfs_fd, data-
> >initial_state));
> +}
> +
> +static void cleanup(data_t *data)
> +{
> +	struct itimerspec interval;
> +	igt_plane_t *plane;
> +	uint8_t i;
> +
> +	plane = igt_output_get_plane_type(data->output,
> DRM_PLANE_TYPE_PRIMARY);
> +	igt_plane_set_fb(plane, NULL);
> +	plane = igt_output_get_plane_type(data->output,
> DRM_PLANE_TYPE_OVERLAY);
> +	igt_plane_set_fb(plane, NULL);
> +	igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +
> +	for (i = 0; i < ARRAY_SIZE(data->primary_fb); i++)
> +		igt_remove_fb(data->drm_fd, &data->primary_fb[i]);
> +
> +	for (i = 0; i < ARRAY_SIZE(data->overlay_fb); i++)
> +		igt_remove_fb(data->drm_fd, &data->overlay_fb[i]);
> +
> +	/* Disarm timers */
> +	interval.it_value.tv_nsec = 0;
> +	interval.it_value.tv_sec = 0;
> +	interval.it_interval.tv_nsec = interval.it_value.tv_nsec;
> +	interval.it_interval.tv_sec = interval.it_value.tv_sec;
> +	timerfd_settime(data->invalidate_timerfd, 0, &interval, NULL);
> +	timerfd_settime(data->flip_timerfd, 0, &interval, NULL);
> +	timerfd_settime(data->completed_timerfd, 0, &interval, NULL);
> +}
> +
> +static void invalidate(data_t *data)
> +{
> +	if (data->flip_primary)
> +		overlay_draw(data, &data->overlay_fb[0], data-
> >invalidate_progress);
> +	else
> +		primary_draw(data, &data->primary_fb[0], data-
> >invalidate_progress);
> +
> +	data->invalidate_progress++;
> +	if (data->invalidate_progress == (FRAMEBUFFERS_LEN + 1))
> +		data->invalidate_progress = 0;
> +}
> +
> +static void flip(data_t *data)
> +{
> +	uint8_t next = data->flip_fb_in_use + 1;
> +	struct igt_fb *fb;
> +	igt_plane_t *plane;
> +
> +	if (next == FRAMEBUFFERS_LEN)
> +		next = 0;
> +
> +	if (data->flip_primary) {
> +		plane = igt_output_get_plane_type(data->output,
> DRM_PLANE_TYPE_PRIMARY);
> +		fb = &data->primary_fb[next];
> +	} else {
> +		plane = igt_output_get_plane_type(data->output,
> DRM_PLANE_TYPE_OVERLAY);
> +		fb = &data->overlay_fb[next];
> +	}
> +
> +	igt_plane_set_fb(plane, fb);
> +	igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +	data->flip_fb_in_use = next;
> +}
> +
> +static void run(data_t *data)
> +{
> +	struct pollfd pfd[3];
> +	bool loop = true;
> +
> +	pfd[0].fd = data->invalidate_timerfd;
> +	pfd[0].events = POLLIN;
> +	pfd[0].revents = 0;
> +
> +	pfd[1].fd = data->flip_timerfd;
> +	pfd[1].events = POLLIN;
> +	pfd[1].revents = 0;
> +
> +	pfd[2].fd = data->completed_timerfd;
> +	pfd[2].events = POLLIN;
> +	pfd[2].revents = 0;
> +
> +	while (loop) {
> +		int i, r = poll(pfd, ARRAY_SIZE(pfd), -1);
> +
> +		if (r < 0)
> +			break;
> +		if (r == 0)
> +			continue;
> +
> +		for (i = 0; i < ARRAY_SIZE(pfd); i++) {
> +			uint64_t exp;
> +
> +			if (pfd[i].revents == 0)
> +				continue;
> +
> +			pfd[i].revents = 0;
> +			r = read(pfd[i].fd, &exp, sizeof(exp));
> +			if (r != sizeof(uint64_t) || exp == 0)
> +				continue;
> +
> +			if (pfd[i].fd == data->invalidate_timerfd)
> +				invalidate(data);
> +			else if (pfd[i].fd == data->flip_timerfd)
> +				flip(data);
> +			else if (pfd[i].fd == data->completed_timerfd)
> +				loop = false;
> +		}
> +	}
> +
> +	/* Check if after all this stress the PSR is still in the same
> state */
> +	igt_assert(psr_get_mode(data->debugfs_fd) == data-
> >initial_state);
> +}
> +
> +igt_main
> +{
> +	data_t data = {};
> +
> +	igt_fixture {
> +		data.drm_fd = drm_open_driver_master(DRIVER_INTEL);
> +		data.debugfs_fd = igt_debugfs_dir(data.drm_fd);
> +		data.bops = buf_ops_create(data.drm_fd);
> +		kmstest_set_vt_graphics_mode();
> +
> +		igt_require_f(psr_sink_support(data.drm_fd,
> data.debugfs_fd,
> +					       PSR_MODE_1),
> +			      "Sink does not support PSR\n");
> +
> +		setup_output(&data);
> +
> +		data.invalidate_timerfd =
> timerfd_create(CLOCK_MONOTONIC, 0);
> +		igt_require(data.invalidate_timerfd != -1);
> +		data.flip_timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
> +		igt_require(data.flip_timerfd != -1);
> +		data.completed_timerfd =
> timerfd_create(CLOCK_MONOTONIC, 0);
> +		igt_require(data.completed_timerfd != -1);
> +	}
> +
> +	/*
> +	 * TODO: add cursor plane to the test to mimic even more real
> user
> +	 * usage cases
> +	 */
> +	igt_describe("Mix page flips in primary plane and frontbuffer
> writes "
> +		     "to overlay plane and check for warnings,
> underruns or "
> +		     "PSR state changes");
> +	igt_subtest("flip-primary-invalidate-overlay") {
> +		data.flip_primary = true;
> +		prepare(&data);
> +		run(&data);
> +		cleanup(&data);
> +	}
> +
> +	igt_describe("Mix frontbuffer writes to the primary plane and
> page "
> +		     "flips in the overlay plane and check for
> warnings, "
> +		     "underruns or PSR state changes");
> +	igt_subtest("invalidate-primary-flip-overlay") {
> +		data.flip_primary = false;
> +		prepare(&data);
> +		run(&data);
> +		cleanup(&data);
> +	}
> +
> +	igt_fixture {
> +		buf_ops_destroy(data.bops);
> +		igt_display_fini(&data.display);
> +		close(data.debugfs_fd);
> +		close(data.drm_fd);
> +	}
> +}
> \ No newline at end of file
> diff --git a/tests/meson.build b/tests/meson.build
> index 3af4576f4a..a152d2a052 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -243,6 +243,7 @@ i915_progs = [
>  	'kms_psr',
>  	'kms_psr2_su',
>  	'kms_psr2_sf',
> +	'kms_psr_stress_test',
>  	'kms_pwrite_crc',
>  	'sysfs_clients',
>  	'sysfs_defaults',



More information about the igt-dev mailing list