diff mbox

[v9,5/5] drm/i915/selftests: Introduce live tests of private PAT management

Message ID 1504893999-15403-5-git-send-email-zhi.a.wang@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Wang, Zhi A Sept. 8, 2017, 6:06 p.m. UTC
Introduce two live tests of private PAT management:

igt_ppat_init - This test is to check if all the PPAT configurations are
written into HW.

igt_ppat_get - This test performs several sub-tests on intel_ppat_get()
and intel_ppat_put().

The "perfect match" test case will try to get a PPAT entry with an existing
value, then check if the returned PPAT entry is the same one.

The "alloc entries" test case will run out of PPAT table, and check if all
the requested values are put into the newly allocated PPAT entries.

The negative test case will try to generate a new PPAT value, and get it
when PPAT table is full.

The "partial match" test case will generate a parital matched value from
the existing PPAT table and try to match it.

The "put entries" test case will free all the PPAT entries that allocated
in "alloc entries" test case. It will check if the values of freed PPAT
entries turn into ppat->clear_value.

v9:

- Refine generate_new_value(). (Chris)
- Refine failure output. (Chris)
- Refine test flow of "perfect match". (Chris)
- Introduce another negative test case after "partial match". (Chris)

v8:

- Remove noisy output. (Chris)
- Add negative test case. (Chris)

Cc: Ben Widawsky <benjamin.widawsky@intel.com>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Suggested-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Zhi Wang <zhi.a.wang@intel.com>
---
 drivers/gpu/drm/i915/selftests/i915_gem_gtt.c | 300 ++++++++++++++++++++++++++
 1 file changed, 300 insertions(+)

Comments

Chris Wilson Sept. 8, 2017, 9:32 p.m. UTC | #1
Quoting Zhi Wang (2017-09-08 19:06:39)
> +       /* case 3: negative test 2, suppose PPAT table is still full now */
> +       value = generate_new_value(ppat, value_for_negative_test);
> +       if (!value) {
> +               pr_err("fail to generate new value for negative test 2\n");
> +               ret = -EINVAL;
> +               goto ppat_put;
> +       }
> +
> +       entry = intel_ppat_get(i915, value);
> +       if (!IS_ERR(entry) || PTR_ERR(entry) != -ENOSPC) {
> +               pr_err("fail on negative test\n");
> +               ret = -EINVAL;
> +       }

Then make a hole and it should work again, that's what I meant. Which I
think is important for long running systems :)
-Chris
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index 6b132ca..1b3e31c 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -1094,6 +1094,304 @@  static int igt_ggtt_page(void *arg)
 	return err;
 }
 
+static int check_cnl_ppat(struct drm_i915_private *dev_priv)
+{
+	struct intel_ppat *ppat = &dev_priv->ppat;
+	int i;
+
+	for (i = 0; i < ppat->max_entries; i++) {
+		u32 value = I915_READ(GEN10_PAT_INDEX(i));
+
+		if (value != ppat->entries[i].value) {
+			pr_err("check PPAT failed: expected %x found %x\n",
+			       ppat->entries[i].value, value);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int check_bdw_ppat(struct drm_i915_private *dev_priv)
+{
+	struct intel_ppat *ppat = &dev_priv->ppat;
+	u64 pat, hw_pat;
+	int i;
+
+	pat = hw_pat = 0;
+
+	for (i = 0; i < ppat->max_entries; i++)
+		pat |= GEN8_PPAT(i, ppat->entries[i].value);
+
+	hw_pat = I915_READ(GEN8_PRIVATE_PAT_HI);
+	hw_pat <<= 32;
+	hw_pat |= I915_READ(GEN8_PRIVATE_PAT_LO);
+
+	if (pat != hw_pat) {
+		pr_err("check PPAT failed: expected %llx found %llx\n",
+		        pat, hw_pat);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int igt_ppat_check(void *arg)
+{
+	struct drm_i915_private *i915 = arg;
+	int ret;
+
+	if (!i915->ppat.max_entries)
+		return 0;
+
+	if (INTEL_GEN(i915) >= 10)
+		ret = check_cnl_ppat(i915);
+	else
+		ret = check_bdw_ppat(i915);
+
+	if (ret)
+		pr_err("check PPAT failed\n");
+
+	return ret;
+}
+
+static bool value_is_new(struct intel_ppat *ppat, u8 value)
+{
+	int i;
+
+	for_each_set_bit(i, ppat->used, ppat->max_entries) {
+		if (value != ppat->entries[i].value)
+			continue;
+
+		return false;
+	}
+	return true;
+}
+
+static bool value_for_partial_test(struct intel_ppat *ppat, u8 value)
+{
+	int i;
+
+	if (!value_is_new(ppat, value))
+		return false;
+
+	/*
+	 * At least, there should be one entry whose cache attribute is
+	 * same with the required value.
+	 */
+	for_each_set_bit(i, ppat->used, ppat->max_entries) {
+		if (GEN8_PPAT_GET_CA(value) !=
+		    GEN8_PPAT_GET_CA(ppat->entries[i].value))
+			continue;
+
+		return true;
+	}
+	return false;
+}
+
+static bool value_for_negative_test(struct intel_ppat *ppat, u8 value)
+{
+	int i;
+
+	if (!value_is_new(ppat, value))
+		return false;
+
+	/*
+	 * cache attribute has to be different, so i915_ppat_get() would
+	 * allocate a new entry.
+	 */
+	for_each_set_bit(i, ppat->used, ppat->max_entries) {
+		if (GEN8_PPAT_GET_CA(value) ==
+		    GEN8_PPAT_GET_CA(ppat->entries[i].value))
+			return false;
+	}
+	return true;
+}
+
+static u8 generate_new_value(struct intel_ppat *ppat,
+			     bool (*check_value)(struct intel_ppat *, u8))
+{
+	u8 ca[] = { GEN8_PPAT_WB, GEN8_PPAT_WT, GEN8_PPAT_UC, GEN8_PPAT_WC };
+	u8 tc[] = { GEN8_PPAT_LLC, GEN8_PPAT_LLCELLC, GEN8_PPAT_LLCeLLC };
+	u8 age[] = { GEN8_PPAT_AGE(3), GEN8_PPAT_AGE(2), GEN8_PPAT_AGE(1),
+		     GEN8_PPAT_AGE(0) };
+	int ca_index, tc_index, age_index;
+	u8 value;
+
+#define for_each_ppat_attr(ca_index, tc_index, age_index) \
+	for ((ca_index) = 0 ; (ca_index) < ARRAY_SIZE(ca); (ca_index)++) \
+	for ((tc_index) = 0; (tc_index) < ARRAY_SIZE(tc); (tc_index)++) \
+	for ((age_index) = 0; (age_index) < ARRAY_SIZE(age); (age_index)++)
+
+	for_each_ppat_attr(ca_index, tc_index, age_index) {
+		value = age[age_index] | ca[ca_index] | tc[tc_index];
+		if (check_value(ppat, value))
+			return value;
+	}
+#undef for_each_ppat_attr
+	return 0;
+}
+
+static int igt_ppat_get(void *arg)
+{
+	struct drm_i915_private *i915 = arg;
+	struct intel_ppat *ppat = &i915->ppat;
+	const struct intel_ppat_entry **entries;
+	const struct intel_ppat_entry *entry;
+	unsigned int size = 0;
+	u8 value;
+	int i, ret;
+
+	if (!ppat->max_entries)
+		return 0;
+
+	ret = igt_ppat_check(i915);
+	if (ret)
+		return ret;
+
+	/* case 1: perfect match */
+	for_each_set_bit(i, ppat->used, ppat->max_entries) {
+		entry = intel_ppat_get(i915, ppat->entries[i].value);
+		if (IS_ERR(entry))
+			return PTR_ERR(entry);
+
+		if (entry != &ppat->entries[i]) {
+			pr_err("entry is not expected\n");
+			intel_ppat_put(entry);
+			return -EINVAL;
+		}
+		intel_ppat_put(entry);
+	}
+
+	/* case 2: alloc new entries */
+	entries = NULL;
+	ret = 0;
+
+	while (!bitmap_full(ppat->used, ppat->max_entries)) {
+		const struct intel_ppat_entry **p;
+		DECLARE_BITMAP(used, INTEL_MAX_PPAT_ENTRIES);
+
+		bitmap_copy(used, ppat->used, ppat->max_entries);
+
+		p = krealloc(entries, (size + 1) *
+				   sizeof(struct intel_ppat_entry *),
+				   GFP_KERNEL);
+		if (!p) {
+			ret = -ENOMEM;
+			break;
+		}
+		entries = p;
+
+		p = &entries[size++];
+		*p = NULL;
+
+		value = generate_new_value(ppat, value_is_new);
+		if (!value) {
+			pr_err("fail to generate new value\n");
+			ret = -ENOSPC;
+			break;
+		}
+
+		*p = entry = intel_ppat_get(i915, value);
+		if (IS_ERR(entry)) {
+			pr_err("fail to get new entry\n");
+			ret = PTR_ERR(entry);
+			break;
+		}
+
+		if (entry->value != value) {
+			pr_err("value is not expected, expected %x found %x\n",
+			       value, entry->value);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (bitmap_equal(used, ppat->used, ppat->max_entries)) {
+			pr_err("fail to alloc a new entry\n");
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+	if (ret)
+		goto ppat_put;
+
+	/* case 3: negative test 1, suppose PPAT table is full now */
+	value = generate_new_value(ppat, value_for_negative_test);
+	if (!value) {
+		pr_err("fail to generate new value for negative test 1\n");
+		ret = -EINVAL;
+		goto ppat_put;
+	}
+
+	entry = intel_ppat_get(i915, value);
+	if (!IS_ERR(entry) || PTR_ERR(entry) != -ENOSPC) {
+		pr_err("fail on negative test\n");
+		ret = -EINVAL;
+		goto ppat_put;
+	}
+
+	/* case 4: partial match */
+	value = generate_new_value(ppat, value_for_partial_test);
+	if (!value) {
+		pr_err("fail to generate new value for partial test\n");
+		ret = -EINVAL;
+		goto ppat_put;
+	}
+
+	entry = intel_ppat_get(i915, value);
+	if (IS_ERR(entry)) {
+		pr_err("fail to get new entry\n");
+		ret = PTR_ERR(entry);
+		goto ppat_put;
+	}
+
+	if (!(entry->value != value &&
+	     GEN8_PPAT_GET_CA(entry->value) == GEN8_PPAT_GET_CA(value))) {
+		pr_err("value is not expected, expected %x found %x\n",
+				value, entry->value);
+		ret = -EINVAL;
+		goto ppat_put;
+	}
+
+	intel_ppat_put(entry);
+
+	/* case 3: negative test 2, suppose PPAT table is still full now */
+	value = generate_new_value(ppat, value_for_negative_test);
+	if (!value) {
+		pr_err("fail to generate new value for negative test 2\n");
+		ret = -EINVAL;
+		goto ppat_put;
+	}
+
+	entry = intel_ppat_get(i915, value);
+	if (!IS_ERR(entry) || PTR_ERR(entry) != -ENOSPC) {
+		pr_err("fail on negative test\n");
+		ret = -EINVAL;
+	}
+
+ppat_put:
+	if (entries) {
+		for (i = 0; i < size; i++) {
+			if (IS_ERR(entries[i]) || !entries[i])
+				continue;
+
+			intel_ppat_put(entries[i]);
+
+			if (entries[i]->value != ppat->clear_value) {
+				pr_err("fail to put ppat value\n");
+				ret = -EINVAL;
+				break;
+			}
+		}
+		kfree(entries);
+		entries = NULL;
+	}
+
+	if (ret)
+		return ret;
+
+	return igt_ppat_check(i915);
+}
+
 static void track_vma_bind(struct i915_vma *vma)
 {
 	struct drm_i915_gem_object *obj = vma->obj;
@@ -1560,6 +1858,8 @@  int i915_gem_gtt_live_selftests(struct drm_i915_private *i915)
 		SUBTEST(igt_ggtt_pot),
 		SUBTEST(igt_ggtt_fill),
 		SUBTEST(igt_ggtt_page),
+		SUBTEST(igt_ppat_check),
+		SUBTEST(igt_ppat_get),
 	};
 
 	GEM_BUG_ON(offset_in_page(i915->ggtt.base.total));