Commit 3d81d589 authored by Chris Wilson's avatar Chris Wilson
Browse files

drm/i915: Test exhaustion of the mmap space



An unlikely error condition that we can simulate by stealing most of
the range before trying to insert new objects.

Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarMatthew Auld <matthew.auld@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170213171558.20942-20-chris@chris-wilson.co.uk
parent 48d89817
Loading
Loading
Loading
Loading
+138 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include "../i915_selftest.h"

#include "mock_gem_device.h"
#include "huge_gem_object.h"

static int igt_gem_object(void *arg)
{
@@ -432,6 +433,142 @@ out:
	return err;
}

static int make_obj_busy(struct drm_i915_gem_object *obj)
{
	struct drm_i915_private *i915 = to_i915(obj->base.dev);
	struct drm_i915_gem_request *rq;
	struct i915_vma *vma;
	int err;

	vma = i915_vma_instance(obj, &i915->ggtt.base, NULL);
	if (IS_ERR(vma))
		return PTR_ERR(vma);

	err = i915_vma_pin(vma, 0, 0, PIN_USER);
	if (err)
		return err;

	rq = i915_gem_request_alloc(i915->engine[RCS], i915->kernel_context);
	if (IS_ERR(rq)) {
		i915_vma_unpin(vma);
		return PTR_ERR(rq);
	}

	i915_vma_move_to_active(vma, rq, 0);
	i915_add_request(rq);

	i915_gem_object_set_active_reference(obj);
	i915_vma_unpin(vma);
	return 0;
}

static bool assert_mmap_offset(struct drm_i915_private *i915,
			       unsigned long size,
			       int expected)
{
	struct drm_i915_gem_object *obj;
	int err;

	obj = i915_gem_object_create_internal(i915, size);
	if (IS_ERR(obj))
		return PTR_ERR(obj);

	err = i915_gem_object_create_mmap_offset(obj);
	i915_gem_object_put(obj);

	return err == expected;
}

static int igt_mmap_offset_exhaustion(void *arg)
{
	struct drm_i915_private *i915 = arg;
	struct drm_mm *mm = &i915->drm.vma_offset_manager->vm_addr_space_mm;
	struct drm_i915_gem_object *obj;
	struct drm_mm_node resv, *hole;
	u64 hole_start, hole_end;
	int loop, err;

	/* Trim the device mmap space to only a page */
	memset(&resv, 0, sizeof(resv));
	drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
		resv.start = hole_start;
		resv.size = hole_end - hole_start - 1; /* PAGE_SIZE units */
		err = drm_mm_reserve_node(mm, &resv);
		if (err) {
			pr_err("Failed to trim VMA manager, err=%d\n", err);
			return err;
		}
		break;
	}

	/* Just fits! */
	if (!assert_mmap_offset(i915, PAGE_SIZE, 0)) {
		pr_err("Unable to insert object into single page hole\n");
		err = -EINVAL;
		goto out;
	}

	/* Too large */
	if (!assert_mmap_offset(i915, 2*PAGE_SIZE, -ENOSPC)) {
		pr_err("Unexpectedly succeeded in inserting too large object into single page hole\n");
		err = -EINVAL;
		goto out;
	}

	/* Fill the hole, further allocation attempts should then fail */
	obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
	if (IS_ERR(obj)) {
		err = PTR_ERR(obj);
		goto out;
	}

	err = i915_gem_object_create_mmap_offset(obj);
	if (err) {
		pr_err("Unable to insert object into reclaimed hole\n");
		goto err_obj;
	}

	if (!assert_mmap_offset(i915, PAGE_SIZE, -ENOSPC)) {
		pr_err("Unexpectedly succeeded in inserting object into no holes!\n");
		err = -EINVAL;
		goto err_obj;
	}

	i915_gem_object_put(obj);

	/* Now fill with busy dead objects that we expect to reap */
	for (loop = 0; loop < 3; loop++) {
		obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
		if (IS_ERR(obj)) {
			err = PTR_ERR(obj);
			goto out;
		}

		mutex_lock(&i915->drm.struct_mutex);
		err = make_obj_busy(obj);
		mutex_unlock(&i915->drm.struct_mutex);
		if (err) {
			pr_err("[loop %d] Failed to busy the object\n", loop);
			goto err_obj;
		}

		GEM_BUG_ON(!i915_gem_object_is_active(obj));
		err = i915_gem_object_create_mmap_offset(obj);
		if (err) {
			pr_err("[loop %d] i915_gem_object_create_mmap_offset failed with err=%d\n",
			       loop, err);
			goto out;
		}
	}

out:
	drm_mm_remove_node(&resv);
	return err;
err_obj:
	i915_gem_object_put(obj);
	goto out;
}

int i915_gem_object_mock_selftests(void)
{
	static const struct i915_subtest tests[] = {
@@ -456,6 +593,7 @@ int i915_gem_object_live_selftests(struct drm_i915_private *i915)
	static const struct i915_subtest tests[] = {
		SUBTEST(igt_gem_huge),
		SUBTEST(igt_partial_tiling),
		SUBTEST(igt_mmap_offset_exhaustion),
	};

	return i915_subtests(tests, i915);