[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