Re: [PATCH v8 5/5] drm/i915/selftests: Introduce live tests of private PAT management

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxx>
> Cc: Rodrigo Vivi <rodrigo.vivi@xxxxxxxxx>
> Cc: Joonas Lahtinen <joonas.lahtinen@xxxxxxxxxxxxxxx>
> Suggested-by: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx>
> Signed-off-by: Zhi Wang <zhi.a.wang@xxxxxxxxx>
> ---
>  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
_______________________________________________
Intel-gfx mailing list
Intel-gfx@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/intel-gfx




[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux