[Intel-gfx] [PATCH v8 5/5] drm/i915/selftests: Introduce live tests of private PAT management
Chris Wilson
chris at chris-wilson.co.uk
Fri Sep 8 14:42:15 UTC 2017
Quoting Zhi Wang (2017-09-08 10:05:53)
> Introduce two live tests of private PAT management:
>
> igt_ppat_init - This test is to check if all the PPAT configuration is
> 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.
>
> v8:
>
> - Remove noisy output. (Chris)
> - Add negative test case. (Chris)
>
> Cc: Ben Widawsky <benjamin.widawsky at intel.com>
> Cc: Rodrigo Vivi <rodrigo.vivi at intel.com>
> Cc: Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
> Suggested-by: Chris Wilson <chris at chris-wilson.co.uk>
> Signed-off-by: Zhi Wang <zhi.a.wang at intel.com>
> ---
> drivers/gpu/drm/i915/selftests/i915_gem_gtt.c | 276 ++++++++++++++++++++++++++
> 1 file changed, 276 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> index 6b132ca..c5179ce 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> @@ -1094,6 +1094,280 @@ 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("expected PPAT value isn't written into HW\n");
Always helpful to include details such as expected and found values.
> + 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("expected PPAT value isn't written into HW\n");
> + 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);
> +
> + return ret;
> +}
Excellent little checker.
> +
> +static u8 generate_new_value(struct intel_ppat *ppat, bool partial,
> + bool negative)
More than one bool parameter is a recipe for confusion :)
> +{
> + 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) };
> + u8 value = 0;
> + bool same;
> + int ca_index, tc_index, age_index, i;
> +
> +#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];
> + same = false;
> +
> + for_each_set_bit(i, ppat->used, ppat->max_entries) {
> + if (value != ppat->entries[i].value)
> + continue;
> +
> + same = true;
> + break;
> + }
> +
> + if (same)
> + continue;
> +
> + if (!partial && !negative)
> + return value;
> +
> + if (partial) {
> + /* cache attribute has to be the same. */
> + 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 value;
> + }
> + }
> +
> + if (negative) {
> + /* cache attribute has to be new. */
> + same = false;
> + for_each_set_bit(i, ppat->used, ppat->max_entries) {
> + if (GEN8_PPAT_GET_CA(value) ==
> + GEN8_PPAT_GET_CA(ppat->entries[i].value)) {
> + same = true;
> + break;
> + }
> + }
> + if (same)
> + continue;
> + return value;
> + }
> + }
> +#undef for_each_ppat_attr
> + return 0;
> +}
> +
> +static inline bool ppat_table_is_full(struct intel_ppat *ppat)
> +{
> + return bitmap_weight(ppat->used, ppat->max_entries) ==
> + ppat->max_entries;
return bitmap_full();
> +}
> +
> +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 */
> + entry = intel_ppat_get(i915, ppat->entries[0].value);
> + if (IS_ERR(entry))
> + return PTR_ERR(entry);
> +
> + if (entry != &ppat->entries[0]) {
> + pr_err("not expected entry\n");
> + intel_ppat_put(entry);
> + return -EINVAL;
> + }
Ok, but finding entries[0] is easy. ;)
for_each_bit(bit, &used) {
entry = intel_ppat_get(i915, ppat->entries[bit].value);
if (IS_ERR(entry)) {
"found nothing for index %d, value=%x\n"
}
if (entry->value != ppat->entries[bit].value)) {
"lookup failed"
}
}
> +
> + intel_ppat_put(entry);
> +
> + /* case 2: alloc new entries */
> + entries = NULL;
> + ret = 0;
> +
> + while (!ppat_table_is_full(ppat)) {
> + 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 = -ENOSPC;
> + break;
> + }
> + entries = p;
> +
> + p = &entries[size++];
> + *p = NULL;
> +
> + value = generate_new_value(ppat, false, false);
> + if (!value) {
> + pr_err("cannot fill the unused PPAT entries?\n");
> + ret = -EINVAL;
> + 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("fail to get expected new value\n");
> + ret = -EINVAL;
> + break;
> + }
> +
> + if (bitmap_equal(used, ppat->used, ppat->max_entries)) {
> + pr_err("fail to alloc a new entry\n");
> + ret = -EINVAL;
> + break;
> + }
> + }
Ok.
> +
> + if (ret)
> + goto ppat_put;
> +
> + /* case 3: negative test, suppose PPAT table is full now */
> + value = generate_new_value(ppat, false, true);
> + if (!value) {
> + pr_err("fail to get new value\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;
> + }
Ok.
> +
> + ret = 0;
> +
> + /* case 4: partial match */
> + value = generate_new_value(ppat, true, false);
> + if (!value) {
> + pr_err("fail to get new value\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("fail to get expected value\n");
> + ret = -EINVAL;
> + }
> +
> + intel_ppat_put(entry);
Now check that you can allocate a new entry, the table is still full at
this point.
> +
> +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;
> +
> + ret = igt_ppat_check(i915);
> + if (ret)
> + return ret;
Makes sense to run this after every phase. Especially after filling and
then after freeing.
-Chris
More information about the Intel-gfx
mailing list