Message ID | 20190313144401.17735-8-chris@chris-wilson.co.uk (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [01/39] drm/i915: Hold a ref to the ring while retiring | expand |
On Wed, Mar 13, 2019 at 02:43:30PM +0000, Chris Wilson wrote: > In preparation to making the ppGTT binding for a context explicit (to > facilitate reusing the same ppGTT between different contexts), allow the > user to create and destroy named ppGTT. > > v2: Replace global barrier for swapping over the ppgtt and tlbs with a > local context barrier (Tvrtko) > v3: serialise with struct_mutex; it's lazy but required dammit > v4: Rewrite igt_ctx_shared_exec to be more different (aimed to be more > similarly, turned out different!) > > v2: Fix up test unwind for aliasing-ppgtt (snb) > v3: Tighten language for uapi struct drm_i915_gem_vm_control. > v4: Patch the context image for runtime ppgtt switching! > > Testcase: igt/gem_ctx_param/vm > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> > Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com> > --- > drivers/gpu/drm/i915/i915_drv.c | 2 + > drivers/gpu/drm/i915/i915_drv.h | 3 + > drivers/gpu/drm/i915/i915_gem_context.c | 322 +++++++++++++++++- > drivers/gpu/drm/i915/i915_gem_context.h | 5 + > drivers/gpu/drm/i915/i915_gem_gtt.c | 30 +- > drivers/gpu/drm/i915/i915_gem_gtt.h | 17 +- > drivers/gpu/drm/i915/selftests/huge_pages.c | 1 - > .../gpu/drm/i915/selftests/i915_gem_context.c | 237 ++++++++++--- > drivers/gpu/drm/i915/selftests/i915_gem_gtt.c | 1 - > drivers/gpu/drm/i915/selftests/mock_context.c | 8 +- > include/uapi/drm/i915_drm.h | 43 +++ > 11 files changed, 594 insertions(+), 75 deletions(-) > > diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c > index 0d743907e7bc..5d53efc4c5d9 100644 > --- a/drivers/gpu/drm/i915/i915_drv.c > +++ b/drivers/gpu/drm/i915/i915_drv.c > @@ -3121,6 +3121,8 @@ static const struct drm_ioctl_desc i915_ioctls[] = { > DRM_IOCTL_DEF_DRV(I915_PERF_ADD_CONFIG, i915_perf_add_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), > DRM_IOCTL_DEF_DRV(I915_PERF_REMOVE_CONFIG, i915_perf_remove_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), > DRM_IOCTL_DEF_DRV(I915_QUERY, i915_query_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), > + DRM_IOCTL_DEF_DRV(I915_GEM_VM_CREATE, i915_gem_vm_create_ioctl, DRM_RENDER_ALLOW), > + DRM_IOCTL_DEF_DRV(I915_GEM_VM_DESTROY, i915_gem_vm_destroy_ioctl, DRM_RENDER_ALLOW), > }; > > static struct drm_driver driver = { > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h > index dc63303225fc..4675355916ff 100644 > --- a/drivers/gpu/drm/i915/i915_drv.h > +++ b/drivers/gpu/drm/i915/i915_drv.h > @@ -218,6 +218,9 @@ struct drm_i915_file_private { > } mm; > struct idr context_idr; > > + struct mutex vm_lock; > + struct idr vm_idr; > + > unsigned int bsd_engine; > > /* > diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c > index 4af51b689cbd..71464ae91d61 100644 > --- a/drivers/gpu/drm/i915/i915_gem_context.c > +++ b/drivers/gpu/drm/i915/i915_gem_context.c > @@ -120,12 +120,15 @@ static void lut_close(struct i915_gem_context *ctx) > list_del(&lut->obj_link); > i915_lut_handle_free(lut); > } > + INIT_LIST_HEAD(&ctx->handles_list); > > rcu_read_lock(); > radix_tree_for_each_slot(slot, &ctx->handles_vma, &iter, 0) { > struct i915_vma *vma = rcu_dereference_raw(*slot); > > radix_tree_iter_delete(&ctx->handles_vma, &iter, slot); > + > + vma->open_count--; > __i915_gem_object_release_unless_active(vma->obj); > } > rcu_read_unlock(); > @@ -306,7 +309,7 @@ static void context_close(struct i915_gem_context *ctx) > */ > lut_close(ctx); > if (ctx->ppgtt) > - i915_ppgtt_close(&ctx->ppgtt->vm); > + i915_ppgtt_close(ctx->ppgtt); > > ctx->file_priv = ERR_PTR(-EBADF); > i915_gem_context_put(ctx); > @@ -417,6 +420,32 @@ static void __destroy_hw_context(struct i915_gem_context *ctx, > context_close(ctx); > } > > +static struct i915_hw_ppgtt * > +__set_ppgtt(struct i915_gem_context *ctx, struct i915_hw_ppgtt *ppgtt) > +{ > + struct i915_hw_ppgtt *old = ctx->ppgtt; > + > + i915_ppgtt_open(ppgtt); > + ctx->ppgtt = i915_ppgtt_get(ppgtt); > + > + ctx->desc_template = default_desc_template(ctx->i915, ppgtt); > + > + return old; > +} > + > +static void __assign_ppgtt(struct i915_gem_context *ctx, > + struct i915_hw_ppgtt *ppgtt) > +{ > + if (ppgtt == ctx->ppgtt) > + return; > + > + ppgtt = __set_ppgtt(ctx, ppgtt); > + if (ppgtt) { > + i915_ppgtt_close(ppgtt); > + i915_ppgtt_put(ppgtt); > + } > +} > + > static struct i915_gem_context * > i915_gem_create_context(struct drm_i915_private *dev_priv, > struct drm_i915_file_private *file_priv) > @@ -443,8 +472,8 @@ i915_gem_create_context(struct drm_i915_private *dev_priv, > return ERR_CAST(ppgtt); > } > > - ctx->ppgtt = ppgtt; > - ctx->desc_template = default_desc_template(dev_priv, ppgtt); > + __assign_ppgtt(ctx, ppgtt); > + i915_ppgtt_put(ppgtt); > } > > trace_i915_context_create(ctx); > @@ -625,19 +654,29 @@ static int context_idr_cleanup(int id, void *p, void *data) > return 0; > } > > +static int vm_idr_cleanup(int id, void *p, void *data) > +{ > + i915_ppgtt_put(p); > + return 0; > +} > + > int i915_gem_context_open(struct drm_i915_private *i915, > struct drm_file *file) > { > struct drm_i915_file_private *file_priv = file->driver_priv; > struct i915_gem_context *ctx; > > + mutex_init(&file_priv->vm_lock); > + > idr_init(&file_priv->context_idr); > + idr_init_base(&file_priv->vm_idr, 1); > > mutex_lock(&i915->drm.struct_mutex); > ctx = i915_gem_create_context(i915, file_priv); > mutex_unlock(&i915->drm.struct_mutex); > if (IS_ERR(ctx)) { > idr_destroy(&file_priv->context_idr); > + idr_destroy(&file_priv->vm_idr); > return PTR_ERR(ctx); > } > > @@ -654,6 +693,89 @@ void i915_gem_context_close(struct drm_file *file) > > idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL); > idr_destroy(&file_priv->context_idr); > + > + idr_for_each(&file_priv->vm_idr, vm_idr_cleanup, NULL); > + idr_destroy(&file_priv->vm_idr); > + > + mutex_destroy(&file_priv->vm_lock); > +} > + > +int i915_gem_vm_create_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file) > +{ > + struct drm_i915_private *i915 = to_i915(dev); > + struct drm_i915_gem_vm_control *args = data; > + struct drm_i915_file_private *file_priv = file->driver_priv; > + struct i915_hw_ppgtt *ppgtt; > + int err; > + > + if (!HAS_FULL_PPGTT(i915)) > + return -ENODEV; > + > + if (args->flags) > + return -EINVAL; > + > + if (args->extensions) > + return -EINVAL; > + > + ppgtt = i915_ppgtt_create(i915, file_priv); > + if (IS_ERR(ppgtt)) > + return PTR_ERR(ppgtt); > + > + err = mutex_lock_interruptible(&file_priv->vm_lock); > + if (err) > + goto err_put; > + > + err = idr_alloc(&file_priv->vm_idr, ppgtt, 0, 0, GFP_KERNEL); > + mutex_unlock(&file_priv->vm_lock); > + if (err < 0) > + goto err_put; > + > + GEM_BUG_ON(err == 0); /* reserved for default/unassigned ppgtt */ > + ppgtt->user_handle = err; > + args->id = err; > + return 0; > + > +err_put: > + i915_ppgtt_put(ppgtt); > + return err; > +} > + > +int i915_gem_vm_destroy_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file) > +{ > + struct drm_i915_file_private *file_priv = file->driver_priv; > + struct drm_i915_gem_vm_control *args = data; > + struct i915_hw_ppgtt *ppgtt; > + int err; > + u32 id; > + > + if (args->flags) > + return -EINVAL; > + > + if (args->extensions) > + return -EINVAL; > + > + id = args->id; > + if (!id) > + return -ENOENT; > + > + err = mutex_lock_interruptible(&file_priv->vm_lock); > + if (err) > + return err; > + > + ppgtt = idr_remove(&file_priv->vm_idr, id); > + if (ppgtt) { > + GEM_BUG_ON(!ppgtt->user_handle); > + ppgtt->user_handle = 0; > + } > + > + mutex_unlock(&file_priv->vm_lock); > + if (!ppgtt) > + return -ENOENT; > + > + i915_ppgtt_put(ppgtt); > + return 0; > } > > static struct i915_request * > @@ -697,12 +819,13 @@ static void cb_retire(struct i915_active *base) > I915_SELFTEST_DECLARE(static unsigned long context_barrier_inject_fault); > static int context_barrier_task(struct i915_gem_context *ctx, > unsigned long engines, > + int (*emit)(struct i915_request *rq, void *data), > void (*task)(void *data), > void *data) > { > struct drm_i915_private *i915 = ctx->i915; > struct context_barrier_task *cb; > - struct intel_context *ce; > + struct intel_context *ce, *next; > intel_wakeref_t wakeref; > int err = 0; > > @@ -717,11 +840,11 @@ static int context_barrier_task(struct i915_gem_context *ctx, > i915_active_acquire(&cb->base); > > wakeref = intel_runtime_pm_get(i915); > - list_for_each_entry(ce, &ctx->active_engines, active_link) { > + rbtree_postorder_for_each_entry_safe(ce, next, &ctx->hw_contexts, node) { > struct intel_engine_cs *engine = ce->engine; > struct i915_request *rq; > > - if (!(ce->engine->mask & engines)) > + if (!(engine->mask & engines)) > continue; > > if (I915_SELFTEST_ONLY(context_barrier_inject_fault & > @@ -736,7 +859,12 @@ static int context_barrier_task(struct i915_gem_context *ctx, > break; > } > > - err = i915_active_ref(&cb->base, rq->fence.context, rq); > + err = 0; > + if (emit) > + err = emit(rq, data); > + if (err == 0) > + err = i915_active_ref(&cb->base, rq->fence.context, rq); > + > i915_request_add(rq); > if (err) > break; > @@ -799,6 +927,173 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *i915, > return 0; > } > > +static int get_ppgtt(struct i915_gem_context *ctx, > + struct drm_i915_gem_context_param *args) > +{ > + struct drm_i915_file_private *file_priv = ctx->file_priv; > + struct i915_hw_ppgtt *ppgtt; > + int ret; > + > + if (!ctx->ppgtt) > + return -ENODEV; > + > + /* XXX rcu acquire? */ > + ret = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex); > + if (ret) > + return ret; > + > + ppgtt = i915_ppgtt_get(ctx->ppgtt); > + mutex_unlock(&ctx->i915->drm.struct_mutex); > + > + ret = mutex_lock_interruptible(&file_priv->vm_lock); > + if (ret) > + goto err_put; > + > + if (!ppgtt->user_handle) { > + ret = idr_alloc(&file_priv->vm_idr, ppgtt, 0, 0, GFP_KERNEL); > + GEM_BUG_ON(!ret); > + if (ret < 0) > + goto err_unlock; > + > + ppgtt->user_handle = ret; > + i915_ppgtt_get(ppgtt); > + } > + > + args->size = 0; > + args->value = ppgtt->user_handle; > + > + ret = 0; > +err_unlock: > + mutex_unlock(&file_priv->vm_lock); > +err_put: > + i915_ppgtt_put(ppgtt); > + return ret; > +} > + > +static void set_ppgtt_barrier(void *data) > +{ > + struct i915_hw_ppgtt *old = data; > + > + if (INTEL_GEN(old->vm.i915) < 8) > + gen6_ppgtt_unpin_all(old); > + > + i915_ppgtt_close(old); > + i915_ppgtt_put(old); > +} > + > +static int emit_ppgtt_update(struct i915_request *rq, void *data) > +{ > + struct i915_hw_ppgtt *ppgtt = rq->gem_context->ppgtt; > + struct intel_engine_cs *engine = rq->engine; > + u32 *cs; > + int i; > + > + if (i915_vm_is_48bit(&ppgtt->vm)) { > + const dma_addr_t pd_daddr = px_dma(&ppgtt->pml4); > + > + cs = intel_ring_begin(rq, 6); > + if (IS_ERR(cs)) > + return PTR_ERR(cs); > + > + *cs++ = MI_LOAD_REGISTER_IMM(2); > + > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, 0)); > + *cs++ = upper_32_bits(pd_daddr); > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, 0)); > + *cs++ = lower_32_bits(pd_daddr); > + > + *cs++ = MI_NOOP; > + intel_ring_advance(rq, cs); > + } else if (HAS_LOGICAL_RING_CONTEXTS(engine->i915)) { > + cs = intel_ring_begin(rq, 4 * GEN8_3LVL_PDPES + 2); > + if (IS_ERR(cs)) > + return PTR_ERR(cs); > + > + *cs++ = MI_LOAD_REGISTER_IMM(2 * GEN8_3LVL_PDPES); > + for (i = GEN8_3LVL_PDPES; i--; ) { > + const dma_addr_t pd_daddr = i915_page_dir_dma_addr(ppgtt, i); > + > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, i)); > + *cs++ = upper_32_bits(pd_daddr); > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, i)); > + *cs++ = lower_32_bits(pd_daddr); > + } > + *cs++ = MI_NOOP; > + intel_ring_advance(rq, cs); > + } else { > + /* ppGTT is not part of the legacy context image */ > + gen6_ppgtt_pin(ppgtt); > + } > + > + return 0; > +} > + > +static int set_ppgtt(struct i915_gem_context *ctx, > + struct drm_i915_gem_context_param *args) > +{ > + struct drm_i915_file_private *file_priv = ctx->file_priv; > + struct i915_hw_ppgtt *ppgtt, *old; > + int err; > + > + if (args->size) > + return -EINVAL; > + > + if (!ctx->ppgtt) > + return -ENODEV; > + > + if (upper_32_bits(args->value)) > + return -ENOENT; > + > + err = mutex_lock_interruptible(&file_priv->vm_lock); > + if (err) > + return err; > + > + ppgtt = idr_find(&file_priv->vm_idr, args->value); > + if (ppgtt) { > + GEM_BUG_ON(ppgtt->user_handle != args->value); > + i915_ppgtt_get(ppgtt); > + } > + mutex_unlock(&file_priv->vm_lock); > + if (!ppgtt) > + return -ENOENT; > + > + err = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex); > + if (err) > + goto out; > + > + if (ppgtt == ctx->ppgtt) > + goto unlock; > + > + /* Teardown the existing obj:vma cache, it will have to be rebuilt. */ > + lut_close(ctx); > + > + old = __set_ppgtt(ctx, ppgtt); > + > + /* > + * We need to flush any requests using the current ppgtt before > + * we release it as the requests do not hold a reference themselves, > + * only indirectly through the context. > + */ > + err = context_barrier_task(ctx, ALL_ENGINES, maybe while we don't add this here we could move context_barrier_task to avoid breaking compilations without selftest? drivers/gpu/drm/i915/i915_gem_context.c:698:12: warning: ‘context_barrier_task’ defined but not used [-Wunused-function] static int context_barrier_task(struct i915_gem_context *ctx, ^~~~~~~~~~~~~~~~~~~~ > + emit_ppgtt_update, > + set_ppgtt_barrier, > + old); > + if (err) { > + ctx->ppgtt = old; > + ctx->desc_template = default_desc_template(ctx->i915, old); > + > + i915_ppgtt_close(ppgtt); > + i915_ppgtt_put(ppgtt); > + } > + > +unlock: > + mutex_unlock(&ctx->i915->drm.struct_mutex); > + > +out: > + i915_ppgtt_put(ppgtt); > + return err; > +} > + > static bool client_is_banned(struct drm_i915_file_private *file_priv) > { > return atomic_read(&file_priv->ban_score) >= I915_CLIENT_SCORE_BANNED; > @@ -973,6 +1268,9 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, > case I915_CONTEXT_PARAM_SSEU: > ret = get_sseu(ctx, args); > break; > + case I915_CONTEXT_PARAM_VM: > + ret = get_ppgtt(ctx, args); > + break; > default: > ret = -EINVAL; > break; > @@ -1274,9 +1572,6 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, > return -ENOENT; > > switch (args->param) { > - case I915_CONTEXT_PARAM_BAN_PERIOD: > - ret = -EINVAL; > - break; > case I915_CONTEXT_PARAM_NO_ZEROMAP: > if (args->size) > ret = -EINVAL; > @@ -1332,9 +1627,16 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, > I915_USER_PRIORITY(priority); > } > break; > + > case I915_CONTEXT_PARAM_SSEU: > ret = set_sseu(ctx, args); > break; > + > + case I915_CONTEXT_PARAM_VM: > + ret = set_ppgtt(ctx, args); > + break; > + > + case I915_CONTEXT_PARAM_BAN_PERIOD: > default: > ret = -EINVAL; > break; > diff --git a/drivers/gpu/drm/i915/i915_gem_context.h b/drivers/gpu/drm/i915/i915_gem_context.h > index 5a32c4b4816f..1e670372892c 100644 > --- a/drivers/gpu/drm/i915/i915_gem_context.h > +++ b/drivers/gpu/drm/i915/i915_gem_context.h > @@ -153,6 +153,11 @@ void i915_gem_context_release(struct kref *ctx_ref); > struct i915_gem_context * > i915_gem_context_create_gvt(struct drm_device *dev); > > +int i915_gem_vm_create_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file); > +int i915_gem_vm_destroy_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file); > + > int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, > struct drm_file *file); > int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, > diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c > index dac08d9c3fab..c0972cd95283 100644 > --- a/drivers/gpu/drm/i915/i915_gem_gtt.c > +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c > @@ -1939,6 +1939,8 @@ int gen6_ppgtt_pin(struct i915_hw_ppgtt *base) > struct gen6_hw_ppgtt *ppgtt = to_gen6_ppgtt(base); > int err; > > + GEM_BUG_ON(ppgtt->base.vm.closed); > + > /* > * Workaround the limited maximum vma->pin_count and the aliasing_ppgtt > * which will be pinned into every active context. > @@ -1977,6 +1979,17 @@ void gen6_ppgtt_unpin(struct i915_hw_ppgtt *base) > i915_vma_unpin(ppgtt->vma); > } > > +void gen6_ppgtt_unpin_all(struct i915_hw_ppgtt *base) > +{ > + struct gen6_hw_ppgtt *ppgtt = to_gen6_ppgtt(base); > + > + if (!ppgtt->pin_count) > + return; > + > + ppgtt->pin_count = 0; > + i915_vma_unpin(ppgtt->vma); > +} > + > static struct i915_hw_ppgtt *gen6_ppgtt_create(struct drm_i915_private *i915) > { > struct i915_ggtt * const ggtt = &i915->ggtt; > @@ -2099,10 +2112,21 @@ i915_ppgtt_create(struct drm_i915_private *i915, > return ppgtt; > } > > -void i915_ppgtt_close(struct i915_address_space *vm) > +void i915_ppgtt_open(struct i915_hw_ppgtt *ppgtt) > { > - GEM_BUG_ON(vm->closed); > - vm->closed = true; > + GEM_BUG_ON(ppgtt->vm.closed); > + > + ppgtt->open_count++; > +} > + > +void i915_ppgtt_close(struct i915_hw_ppgtt *ppgtt) > +{ > + GEM_BUG_ON(!ppgtt->open_count); > + if (--ppgtt->open_count) > + return; > + > + GEM_BUG_ON(ppgtt->vm.closed); > + ppgtt->vm.closed = true; > } > > static void ppgtt_destroy_vma(struct i915_address_space *vm) > diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h > index a47e11e6fc1b..5973d92e45fc 100644 > --- a/drivers/gpu/drm/i915/i915_gem_gtt.h > +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h > @@ -391,11 +391,15 @@ struct i915_hw_ppgtt { > struct kref ref; > > unsigned long pd_dirty_engines; > + unsigned int open_count; > + > union { > struct i915_pml4 pml4; /* GEN8+ & 48b PPGTT */ > struct i915_page_directory_pointer pdp; /* GEN8+ */ > struct i915_page_directory pd; /* GEN6-7 */ > }; > + > + u32 user_handle; > }; > > struct gen6_hw_ppgtt { > @@ -606,12 +610,16 @@ int i915_ppgtt_init_hw(struct drm_i915_private *dev_priv); > void i915_ppgtt_release(struct kref *kref); > struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_i915_private *dev_priv, > struct drm_i915_file_private *fpriv); > -void i915_ppgtt_close(struct i915_address_space *vm); > -static inline void i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt) > + > +void i915_ppgtt_open(struct i915_hw_ppgtt *ppgtt); > +void i915_ppgtt_close(struct i915_hw_ppgtt *ppgtt); > + > +static inline struct i915_hw_ppgtt *i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt) > { > - if (ppgtt) > - kref_get(&ppgtt->ref); > + kref_get(&ppgtt->ref); > + return ppgtt; > } > + > static inline void i915_ppgtt_put(struct i915_hw_ppgtt *ppgtt) > { > if (ppgtt) > @@ -620,6 +628,7 @@ static inline void i915_ppgtt_put(struct i915_hw_ppgtt *ppgtt) > > int gen6_ppgtt_pin(struct i915_hw_ppgtt *base); > void gen6_ppgtt_unpin(struct i915_hw_ppgtt *base); > +void gen6_ppgtt_unpin_all(struct i915_hw_ppgtt *base); > > void i915_check_and_clear_faults(struct drm_i915_private *dev_priv); > void i915_gem_suspend_gtt_mappings(struct drm_i915_private *dev_priv); > diff --git a/drivers/gpu/drm/i915/selftests/huge_pages.c b/drivers/gpu/drm/i915/selftests/huge_pages.c > index 1e66cff985f8..0b7740dc18cb 100644 > --- a/drivers/gpu/drm/i915/selftests/huge_pages.c > +++ b/drivers/gpu/drm/i915/selftests/huge_pages.c > @@ -1734,7 +1734,6 @@ int i915_gem_huge_page_mock_selftests(void) > err = i915_subtests(tests, ppgtt); > > out_close: > - i915_ppgtt_close(&ppgtt->vm); > i915_ppgtt_put(ppgtt); > > out_unlock: > diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/selftests/i915_gem_context.c > index 4399ef9ebf15..1ba354a916c0 100644 > --- a/drivers/gpu/drm/i915/selftests/i915_gem_context.c > +++ b/drivers/gpu/drm/i915/selftests/i915_gem_context.c > @@ -372,7 +372,8 @@ static int cpu_fill(struct drm_i915_gem_object *obj, u32 value) > return 0; > } > > -static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max) > +static noinline int cpu_check(struct drm_i915_gem_object *obj, > + unsigned int idx, unsigned int max) > { > unsigned int n, m, needs_flush; > int err; > @@ -390,8 +391,10 @@ static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max) > > for (m = 0; m < max; m++) { > if (map[m] != m) { > - pr_err("Invalid value at page %d, offset %d: found %x expected %x\n", > - n, m, map[m], m); > + pr_err("%pS: Invalid value at object %d page %d/%ld, offset %d/%d: found %x expected %x\n", > + __builtin_return_address(0), idx, > + n, real_page_count(obj), m, max, > + map[m], m); > err = -EINVAL; > goto out_unmap; > } > @@ -399,8 +402,9 @@ static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max) > > for (; m < DW_PER_PAGE; m++) { > if (map[m] != STACK_MAGIC) { > - pr_err("Invalid value at page %d, offset %d: found %x expected %x\n", > - n, m, map[m], STACK_MAGIC); > + pr_err("%pS: Invalid value at object %d page %d, offset %d: found %x expected %x (uninitialised)\n", > + __builtin_return_address(0), idx, n, m, > + map[m], STACK_MAGIC); > err = -EINVAL; > goto out_unmap; > } > @@ -478,12 +482,8 @@ static unsigned long max_dwords(struct drm_i915_gem_object *obj) > static int igt_ctx_exec(void *arg) > { > struct drm_i915_private *i915 = arg; > - struct drm_i915_gem_object *obj = NULL; > - unsigned long ncontexts, ndwords, dw; > - struct igt_live_test t; > - struct drm_file *file; > - IGT_TIMEOUT(end_time); > - LIST_HEAD(objects); > + struct intel_engine_cs *engine; > + enum intel_engine_id id; > int err = -ENODEV; > > /* > @@ -495,44 +495,169 @@ static int igt_ctx_exec(void *arg) > if (!DRIVER_CAPS(i915)->has_logical_contexts) > return 0; > > + for_each_engine(engine, i915, id) { > + struct drm_i915_gem_object *obj = NULL; > + unsigned long ncontexts, ndwords, dw; > + struct igt_live_test t; > + struct drm_file *file; > + IGT_TIMEOUT(end_time); > + LIST_HEAD(objects); > + > + if (!intel_engine_can_store_dword(engine)) > + continue; > + > + if (!engine->context_size) > + continue; /* No logical context support in HW */ > + > + file = mock_file(i915); > + if (IS_ERR(file)) > + return PTR_ERR(file); > + > + mutex_lock(&i915->drm.struct_mutex); > + > + err = igt_live_test_begin(&t, i915, __func__, engine->name); > + if (err) > + goto out_unlock; > + > + ncontexts = 0; > + ndwords = 0; > + dw = 0; > + while (!time_after(jiffies, end_time)) { > + struct i915_gem_context *ctx; > + intel_wakeref_t wakeref; > + > + ctx = i915_gem_create_context(i915, file->driver_priv); > + if (IS_ERR(ctx)) { > + err = PTR_ERR(ctx); > + goto out_unlock; > + } > + > + if (!obj) { > + obj = create_test_object(ctx, file, &objects); > + if (IS_ERR(obj)) { > + err = PTR_ERR(obj); > + goto out_unlock; > + } > + } > + > + with_intel_runtime_pm(i915, wakeref) > + err = gpu_fill(obj, ctx, engine, dw); > + if (err) { > + pr_err("Failed to fill dword %lu [%lu/%lu] with gpu (%s) in ctx %u [full-ppgtt? %s], err=%d\n", > + ndwords, dw, max_dwords(obj), > + engine->name, ctx->hw_id, > + yesno(!!ctx->ppgtt), err); > + goto out_unlock; > + } > + > + if (++dw == max_dwords(obj)) { > + obj = NULL; > + dw = 0; > + } > + > + ndwords++; > + ncontexts++; > + } > + > + pr_info("Submitted %lu contexts to %s, filling %lu dwords\n", > + ncontexts, engine->name, ndwords); > + > + ncontexts = dw = 0; > + list_for_each_entry(obj, &objects, st_link) { > + unsigned int rem = > + min_t(unsigned int, ndwords - dw, max_dwords(obj)); > + > + err = cpu_check(obj, ncontexts++, rem); > + if (err) > + break; > + > + dw += rem; > + } > + > +out_unlock: > + if (igt_live_test_end(&t)) > + err = -EIO; > + mutex_unlock(&i915->drm.struct_mutex); > + > + mock_file_free(i915, file); > + if (err) > + return err; > + } > + > + return 0; > +} > + > +static int igt_shared_ctx_exec(void *arg) > +{ > + struct drm_i915_private *i915 = arg; > + struct i915_gem_context *parent; > + struct intel_engine_cs *engine; > + enum intel_engine_id id; > + struct igt_live_test t; > + struct drm_file *file; > + int err = 0; > + > + /* > + * Create a few different contexts with the same mm and write > + * through each ctx using the GPU making sure those writes end > + * up in the expected pages of our obj. > + */ > + if (!DRIVER_CAPS(i915)->has_logical_contexts) > + return 0; > + > file = mock_file(i915); > if (IS_ERR(file)) > return PTR_ERR(file); > > mutex_lock(&i915->drm.struct_mutex); > > + parent = i915_gem_create_context(i915, file->driver_priv); > + if (IS_ERR(parent)) { > + err = PTR_ERR(parent); > + goto out_unlock; > + } > + > + if (!parent->ppgtt) { /* not full-ppgtt; nothing to share */ > + err = -ENODEV; > + goto out_unlock; > + } > + > err = igt_live_test_begin(&t, i915, __func__, ""); > if (err) > goto out_unlock; > > - ncontexts = 0; > - ndwords = 0; > - dw = 0; > - while (!time_after(jiffies, end_time)) { > - struct intel_engine_cs *engine; > - struct i915_gem_context *ctx; > - unsigned int id; > + for_each_engine(engine, i915, id) { > + unsigned long ncontexts, ndwords, dw; > + struct drm_i915_gem_object *obj = NULL; > + struct i915_gem_context *ctx = NULL; > + IGT_TIMEOUT(end_time); > + LIST_HEAD(objects); > > - ctx = i915_gem_create_context(i915, file->driver_priv); > - if (IS_ERR(ctx)) { > - err = PTR_ERR(ctx); > - goto out_unlock; > - } > + if (!intel_engine_can_store_dword(engine)) > + continue; > > - for_each_engine(engine, i915, id) { > + dw = 0; > + ndwords = 0; > + ncontexts = 0; > + while (!time_after(jiffies, end_time)) { > intel_wakeref_t wakeref; > > - if (!engine->context_size) > - continue; /* No logical context support in HW */ > + if (ctx) > + __destroy_hw_context(ctx, file->driver_priv); > > - if (!intel_engine_can_store_dword(engine)) > - continue; > + ctx = i915_gem_create_context(i915, file->driver_priv); > + if (IS_ERR(ctx)) { > + err = PTR_ERR(ctx); > + goto out_test; > + } > + > + __assign_ppgtt(ctx, parent->ppgtt); > > if (!obj) { > - obj = create_test_object(ctx, file, &objects); > + obj = create_test_object(parent, file, &objects); > if (IS_ERR(obj)) { > err = PTR_ERR(obj); > - goto out_unlock; > + goto out_test; > } > } > > @@ -544,35 +669,36 @@ static int igt_ctx_exec(void *arg) > ndwords, dw, max_dwords(obj), > engine->name, ctx->hw_id, > yesno(!!ctx->ppgtt), err); > - goto out_unlock; > + goto out_test; > } > > if (++dw == max_dwords(obj)) { > obj = NULL; > dw = 0; > } > + > ndwords++; > + ncontexts++; > } > - ncontexts++; > - } > - pr_info("Submitted %lu contexts (across %u engines), filling %lu dwords\n", > - ncontexts, RUNTIME_INFO(i915)->num_engines, ndwords); > + pr_info("Submitted %lu contexts to %s, filling %lu dwords\n", > + ncontexts, engine->name, ndwords); > > - dw = 0; > - list_for_each_entry(obj, &objects, st_link) { > - unsigned int rem = > - min_t(unsigned int, ndwords - dw, max_dwords(obj)); > + ncontexts = dw = 0; > + list_for_each_entry(obj, &objects, st_link) { > + unsigned int rem = > + min_t(unsigned int, ndwords - dw, max_dwords(obj)); > > - err = cpu_check(obj, rem); > - if (err) > - break; > + err = cpu_check(obj, ncontexts++, rem); > + if (err) > + goto out_test; > > - dw += rem; > + dw += rem; > + } > } > - > -out_unlock: > +out_test: > if (igt_live_test_end(&t)) > err = -EIO; > +out_unlock: > mutex_unlock(&i915->drm.struct_mutex); > > mock_file_free(i915, file); > @@ -1048,7 +1174,7 @@ static int igt_ctx_readonly(void *arg) > struct drm_i915_gem_object *obj = NULL; > struct i915_gem_context *ctx; > struct i915_hw_ppgtt *ppgtt; > - unsigned long ndwords, dw; > + unsigned long idx, ndwords, dw; > struct igt_live_test t; > struct drm_file *file; > I915_RND_STATE(prng); > @@ -1129,6 +1255,7 @@ static int igt_ctx_readonly(void *arg) > ndwords, RUNTIME_INFO(i915)->num_engines); > > dw = 0; > + idx = 0; > list_for_each_entry(obj, &objects, st_link) { > unsigned int rem = > min_t(unsigned int, ndwords - dw, max_dwords(obj)); > @@ -1138,7 +1265,7 @@ static int igt_ctx_readonly(void *arg) > if (i915_gem_object_is_readonly(obj)) > num_writes = 0; > > - err = cpu_check(obj, num_writes); > + err = cpu_check(obj, idx++, num_writes); > if (err) > break; > > @@ -1626,7 +1753,8 @@ static int mock_context_barrier(void *arg) > } > > counter = 0; > - err = context_barrier_task(ctx, 0, mock_barrier_task, &counter); > + err = context_barrier_task(ctx, 0, > + NULL, mock_barrier_task, &counter); > if (err) { > pr_err("Failed at line %d, err=%d\n", __LINE__, err); > goto out; > @@ -1638,8 +1766,8 @@ static int mock_context_barrier(void *arg) > } > > counter = 0; > - err = context_barrier_task(ctx, > - ALL_ENGINES, mock_barrier_task, &counter); > + err = context_barrier_task(ctx, ALL_ENGINES, > + NULL, mock_barrier_task, &counter); > if (err) { > pr_err("Failed at line %d, err=%d\n", __LINE__, err); > goto out; > @@ -1662,8 +1790,8 @@ static int mock_context_barrier(void *arg) > > counter = 0; > context_barrier_inject_fault = BIT(RCS0); > - err = context_barrier_task(ctx, > - ALL_ENGINES, mock_barrier_task, &counter); > + err = context_barrier_task(ctx, ALL_ENGINES, > + NULL, mock_barrier_task, &counter); > context_barrier_inject_fault = 0; > if (err == -ENXIO) > err = 0; > @@ -1677,8 +1805,8 @@ static int mock_context_barrier(void *arg) > goto out; > > counter = 0; > - err = context_barrier_task(ctx, > - ALL_ENGINES, mock_barrier_task, &counter); > + err = context_barrier_task(ctx, ALL_ENGINES, > + NULL, mock_barrier_task, &counter); > if (err) { > pr_err("Failed at line %d, err=%d\n", __LINE__, err); > goto out; > @@ -1726,6 +1854,7 @@ int i915_gem_context_live_selftests(struct drm_i915_private *dev_priv) > SUBTEST(igt_ctx_exec), > SUBTEST(igt_ctx_readonly), > SUBTEST(igt_ctx_sseu), > + SUBTEST(igt_shared_ctx_exec), > SUBTEST(igt_vm_isolation), > }; > > diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c > index 826fd51c331e..57b3d9867070 100644 > --- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c > +++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c > @@ -1020,7 +1020,6 @@ static int exercise_ppgtt(struct drm_i915_private *dev_priv, > > err = func(dev_priv, &ppgtt->vm, 0, ppgtt->vm.total, end_time); > > - i915_ppgtt_close(&ppgtt->vm); > i915_ppgtt_put(ppgtt); > out_unlock: > mutex_unlock(&dev_priv->drm.struct_mutex); > diff --git a/drivers/gpu/drm/i915/selftests/mock_context.c b/drivers/gpu/drm/i915/selftests/mock_context.c > index 8efa6892c6cd..f90328b21763 100644 > --- a/drivers/gpu/drm/i915/selftests/mock_context.c > +++ b/drivers/gpu/drm/i915/selftests/mock_context.c > @@ -54,13 +54,17 @@ mock_context(struct drm_i915_private *i915, > goto err_handles; > > if (name) { > + struct i915_hw_ppgtt *ppgtt; > + > ctx->name = kstrdup(name, GFP_KERNEL); > if (!ctx->name) > goto err_put; > > - ctx->ppgtt = mock_ppgtt(i915, name); > - if (!ctx->ppgtt) > + ppgtt = mock_ppgtt(i915, name); > + if (!ppgtt) > goto err_put; > + > + __set_ppgtt(ctx, ppgtt); > } > > return ctx; > diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h > index 1c69ed16a923..5079e25909ef 100644 > --- a/include/uapi/drm/i915_drm.h > +++ b/include/uapi/drm/i915_drm.h > @@ -343,6 +343,8 @@ typedef struct _drm_i915_sarea { > #define DRM_I915_PERF_ADD_CONFIG 0x37 > #define DRM_I915_PERF_REMOVE_CONFIG 0x38 > #define DRM_I915_QUERY 0x39 > +#define DRM_I915_GEM_VM_CREATE 0x3a > +#define DRM_I915_GEM_VM_DESTROY 0x3b > /* Must be kept compact -- no holes */ > > #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) > @@ -402,6 +404,8 @@ typedef struct _drm_i915_sarea { > #define DRM_IOCTL_I915_PERF_ADD_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config) > #define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64) > #define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query) > +#define DRM_IOCTL_I915_GEM_VM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_VM_CREATE, struct drm_i915_gem_vm_control) > +#define DRM_IOCTL_I915_GEM_VM_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_VM_DESTROY, struct drm_i915_gem_vm_control) > > /* Allow drivers to submit batchbuffers directly to hardware, relying > * on the security mechanisms provided by hardware. > @@ -1453,6 +1457,33 @@ struct drm_i915_gem_context_destroy { > __u32 pad; > }; > > +/* > + * DRM_I915_GEM_VM_CREATE - > + * > + * Create a new virtual memory address space (ppGTT) for use within a context > + * on the same file. Extensions can be provided to configure exactly how the > + * address space is setup upon creation. > + * > + * The id of new VM (bound to the fd) for use with I915_CONTEXT_PARAM_VM is > + * returned in the outparam @id. > + * > + * No flags are defined, with all bits reserved and must be zero. > + * > + * An extension chain maybe provided, starting with @extensions, and terminated > + * by the @next_extension being 0. Currently, no extensions are defined. > + * > + * DRM_I915_GEM_VM_DESTROY - > + * > + * Destroys a previously created VM id, specified in @id. > + * > + * No extensions or flags are allowed currently, and so must be zero. > + */ > +struct drm_i915_gem_vm_control { > + __u64 extensions; > + __u32 flags; > + __u32 id; > +}; > + > struct drm_i915_reg_read { > /* > * Register offset. > @@ -1542,7 +1573,19 @@ struct drm_i915_gem_context_param { > * On creation, all new contexts are marked as recoverable. > */ > #define I915_CONTEXT_PARAM_RECOVERABLE 0x8 > + > + /* > + * The id of the associated virtual memory address space (ppGTT) of > + * this context. Can be retrieved and passed to another context > + * (on the same fd) for both to use the same ppGTT and so share > + * address layouts, and avoid reloading the page tables on context > + * switches between themselves. > + * > + * See DRM_I915_GEM_VM_CREATE and DRM_I915_GEM_VM_DESTROY. > + */ > +#define I915_CONTEXT_PARAM_VM 0x9 > /* Must be kept compact -- no holes and well documented */ > + > __u64 value; > }; > > -- > 2.20.1 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Quoting Rodrigo Vivi (2019-03-13 20:11:21) > On Wed, Mar 13, 2019 at 02:43:30PM +0000, Chris Wilson wrote: > > + /* > > + * We need to flush any requests using the current ppgtt before > > + * we release it as the requests do not hold a reference themselves, > > + * only indirectly through the context. > > + */ > > + err = context_barrier_task(ctx, ALL_ENGINES, > > maybe while we don't add this here we could move context_barrier_task > to avoid breaking compilations without selftest? > > drivers/gpu/drm/i915/i915_gem_context.c:698:12: warning: ‘context_barrier_task’ defined but not used [-Wunused-function] > static int context_barrier_task(struct i915_gem_context *ctx, So what's your objection to this code that uses it? -Chris
On Wed, Mar 13, 2019 at 08:47:41PM +0000, Chris Wilson wrote: > Quoting Rodrigo Vivi (2019-03-13 20:11:21) > > On Wed, Mar 13, 2019 at 02:43:30PM +0000, Chris Wilson wrote: > > > + /* > > > + * We need to flush any requests using the current ppgtt before > > > + * we release it as the requests do not hold a reference themselves, > > > + * only indirectly through the context. > > > + */ > > > + err = context_barrier_task(ctx, ALL_ENGINES, > > > > maybe while we don't add this here we could move context_barrier_task > > to avoid breaking compilations without selftest? > > > > drivers/gpu/drm/i915/i915_gem_context.c:698:12: warning: ‘context_barrier_task’ defined but not used [-Wunused-function] > > static int context_barrier_task(struct i915_gem_context *ctx, > > So what's your objection to this code that uses it? no objection to this code... my only concern is *if* this takes a while to merge we could be breaking few builds around because the lack of this patch here. So I wondered if we should just wait or we could move context_barrier_task to selftest/i915_gem_context.c and this patch would need to be modified to move it back. > -Chris
On 13/03/2019 14:43, Chris Wilson wrote: > In preparation to making the ppGTT binding for a context explicit (to > facilitate reusing the same ppGTT between different contexts), allow the > user to create and destroy named ppGTT. > > v2: Replace global barrier for swapping over the ppgtt and tlbs with a > local context barrier (Tvrtko) > v3: serialise with struct_mutex; it's lazy but required dammit > v4: Rewrite igt_ctx_shared_exec to be more different (aimed to be more > similarly, turned out different!) > > v2: Fix up test unwind for aliasing-ppgtt (snb) > v3: Tighten language for uapi struct drm_i915_gem_vm_control. > v4: Patch the context image for runtime ppgtt switching! -ECHANGELOGNOTMONOTONIC :)) > Testcase: igt/gem_ctx_param/vm > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> > Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com> > --- > drivers/gpu/drm/i915/i915_drv.c | 2 + > drivers/gpu/drm/i915/i915_drv.h | 3 + > drivers/gpu/drm/i915/i915_gem_context.c | 322 +++++++++++++++++- > drivers/gpu/drm/i915/i915_gem_context.h | 5 + > drivers/gpu/drm/i915/i915_gem_gtt.c | 30 +- > drivers/gpu/drm/i915/i915_gem_gtt.h | 17 +- > drivers/gpu/drm/i915/selftests/huge_pages.c | 1 - > .../gpu/drm/i915/selftests/i915_gem_context.c | 237 ++++++++++--- > drivers/gpu/drm/i915/selftests/i915_gem_gtt.c | 1 - > drivers/gpu/drm/i915/selftests/mock_context.c | 8 +- > include/uapi/drm/i915_drm.h | 43 +++ > 11 files changed, 594 insertions(+), 75 deletions(-) > > diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c > index 0d743907e7bc..5d53efc4c5d9 100644 > --- a/drivers/gpu/drm/i915/i915_drv.c > +++ b/drivers/gpu/drm/i915/i915_drv.c > @@ -3121,6 +3121,8 @@ static const struct drm_ioctl_desc i915_ioctls[] = { > DRM_IOCTL_DEF_DRV(I915_PERF_ADD_CONFIG, i915_perf_add_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), > DRM_IOCTL_DEF_DRV(I915_PERF_REMOVE_CONFIG, i915_perf_remove_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), > DRM_IOCTL_DEF_DRV(I915_QUERY, i915_query_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), > + DRM_IOCTL_DEF_DRV(I915_GEM_VM_CREATE, i915_gem_vm_create_ioctl, DRM_RENDER_ALLOW), > + DRM_IOCTL_DEF_DRV(I915_GEM_VM_DESTROY, i915_gem_vm_destroy_ioctl, DRM_RENDER_ALLOW), > }; > > static struct drm_driver driver = { > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h > index dc63303225fc..4675355916ff 100644 > --- a/drivers/gpu/drm/i915/i915_drv.h > +++ b/drivers/gpu/drm/i915/i915_drv.h > @@ -218,6 +218,9 @@ struct drm_i915_file_private { > } mm; > struct idr context_idr; > > + struct mutex vm_lock; > + struct idr vm_idr; > + > unsigned int bsd_engine; > > /* > diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c > index 4af51b689cbd..71464ae91d61 100644 > --- a/drivers/gpu/drm/i915/i915_gem_context.c > +++ b/drivers/gpu/drm/i915/i915_gem_context.c > @@ -120,12 +120,15 @@ static void lut_close(struct i915_gem_context *ctx) > list_del(&lut->obj_link); > i915_lut_handle_free(lut); > } > + INIT_LIST_HEAD(&ctx->handles_list); > > rcu_read_lock(); > radix_tree_for_each_slot(slot, &ctx->handles_vma, &iter, 0) { > struct i915_vma *vma = rcu_dereference_raw(*slot); > > radix_tree_iter_delete(&ctx->handles_vma, &iter, slot); > + > + vma->open_count--; > __i915_gem_object_release_unless_active(vma->obj); > } > rcu_read_unlock(); > @@ -306,7 +309,7 @@ static void context_close(struct i915_gem_context *ctx) > */ > lut_close(ctx); > if (ctx->ppgtt) > - i915_ppgtt_close(&ctx->ppgtt->vm); > + i915_ppgtt_close(ctx->ppgtt); > > ctx->file_priv = ERR_PTR(-EBADF); > i915_gem_context_put(ctx); > @@ -417,6 +420,32 @@ static void __destroy_hw_context(struct i915_gem_context *ctx, > context_close(ctx); > } > > +static struct i915_hw_ppgtt * > +__set_ppgtt(struct i915_gem_context *ctx, struct i915_hw_ppgtt *ppgtt) > +{ > + struct i915_hw_ppgtt *old = ctx->ppgtt; > + > + i915_ppgtt_open(ppgtt); > + ctx->ppgtt = i915_ppgtt_get(ppgtt); > + > + ctx->desc_template = default_desc_template(ctx->i915, ppgtt); > + > + return old; > +} > + > +static void __assign_ppgtt(struct i915_gem_context *ctx, > + struct i915_hw_ppgtt *ppgtt) > +{ > + if (ppgtt == ctx->ppgtt) > + return; > + > + ppgtt = __set_ppgtt(ctx, ppgtt); > + if (ppgtt) { > + i915_ppgtt_close(ppgtt); > + i915_ppgtt_put(ppgtt); > + } > +} > + > static struct i915_gem_context * > i915_gem_create_context(struct drm_i915_private *dev_priv, > struct drm_i915_file_private *file_priv) > @@ -443,8 +472,8 @@ i915_gem_create_context(struct drm_i915_private *dev_priv, > return ERR_CAST(ppgtt); > } > > - ctx->ppgtt = ppgtt; > - ctx->desc_template = default_desc_template(dev_priv, ppgtt); > + __assign_ppgtt(ctx, ppgtt); > + i915_ppgtt_put(ppgtt); > } > > trace_i915_context_create(ctx); > @@ -625,19 +654,29 @@ static int context_idr_cleanup(int id, void *p, void *data) > return 0; > } > > +static int vm_idr_cleanup(int id, void *p, void *data) > +{ > + i915_ppgtt_put(p); > + return 0; > +} > + > int i915_gem_context_open(struct drm_i915_private *i915, > struct drm_file *file) > { > struct drm_i915_file_private *file_priv = file->driver_priv; > struct i915_gem_context *ctx; > > + mutex_init(&file_priv->vm_lock); > + > idr_init(&file_priv->context_idr); > + idr_init_base(&file_priv->vm_idr, 1); > > mutex_lock(&i915->drm.struct_mutex); > ctx = i915_gem_create_context(i915, file_priv); > mutex_unlock(&i915->drm.struct_mutex); > if (IS_ERR(ctx)) { > idr_destroy(&file_priv->context_idr); > + idr_destroy(&file_priv->vm_idr); > return PTR_ERR(ctx); > } > > @@ -654,6 +693,89 @@ void i915_gem_context_close(struct drm_file *file) > > idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL); > idr_destroy(&file_priv->context_idr); > + > + idr_for_each(&file_priv->vm_idr, vm_idr_cleanup, NULL); > + idr_destroy(&file_priv->vm_idr); > + > + mutex_destroy(&file_priv->vm_lock); > +} > + > +int i915_gem_vm_create_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file) > +{ > + struct drm_i915_private *i915 = to_i915(dev); > + struct drm_i915_gem_vm_control *args = data; > + struct drm_i915_file_private *file_priv = file->driver_priv; > + struct i915_hw_ppgtt *ppgtt; > + int err; > + > + if (!HAS_FULL_PPGTT(i915)) > + return -ENODEV; > + > + if (args->flags) > + return -EINVAL; > + > + if (args->extensions) > + return -EINVAL; > + > + ppgtt = i915_ppgtt_create(i915, file_priv); > + if (IS_ERR(ppgtt)) > + return PTR_ERR(ppgtt); > + > + err = mutex_lock_interruptible(&file_priv->vm_lock); > + if (err) > + goto err_put; > + > + err = idr_alloc(&file_priv->vm_idr, ppgtt, 0, 0, GFP_KERNEL); > + mutex_unlock(&file_priv->vm_lock); > + if (err < 0) > + goto err_put; > + > + GEM_BUG_ON(err == 0); /* reserved for default/unassigned ppgtt */ > + ppgtt->user_handle = err; > + args->id = err; > + return 0; > + > +err_put: > + i915_ppgtt_put(ppgtt); > + return err; > +} > + > +int i915_gem_vm_destroy_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file) > +{ > + struct drm_i915_file_private *file_priv = file->driver_priv; > + struct drm_i915_gem_vm_control *args = data; > + struct i915_hw_ppgtt *ppgtt; > + int err; > + u32 id; > + > + if (args->flags) > + return -EINVAL; > + > + if (args->extensions) > + return -EINVAL; > + > + id = args->id; > + if (!id) > + return -ENOENT; > + > + err = mutex_lock_interruptible(&file_priv->vm_lock); > + if (err) > + return err; > + > + ppgtt = idr_remove(&file_priv->vm_idr, id); > + if (ppgtt) { > + GEM_BUG_ON(!ppgtt->user_handle); > + ppgtt->user_handle = 0; > + } > + > + mutex_unlock(&file_priv->vm_lock); > + if (!ppgtt) > + return -ENOENT; > + > + i915_ppgtt_put(ppgtt); > + return 0; > } > > static struct i915_request * > @@ -697,12 +819,13 @@ static void cb_retire(struct i915_active *base) > I915_SELFTEST_DECLARE(static unsigned long context_barrier_inject_fault); > static int context_barrier_task(struct i915_gem_context *ctx, > unsigned long engines, > + int (*emit)(struct i915_request *rq, void *data), > void (*task)(void *data), > void *data) > { > struct drm_i915_private *i915 = ctx->i915; > struct context_barrier_task *cb; > - struct intel_context *ce; > + struct intel_context *ce, *next; > intel_wakeref_t wakeref; > int err = 0; > > @@ -717,11 +840,11 @@ static int context_barrier_task(struct i915_gem_context *ctx, > i915_active_acquire(&cb->base); > > wakeref = intel_runtime_pm_get(i915); > - list_for_each_entry(ce, &ctx->active_engines, active_link) { > + rbtree_postorder_for_each_entry_safe(ce, next, &ctx->hw_contexts, node) { > struct intel_engine_cs *engine = ce->engine; > struct i915_request *rq; > > - if (!(ce->engine->mask & engines)) > + if (!(engine->mask & engines)) > continue; > > if (I915_SELFTEST_ONLY(context_barrier_inject_fault & > @@ -736,7 +859,12 @@ static int context_barrier_task(struct i915_gem_context *ctx, > break; > } > > - err = i915_active_ref(&cb->base, rq->fence.context, rq); > + err = 0; > + if (emit) > + err = emit(rq, data); > + if (err == 0) > + err = i915_active_ref(&cb->base, rq->fence.context, rq); > + > i915_request_add(rq); > if (err) > break; > @@ -799,6 +927,173 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *i915, > return 0; > } > > +static int get_ppgtt(struct i915_gem_context *ctx, > + struct drm_i915_gem_context_param *args) > +{ > + struct drm_i915_file_private *file_priv = ctx->file_priv; > + struct i915_hw_ppgtt *ppgtt; > + int ret; > + > + if (!ctx->ppgtt) > + return -ENODEV; > + > + /* XXX rcu acquire? */ > + ret = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex); > + if (ret) > + return ret; > + > + ppgtt = i915_ppgtt_get(ctx->ppgtt); > + mutex_unlock(&ctx->i915->drm.struct_mutex); > + > + ret = mutex_lock_interruptible(&file_priv->vm_lock); > + if (ret) > + goto err_put; > + > + if (!ppgtt->user_handle) { > + ret = idr_alloc(&file_priv->vm_idr, ppgtt, 0, 0, GFP_KERNEL); > + GEM_BUG_ON(!ret); > + if (ret < 0) > + goto err_unlock; > + > + ppgtt->user_handle = ret; > + i915_ppgtt_get(ppgtt); > + } > + > + args->size = 0; > + args->value = ppgtt->user_handle; > + > + ret = 0; > +err_unlock: > + mutex_unlock(&file_priv->vm_lock); > +err_put: > + i915_ppgtt_put(ppgtt); > + return ret; > +} > + > +static void set_ppgtt_barrier(void *data) > +{ > + struct i915_hw_ppgtt *old = data; > + > + if (INTEL_GEN(old->vm.i915) < 8) > + gen6_ppgtt_unpin_all(old); Is ppgtt sharing interesting for aliasing ppgtt? Ah HSW now has proper.. > + > + i915_ppgtt_close(old); > + i915_ppgtt_put(old); > +} > + > +static int emit_ppgtt_update(struct i915_request *rq, void *data) > +{ > + struct i915_hw_ppgtt *ppgtt = rq->gem_context->ppgtt; > + struct intel_engine_cs *engine = rq->engine; > + u32 *cs; > + int i; > + > + if (i915_vm_is_48bit(&ppgtt->vm)) { > + const dma_addr_t pd_daddr = px_dma(&ppgtt->pml4); > + > + cs = intel_ring_begin(rq, 6); > + if (IS_ERR(cs)) > + return PTR_ERR(cs); > + > + *cs++ = MI_LOAD_REGISTER_IMM(2); > + > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, 0)); > + *cs++ = upper_32_bits(pd_daddr); > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, 0)); > + *cs++ = lower_32_bits(pd_daddr); Only one out of four these beasties needs to be updated? (In contrast to virtual_update_register_offsets.) As you can see I am not too familiar with pgtable management. > + > + *cs++ = MI_NOOP; > + intel_ring_advance(rq, cs); > + } else if (HAS_LOGICAL_RING_CONTEXTS(engine->i915)) { > + cs = intel_ring_begin(rq, 4 * GEN8_3LVL_PDPES + 2); > + if (IS_ERR(cs)) > + return PTR_ERR(cs); > + > + *cs++ = MI_LOAD_REGISTER_IMM(2 * GEN8_3LVL_PDPES); > + for (i = GEN8_3LVL_PDPES; i--; ) { > + const dma_addr_t pd_daddr = i915_page_dir_dma_addr(ppgtt, i); > + > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, i)); > + *cs++ = upper_32_bits(pd_daddr); > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, i)); > + *cs++ = lower_32_bits(pd_daddr); > + } > + *cs++ = MI_NOOP; > + intel_ring_advance(rq, cs); So a running context will update it's own context image. And it doesn't get overwritten on context save? Are there tests which hit this path? > + } else { > + /* ppGTT is not part of the legacy context image */ > + gen6_ppgtt_pin(ppgtt); What is happening on this path - why is the pin needed here only to be unpinned in the barrier callback? This is not handled by normal request flow? Regards, Tvrtko > + } > + > + return 0; > +} > + > +static int set_ppgtt(struct i915_gem_context *ctx, > + struct drm_i915_gem_context_param *args) > +{ > + struct drm_i915_file_private *file_priv = ctx->file_priv; > + struct i915_hw_ppgtt *ppgtt, *old; > + int err; > + > + if (args->size) > + return -EINVAL; > + > + if (!ctx->ppgtt) > + return -ENODEV; > + > + if (upper_32_bits(args->value)) > + return -ENOENT; > + > + err = mutex_lock_interruptible(&file_priv->vm_lock); > + if (err) > + return err; > + > + ppgtt = idr_find(&file_priv->vm_idr, args->value); > + if (ppgtt) { > + GEM_BUG_ON(ppgtt->user_handle != args->value); > + i915_ppgtt_get(ppgtt); > + } > + mutex_unlock(&file_priv->vm_lock); > + if (!ppgtt) > + return -ENOENT; > + > + err = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex); > + if (err) > + goto out; > + > + if (ppgtt == ctx->ppgtt) > + goto unlock; > + > + /* Teardown the existing obj:vma cache, it will have to be rebuilt. */ > + lut_close(ctx); > + > + old = __set_ppgtt(ctx, ppgtt); > + > + /* > + * We need to flush any requests using the current ppgtt before > + * we release it as the requests do not hold a reference themselves, > + * only indirectly through the context. > + */ > + err = context_barrier_task(ctx, ALL_ENGINES, > + emit_ppgtt_update, > + set_ppgtt_barrier, > + old); > + if (err) { > + ctx->ppgtt = old; > + ctx->desc_template = default_desc_template(ctx->i915, old); > + > + i915_ppgtt_close(ppgtt); > + i915_ppgtt_put(ppgtt); > + } > + > +unlock: > + mutex_unlock(&ctx->i915->drm.struct_mutex); > + > +out: > + i915_ppgtt_put(ppgtt); > + return err; > +} > + > static bool client_is_banned(struct drm_i915_file_private *file_priv) > { > return atomic_read(&file_priv->ban_score) >= I915_CLIENT_SCORE_BANNED; > @@ -973,6 +1268,9 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, > case I915_CONTEXT_PARAM_SSEU: > ret = get_sseu(ctx, args); > break; > + case I915_CONTEXT_PARAM_VM: > + ret = get_ppgtt(ctx, args); > + break; > default: > ret = -EINVAL; > break; > @@ -1274,9 +1572,6 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, > return -ENOENT; > > switch (args->param) { > - case I915_CONTEXT_PARAM_BAN_PERIOD: > - ret = -EINVAL; > - break; > case I915_CONTEXT_PARAM_NO_ZEROMAP: > if (args->size) > ret = -EINVAL; > @@ -1332,9 +1627,16 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, > I915_USER_PRIORITY(priority); > } > break; > + > case I915_CONTEXT_PARAM_SSEU: > ret = set_sseu(ctx, args); > break; > + > + case I915_CONTEXT_PARAM_VM: > + ret = set_ppgtt(ctx, args); > + break; > + > + case I915_CONTEXT_PARAM_BAN_PERIOD: > default: > ret = -EINVAL; > break; > diff --git a/drivers/gpu/drm/i915/i915_gem_context.h b/drivers/gpu/drm/i915/i915_gem_context.h > index 5a32c4b4816f..1e670372892c 100644 > --- a/drivers/gpu/drm/i915/i915_gem_context.h > +++ b/drivers/gpu/drm/i915/i915_gem_context.h > @@ -153,6 +153,11 @@ void i915_gem_context_release(struct kref *ctx_ref); > struct i915_gem_context * > i915_gem_context_create_gvt(struct drm_device *dev); > > +int i915_gem_vm_create_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file); > +int i915_gem_vm_destroy_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file); > + > int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, > struct drm_file *file); > int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, > diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c > index dac08d9c3fab..c0972cd95283 100644 > --- a/drivers/gpu/drm/i915/i915_gem_gtt.c > +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c > @@ -1939,6 +1939,8 @@ int gen6_ppgtt_pin(struct i915_hw_ppgtt *base) > struct gen6_hw_ppgtt *ppgtt = to_gen6_ppgtt(base); > int err; > > + GEM_BUG_ON(ppgtt->base.vm.closed); > + > /* > * Workaround the limited maximum vma->pin_count and the aliasing_ppgtt > * which will be pinned into every active context. > @@ -1977,6 +1979,17 @@ void gen6_ppgtt_unpin(struct i915_hw_ppgtt *base) > i915_vma_unpin(ppgtt->vma); > } > > +void gen6_ppgtt_unpin_all(struct i915_hw_ppgtt *base) > +{ > + struct gen6_hw_ppgtt *ppgtt = to_gen6_ppgtt(base); > + > + if (!ppgtt->pin_count) > + return; > + > + ppgtt->pin_count = 0; > + i915_vma_unpin(ppgtt->vma); > +} > + > static struct i915_hw_ppgtt *gen6_ppgtt_create(struct drm_i915_private *i915) > { > struct i915_ggtt * const ggtt = &i915->ggtt; > @@ -2099,10 +2112,21 @@ i915_ppgtt_create(struct drm_i915_private *i915, > return ppgtt; > } > > -void i915_ppgtt_close(struct i915_address_space *vm) > +void i915_ppgtt_open(struct i915_hw_ppgtt *ppgtt) > { > - GEM_BUG_ON(vm->closed); > - vm->closed = true; > + GEM_BUG_ON(ppgtt->vm.closed); > + > + ppgtt->open_count++; > +} > + > +void i915_ppgtt_close(struct i915_hw_ppgtt *ppgtt) > +{ > + GEM_BUG_ON(!ppgtt->open_count); > + if (--ppgtt->open_count) > + return; > + > + GEM_BUG_ON(ppgtt->vm.closed); > + ppgtt->vm.closed = true; > } > > static void ppgtt_destroy_vma(struct i915_address_space *vm) > diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h > index a47e11e6fc1b..5973d92e45fc 100644 > --- a/drivers/gpu/drm/i915/i915_gem_gtt.h > +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h > @@ -391,11 +391,15 @@ struct i915_hw_ppgtt { > struct kref ref; > > unsigned long pd_dirty_engines; > + unsigned int open_count; > + > union { > struct i915_pml4 pml4; /* GEN8+ & 48b PPGTT */ > struct i915_page_directory_pointer pdp; /* GEN8+ */ > struct i915_page_directory pd; /* GEN6-7 */ > }; > + > + u32 user_handle; > }; > cb > struct gen6_hw_ppgtt { > @@ -606,12 +610,16 @@ int i915_ppgtt_init_hw(struct drm_i915_private *dev_priv); > void i915_ppgtt_release(struct kref *kref); > struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_i915_private *dev_priv, > struct drm_i915_file_private *fpriv); > -void i915_ppgtt_close(struct i915_address_space *vm); > -static inline void i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt) > + > +void i915_ppgtt_open(struct i915_hw_ppgtt *ppgtt); > +void i915_ppgtt_close(struct i915_hw_ppgtt *ppgtt); > + > +static inline struct i915_hw_ppgtt *i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt) > { > - if (ppgtt) > - kref_get(&ppgtt->ref); > + kref_get(&ppgtt->ref); > + return ppgtt; > } > + > static inline void i915_ppgtt_put(struct i915_hw_ppgtt *ppgtt) > { > if (ppgtt) > @@ -620,6 +628,7 @@ static inline void i915_ppgtt_put(struct i915_hw_ppgtt *ppgtt) > > int gen6_ppgtt_pin(struct i915_hw_ppgtt *base); > void gen6_ppgtt_unpin(struct i915_hw_ppgtt *base); > +void gen6_ppgtt_unpin_all(struct i915_hw_ppgtt *base); > > void i915_check_and_clear_faults(struct drm_i915_private *dev_priv); > void i915_gem_suspend_gtt_mappings(struct drm_i915_private *dev_priv); > diff --git a/drivers/gpu/drm/i915/selftests/huge_pages.c b/drivers/gpu/drm/i915/selftests/huge_pages.c > index 1e66cff985f8..0b7740dc18cb 100644 > --- a/drivers/gpu/drm/i915/selftests/huge_pages.c > +++ b/drivers/gpu/drm/i915/selftests/huge_pages.c > @@ -1734,7 +1734,6 @@ int i915_gem_huge_page_mock_selftests(void) > err = i915_subtests(tests, ppgtt); > > out_close: > - i915_ppgtt_close(&ppgtt->vm); > i915_ppgtt_put(ppgtt); > > out_unlock: > diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/selftests/i915_gem_context.c > index 4399ef9ebf15..1ba354a916c0 100644 > --- a/drivers/gpu/drm/i915/selftests/i915_gem_context.c > +++ b/drivers/gpu/drm/i915/selftests/i915_gem_context.c > @@ -372,7 +372,8 @@ static int cpu_fill(struct drm_i915_gem_object *obj, u32 value) > return 0; > } > > -static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max) > +static noinline int cpu_check(struct drm_i915_gem_object *obj, > + unsigned int idx, unsigned int max) > { > unsigned int n, m, needs_flush; > int err; > @@ -390,8 +391,10 @@ static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max) > > for (m = 0; m < max; m++) { > if (map[m] != m) { > - pr_err("Invalid value at page %d, offset %d: found %x expected %x\n", > - n, m, map[m], m); > + pr_err("%pS: Invalid value at object %d page %d/%ld, offset %d/%d: found %x expected %x\n", > + __builtin_return_address(0), idx, > + n, real_page_count(obj), m, max, > + map[m], m); > err = -EINVAL; > goto out_unmap; > } > @@ -399,8 +402,9 @@ static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max) > > for (; m < DW_PER_PAGE; m++) { > if (map[m] != STACK_MAGIC) { > - pr_err("Invalid value at page %d, offset %d: found %x expected %x\n", > - n, m, map[m], STACK_MAGIC); > + pr_err("%pS: Invalid value at object %d page %d, offset %d: found %x expected %x (uninitialised)\n", > + __builtin_return_address(0), idx, n, m, > + map[m], STACK_MAGIC); > err = -EINVAL; > goto out_unmap; > } > @@ -478,12 +482,8 @@ static unsigned long max_dwords(struct drm_i915_gem_object *obj) > static int igt_ctx_exec(void *arg) > { > struct drm_i915_private *i915 = arg; > - struct drm_i915_gem_object *obj = NULL; > - unsigned long ncontexts, ndwords, dw; > - struct igt_live_test t; > - struct drm_file *file; > - IGT_TIMEOUT(end_time); > - LIST_HEAD(objects); > + struct intel_engine_cs *engine; > + enum intel_engine_id id; > int err = -ENODEV; > > /* > @@ -495,44 +495,169 @@ static int igt_ctx_exec(void *arg) > if (!DRIVER_CAPS(i915)->has_logical_contexts) > return 0; > > + for_each_engine(engine, i915, id) { > + struct drm_i915_gem_object *obj = NULL; > + unsigned long ncontexts, ndwords, dw; > + struct igt_live_test t; > + struct drm_file *file; > + IGT_TIMEOUT(end_time); > + LIST_HEAD(objects); > + > + if (!intel_engine_can_store_dword(engine)) > + continue; > + > + if (!engine->context_size) > + continue; /* No logical context support in HW */ > + > + file = mock_file(i915); > + if (IS_ERR(file)) > + return PTR_ERR(file); > + > + mutex_lock(&i915->drm.struct_mutex); > + > + err = igt_live_test_begin(&t, i915, __func__, engine->name); > + if (err) > + goto out_unlock; > + > + ncontexts = 0; > + ndwords = 0; > + dw = 0; > + while (!time_after(jiffies, end_time)) { > + struct i915_gem_context *ctx; > + intel_wakeref_t wakeref; > + > + ctx = i915_gem_create_context(i915, file->driver_priv); > + if (IS_ERR(ctx)) { > + err = PTR_ERR(ctx); > + goto out_unlock; > + } > + > + if (!obj) { > + obj = create_test_object(ctx, file, &objects); > + if (IS_ERR(obj)) { > + err = PTR_ERR(obj); > + goto out_unlock; > + } > + } > + > + with_intel_runtime_pm(i915, wakeref) > + err = gpu_fill(obj, ctx, engine, dw); > + if (err) { > + pr_err("Failed to fill dword %lu [%lu/%lu] with gpu (%s) in ctx %u [full-ppgtt? %s], err=%d\n", > + ndwords, dw, max_dwords(obj), > + engine->name, ctx->hw_id, > + yesno(!!ctx->ppgtt), err); > + goto out_unlock; > + } > + > + if (++dw == max_dwords(obj)) { > + obj = NULL; > + dw = 0; > + } > + > + ndwords++; > + ncontexts++; > + } > + > + pr_info("Submitted %lu contexts to %s, filling %lu dwords\n", > + ncontexts, engine->name, ndwords); > + > + ncontexts = dw = 0; > + list_for_each_entry(obj, &objects, st_link) { > + unsigned int rem = > + min_t(unsigned int, ndwords - dw, max_dwords(obj)); > + > + err = cpu_check(obj, ncontexts++, rem); > + if (err) > + break; > + > + dw += rem; > + } > + > +out_unlock: > + if (igt_live_test_end(&t)) > + err = -EIO; > + mutex_unlock(&i915->drm.struct_mutex); > + > + mock_file_free(i915, file); > + if (err) > + return err; > + } > + > + return 0; > +} > + > +static int igt_shared_ctx_exec(void *arg) > +{ > + struct drm_i915_private *i915 = arg; > + struct i915_gem_context *parent; > + struct intel_engine_cs *engine; > + enum intel_engine_id id; > + struct igt_live_test t; > + struct drm_file *file; > + int err = 0; > + > + /* > + * Create a few different contexts with the same mm and write > + * through each ctx using the GPU making sure those writes end > + * up in the expected pages of our obj. > + */ > + if (!DRIVER_CAPS(i915)->has_logical_contexts) > + return 0; > + > file = mock_file(i915); > if (IS_ERR(file)) > return PTR_ERR(file); > > mutex_lock(&i915->drm.struct_mutex); > > + parent = i915_gem_create_context(i915, file->driver_priv); > + if (IS_ERR(parent)) { > + err = PTR_ERR(parent); > + goto out_unlock; > + } > + > + if (!parent->ppgtt) { /* not full-ppgtt; nothing to share */ > + err = -ENODEV; > + goto out_unlock; > + } > + > err = igt_live_test_begin(&t, i915, __func__, ""); > if (err) > goto out_unlock; > > - ncontexts = 0; > - ndwords = 0; > - dw = 0; > - while (!time_after(jiffies, end_time)) { > - struct intel_engine_cs *engine; > - struct i915_gem_context *ctx; > - unsigned int id; > + for_each_engine(engine, i915, id) { > + unsigned long ncontexts, ndwords, dw; > + struct drm_i915_gem_object *obj = NULL; > + struct i915_gem_context *ctx = NULL; > + IGT_TIMEOUT(end_time); > + LIST_HEAD(objects); > > - ctx = i915_gem_create_context(i915, file->driver_priv); > - if (IS_ERR(ctx)) { > - err = PTR_ERR(ctx); > - goto out_unlock; > - } > + if (!intel_engine_can_store_dword(engine)) > + continue; > > - for_each_engine(engine, i915, id) { > + dw = 0; > + ndwords = 0; > + ncontexts = 0; > + while (!time_after(jiffies, end_time)) { > intel_wakeref_t wakeref; > > - if (!engine->context_size) > - continue; /* No logical context support in HW */ > + if (ctx) > + __destroy_hw_context(ctx, file->driver_priv); > > - if (!intel_engine_can_store_dword(engine)) > - continue; > + ctx = i915_gem_create_context(i915, file->driver_priv); > + if (IS_ERR(ctx)) { > + err = PTR_ERR(ctx); > + goto out_test; > + } > + > + __assign_ppgtt(ctx, parent->ppgtt); > > if (!obj) { > - obj = create_test_object(ctx, file, &objects); > + obj = create_test_object(parent, file, &objects); > if (IS_ERR(obj)) { > err = PTR_ERR(obj); > - goto out_unlock; > + goto out_test; > } > } > > @@ -544,35 +669,36 @@ static int igt_ctx_exec(void *arg) > ndwords, dw, max_dwords(obj), > engine->name, ctx->hw_id, > yesno(!!ctx->ppgtt), err); > - goto out_unlock; > + goto out_test; > } > > if (++dw == max_dwords(obj)) { > obj = NULL; > dw = 0; > } > + > ndwords++; > + ncontexts++; > } > - ncontexts++; > - } > - pr_info("Submitted %lu contexts (across %u engines), filling %lu dwords\n", > - ncontexts, RUNTIME_INFO(i915)->num_engines, ndwords); > + pr_info("Submitted %lu contexts to %s, filling %lu dwords\n", > + ncontexts, engine->name, ndwords); > > - dw = 0; > - list_for_each_entry(obj, &objects, st_link) { > - unsigned int rem = > - min_t(unsigned int, ndwords - dw, max_dwords(obj)); > + ncontexts = dw = 0; > + list_for_each_entry(obj, &objects, st_link) { > + unsigned int rem = > + min_t(unsigned int, ndwords - dw, max_dwords(obj)); > > - err = cpu_check(obj, rem); > - if (err) > - break; > + err = cpu_check(obj, ncontexts++, rem); > + if (err) > + goto out_test; > > - dw += rem; > + dw += rem; > + } > } > - > -out_unlock: > +out_test: > if (igt_live_test_end(&t)) > err = -EIO; > +out_unlock: > mutex_unlock(&i915->drm.struct_mutex); > > mock_file_free(i915, file); > @@ -1048,7 +1174,7 @@ static int igt_ctx_readonly(void *arg) > struct drm_i915_gem_object *obj = NULL; > struct i915_gem_context *ctx; > struct i915_hw_ppgtt *ppgtt; > - unsigned long ndwords, dw; > + unsigned long idx, ndwords, dw; > struct igt_live_test t; > struct drm_file *file; > I915_RND_STATE(prng); > @@ -1129,6 +1255,7 @@ static int igt_ctx_readonly(void *arg) > ndwords, RUNTIME_INFO(i915)->num_engines); > > dw = 0; > + idx = 0; > list_for_each_entry(obj, &objects, st_link) { > unsigned int rem = > min_t(unsigned int, ndwords - dw, max_dwords(obj)); > @@ -1138,7 +1265,7 @@ static int igt_ctx_readonly(void *arg) > if (i915_gem_object_is_readonly(obj)) > num_writes = 0; > > - err = cpu_check(obj, num_writes); > + err = cpu_check(obj, idx++, num_writes); > if (err) > break; > > @@ -1626,7 +1753,8 @@ static int mock_context_barrier(void *arg) > } > > counter = 0; > - err = context_barrier_task(ctx, 0, mock_barrier_task, &counter); > + err = context_barrier_task(ctx, 0, > + NULL, mock_barrier_task, &counter); > if (err) { > pr_err("Failed at line %d, err=%d\n", __LINE__, err); > goto out; > @@ -1638,8 +1766,8 @@ static int mock_context_barrier(void *arg) > } > > counter = 0; > - err = context_barrier_task(ctx, > - ALL_ENGINES, mock_barrier_task, &counter); > + err = context_barrier_task(ctx, ALL_ENGINES, > + NULL, mock_barrier_task, &counter); > if (err) { > pr_err("Failed at line %d, err=%d\n", __LINE__, err); > goto out; > @@ -1662,8 +1790,8 @@ static int mock_context_barrier(void *arg) > > counter = 0; > context_barrier_inject_fault = BIT(RCS0); > - err = context_barrier_task(ctx, > - ALL_ENGINES, mock_barrier_task, &counter); > + err = context_barrier_task(ctx, ALL_ENGINES, > + NULL, mock_barrier_task, &counter); > context_barrier_inject_fault = 0; > if (err == -ENXIO) > err = 0; > @@ -1677,8 +1805,8 @@ static int mock_context_barrier(void *arg) > goto out; > > counter = 0; > - err = context_barrier_task(ctx, > - ALL_ENGINES, mock_barrier_task, &counter); > + err = context_barrier_task(ctx, ALL_ENGINES, > + NULL, mock_barrier_task, &counter); > if (err) { > pr_err("Failed at line %d, err=%d\n", __LINE__, err); > goto out; > @@ -1726,6 +1854,7 @@ int i915_gem_context_live_selftests(struct drm_i915_private *dev_priv) > SUBTEST(igt_ctx_exec), > SUBTEST(igt_ctx_readonly), > SUBTEST(igt_ctx_sseu), > + SUBTEST(igt_shared_ctx_exec), > SUBTEST(igt_vm_isolation), > }; > > diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c > index 826fd51c331e..57b3d9867070 100644 > --- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c > +++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c > @@ -1020,7 +1020,6 @@ static int exercise_ppgtt(struct drm_i915_private *dev_priv, > > err = func(dev_priv, &ppgtt->vm, 0, ppgtt->vm.total, end_time); > > - i915_ppgtt_close(&ppgtt->vm); > i915_ppgtt_put(ppgtt); > out_unlock: > mutex_unlock(&dev_priv->drm.struct_mutex); > diff --git a/drivers/gpu/drm/i915/selftests/mock_context.c b/drivers/gpu/drm/i915/selftests/mock_context.c > index 8efa6892c6cd..f90328b21763 100644 > --- a/drivers/gpu/drm/i915/selftests/mock_context.c > +++ b/drivers/gpu/drm/i915/selftests/mock_context.c > @@ -54,13 +54,17 @@ mock_context(struct drm_i915_private *i915, > goto err_handles; > > if (name) { > + struct i915_hw_ppgtt *ppgtt; > + > ctx->name = kstrdup(name, GFP_KERNEL); > if (!ctx->name) > goto err_put; > > - ctx->ppgtt = mock_ppgtt(i915, name); > - if (!ctx->ppgtt) > + ppgtt = mock_ppgtt(i915, name); > + if (!ppgtt) > goto err_put; > + > + __set_ppgtt(ctx, ppgtt); > } > > return ctx; > diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h > index 1c69ed16a923..5079e25909ef 100644 > --- a/include/uapi/drm/i915_drm.h > +++ b/include/uapi/drm/i915_drm.h > @@ -343,6 +343,8 @@ typedef struct _drm_i915_sarea { > #define DRM_I915_PERF_ADD_CONFIG 0x37 > #define DRM_I915_PERF_REMOVE_CONFIG 0x38 > #define DRM_I915_QUERY 0x39 > +#define DRM_I915_GEM_VM_CREATE 0x3a > +#define DRM_I915_GEM_VM_DESTROY 0x3b > /* Must be kept compact -- no holes */ > > #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) > @@ -402,6 +404,8 @@ typedef struct _drm_i915_sarea { > #define DRM_IOCTL_I915_PERF_ADD_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config) > #define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64) > #define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query) > +#define DRM_IOCTL_I915_GEM_VM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_VM_CREATE, struct drm_i915_gem_vm_control) > +#define DRM_IOCTL_I915_GEM_VM_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_VM_DESTROY, struct drm_i915_gem_vm_control) > > /* Allow drivers to submit batchbuffers directly to hardware, relying > * on the security mechanisms provided by hardware. > @@ -1453,6 +1457,33 @@ struct drm_i915_gem_context_destroy { > __u32 pad; > }; > > +/* > + * DRM_I915_GEM_VM_CREATE - > + * > + * Create a new virtual memory address space (ppGTT) for use within a context > + * on the same file. Extensions can be provided to configure exactly how the > + * address space is setup upon creation. > + * > + * The id of new VM (bound to the fd) for use with I915_CONTEXT_PARAM_VM is > + * returned in the outparam @id. > + * > + * No flags are defined, with all bits reserved and must be zero. > + * > + * An extension chain maybe provided, starting with @extensions, and terminated > + * by the @next_extension being 0. Currently, no extensions are defined. > + * > + * DRM_I915_GEM_VM_DESTROY - > + * > + * Destroys a previously created VM id, specified in @id. > + * > + * No extensions or flags are allowed currently, and so must be zero. > + */ > +struct drm_i915_gem_vm_control { > + __u64 extensions; > + __u32 flags; > + __u32 id; > +}; > + > struct drm_i915_reg_read { > /* > * Register offset. > @@ -1542,7 +1573,19 @@ struct drm_i915_gem_context_param { > * On creation, all new contexts are marked as recoverable. > */ > #define I915_CONTEXT_PARAM_RECOVERABLE 0x8 > + > + /* > + * The id of the associated virtual memory address space (ppGTT) of > + * this context. Can be retrieved and passed to another context > + * (on the same fd) for both to use the same ppGTT and so share > + * address layouts, and avoid reloading the page tables on context > + * switches between themselves. > + * > + * See DRM_I915_GEM_VM_CREATE and DRM_I915_GEM_VM_DESTROY. > + */ > +#define I915_CONTEXT_PARAM_VM 0x9 > /* Must be kept compact -- no holes and well documented */ > + > __u64 value; > }; > >
Quoting Tvrtko Ursulin (2019-03-14 16:07:17) > On 13/03/2019 14:43, Chris Wilson wrote: > > In preparation to making the ppGTT binding for a context explicit (to > > facilitate reusing the same ppGTT between different contexts), allow the > > user to create and destroy named ppGTT. > > > > v2: Replace global barrier for swapping over the ppgtt and tlbs with a > > local context barrier (Tvrtko) > > v3: serialise with struct_mutex; it's lazy but required dammit > > v4: Rewrite igt_ctx_shared_exec to be more different (aimed to be more > > similarly, turned out different!) > > > > v2: Fix up test unwind for aliasing-ppgtt (snb) > > v3: Tighten language for uapi struct drm_i915_gem_vm_control. > > v4: Patch the context image for runtime ppgtt switching! > > -ECHANGELOGNOTMONOTONIC :)) I shall randomly delete half of them. Thanos rules. > > +static void set_ppgtt_barrier(void *data) > > +{ > > + struct i915_hw_ppgtt *old = data; > > + > > + if (INTEL_GEN(old->vm.i915) < 8) > > + gen6_ppgtt_unpin_all(old); > > Is ppgtt sharing interesting for aliasing ppgtt? Ah HSW now has proper.. > > > + > > + i915_ppgtt_close(old); > > + i915_ppgtt_put(old); > > +} > > + > > +static int emit_ppgtt_update(struct i915_request *rq, void *data) > > +{ > > + struct i915_hw_ppgtt *ppgtt = rq->gem_context->ppgtt; > > + struct intel_engine_cs *engine = rq->engine; > > + u32 *cs; > > + int i; > > + > > + if (i915_vm_is_48bit(&ppgtt->vm)) { > > + const dma_addr_t pd_daddr = px_dma(&ppgtt->pml4); > > + > > + cs = intel_ring_begin(rq, 6); > > + if (IS_ERR(cs)) > > + return PTR_ERR(cs); > > + > > + *cs++ = MI_LOAD_REGISTER_IMM(2); > > + > > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, 0)); > > + *cs++ = upper_32_bits(pd_daddr); > > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, 0)); > > + *cs++ = lower_32_bits(pd_daddr); > > Only one out of four these beasties needs to be updated? (In contrast to > virtual_update_register_offsets.) As you can see I am not too familiar > with pgtable management. Our notes say that the HW ignores 1..3, so I was wondering if we could condense these two into one with a bit of jiggery pokery to pluck the right dma address. Might also simplify execlists_init_reg_state() that tiny bit. > > + > > + *cs++ = MI_NOOP; > > + intel_ring_advance(rq, cs); > > + } else if (HAS_LOGICAL_RING_CONTEXTS(engine->i915)) { > > + cs = intel_ring_begin(rq, 4 * GEN8_3LVL_PDPES + 2); > > + if (IS_ERR(cs)) > > + return PTR_ERR(cs); > > + > > + *cs++ = MI_LOAD_REGISTER_IMM(2 * GEN8_3LVL_PDPES); > > + for (i = GEN8_3LVL_PDPES; i--; ) { > > + const dma_addr_t pd_daddr = i915_page_dir_dma_addr(ppgtt, i); > > + > > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, i)); > > + *cs++ = upper_32_bits(pd_daddr); > > + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, i)); > > + *cs++ = lower_32_bits(pd_daddr); > > + } > > + *cs++ = MI_NOOP; > > + intel_ring_advance(rq, cs); > > So a running context will update it's own context image. And it doesn't > get overwritten on context save? Indeed. It is how we update PD at runtime. > Are there tests which hit this path? Yes. As always a serendipitous discovery. :) The simplest of gem_execbuf(); gem_context_set_param(NEW_VM); gem_execbuf(); thankfully had a very recognisable GPU hang. > > + } else { > > + /* ppGTT is not part of the legacy context image */ > > + gen6_ppgtt_pin(ppgtt); > > What is happening on this path - why is the pin needed here only to be > unpinned in the barrier callback? This is not handled by normal request > flow? It's because we pin the ppgtt as part of pinning the context (for gen6/gen7). Since the context is already pinned, we don't notice the new ctx->ppgtt. Hence we have to manually acquire a pin on ctx->ppgtt and discard the old pins. -Chris
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 0d743907e7bc..5d53efc4c5d9 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -3121,6 +3121,8 @@ static const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_PERF_ADD_CONFIG, i915_perf_add_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_PERF_REMOVE_CONFIG, i915_perf_remove_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_QUERY, i915_query_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_GEM_VM_CREATE, i915_gem_vm_create_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_GEM_VM_DESTROY, i915_gem_vm_destroy_ioctl, DRM_RENDER_ALLOW), }; static struct drm_driver driver = { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index dc63303225fc..4675355916ff 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -218,6 +218,9 @@ struct drm_i915_file_private { } mm; struct idr context_idr; + struct mutex vm_lock; + struct idr vm_idr; + unsigned int bsd_engine; /* diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 4af51b689cbd..71464ae91d61 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -120,12 +120,15 @@ static void lut_close(struct i915_gem_context *ctx) list_del(&lut->obj_link); i915_lut_handle_free(lut); } + INIT_LIST_HEAD(&ctx->handles_list); rcu_read_lock(); radix_tree_for_each_slot(slot, &ctx->handles_vma, &iter, 0) { struct i915_vma *vma = rcu_dereference_raw(*slot); radix_tree_iter_delete(&ctx->handles_vma, &iter, slot); + + vma->open_count--; __i915_gem_object_release_unless_active(vma->obj); } rcu_read_unlock(); @@ -306,7 +309,7 @@ static void context_close(struct i915_gem_context *ctx) */ lut_close(ctx); if (ctx->ppgtt) - i915_ppgtt_close(&ctx->ppgtt->vm); + i915_ppgtt_close(ctx->ppgtt); ctx->file_priv = ERR_PTR(-EBADF); i915_gem_context_put(ctx); @@ -417,6 +420,32 @@ static void __destroy_hw_context(struct i915_gem_context *ctx, context_close(ctx); } +static struct i915_hw_ppgtt * +__set_ppgtt(struct i915_gem_context *ctx, struct i915_hw_ppgtt *ppgtt) +{ + struct i915_hw_ppgtt *old = ctx->ppgtt; + + i915_ppgtt_open(ppgtt); + ctx->ppgtt = i915_ppgtt_get(ppgtt); + + ctx->desc_template = default_desc_template(ctx->i915, ppgtt); + + return old; +} + +static void __assign_ppgtt(struct i915_gem_context *ctx, + struct i915_hw_ppgtt *ppgtt) +{ + if (ppgtt == ctx->ppgtt) + return; + + ppgtt = __set_ppgtt(ctx, ppgtt); + if (ppgtt) { + i915_ppgtt_close(ppgtt); + i915_ppgtt_put(ppgtt); + } +} + static struct i915_gem_context * i915_gem_create_context(struct drm_i915_private *dev_priv, struct drm_i915_file_private *file_priv) @@ -443,8 +472,8 @@ i915_gem_create_context(struct drm_i915_private *dev_priv, return ERR_CAST(ppgtt); } - ctx->ppgtt = ppgtt; - ctx->desc_template = default_desc_template(dev_priv, ppgtt); + __assign_ppgtt(ctx, ppgtt); + i915_ppgtt_put(ppgtt); } trace_i915_context_create(ctx); @@ -625,19 +654,29 @@ static int context_idr_cleanup(int id, void *p, void *data) return 0; } +static int vm_idr_cleanup(int id, void *p, void *data) +{ + i915_ppgtt_put(p); + return 0; +} + int i915_gem_context_open(struct drm_i915_private *i915, struct drm_file *file) { struct drm_i915_file_private *file_priv = file->driver_priv; struct i915_gem_context *ctx; + mutex_init(&file_priv->vm_lock); + idr_init(&file_priv->context_idr); + idr_init_base(&file_priv->vm_idr, 1); mutex_lock(&i915->drm.struct_mutex); ctx = i915_gem_create_context(i915, file_priv); mutex_unlock(&i915->drm.struct_mutex); if (IS_ERR(ctx)) { idr_destroy(&file_priv->context_idr); + idr_destroy(&file_priv->vm_idr); return PTR_ERR(ctx); } @@ -654,6 +693,89 @@ void i915_gem_context_close(struct drm_file *file) idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL); idr_destroy(&file_priv->context_idr); + + idr_for_each(&file_priv->vm_idr, vm_idr_cleanup, NULL); + idr_destroy(&file_priv->vm_idr); + + mutex_destroy(&file_priv->vm_lock); +} + +int i915_gem_vm_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_private *i915 = to_i915(dev); + struct drm_i915_gem_vm_control *args = data; + struct drm_i915_file_private *file_priv = file->driver_priv; + struct i915_hw_ppgtt *ppgtt; + int err; + + if (!HAS_FULL_PPGTT(i915)) + return -ENODEV; + + if (args->flags) + return -EINVAL; + + if (args->extensions) + return -EINVAL; + + ppgtt = i915_ppgtt_create(i915, file_priv); + if (IS_ERR(ppgtt)) + return PTR_ERR(ppgtt); + + err = mutex_lock_interruptible(&file_priv->vm_lock); + if (err) + goto err_put; + + err = idr_alloc(&file_priv->vm_idr, ppgtt, 0, 0, GFP_KERNEL); + mutex_unlock(&file_priv->vm_lock); + if (err < 0) + goto err_put; + + GEM_BUG_ON(err == 0); /* reserved for default/unassigned ppgtt */ + ppgtt->user_handle = err; + args->id = err; + return 0; + +err_put: + i915_ppgtt_put(ppgtt); + return err; +} + +int i915_gem_vm_destroy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_i915_gem_vm_control *args = data; + struct i915_hw_ppgtt *ppgtt; + int err; + u32 id; + + if (args->flags) + return -EINVAL; + + if (args->extensions) + return -EINVAL; + + id = args->id; + if (!id) + return -ENOENT; + + err = mutex_lock_interruptible(&file_priv->vm_lock); + if (err) + return err; + + ppgtt = idr_remove(&file_priv->vm_idr, id); + if (ppgtt) { + GEM_BUG_ON(!ppgtt->user_handle); + ppgtt->user_handle = 0; + } + + mutex_unlock(&file_priv->vm_lock); + if (!ppgtt) + return -ENOENT; + + i915_ppgtt_put(ppgtt); + return 0; } static struct i915_request * @@ -697,12 +819,13 @@ static void cb_retire(struct i915_active *base) I915_SELFTEST_DECLARE(static unsigned long context_barrier_inject_fault); static int context_barrier_task(struct i915_gem_context *ctx, unsigned long engines, + int (*emit)(struct i915_request *rq, void *data), void (*task)(void *data), void *data) { struct drm_i915_private *i915 = ctx->i915; struct context_barrier_task *cb; - struct intel_context *ce; + struct intel_context *ce, *next; intel_wakeref_t wakeref; int err = 0; @@ -717,11 +840,11 @@ static int context_barrier_task(struct i915_gem_context *ctx, i915_active_acquire(&cb->base); wakeref = intel_runtime_pm_get(i915); - list_for_each_entry(ce, &ctx->active_engines, active_link) { + rbtree_postorder_for_each_entry_safe(ce, next, &ctx->hw_contexts, node) { struct intel_engine_cs *engine = ce->engine; struct i915_request *rq; - if (!(ce->engine->mask & engines)) + if (!(engine->mask & engines)) continue; if (I915_SELFTEST_ONLY(context_barrier_inject_fault & @@ -736,7 +859,12 @@ static int context_barrier_task(struct i915_gem_context *ctx, break; } - err = i915_active_ref(&cb->base, rq->fence.context, rq); + err = 0; + if (emit) + err = emit(rq, data); + if (err == 0) + err = i915_active_ref(&cb->base, rq->fence.context, rq); + i915_request_add(rq); if (err) break; @@ -799,6 +927,173 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *i915, return 0; } +static int get_ppgtt(struct i915_gem_context *ctx, + struct drm_i915_gem_context_param *args) +{ + struct drm_i915_file_private *file_priv = ctx->file_priv; + struct i915_hw_ppgtt *ppgtt; + int ret; + + if (!ctx->ppgtt) + return -ENODEV; + + /* XXX rcu acquire? */ + ret = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex); + if (ret) + return ret; + + ppgtt = i915_ppgtt_get(ctx->ppgtt); + mutex_unlock(&ctx->i915->drm.struct_mutex); + + ret = mutex_lock_interruptible(&file_priv->vm_lock); + if (ret) + goto err_put; + + if (!ppgtt->user_handle) { + ret = idr_alloc(&file_priv->vm_idr, ppgtt, 0, 0, GFP_KERNEL); + GEM_BUG_ON(!ret); + if (ret < 0) + goto err_unlock; + + ppgtt->user_handle = ret; + i915_ppgtt_get(ppgtt); + } + + args->size = 0; + args->value = ppgtt->user_handle; + + ret = 0; +err_unlock: + mutex_unlock(&file_priv->vm_lock); +err_put: + i915_ppgtt_put(ppgtt); + return ret; +} + +static void set_ppgtt_barrier(void *data) +{ + struct i915_hw_ppgtt *old = data; + + if (INTEL_GEN(old->vm.i915) < 8) + gen6_ppgtt_unpin_all(old); + + i915_ppgtt_close(old); + i915_ppgtt_put(old); +} + +static int emit_ppgtt_update(struct i915_request *rq, void *data) +{ + struct i915_hw_ppgtt *ppgtt = rq->gem_context->ppgtt; + struct intel_engine_cs *engine = rq->engine; + u32 *cs; + int i; + + if (i915_vm_is_48bit(&ppgtt->vm)) { + const dma_addr_t pd_daddr = px_dma(&ppgtt->pml4); + + cs = intel_ring_begin(rq, 6); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = MI_LOAD_REGISTER_IMM(2); + + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, 0)); + *cs++ = upper_32_bits(pd_daddr); + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, 0)); + *cs++ = lower_32_bits(pd_daddr); + + *cs++ = MI_NOOP; + intel_ring_advance(rq, cs); + } else if (HAS_LOGICAL_RING_CONTEXTS(engine->i915)) { + cs = intel_ring_begin(rq, 4 * GEN8_3LVL_PDPES + 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = MI_LOAD_REGISTER_IMM(2 * GEN8_3LVL_PDPES); + for (i = GEN8_3LVL_PDPES; i--; ) { + const dma_addr_t pd_daddr = i915_page_dir_dma_addr(ppgtt, i); + + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, i)); + *cs++ = upper_32_bits(pd_daddr); + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, i)); + *cs++ = lower_32_bits(pd_daddr); + } + *cs++ = MI_NOOP; + intel_ring_advance(rq, cs); + } else { + /* ppGTT is not part of the legacy context image */ + gen6_ppgtt_pin(ppgtt); + } + + return 0; +} + +static int set_ppgtt(struct i915_gem_context *ctx, + struct drm_i915_gem_context_param *args) +{ + struct drm_i915_file_private *file_priv = ctx->file_priv; + struct i915_hw_ppgtt *ppgtt, *old; + int err; + + if (args->size) + return -EINVAL; + + if (!ctx->ppgtt) + return -ENODEV; + + if (upper_32_bits(args->value)) + return -ENOENT; + + err = mutex_lock_interruptible(&file_priv->vm_lock); + if (err) + return err; + + ppgtt = idr_find(&file_priv->vm_idr, args->value); + if (ppgtt) { + GEM_BUG_ON(ppgtt->user_handle != args->value); + i915_ppgtt_get(ppgtt); + } + mutex_unlock(&file_priv->vm_lock); + if (!ppgtt) + return -ENOENT; + + err = mutex_lock_interruptible(&ctx->i915->drm.struct_mutex); + if (err) + goto out; + + if (ppgtt == ctx->ppgtt) + goto unlock; + + /* Teardown the existing obj:vma cache, it will have to be rebuilt. */ + lut_close(ctx); + + old = __set_ppgtt(ctx, ppgtt); + + /* + * We need to flush any requests using the current ppgtt before + * we release it as the requests do not hold a reference themselves, + * only indirectly through the context. + */ + err = context_barrier_task(ctx, ALL_ENGINES, + emit_ppgtt_update, + set_ppgtt_barrier, + old); + if (err) { + ctx->ppgtt = old; + ctx->desc_template = default_desc_template(ctx->i915, old); + + i915_ppgtt_close(ppgtt); + i915_ppgtt_put(ppgtt); + } + +unlock: + mutex_unlock(&ctx->i915->drm.struct_mutex); + +out: + i915_ppgtt_put(ppgtt); + return err; +} + static bool client_is_banned(struct drm_i915_file_private *file_priv) { return atomic_read(&file_priv->ban_score) >= I915_CLIENT_SCORE_BANNED; @@ -973,6 +1268,9 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, case I915_CONTEXT_PARAM_SSEU: ret = get_sseu(ctx, args); break; + case I915_CONTEXT_PARAM_VM: + ret = get_ppgtt(ctx, args); + break; default: ret = -EINVAL; break; @@ -1274,9 +1572,6 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, return -ENOENT; switch (args->param) { - case I915_CONTEXT_PARAM_BAN_PERIOD: - ret = -EINVAL; - break; case I915_CONTEXT_PARAM_NO_ZEROMAP: if (args->size) ret = -EINVAL; @@ -1332,9 +1627,16 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, I915_USER_PRIORITY(priority); } break; + case I915_CONTEXT_PARAM_SSEU: ret = set_sseu(ctx, args); break; + + case I915_CONTEXT_PARAM_VM: + ret = set_ppgtt(ctx, args); + break; + + case I915_CONTEXT_PARAM_BAN_PERIOD: default: ret = -EINVAL; break; diff --git a/drivers/gpu/drm/i915/i915_gem_context.h b/drivers/gpu/drm/i915/i915_gem_context.h index 5a32c4b4816f..1e670372892c 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.h +++ b/drivers/gpu/drm/i915/i915_gem_context.h @@ -153,6 +153,11 @@ void i915_gem_context_release(struct kref *ctx_ref); struct i915_gem_context * i915_gem_context_create_gvt(struct drm_device *dev); +int i915_gem_vm_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +int i915_gem_vm_destroy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); + int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file); int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index dac08d9c3fab..c0972cd95283 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -1939,6 +1939,8 @@ int gen6_ppgtt_pin(struct i915_hw_ppgtt *base) struct gen6_hw_ppgtt *ppgtt = to_gen6_ppgtt(base); int err; + GEM_BUG_ON(ppgtt->base.vm.closed); + /* * Workaround the limited maximum vma->pin_count and the aliasing_ppgtt * which will be pinned into every active context. @@ -1977,6 +1979,17 @@ void gen6_ppgtt_unpin(struct i915_hw_ppgtt *base) i915_vma_unpin(ppgtt->vma); } +void gen6_ppgtt_unpin_all(struct i915_hw_ppgtt *base) +{ + struct gen6_hw_ppgtt *ppgtt = to_gen6_ppgtt(base); + + if (!ppgtt->pin_count) + return; + + ppgtt->pin_count = 0; + i915_vma_unpin(ppgtt->vma); +} + static struct i915_hw_ppgtt *gen6_ppgtt_create(struct drm_i915_private *i915) { struct i915_ggtt * const ggtt = &i915->ggtt; @@ -2099,10 +2112,21 @@ i915_ppgtt_create(struct drm_i915_private *i915, return ppgtt; } -void i915_ppgtt_close(struct i915_address_space *vm) +void i915_ppgtt_open(struct i915_hw_ppgtt *ppgtt) { - GEM_BUG_ON(vm->closed); - vm->closed = true; + GEM_BUG_ON(ppgtt->vm.closed); + + ppgtt->open_count++; +} + +void i915_ppgtt_close(struct i915_hw_ppgtt *ppgtt) +{ + GEM_BUG_ON(!ppgtt->open_count); + if (--ppgtt->open_count) + return; + + GEM_BUG_ON(ppgtt->vm.closed); + ppgtt->vm.closed = true; } static void ppgtt_destroy_vma(struct i915_address_space *vm) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index a47e11e6fc1b..5973d92e45fc 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -391,11 +391,15 @@ struct i915_hw_ppgtt { struct kref ref; unsigned long pd_dirty_engines; + unsigned int open_count; + union { struct i915_pml4 pml4; /* GEN8+ & 48b PPGTT */ struct i915_page_directory_pointer pdp; /* GEN8+ */ struct i915_page_directory pd; /* GEN6-7 */ }; + + u32 user_handle; }; struct gen6_hw_ppgtt { @@ -606,12 +610,16 @@ int i915_ppgtt_init_hw(struct drm_i915_private *dev_priv); void i915_ppgtt_release(struct kref *kref); struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_i915_private *dev_priv, struct drm_i915_file_private *fpriv); -void i915_ppgtt_close(struct i915_address_space *vm); -static inline void i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt) + +void i915_ppgtt_open(struct i915_hw_ppgtt *ppgtt); +void i915_ppgtt_close(struct i915_hw_ppgtt *ppgtt); + +static inline struct i915_hw_ppgtt *i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt) { - if (ppgtt) - kref_get(&ppgtt->ref); + kref_get(&ppgtt->ref); + return ppgtt; } + static inline void i915_ppgtt_put(struct i915_hw_ppgtt *ppgtt) { if (ppgtt) @@ -620,6 +628,7 @@ static inline void i915_ppgtt_put(struct i915_hw_ppgtt *ppgtt) int gen6_ppgtt_pin(struct i915_hw_ppgtt *base); void gen6_ppgtt_unpin(struct i915_hw_ppgtt *base); +void gen6_ppgtt_unpin_all(struct i915_hw_ppgtt *base); void i915_check_and_clear_faults(struct drm_i915_private *dev_priv); void i915_gem_suspend_gtt_mappings(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/selftests/huge_pages.c b/drivers/gpu/drm/i915/selftests/huge_pages.c index 1e66cff985f8..0b7740dc18cb 100644 --- a/drivers/gpu/drm/i915/selftests/huge_pages.c +++ b/drivers/gpu/drm/i915/selftests/huge_pages.c @@ -1734,7 +1734,6 @@ int i915_gem_huge_page_mock_selftests(void) err = i915_subtests(tests, ppgtt); out_close: - i915_ppgtt_close(&ppgtt->vm); i915_ppgtt_put(ppgtt); out_unlock: diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/selftests/i915_gem_context.c index 4399ef9ebf15..1ba354a916c0 100644 --- a/drivers/gpu/drm/i915/selftests/i915_gem_context.c +++ b/drivers/gpu/drm/i915/selftests/i915_gem_context.c @@ -372,7 +372,8 @@ static int cpu_fill(struct drm_i915_gem_object *obj, u32 value) return 0; } -static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max) +static noinline int cpu_check(struct drm_i915_gem_object *obj, + unsigned int idx, unsigned int max) { unsigned int n, m, needs_flush; int err; @@ -390,8 +391,10 @@ static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max) for (m = 0; m < max; m++) { if (map[m] != m) { - pr_err("Invalid value at page %d, offset %d: found %x expected %x\n", - n, m, map[m], m); + pr_err("%pS: Invalid value at object %d page %d/%ld, offset %d/%d: found %x expected %x\n", + __builtin_return_address(0), idx, + n, real_page_count(obj), m, max, + map[m], m); err = -EINVAL; goto out_unmap; } @@ -399,8 +402,9 @@ static int cpu_check(struct drm_i915_gem_object *obj, unsigned int max) for (; m < DW_PER_PAGE; m++) { if (map[m] != STACK_MAGIC) { - pr_err("Invalid value at page %d, offset %d: found %x expected %x\n", - n, m, map[m], STACK_MAGIC); + pr_err("%pS: Invalid value at object %d page %d, offset %d: found %x expected %x (uninitialised)\n", + __builtin_return_address(0), idx, n, m, + map[m], STACK_MAGIC); err = -EINVAL; goto out_unmap; } @@ -478,12 +482,8 @@ static unsigned long max_dwords(struct drm_i915_gem_object *obj) static int igt_ctx_exec(void *arg) { struct drm_i915_private *i915 = arg; - struct drm_i915_gem_object *obj = NULL; - unsigned long ncontexts, ndwords, dw; - struct igt_live_test t; - struct drm_file *file; - IGT_TIMEOUT(end_time); - LIST_HEAD(objects); + struct intel_engine_cs *engine; + enum intel_engine_id id; int err = -ENODEV; /* @@ -495,44 +495,169 @@ static int igt_ctx_exec(void *arg) if (!DRIVER_CAPS(i915)->has_logical_contexts) return 0; + for_each_engine(engine, i915, id) { + struct drm_i915_gem_object *obj = NULL; + unsigned long ncontexts, ndwords, dw; + struct igt_live_test t; + struct drm_file *file; + IGT_TIMEOUT(end_time); + LIST_HEAD(objects); + + if (!intel_engine_can_store_dword(engine)) + continue; + + if (!engine->context_size) + continue; /* No logical context support in HW */ + + file = mock_file(i915); + if (IS_ERR(file)) + return PTR_ERR(file); + + mutex_lock(&i915->drm.struct_mutex); + + err = igt_live_test_begin(&t, i915, __func__, engine->name); + if (err) + goto out_unlock; + + ncontexts = 0; + ndwords = 0; + dw = 0; + while (!time_after(jiffies, end_time)) { + struct i915_gem_context *ctx; + intel_wakeref_t wakeref; + + ctx = i915_gem_create_context(i915, file->driver_priv); + if (IS_ERR(ctx)) { + err = PTR_ERR(ctx); + goto out_unlock; + } + + if (!obj) { + obj = create_test_object(ctx, file, &objects); + if (IS_ERR(obj)) { + err = PTR_ERR(obj); + goto out_unlock; + } + } + + with_intel_runtime_pm(i915, wakeref) + err = gpu_fill(obj, ctx, engine, dw); + if (err) { + pr_err("Failed to fill dword %lu [%lu/%lu] with gpu (%s) in ctx %u [full-ppgtt? %s], err=%d\n", + ndwords, dw, max_dwords(obj), + engine->name, ctx->hw_id, + yesno(!!ctx->ppgtt), err); + goto out_unlock; + } + + if (++dw == max_dwords(obj)) { + obj = NULL; + dw = 0; + } + + ndwords++; + ncontexts++; + } + + pr_info("Submitted %lu contexts to %s, filling %lu dwords\n", + ncontexts, engine->name, ndwords); + + ncontexts = dw = 0; + list_for_each_entry(obj, &objects, st_link) { + unsigned int rem = + min_t(unsigned int, ndwords - dw, max_dwords(obj)); + + err = cpu_check(obj, ncontexts++, rem); + if (err) + break; + + dw += rem; + } + +out_unlock: + if (igt_live_test_end(&t)) + err = -EIO; + mutex_unlock(&i915->drm.struct_mutex); + + mock_file_free(i915, file); + if (err) + return err; + } + + return 0; +} + +static int igt_shared_ctx_exec(void *arg) +{ + struct drm_i915_private *i915 = arg; + struct i915_gem_context *parent; + struct intel_engine_cs *engine; + enum intel_engine_id id; + struct igt_live_test t; + struct drm_file *file; + int err = 0; + + /* + * Create a few different contexts with the same mm and write + * through each ctx using the GPU making sure those writes end + * up in the expected pages of our obj. + */ + if (!DRIVER_CAPS(i915)->has_logical_contexts) + return 0; + file = mock_file(i915); if (IS_ERR(file)) return PTR_ERR(file); mutex_lock(&i915->drm.struct_mutex); + parent = i915_gem_create_context(i915, file->driver_priv); + if (IS_ERR(parent)) { + err = PTR_ERR(parent); + goto out_unlock; + } + + if (!parent->ppgtt) { /* not full-ppgtt; nothing to share */ + err = -ENODEV; + goto out_unlock; + } + err = igt_live_test_begin(&t, i915, __func__, ""); if (err) goto out_unlock; - ncontexts = 0; - ndwords = 0; - dw = 0; - while (!time_after(jiffies, end_time)) { - struct intel_engine_cs *engine; - struct i915_gem_context *ctx; - unsigned int id; + for_each_engine(engine, i915, id) { + unsigned long ncontexts, ndwords, dw; + struct drm_i915_gem_object *obj = NULL; + struct i915_gem_context *ctx = NULL; + IGT_TIMEOUT(end_time); + LIST_HEAD(objects); - ctx = i915_gem_create_context(i915, file->driver_priv); - if (IS_ERR(ctx)) { - err = PTR_ERR(ctx); - goto out_unlock; - } + if (!intel_engine_can_store_dword(engine)) + continue; - for_each_engine(engine, i915, id) { + dw = 0; + ndwords = 0; + ncontexts = 0; + while (!time_after(jiffies, end_time)) { intel_wakeref_t wakeref; - if (!engine->context_size) - continue; /* No logical context support in HW */ + if (ctx) + __destroy_hw_context(ctx, file->driver_priv); - if (!intel_engine_can_store_dword(engine)) - continue; + ctx = i915_gem_create_context(i915, file->driver_priv); + if (IS_ERR(ctx)) { + err = PTR_ERR(ctx); + goto out_test; + } + + __assign_ppgtt(ctx, parent->ppgtt); if (!obj) { - obj = create_test_object(ctx, file, &objects); + obj = create_test_object(parent, file, &objects); if (IS_ERR(obj)) { err = PTR_ERR(obj); - goto out_unlock; + goto out_test; } } @@ -544,35 +669,36 @@ static int igt_ctx_exec(void *arg) ndwords, dw, max_dwords(obj), engine->name, ctx->hw_id, yesno(!!ctx->ppgtt), err); - goto out_unlock; + goto out_test; } if (++dw == max_dwords(obj)) { obj = NULL; dw = 0; } + ndwords++; + ncontexts++; } - ncontexts++; - } - pr_info("Submitted %lu contexts (across %u engines), filling %lu dwords\n", - ncontexts, RUNTIME_INFO(i915)->num_engines, ndwords); + pr_info("Submitted %lu contexts to %s, filling %lu dwords\n", + ncontexts, engine->name, ndwords); - dw = 0; - list_for_each_entry(obj, &objects, st_link) { - unsigned int rem = - min_t(unsigned int, ndwords - dw, max_dwords(obj)); + ncontexts = dw = 0; + list_for_each_entry(obj, &objects, st_link) { + unsigned int rem = + min_t(unsigned int, ndwords - dw, max_dwords(obj)); - err = cpu_check(obj, rem); - if (err) - break; + err = cpu_check(obj, ncontexts++, rem); + if (err) + goto out_test; - dw += rem; + dw += rem; + } } - -out_unlock: +out_test: if (igt_live_test_end(&t)) err = -EIO; +out_unlock: mutex_unlock(&i915->drm.struct_mutex); mock_file_free(i915, file); @@ -1048,7 +1174,7 @@ static int igt_ctx_readonly(void *arg) struct drm_i915_gem_object *obj = NULL; struct i915_gem_context *ctx; struct i915_hw_ppgtt *ppgtt; - unsigned long ndwords, dw; + unsigned long idx, ndwords, dw; struct igt_live_test t; struct drm_file *file; I915_RND_STATE(prng); @@ -1129,6 +1255,7 @@ static int igt_ctx_readonly(void *arg) ndwords, RUNTIME_INFO(i915)->num_engines); dw = 0; + idx = 0; list_for_each_entry(obj, &objects, st_link) { unsigned int rem = min_t(unsigned int, ndwords - dw, max_dwords(obj)); @@ -1138,7 +1265,7 @@ static int igt_ctx_readonly(void *arg) if (i915_gem_object_is_readonly(obj)) num_writes = 0; - err = cpu_check(obj, num_writes); + err = cpu_check(obj, idx++, num_writes); if (err) break; @@ -1626,7 +1753,8 @@ static int mock_context_barrier(void *arg) } counter = 0; - err = context_barrier_task(ctx, 0, mock_barrier_task, &counter); + err = context_barrier_task(ctx, 0, + NULL, mock_barrier_task, &counter); if (err) { pr_err("Failed at line %d, err=%d\n", __LINE__, err); goto out; @@ -1638,8 +1766,8 @@ static int mock_context_barrier(void *arg) } counter = 0; - err = context_barrier_task(ctx, - ALL_ENGINES, mock_barrier_task, &counter); + err = context_barrier_task(ctx, ALL_ENGINES, + NULL, mock_barrier_task, &counter); if (err) { pr_err("Failed at line %d, err=%d\n", __LINE__, err); goto out; @@ -1662,8 +1790,8 @@ static int mock_context_barrier(void *arg) counter = 0; context_barrier_inject_fault = BIT(RCS0); - err = context_barrier_task(ctx, - ALL_ENGINES, mock_barrier_task, &counter); + err = context_barrier_task(ctx, ALL_ENGINES, + NULL, mock_barrier_task, &counter); context_barrier_inject_fault = 0; if (err == -ENXIO) err = 0; @@ -1677,8 +1805,8 @@ static int mock_context_barrier(void *arg) goto out; counter = 0; - err = context_barrier_task(ctx, - ALL_ENGINES, mock_barrier_task, &counter); + err = context_barrier_task(ctx, ALL_ENGINES, + NULL, mock_barrier_task, &counter); if (err) { pr_err("Failed at line %d, err=%d\n", __LINE__, err); goto out; @@ -1726,6 +1854,7 @@ int i915_gem_context_live_selftests(struct drm_i915_private *dev_priv) SUBTEST(igt_ctx_exec), SUBTEST(igt_ctx_readonly), SUBTEST(igt_ctx_sseu), + SUBTEST(igt_shared_ctx_exec), SUBTEST(igt_vm_isolation), }; diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c index 826fd51c331e..57b3d9867070 100644 --- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c @@ -1020,7 +1020,6 @@ static int exercise_ppgtt(struct drm_i915_private *dev_priv, err = func(dev_priv, &ppgtt->vm, 0, ppgtt->vm.total, end_time); - i915_ppgtt_close(&ppgtt->vm); i915_ppgtt_put(ppgtt); out_unlock: mutex_unlock(&dev_priv->drm.struct_mutex); diff --git a/drivers/gpu/drm/i915/selftests/mock_context.c b/drivers/gpu/drm/i915/selftests/mock_context.c index 8efa6892c6cd..f90328b21763 100644 --- a/drivers/gpu/drm/i915/selftests/mock_context.c +++ b/drivers/gpu/drm/i915/selftests/mock_context.c @@ -54,13 +54,17 @@ mock_context(struct drm_i915_private *i915, goto err_handles; if (name) { + struct i915_hw_ppgtt *ppgtt; + ctx->name = kstrdup(name, GFP_KERNEL); if (!ctx->name) goto err_put; - ctx->ppgtt = mock_ppgtt(i915, name); - if (!ctx->ppgtt) + ppgtt = mock_ppgtt(i915, name); + if (!ppgtt) goto err_put; + + __set_ppgtt(ctx, ppgtt); } return ctx; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 1c69ed16a923..5079e25909ef 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -343,6 +343,8 @@ typedef struct _drm_i915_sarea { #define DRM_I915_PERF_ADD_CONFIG 0x37 #define DRM_I915_PERF_REMOVE_CONFIG 0x38 #define DRM_I915_QUERY 0x39 +#define DRM_I915_GEM_VM_CREATE 0x3a +#define DRM_I915_GEM_VM_DESTROY 0x3b /* Must be kept compact -- no holes */ #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) @@ -402,6 +404,8 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_PERF_ADD_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config) #define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64) #define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query) +#define DRM_IOCTL_I915_GEM_VM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_VM_CREATE, struct drm_i915_gem_vm_control) +#define DRM_IOCTL_I915_GEM_VM_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_VM_DESTROY, struct drm_i915_gem_vm_control) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -1453,6 +1457,33 @@ struct drm_i915_gem_context_destroy { __u32 pad; }; +/* + * DRM_I915_GEM_VM_CREATE - + * + * Create a new virtual memory address space (ppGTT) for use within a context + * on the same file. Extensions can be provided to configure exactly how the + * address space is setup upon creation. + * + * The id of new VM (bound to the fd) for use with I915_CONTEXT_PARAM_VM is + * returned in the outparam @id. + * + * No flags are defined, with all bits reserved and must be zero. + * + * An extension chain maybe provided, starting with @extensions, and terminated + * by the @next_extension being 0. Currently, no extensions are defined. + * + * DRM_I915_GEM_VM_DESTROY - + * + * Destroys a previously created VM id, specified in @id. + * + * No extensions or flags are allowed currently, and so must be zero. + */ +struct drm_i915_gem_vm_control { + __u64 extensions; + __u32 flags; + __u32 id; +}; + struct drm_i915_reg_read { /* * Register offset. @@ -1542,7 +1573,19 @@ struct drm_i915_gem_context_param { * On creation, all new contexts are marked as recoverable. */ #define I915_CONTEXT_PARAM_RECOVERABLE 0x8 + + /* + * The id of the associated virtual memory address space (ppGTT) of + * this context. Can be retrieved and passed to another context + * (on the same fd) for both to use the same ppGTT and so share + * address layouts, and avoid reloading the page tables on context + * switches between themselves. + * + * See DRM_I915_GEM_VM_CREATE and DRM_I915_GEM_VM_DESTROY. + */ +#define I915_CONTEXT_PARAM_VM 0x9 /* Must be kept compact -- no holes and well documented */ + __u64 value; };
In preparation to making the ppGTT binding for a context explicit (to facilitate reusing the same ppGTT between different contexts), allow the user to create and destroy named ppGTT. v2: Replace global barrier for swapping over the ppgtt and tlbs with a local context barrier (Tvrtko) v3: serialise with struct_mutex; it's lazy but required dammit v4: Rewrite igt_ctx_shared_exec to be more different (aimed to be more similarly, turned out different!) v2: Fix up test unwind for aliasing-ppgtt (snb) v3: Tighten language for uapi struct drm_i915_gem_vm_control. v4: Patch the context image for runtime ppgtt switching! Testcase: igt/gem_ctx_param/vm Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com> --- drivers/gpu/drm/i915/i915_drv.c | 2 + drivers/gpu/drm/i915/i915_drv.h | 3 + drivers/gpu/drm/i915/i915_gem_context.c | 322 +++++++++++++++++- drivers/gpu/drm/i915/i915_gem_context.h | 5 + drivers/gpu/drm/i915/i915_gem_gtt.c | 30 +- drivers/gpu/drm/i915/i915_gem_gtt.h | 17 +- drivers/gpu/drm/i915/selftests/huge_pages.c | 1 - .../gpu/drm/i915/selftests/i915_gem_context.c | 237 ++++++++++--- drivers/gpu/drm/i915/selftests/i915_gem_gtt.c | 1 - drivers/gpu/drm/i915/selftests/mock_context.c | 8 +- include/uapi/drm/i915_drm.h | 43 +++ 11 files changed, 594 insertions(+), 75 deletions(-)