[igt-dev] [PATCH i-g-t v4] igt/tests: kms_plane_stress: Add plane stress test

Petri Latvala petri.latvala at intel.com
Tue Mar 26 09:11:41 UTC 2019


On Mon, Mar 25, 2019 at 05:54:40PM +0200, Stanislav Lisovskiy wrote:
> This test attempts to utilize all connected
> outputs at the same time, using maximum possible
> resolution and amount of planes, to check whether
> we are hiting any kind of bandwidth, watermark or
> other limitations.
> 
> v2: Added cpu and gpu load threads, which consume
>     additional bandwidth.
> 
> v3: Removed binary picture file, using pattern fb
>     instead.
> 
> v4: Moved FB creation/removal to better place.
> 
> Signed-off-by: Stanislav Lisovskiy <stanislav.lisovskiy at intel.com>
> ---
>  lib/igt_kms.h            |   7 +
>  tests/Makefile.sources   |   1 +
>  tests/kms_plane_stress.c | 546 +++++++++++++++++++++++++++++++++++++++
>  tests/meson.build        |   1 +
>  4 files changed, 555 insertions(+)
>  create mode 100644 tests/kms_plane_stress.c
> 
> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
> index 407f3d64..0e6784ca 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -519,6 +519,13 @@ static inline bool igt_output_is_connected(igt_output_t *output)
>  		for_each_if ((((output) = &(display)->outputs[con__]), \
>  			     igt_pipe_connector_valid((pipe), (output))))
>  
> +#define for_each_pipe_with_new_output(display, pipe, output) \
> +			for (int con = 0, (pipe) = 0; con < (display)->n_outputs; con++) \
> +				for_each_if((output) = &(display)->outputs[con]) \
> +					for_each_if((pipe) < IGT_MAX_PIPES) \
> +						for_each_if(igt_pipe_connector_valid((pipe), (output)))


Docs for this. And a very good explanation what is different from
for_each_pipe_with_valid_output().


> +
> +
>  igt_output_t **__igt_pipe_populate_outputs(igt_display_t *display,
>  					   igt_output_t **chosen_outputs);
>  
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index 71ccf00a..9d04d888 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -59,6 +59,7 @@ TESTS_progs = \
>  	kms_plane_lowres \
>  	kms_plane_multiple \
>  	kms_plane_scaling \
> +	kms_plane_stress \
>  	kms_prop_blob \
>  	kms_properties \
>  	kms_psr \
> diff --git a/tests/kms_plane_stress.c b/tests/kms_plane_stress.c
> new file mode 100644
> index 00000000..8a244dc7
> --- /dev/null
> +++ b/tests/kms_plane_stress.c
> @@ -0,0 +1,546 @@
> +/*
> + * Copyright © 2016 Intel Corporation

2016?


> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#include "igt.h"
> +#include "igt_rand.h"
> +#include "drmtest.h"
> +#include "sw_sync.h"
> +#include <errno.h>
> +#include <pthread.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <time.h>
> +#include <poll.h>
> +#include <pthread.h>
> +
> +#ifndef DRM_CAP_CURSOR_WIDTH
> +#define DRM_CAP_CURSOR_WIDTH 0x8
> +#endif
> +#ifndef DRM_CAP_CURSOR_HEIGHT
> +#define DRM_CAP_CURSOR_HEIGHT 0x9
> +#endif
> +
> +
> +drmModeModeInfo uhd_mode_60hz = {
> +	.name = "3840x2160 at 60hz",
> +	.vrefresh = 60,
> +	.clock = 142667*2,
> +	.hdisplay = 3840,
> +	.hsync_start = 1936*2,
> +	.hsync_end = 1952*2,
> +	.htotal = 2104*2,
> +	.vdisplay = 2160,
> +	.vsync_start = 1083*2,
> +	.vsync_end = 1097*2,
> +	.vtotal = 1128*2,
> +	.flags = 0xa,
> +};
> +
> +#define N_FORMATS 3
> +static const uint32_t formats[N_FORMATS] = {
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_RGB565,
> +	DRM_FORMAT_XRGB2101010,
> +};
> +
> +#define N_TILING_METHODS 3
> +static const uint64_t tilings[N_TILING_METHODS] = {
> +	DRM_FORMAT_MOD_LINEAR,
> +	I915_FORMAT_MOD_X_TILED,
> +	I915_FORMAT_MOD_Y_TILED,
> +};
> +
> +static const char *format_str(int format_index)
> +{
> +	switch (formats[format_index]) {
> +	case DRM_FORMAT_RGB565:
> +		return "rgb565";
> +	case DRM_FORMAT_XRGB8888:
> +		return "xrgb8888";
> +	case DRM_FORMAT_XRGB2101010:
> +		return "xrgb2101010";
> +	default:
> +		igt_assert(false);
> +	}
> +}
> +
> +static const char *tiling_str(int tiling_index)
> +{
> +	switch (tilings[tiling_index]) {
> +	case DRM_FORMAT_MOD_LINEAR:
> +		return "untiled";
> +	case I915_FORMAT_MOD_X_TILED:
> +		return "xtiled";
> +	case I915_FORMAT_MOD_Y_TILED:
> +		return "ytiled";
> +	default:
> +		igt_assert(false);
> +	}
> +}
> +
> +
> +#define MAX_CORES 8
> +
> +struct data;
> +
> +struct thread_context {
> +	struct data *data;
> +	int id;
> +	void *buf1;
> +	void *buf2;
> +};
> +
> +struct gpu_context {
> +	struct data *data;
> +	int pipe;
> +	int color;
> +};
> +
> +struct data {
> +	int drm_fd;
> +	igt_display_t display;
> +	int num_planes;
> +	uint32_t format;
> +	uint64_t modifier;
> +	uint32_t devid;
> +	drm_intel_bufmgr *bufmgr;
> +	struct igt_fb fb[IGT_MAX_PIPES];
> +	struct igt_fb cursor_fb[IGT_MAX_PIPES];
> +	pthread_t cpu_thread[MAX_CORES];
> +	bool cpu_thread_stop[MAX_CORES];
> +	struct thread_context cpu_context[MAX_CORES];
> +	struct gpu_context gpu_context[IGT_MAX_PIPES];
> +};
> +
> +
> +struct base_crc {
> +	bool set;
> +	igt_crc_t crc;
> +};
> +
> +igt_pipe_crc_t *pipe_crc;
> +
> +#define BUF_SIZE 128*1024*1024
> +
> +static void *cpu_load(void *d)
> +{
> +	char *buf1, *buf2;
> +	struct thread_context *context = (struct thread_context *)d;
> +	struct data *data = context->data;
> +
> +	buf1 = context->buf1;
> +	buf2 = context->buf2;
> +
> +	data->cpu_thread_stop[context->id] = false;
> +
> +	while(!data->cpu_thread_stop[context->id]) {
> +		memcpy(buf1, buf2, BUF_SIZE);
> +		memcpy(buf2, buf1, BUF_SIZE);
> +	}
> +
> +	return NULL;
> +}
> +
> +
> +static void scratch_buf_init(struct igt_buf *buf,
> +			drm_intel_bo *bo, int width, int height, int stride, int tiling)
> +{
> +	buf->bo = bo;
> +	buf->stride = stride;
> +	buf->tiling = tiling;
> +	buf->size = width * height * 4;
> +	buf->bpp = 32;
> +}
> +
> +
> +static void gpu_load(struct gpu_context *gpu_context)
> +{
> +	struct intel_batchbuffer *batch = NULL;
> +	struct igt_buf dst;
> +	struct data *data = gpu_context->data;
> +	int pipe = gpu_context->pipe;
> +	igt_fillfunc_t gpgpu_fill = NULL;
> +	drm_intel_bo *bo;
> +	struct igt_fb *fb = &data->fb[pipe];
> +
> +	if (!data->devid) {
> +		data->devid = intel_get_drm_devid(data->drm_fd);
> +		igt_require_gem(data->drm_fd);
> +
> +		data->bufmgr = drm_intel_bufmgr_gem_init(data->drm_fd, 4096);
> +		igt_assert(data->bufmgr);
> +	}
> +
> +	gpgpu_fill = igt_get_gpgpu_fillfunc(data->devid);
> +
> +	igt_require_f(gpgpu_fill,
> +	      "no gpgpu-fill function\n");
> +
> +	batch = intel_batchbuffer_alloc(data->bufmgr, data->devid);
> +	igt_assert(batch);
> +
> +	bo = gem_handle_to_libdrm_bo(data->bufmgr, data->drm_fd, "", fb->gem_handle);
> +
> +	scratch_buf_init(&dst, bo, fb->width, fb->height, fb->strides[0], fb->tiling);
> +
> +	gpgpu_fill(batch,
> +	   &dst, 0, 0, (fb->width * 4), fb->height,
> +	   gpu_context->color);
> +
> +	gpu_context->color += 0x10;
> +
> +	intel_batchbuffer_free(batch);
> +}
> +
> +static inline uint32_t pipe_select(enum pipe pipe)
> +{
> +	if (pipe > 1)
> +		return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
> +	else if (pipe > 0)
> +		return DRM_VBLANK_SECONDARY;
> +	else
> +		return 0;
> +}
> +
> +static unsigned get_vblank(int fd, enum pipe pipe, unsigned flags)
> +{
> +	union drm_wait_vblank vbl;
> +
> +	memset(&vbl, 0, sizeof(vbl));
> +	vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe) | flags;
> +	if (drmIoctl(fd, DRM_IOCTL_WAIT_VBLANK, &vbl))
> +		return 0;
> +
> +	return vbl.reply.sequence;
> +}
> +
> +
> +static int plane_stress(struct data *data, igt_output_t *output, enum pipe pipe, drmModeModeInfo *mode, bool gpu)
> +{
> +	igt_plane_t *plane;
> +	uint64_t cursor_width, cursor_height;
> +	int i;
> +	int ret;
> +	igt_crc_t	crc, crc2;
> +
> +	igt_output_override_mode(output, mode);
> +	ret = igt_display_try_commit_atomic(&data->display,
> +			DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
> +	if (ret) {
> +		return ret;
> +	}
> +
> +	igt_output_set_pipe(output, pipe);
> +
> +	igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +
> +	mode = igt_output_get_mode(output);
> +
> +	do_or_die(drmGetCap(data->drm_fd, DRM_CAP_CURSOR_WIDTH, &cursor_width));
> +	do_or_die(drmGetCap(data->drm_fd, DRM_CAP_CURSOR_HEIGHT, &cursor_height));
> +
> +	if (!data->cursor_fb[pipe].fb_id) {
> +		igt_create_color_fb(data->drm_fd, cursor_width, cursor_height,
> +				DRM_FORMAT_ARGB8888,
> +				LOCAL_DRM_FORMAT_MOD_NONE,
> +					1.0,0.0,0.0,
> +					&data->cursor_fb[pipe]);
> +	}
> +
> +	if (!data->fb[pipe].fb_id) {
> +		igt_create_color_pattern_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
> +				    data->format,
> +				    data->modifier,
> +				    0.0, 1.0, 0.0, &data->fb[pipe]);
> +	}
> +
> +	ret = igt_display_try_commit_atomic(&data->display, DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
> +	if (ret) {
> +		igt_info("Plane %d pipe %d try commit failed, exiting\n", i, pipe);
> +		goto cleanup;
> +	}
> +
> +	igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +
> +	i = 0;
> +
> +	for_each_plane_on_pipe(&data->display, pipe, plane) {
> +
> +		if (plane->type == DRM_PLANE_TYPE_CURSOR) {
> +			igt_plane_set_fb(plane, &data->cursor_fb[pipe]);
> +			igt_fb_set_size(&data->cursor_fb[pipe], plane, cursor_width, cursor_height);
> +			igt_plane_set_size(plane, cursor_width, cursor_height);
> +		} else {
> +			igt_plane_set_fb(plane, &data->fb[pipe]);
> +			igt_plane_set_position(plane, 0, 0);
> +			igt_fb_set_size(&data->fb[pipe], plane, mode->hdisplay, mode->vdisplay);
> +			igt_plane_set_size(plane, mode->hdisplay, mode->vdisplay);
> +		}
> +
> +		ret = igt_display_try_commit_atomic(&data->display, DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
> +		if (ret) {
> +			igt_info("Plane %d pipe %d try commit failed, exiting\n", i, pipe);
> +			goto cleanup;
> +		}
> +
> +		if (++i >= data->num_planes)
> +			break;
> +	}
> +
> +	if (gpu) {
> +		gpu_load(&data->gpu_context[pipe]);
> +	}
> +
> +	igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +
> +	get_vblank(data->display.drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
> +
> +	igt_pipe_crc_collect_crc(pipe_crc, &crc);
> +
> +	get_vblank(data->display.drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
> +
> +	igt_pipe_crc_collect_crc(pipe_crc, &crc2);
> +	igt_assert_crc_equal(&crc, &crc2);
> +
> +	return 0;
> +
> +cleanup:
> +	for_each_plane_on_pipe(&data->display, pipe, plane) {
> +		igt_plane_set_fb(plane, NULL);
> +	}
> +	igt_remove_fb(data->display.drm_fd, &data->fb[pipe]);
> +	data->fb[pipe].fb_id = 0;
> +
> +	return ret;
> +}
> +
> +#define DURATION_SEC 5.00
> +
> +static drmModeModeInfo * find_highest_mode(drmModeConnector *connector)
> +{
> +	drmModeModeInfo *highest_mode = NULL;
> +	int j;
> +
> +	for (j = 0; j < connector->count_modes; j++) {
> +		if (!highest_mode) {
> +			highest_mode = &connector->modes[j];
> +		}
> +		else if (connector->modes[j].vdisplay && connector->modes[j].hdisplay) {
> +			if (highest_mode->hdisplay < connector->modes[j].hdisplay)
> +				highest_mode = &connector->modes[j];
> +			}
> +	}
> +
> +	return highest_mode;
> +}
> +
> +static void stress(struct data *data, bool override, bool gpu, int format_idx, int tiling_idx)
> +{
> +	struct timespec start, end;
> +	igt_output_t *output;
> +	int ret;int crtc_id = 0;
> +	int i;
> +	drmModeModeInfo *highest_mode = NULL;
> +
> +	drmModeRes *mode_resources = drmModeGetResources(data->drm_fd);
> +
> +	if (!mode_resources) {
> +		igt_warn("drmModeGetResources failed: %s\n", strerror(errno));
> +		return;
> +	}
> +
> +	clock_gettime(CLOCK_MONOTONIC, &start);
> +
> +	do {
> +		for_each_pipe_with_new_output(&data->display, pipe, output) {
> +			crtc_id = 0;
> +			for (i = 0; i < mode_resources->count_connectors; i++) {
> +				drmModeConnector *connector;
> +
> +				connector = drmModeGetConnectorCurrent(data->drm_fd,
> +							       mode_resources->connectors[i]);
> +				if (!connector) {
> +					igt_warn("could not get connector %i: %s\n", mode_resources->connectors[i], strerror(errno));
> +					continue;
> +				}
> +
> +				if (!connector->count_modes) {
> +					drmModeFreeConnector(connector);
> +					continue;
> +				}
> +
> +				if (connector->connection == DRM_MODE_CONNECTED) {
> +					if (crtc_id == pipe) {
> +						if (!override) {
> +							highest_mode = find_highest_mode(connector);
> +						}
> +						else
> +							highest_mode = &uhd_mode_60hz;
> +
> +						data->format = formats[format_idx];
> +						data->modifier = tilings[tiling_idx];
> +						data->num_planes = data->display.pipes[pipe].n_planes;
> +
> +						igt_info("Pipe %s %d planes, using mode:", kmstest_pipe_name(pipe),
> +								data->num_planes);
> +
> +						kmstest_dump_mode(highest_mode);
> +
> +						pipe_crc = igt_pipe_crc_new(data->drm_fd, pipe,
> +							INTEL_PIPE_CRC_SOURCE_AUTO);
> +
> +						ret = plane_stress(data, output, pipe, highest_mode, gpu);
> +
> +						igt_pipe_crc_free(pipe_crc);
> +					}
> +					crtc_id++;
> +				}
> +				drmModeFreeConnector(connector);
> +			}
> +			pipe++;
> +		}
> +		clock_gettime(CLOCK_MONOTONIC, &end);
> +	} while ((igt_time_elapsed(&start, &end) < DURATION_SEC) && !ret);


Is there a reason for using clock_gettime with explicit
CLOCK_MONOTONIC instead of igt_gettime? And igt_time_elapsed instead
of igt_until_timeout?

> +
> +	drmModeFreeResources(mode_resources);
> +}
> +
> +static void test(struct data *data, bool override, bool cpu, bool gpu)
> +{
> +	int i;
> +	int number_of_cores = min(sysconf(_SC_NPROCESSORS_ONLN), MAX_CORES);
> +	int format_idx, tiling_idx;
> +
> +	igt_fixture {
> +		for (i = 0; i < IGT_MAX_PIPES; i++) {
> +				data->fb[i].fb_id = 0;
> +				data->cursor_fb[i].fb_id = 0;
> +		}
> +		if (gpu) {
> +			for (i = 0; i < IGT_MAX_PIPES; i++) {
> +				data->gpu_context[i].data = data;
> +				data->gpu_context[i].pipe = i;
> +			}
> +		}
> +
> +		if (cpu) {
> +			for (i = 0; i < number_of_cores;i++) {
> +				data->cpu_context[i].buf1 = malloc(BUF_SIZE);
> +				data->cpu_context[i].buf2 = malloc(BUF_SIZE);
> +				data->cpu_context[i].id = i;
> +				data->cpu_context[i].data = data;
> +				pthread_create(&data->cpu_thread[i], NULL, cpu_load, (void*)&data->cpu_context[i]);
> +			}
> +		}
> +	}
> +
> +	for (format_idx = 0; format_idx < N_FORMATS; format_idx++) {
> +		for (tiling_idx = 0; tiling_idx < N_TILING_METHODS; tiling_idx++) {
> +
> +			igt_subtest_f("stress%s%s%s-%s-%s",
> +				      override == false ? "" : "-mode-override",
> +				      cpu == false ? "" : "-cpu-load",
> +				      gpu == false ? "" : "-gpu-load",
> +				      format_str(format_idx),
> +				      tiling_str(tiling_idx))


Did you mean to have some braces here?


-- 
Petri Latvala



> +
> +			stress(data, override, gpu, format_idx, tiling_idx);
> +
> +			/*
> +			 * As we change tiling/format we need a new FB
> +			 */
> +			for (i = 0; i < IGT_MAX_PIPES; i++) {
> +				if (data->fb[i].fb_id) {
> +					igt_remove_fb(data->display.drm_fd, &data->fb[i]);
> +					data->fb[i].fb_id = 0;
> +				}
> +				if (data->cursor_fb[i].fb_id) {
> +					igt_remove_fb(data->display.drm_fd, &data->cursor_fb[i]);
> +					data->cursor_fb[i].fb_id = 0;
> +				}
> +			}
> +		}
> +	}
> +
> +	igt_fixture {
> +		if (cpu) {
> +			for (i = 0; i < number_of_cores;i++) {
> +				data->cpu_thread_stop[i] = true;
> +				pthread_join(data->cpu_thread[i], NULL);
> +			}
> +			for (i = 0;i < number_of_cores; i++) {
> +				free(data->cpu_context[i].buf1);
> +				free(data->cpu_context[i].buf2);
> +			}
> +		}
> +	}
> +}
> +
> +
> +struct data data = {
> +	.num_planes = 7,
> +	.format = DRM_FORMAT_XRGB8888,
> +	.modifier = DRM_FORMAT_MOD_LINEAR,
> +	.devid = 0,
> +};
> +
> +
> +igt_main
> +{
> +	bool mode_override = false;
> +	bool cpu_load = false;
> +	bool gpu_load = false;
> +
> +	igt_fixture {
> +		data.drm_fd = data.display.drm_fd = drm_open_driver_master(DRIVER_ANY);
> +
> +		kmstest_set_vt_graphics_mode();
> +
> +		igt_display_require(&data.display, data.display.drm_fd);
> +		igt_require(data.display.is_atomic);
> +		igt_display_require_output(&data.display);
> +	}
> +
> +	test(&data, mode_override, cpu_load, gpu_load);
> +
> +	cpu_load = true;
> +	gpu_load = true;
> +
> +	test(&data, mode_override, cpu_load, gpu_load);
> +
> +	mode_override = true;
> +	cpu_load = false;
> +	gpu_load = false;
> +
> +	test(&data, mode_override, cpu_load, gpu_load);
> +
> +	cpu_load = true;
> +	gpu_load = true;
> +
> +	test(&data, mode_override, cpu_load, gpu_load);
> +
> +	igt_fixture {
> +		igt_display_fini(&data.display);
> +	}
> +}
> \ No newline at end of file
> diff --git a/tests/meson.build b/tests/meson.build
> index 9015f809..c586642c 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -46,6 +46,7 @@ test_progs = [
>  	'kms_plane_lowres',
>  	'kms_plane_multiple',
>  	'kms_plane_scaling',
> +	'kms_plane_stress',
>  	'kms_prop_blob',
>  	'kms_properties',
>  	'kms_psr',
> -- 
> 2.17.1
> 
> _______________________________________________
> igt-dev mailing list
> igt-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/igt-dev


More information about the igt-dev mailing list