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

Lisovskiy, Stanislav stanislav.lisovskiy at intel.com
Tue Mar 26 12:00:46 UTC 2019


On Tue, 2019-03-26 at 11:11 +0200, Petri Latvala wrote:
> 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_co
> > nnector_valid((pipe), (output)))
> 
> 
> Docs for this. And a very good explanation what is different from
> for_each_pipe_with_valid_output().

Sure, will add those. The reasoning is that
for_each_pipe_with_valid_output will try to test every possible
combination of pipe and output, while in this case it is not needed.
 
This test case is different from for example kms_atomic_transition,
because it attempts to test all the pipes connected to outputs
simultaneously, while most of current test cases do it one by one.

> 
> 
> > +
> > +
> >  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_res
> > ources->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(highe
> > st_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?

In fact no, I simply saw a lot of test cases doing so, like kms_vblank
and used it as a reference. Considering that igt_gettime can do the
same thing, I guess it will make things easier for me, just as
igt_until_timeout macro.
I mostly try to use existing IGT functions instead of inventing my own,
however this time probably I used wrong example here :)

> 
> > +
> > +	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?
> 
> 


More information about the igt-dev mailing list