[Intel-gfx] [PATCH igt] tests/kms_fence_pin_leak: Exercise full ppgtt fence pin_count leak in the kernel
Daniel Vetter
daniel at ffwll.ch
Tue May 13 10:42:19 CEST 2014
On Tue, May 13, 2014 at 11:24:48AM +0300, Ville Syrjälä wrote:
> On Mon, May 12, 2014 at 08:34:07PM +0200, Daniel Vetter wrote:
> > On Mon, May 12, 2014 at 08:46:24PM +0300, ville.syrjala at linux.intel.com wrote:
> > > From: Ville Syrjälä <ville.syrjala at linux.intel.com>
> > >
> > > The kernel full ppgtt support has a bug where it can drop a pinned
> > > fence to the floor, hence we leak the pin_count as the subsequent
> > > fence unpin becomes a nop. We can trigger it easily by unbinding a
> > > buffer from a ppgtt address space while the buffer is simultaneosly
> > > being used for scanout.
> > >
> > > Monitor i915_gem_fence_regs to detect the leak while performing
> > > the required set of operations in a loop.
> >
> > do we really need that part? I've hoped just leaking enough fences until
> > the kernel blows up should be sufficient.
>
> Sure if you want to let it run for >5 years. That's how long it would
> take to make the pin_count overflow on my IVB machine, assuming you hit
> the bug on every iteration of the loop, which for some reason doesn't
> seem to be the case here.
Yeah, that's only feasible if we restrict the pin count like we already do
with obj->pin_count.
> Another option would be to trick the the pin_count to leak for every
> fence registers. Then we wouldn't be able to find a new fence and
> we'd get -EDEADLK from i915_find_fence_reg(). If I try to make sure
> all fences have an object associated with them, it should always
> pick a fence with pin_count==0, so it seems like this approach should
> work. I'll work on a it a bit and we'll see...
That's actually what I've had in mind ;-)
Aside: I'm of course not against using more of debugfs, so if it's too
much work I'm totally ok with this one. But generally I just prefer
black-box tests with as little introspection through debugfs as possible.
Thanks for doing this.
-Daniel
>
> > Generally I'm vary of using debugfs in new tests a bit since we
> > essentially then lock down that debugfs file as abi. And since no one
> > easily notices when a test stops catching a bug that's usually bad. Hence
> > also why I want basic testcases for debugfs infrastructure to make sure it
> > keeps on working.
> > -Daniel
> >
> > >
> > > Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
> > > ---
> > > tests/Makefile.sources | 1 +
> > > tests/kms_fence_pin_leak.c | 224 +++++++++++++++++++++++++++++++++++++++++++++
> > > 2 files changed, 225 insertions(+)
> > > create mode 100644 tests/kms_fence_pin_leak.c
> > >
> > > diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> > > index 5d5dc46..393c4a2 100644
> > > --- a/tests/Makefile.sources
> > > +++ b/tests/Makefile.sources
> > > @@ -60,6 +60,7 @@ TESTS_progs_M = \
> > > kms_addfb \
> > > kms_cursor_crc \
> > > kms_fbc_crc \
> > > + kms_fence_pin_leak \
> > > kms_flip \
> > > kms_flip_tiling \
> > > kms_pipe_crc_basic \
> > > diff --git a/tests/kms_fence_pin_leak.c b/tests/kms_fence_pin_leak.c
> > > new file mode 100644
> > > index 0000000..f299212
> > > --- /dev/null
> > > +++ b/tests/kms_fence_pin_leak.c
> > > @@ -0,0 +1,224 @@
> > > +/*
> > > + * Copyright © 2014 Intel Corporation
> > > + *
> > > + * 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 <errno.h>
> > > +#include <limits.h>
> > > +#include <stdbool.h>
> > > +#include <stdio.h>
> > > +#include <string.h>
> > > +
> > > +#include "drmtest.h"
> > > +#include "igt_debugfs.h"
> > > +#include "igt_kms.h"
> > > +#include "ioctl_wrappers.h"
> > > +#include "intel_chipset.h"
> > > +
> > > +typedef struct {
> > > + int drm_fd;
> > > + uint32_t devid;
> > > + drm_intel_bufmgr *bufmgr;
> > > + igt_display_t display;
> > > +} data_t;
> > > +
> > > +static void exec_nop(data_t *data, uint32_t handle, drm_intel_context *context)
> > > +{
> > > + drm_intel_bo *dst;
> > > + struct intel_batchbuffer *batch;
> > > +
> > > + dst = gem_handle_to_libdrm_bo(data->bufmgr, data->drm_fd, "", handle);
> > > + igt_assert(dst);
> > > +
> > > + batch = intel_batchbuffer_alloc(data->bufmgr, data->devid);
> > > + igt_assert(batch);
> > > +
> > > + /* add the reloc to make sure the kernel will think we write to dst */
> > > + BEGIN_BATCH(4);
> > > + OUT_BATCH(MI_BATCH_BUFFER_END);
> > > + OUT_BATCH(MI_NOOP);
> > > + OUT_RELOC(dst, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0);
> > > + OUT_BATCH(MI_NOOP);
> > > + ADVANCE_BATCH();
> > > +
> > > + intel_batchbuffer_flush_with_context(batch, context);
> > > + intel_batchbuffer_free(batch);
> > > +
> > > + drm_intel_bo_unreference(dst);
> > > +}
> > > +
> > > +static unsigned int total_fence_pin_count(void)
> > > +{
> > > + unsigned int pinned = 0;
> > > + char buf[128];
> > > + FILE *f;
> > > +
> > > + f = igt_debugfs_fopen("i915_gem_fence_regs", "r");
> > > + igt_assert(f);
> > > +
> > > + for (;;) {
> > > + unsigned int pin_count = 0;
> > > + char *ptr;
> > > +
> > > + ptr = fgets(buf, sizeof(buf), f);
> > > + if (!ptr)
> > > + break;
> > > +
> > > + if (sscanf(ptr, "Fence %*u, pin count = %u", &pin_count) == 1)
> > > + pinned += pin_count;
> > > + }
> > > +
> > > + fclose(f);
> > > +
> > > + return pinned;
> > > +}
> > > +
> > > +static bool run_single_test(data_t *data, enum pipe pipe, igt_output_t *output)
> > > +{
> > > + igt_display_t *display = &data->display;
> > > + drmModeModeInfo *mode;
> > > + igt_plane_t *primary;
> > > + unsigned int pin_count;
> > > + struct igt_fb fb[2];
> > > + int i;
> > > +
> > > + igt_output_set_pipe(output, pipe);
> > > + igt_display_commit(display);
> > > +
> > > + if (!output->valid) {
> > > + igt_output_set_pipe(output, PIPE_ANY);
> > > + igt_display_commit(display);
> > > + return false;
> > > + }
> > > +
> > > + mode = igt_output_get_mode(output);
> > > + primary = igt_output_get_plane(output, IGT_PLANE_PRIMARY);
> > > +
> > > + igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
> > > + DRM_FORMAT_XRGB8888,
> > > + true, /* need a fence so must be tiled */
> > > + 0.0, 0.0, 0.0,
> > > + &fb[0]);
> > > + igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
> > > + DRM_FORMAT_XRGB8888,
> > > + true, /* need a fence so must be tiled */
> > > + 0.0, 0.0, 0.0,
> > > + &fb[1]);
> > > +
> > > + igt_plane_set_fb(primary, &fb[0]);
> > > + igt_display_commit(display);
> > > +
> > > + /* sample the fence pin_counts now that we have one scanout buffer pinned */
> > > + pin_count = total_fence_pin_count();
> > > +
> > > + for (i = 0; i < 64; i++) {
> > > + drm_intel_context *ctx;
> > > +
> > > + /*
> > > + * Link fb.gem_handle to the ppgtt vm of ctx so that the context
> > > + * destruction will unbind the obj from the ppgtt vm in question.
> > > + */
> > > + ctx = drm_intel_gem_context_create(data->bufmgr);
> > > + igt_assert(ctx);
> > > + exec_nop(data, fb[i&1].gem_handle, ctx);
> > > + drm_intel_gem_context_destroy(ctx);
> > > +
> > > + /* Force a context switch to make sure ctx gets destroyed for real. */
> > > + exec_nop(data, fb[i&1].gem_handle, NULL);
> > > +
> > > + gem_sync(data->drm_fd, fb[i&1].gem_handle);
> > > +
> > > + /*
> > > + * Pin the new buffer and unpin the old buffer from display. If
> > > + * the kernel is buggy the ppgtt unbind will have dropped the
> > > + * fence for the old buffer, and now the display code will try
> > > + * to unpin only to find no fence there. So the pin_count will leak.
> > > + */
> > > + igt_plane_set_fb(primary, &fb[!(i&1)]);
> > > + igt_display_commit(display);
> > > +
> > > + /*
> > > + * We always have exactly one scanout buffer, so the fence pin_count
> > > + * should remain static. Fail the test if we detect a leak.
> > > + */
> > > + igt_assert(pin_count == total_fence_pin_count());
> > > +
> > > + printf(".");
> > > + fflush(stdout);
> > > + }
> > > +
> > > + igt_plane_set_fb(primary, NULL);
> > > + igt_output_set_pipe(output, PIPE_ANY);
> > > + igt_display_commit(display);
> > > +
> > > + igt_remove_fb(data->drm_fd, &fb[1]);
> > > + igt_remove_fb(data->drm_fd, &fb[0]);
> > > +
> > > + printf("\n");
> > > +
> > > + return true;
> > > +}
> > > +
> > > +static void run_test(data_t *data)
> > > +{
> > > + igt_display_t *display = &data->display;
> > > + igt_output_t *output;
> > > + enum pipe p;
> > > +
> > > + for_each_connected_output(display, output) {
> > > + for (p = 0; p < igt_display_get_n_pipes(display); p++) {
> > > + if (run_single_test(data, p, output))
> > > + return; /* one time ought to be enough */
> > > + }
> > > + }
> > > +
> > > + igt_skip("no valid crtc/connector combinations found\n");
> > > +}
> > > +
> > > +igt_simple_main
> > > +{
> > > + drm_intel_context *ctx;
> > > + data_t data = {};
> > > +
> > > + igt_skip_on_simulation();
> > > +
> > > + data.drm_fd = drm_open_any();
> > > +
> > > + data.devid = intel_get_drm_devid(data.drm_fd);
> > > +
> > > + igt_set_vt_graphics_mode();
> > > +
> > > + data.bufmgr = drm_intel_bufmgr_gem_init(data.drm_fd, 4096);
> > > + igt_assert(data.bufmgr);
> > > + drm_intel_bufmgr_gem_enable_reuse(data.bufmgr);
> > > +
> > > + igt_display_init(&data.display, data.drm_fd);
> > > +
> > > + ctx = drm_intel_gem_context_create(data.bufmgr);
> > > + igt_require(ctx);
> > > + drm_intel_gem_context_destroy(ctx);
> > > +
> > > + run_test(&data);
> > > +
> > > + drm_intel_bufmgr_destroy(data.bufmgr);
> > > + igt_display_fini(&data.display);
> > > +}
> > > --
> > > 1.8.3.2
> > >
> >
> > --
> > Daniel Vetter
> > Software Engineer, Intel Corporation
> > +41 (0) 79 365 57 48 - http://blog.ffwll.ch
>
> --
> Ville Syrjälä
> Intel OTC
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
More information about the Intel-gfx
mailing list