[Intel-gfx] [PATCH 16/21] drm/i915/selftests: huge page tests

Chris Wilson chris at chris-wilson.co.uk
Mon Jul 3 14:48:18 UTC 2017


Quoting Matthew Auld (2017-07-03 15:14:58)
> +static struct i915_vma *
> +gpu_write_dw(struct i915_vma *vma, u64 offset, u32 value)
> +{
> +       struct drm_i915_private *i915 = to_i915(vma->obj->base.dev);
> +       struct drm_i915_gem_object *obj;
> +       struct i915_vma *batch;
> +       unsigned int size;
> +       u32 *cmd;
> +       int err;
> +
> +       GEM_BUG_ON(INTEL_GEN(i915) < 8);

Didn't you enable large pages for snb and earlier?

> +static int gpu_write(struct i915_vma *vma,
> +                    struct i915_gem_context *ctx,
> +                    unsigned long offset,
> +                    u32 value)
> +{
> +       struct drm_i915_private *i915 = to_i915(vma->obj->base.dev);
> +       struct drm_i915_gem_request *rq;
> +       struct i915_vma *batch;
> +       int flags = 0;
> +       int err;
> +
> +       batch = gpu_write_dw(vma, offset, value);
> +       if (IS_ERR(batch))
> +               return PTR_ERR(batch);
> +
> +       rq = i915_gem_request_alloc(i915->engine[RCS], ctx);
> +       if (IS_ERR(rq)) {
> +               err = PTR_ERR(rq);
> +               goto err_batch;
> +       }
> +
> +       err = rq->engine->emit_flush(rq, EMIT_INVALIDATE);
> +       if (err)
> +               goto err_request;
> +
> +       err = i915_switch_context(rq);
> +       if (err)
> +               goto err_request;
> +
> +       err = rq->engine->emit_bb_start(rq,
> +                       batch->node.start, batch->node.size,
> +                       flags);
> +       if (err)
> +               goto err_request;
> +
> +       i915_vma_move_to_active(batch, rq, 0);
> +       i915_gem_object_set_active_reference(batch->obj);
> +       i915_vma_unpin(batch);
> +       i915_vma_close(batch);
> +
> +       i915_vma_move_to_active(vma, rq, 0);
> +
> +       reservation_object_lock(vma->obj->resv, NULL);

vma->resv

> +       reservation_object_add_excl_fence(vma->obj->resv, &rq->fence);
> +       reservation_object_unlock(vma->obj->resv);
> +
> +       __i915_add_request(rq, true);
> +
> +       return 0;
> +
> +err_request:
> +       __i915_add_request(rq, false);
> +err_batch:
> +       i915_vma_unpin(batch);

Leaking batch. Just tie the lifetime of the batch to the request
immediately after allocating the request.

> +
> +       return err;
> +}
> +
> +static inline int igt_can_allocate_thp(struct drm_i915_private *i915)
> +{
> +       return HAS_PAGE_SIZE(i915, I915_GTT_PAGE_SIZE_2M) &&
> +               has_transparent_hugepage();
> +}
> +
> +static int igt_ppgtt_write_huge(void *arg)
> +{
> +       struct i915_hw_ppgtt *ppgtt = arg;
> +       struct drm_i915_private *i915 = ppgtt->base.i915;
> +       struct drm_i915_gem_object *obj;
> +       const unsigned int page_sizes[] = {
> +               I915_GTT_PAGE_SIZE_64K,
> +               I915_GTT_PAGE_SIZE_2M,
> +       };
> +       struct i915_vma *vma;
> +       int err = 0;
> +       int i;
> +
> +       if (!igt_can_allocate_thp(i915))
> +               return 0;
> +
> +       /* Sanity check that the HW uses huge-pages correctly */
> +
> +       ppgtt = i915->kernel_context->ppgtt;
> +
> +       obj = i915_gem_object_create(i915, I915_GTT_PAGE_SIZE_2M);
> +       if (IS_ERR(obj))
> +               return PTR_ERR(obj);

2M is MAX_ORDER, so you don't need thp for this and can just use
i915_gem_object_create_internal() (or both, once to test without relying
on thp, perhaps more common, then once to test thp shmemfs).

> +       err = i915_gem_object_pin_pages(obj);
> +       if (err)
> +               goto out_put;
> +
> +       GEM_BUG_ON(!obj->mm.page_sizes.sg);
> +
> +       if (obj->mm.page_sizes.sg < I915_GTT_PAGE_SIZE_2M) {
> +               pr_err("Failed to allocate huge pages\n");
> +               err = -EINVAL;

Failure? Or just unable to test? Nothing forces the kernel to give you
contiguous space.

> +               goto out_unpin;
> +       }
> +
> +       vma = i915_vma_instance(obj, &ppgtt->base, NULL);
> +       if (IS_ERR(vma)) {
> +               err = PTR_ERR(vma);
> +               goto out_unpin;
> +       }
> +
> +       for (i = 0; i < ARRAY_SIZE(page_sizes); ++i) {
> +               unsigned int page_size = page_sizes[i];
> +               u32 *map;
> +
> +               if (!HAS_PAGE_SIZE(i915, page_size))
> +                       continue;
> +
> +               /* Force the page size */
> +               obj->mm.page_sizes.sg = page_size;
> +
> +               err = i915_gem_object_set_to_gtt_domain(obj, true);
> +               if (err)
> +                       goto out_close;

Gets in the way of the important details, make it coherent and get rid
of this.

> +               err = i915_vma_pin(vma, 0, 0, PIN_USER);
> +               if (err)
> +                       goto out_close;

Move the unbind above this:

/* Force the page size */
i915_vma_unbind(vma);

obj->m.page_sizes.sg = page_size;

i915_vma_pin(vma);
GEM_BUG_ON(vma->page_sizes.sg != page_size);

> +
> +               GEM_BUG_ON(obj->mm.page_sizes.gtt);
> +               GEM_BUG_ON(vma->page_sizes.sg != page_size);
> +               GEM_BUG_ON(!vma->page_sizes.phys);
> +
> +               GEM_BUG_ON(vma->page_sizes.gtt != page_size);
> +               GEM_BUG_ON(!IS_ALIGNED(vma->node.start, I915_GTT_PAGE_SIZE_2M));
> +               GEM_BUG_ON(!IS_ALIGNED(vma->node.size, I915_GTT_PAGE_SIZE_2M));
> +
> +               err = gpu_write(vma, i915->kernel_context, 0, page_size);
> +               if (err) {
> +                       i915_vma_unpin(vma);
> +                       goto out_close;
> +               }
> +
> +               err = i915_gem_object_set_to_cpu_domain(obj, false);
> +               if (err) {
> +                       i915_vma_unpin(vma);
> +                       goto out_close;
> +               }

Again, this is getting in the way of the important details.

> +               i915_vma_unpin(vma);
> +               err = i915_vma_unbind(vma);
> +               if (err)
> +                       goto out_close;
> +
> +               map = i915_gem_object_pin_map(obj, I915_MAP_WB);
> +               if (IS_ERR(map)) {
> +                       err = PTR_ERR(map);
> +                       goto out_close;
> +               }

Do we need to test different PTE bits?

If you don't make this coherent, use

map = pin_map(obj);
set_to_cpu_domain(obj);

for_each_page()
	test

set_to_gtt_domain(obj);
unpin_map(obj);

> +
> +               GEM_BUG_ON(map[0] != page_size);

This is the essential test being performed. Check it, and report an
error properly. Check every page at different offsets!

> +
> +               i915_gem_object_unpin_map(obj);
> +       }
> +
> +out_close:
> +       i915_vma_close(vma);
> +out_unpin:
> +       i915_gem_object_unpin_pages(obj);
> +out_put:
> +       i915_gem_object_put(obj);
> +
> +       return err;
> +}


More information about the Intel-gfx mailing list