diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_gem_context.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_gem_context.c | 118 |
1 files changed, 88 insertions, 30 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index a1e8ecb6adf6..51b7a2171cae 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -113,7 +113,7 @@ static int get_context_size(struct drm_device *dev) case 7: reg = I915_READ(GEN7_CXT_SIZE); if (IS_HASWELL(dev)) - ret = HSW_CXT_TOTAL_SIZE(reg) * 64; + ret = HSW_CXT_TOTAL_SIZE; else ret = GEN7_CXT_TOTAL_SIZE(reg) * 64; break; @@ -124,10 +124,10 @@ static int get_context_size(struct drm_device *dev) return ret; } -static void do_destroy(struct i915_hw_context *ctx) +void i915_gem_context_free(struct kref *ctx_ref) { - if (ctx->file_priv) - idr_remove(&ctx->file_priv->context_idr, ctx->id); + struct i915_hw_context *ctx = container_of(ctx_ref, + typeof(*ctx), ref); drm_gem_object_unreference(&ctx->obj->base); kfree(ctx); @@ -145,6 +145,7 @@ create_hw_context(struct drm_device *dev, if (ctx == NULL) return ERR_PTR(-ENOMEM); + kref_init(&ctx->ref); ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size); if (ctx->obj == NULL) { kfree(ctx); @@ -155,7 +156,8 @@ create_hw_context(struct drm_device *dev, if (INTEL_INFO(dev)->gen >= 7) { ret = i915_gem_object_set_cache_level(ctx->obj, I915_CACHE_LLC_MLC); - if (ret) + /* Failure shouldn't ever happen this early */ + if (WARN_ON(ret)) goto err_out; } @@ -169,18 +171,18 @@ create_hw_context(struct drm_device *dev, if (file_priv == NULL) return ctx; - ctx->file_priv = file_priv; - ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID + 1, 0, GFP_KERNEL); if (ret < 0) goto err_out; + + ctx->file_priv = file_priv; ctx->id = ret; return ctx; err_out: - do_destroy(ctx); + i915_gem_context_unreference(ctx); return ERR_PTR(ret); } @@ -213,12 +215,16 @@ static int create_default_context(struct drm_i915_private *dev_priv) */ dev_priv->ring[RCS].default_context = ctx; ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false, false); - if (ret) + if (ret) { + DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret); goto err_destroy; + } ret = do_switch(ctx); - if (ret) + if (ret) { + DRM_DEBUG_DRIVER("Switch failed %d\n", ret); goto err_unpin; + } DRM_DEBUG_DRIVER("Default HW context loaded\n"); return 0; @@ -226,7 +232,7 @@ static int create_default_context(struct drm_i915_private *dev_priv) err_unpin: i915_gem_object_unpin(ctx->obj); err_destroy: - do_destroy(ctx); + i915_gem_context_unreference(ctx); return ret; } @@ -236,6 +242,7 @@ void i915_gem_context_init(struct drm_device *dev) if (!HAS_HW_CONTEXTS(dev)) { dev_priv->hw_contexts_disabled = true; + DRM_DEBUG_DRIVER("Disabling HW Contexts; old hardware\n"); return; } @@ -248,11 +255,13 @@ void i915_gem_context_init(struct drm_device *dev) if (dev_priv->hw_context_size > (1<<20)) { dev_priv->hw_contexts_disabled = true; + DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size\n"); return; } if (create_default_context(dev_priv)) { dev_priv->hw_contexts_disabled = true; + DRM_DEBUG_DRIVER("Disabling HW Contexts; create failed\n"); return; } @@ -262,6 +271,7 @@ void i915_gem_context_init(struct drm_device *dev) void i915_gem_context_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_hw_context *dctx = dev_priv->ring[RCS].default_context; if (dev_priv->hw_contexts_disabled) return; @@ -271,9 +281,16 @@ void i915_gem_context_fini(struct drm_device *dev) * other code, leading to spurious errors. */ intel_gpu_reset(dev); - i915_gem_object_unpin(dev_priv->ring[RCS].default_context->obj); + i915_gem_object_unpin(dctx->obj); - do_destroy(dev_priv->ring[RCS].default_context); + /* When default context is created and switched to, base object refcount + * will be 2 (+1 from object creation and +1 from do_switch()). + * i915_gem_context_fini() will be called after gpu_idle() has switched + * to default context. So we need to unreference the base object once + * to offset the do_switch part, so that i915_gem_context_unreference() + * can then free the base object correctly. */ + drm_gem_object_unreference(&dctx->obj->base); + i915_gem_context_unreference(dctx); } static int context_idr_cleanup(int id, void *p, void *data) @@ -282,11 +299,38 @@ static int context_idr_cleanup(int id, void *p, void *data) BUG_ON(id == DEFAULT_CONTEXT_ID); - do_destroy(ctx); - + i915_gem_context_unreference(ctx); return 0; } +struct i915_ctx_hang_stats * +i915_gem_context_get_hang_stats(struct intel_ring_buffer *ring, + struct drm_file *file, + u32 id) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + struct drm_i915_file_private *file_priv = file->driver_priv; + struct i915_hw_context *to; + + if (dev_priv->hw_contexts_disabled) + return ERR_PTR(-ENOENT); + + if (ring->id != RCS) + return ERR_PTR(-EINVAL); + + if (file == NULL) + return ERR_PTR(-EINVAL); + + if (id == DEFAULT_CONTEXT_ID) + return &file_priv->hang_stats; + + to = i915_gem_context_get(file->driver_priv, id); + if (to == NULL) + return ERR_PTR(-ENOENT); + + return &to->hang_stats; +} + void i915_gem_context_close(struct drm_device *dev, struct drm_file *file) { struct drm_i915_file_private *file_priv = file->driver_priv; @@ -325,6 +369,7 @@ mi_set_context(struct intel_ring_buffer *ring, if (ret) return ret; + /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw */ if (IS_GEN7(ring->dev)) intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE); else @@ -353,13 +398,13 @@ mi_set_context(struct intel_ring_buffer *ring, static int do_switch(struct i915_hw_context *to) { struct intel_ring_buffer *ring = to->ring; - struct drm_i915_gem_object *from_obj = ring->last_context_obj; + struct i915_hw_context *from = ring->last_context; u32 hw_flags = 0; int ret; - BUG_ON(from_obj != NULL && from_obj->pin_count == 0); + BUG_ON(from != NULL && from->obj != NULL && from->obj->pin_count == 0); - if (from_obj == to->obj) + if (from == to) return 0; ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false); @@ -382,7 +427,7 @@ static int do_switch(struct i915_hw_context *to) if (!to->is_initialized || is_default_context(to)) hw_flags |= MI_RESTORE_INHIBIT; - else if (WARN_ON_ONCE(from_obj == to->obj)) /* not yet expected */ + else if (WARN_ON_ONCE(from == to)) /* not yet expected */ hw_flags |= MI_FORCE_RESTORE; ret = mi_set_context(ring, to, hw_flags); @@ -397,9 +442,9 @@ static int do_switch(struct i915_hw_context *to) * is a bit suboptimal because the retiring can occur simply after the * MI_SET_CONTEXT instead of when the next seqno has completed. */ - if (from_obj != NULL) { - from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; - i915_gem_object_move_to_active(from_obj, ring); + if (from != NULL) { + from->obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; + i915_gem_object_move_to_active(from->obj, ring); /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the * whole damn pipeline, we don't need to explicitly mark the * object dirty. The only exception is that the context must be @@ -407,15 +452,26 @@ static int do_switch(struct i915_hw_context *to) * able to defer doing this until we know the object would be * swapped, but there is no way to do that yet. */ - from_obj->dirty = 1; - BUG_ON(from_obj->ring != ring); - i915_gem_object_unpin(from_obj); + from->obj->dirty = 1; + BUG_ON(from->obj->ring != ring); + + ret = i915_add_request(ring, NULL); + if (ret) { + /* Too late, we've already scheduled a context switch. + * Try to undo the change so that the hw state is + * consistent with out tracking. In case of emergency, + * scream. + */ + WARN_ON(mi_set_context(ring, from, MI_RESTORE_INHIBIT)); + return ret; + } - drm_gem_object_unreference(&from_obj->base); + i915_gem_object_unpin(from->obj); + i915_gem_context_unreference(from); } - drm_gem_object_reference(&to->obj->base); - ring->last_context_obj = to->obj; + i915_gem_context_reference(to); + ring->last_context = to; to->is_initialized = true; return 0; @@ -444,6 +500,8 @@ int i915_switch_context(struct intel_ring_buffer *ring, if (dev_priv->hw_contexts_disabled) return 0; + WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); + if (ring != &dev_priv->ring[RCS]) return 0; @@ -512,8 +570,8 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, return -ENOENT; } - do_destroy(ctx); - + idr_remove(&ctx->file_priv->context_idr, ctx->id); + i915_gem_context_unreference(ctx); mutex_unlock(&dev->struct_mutex); DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id); |