[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
Mon May 12 20:34:07 CEST 2014


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.

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



More information about the Intel-gfx mailing list