diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_gem_context.c')
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gem_context.c | 639 | 
1 files changed, 437 insertions, 202 deletions
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 403309c2a7d..a5ddf3bce9c 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -73,7 +73,7 @@   *   * There are two confusing terms used above:   *  The "current context" means the context which is currently running on the - *  GPU. The GPU has loaded it's state already and has stored away the gtt + *  GPU. The GPU has loaded its state already and has stored away the gtt   *  offset of the BO. The GPU is not actively referencing the data at this   *  offset, but it will on the next context switch. The only way to avoid this   *  is to do a GPU reset. @@ -93,11 +93,60 @@   * I've seen in a spec to date, and that was a workaround for a non-shipping   * part. It should be safe to decrease this, but it's more future proof as is.   */ -#define CONTEXT_ALIGN (64<<10) +#define GEN6_CONTEXT_ALIGN (64<<10) +#define GEN7_CONTEXT_ALIGN 4096 -static struct i915_hw_context * -i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id); -static int do_switch(struct i915_hw_context *to); +static void do_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt) +{ +	struct drm_device *dev = ppgtt->base.dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct i915_address_space *vm = &ppgtt->base; + +	if (ppgtt == dev_priv->mm.aliasing_ppgtt || +	    (list_empty(&vm->active_list) && list_empty(&vm->inactive_list))) { +		ppgtt->base.cleanup(&ppgtt->base); +		return; +	} + +	/* +	 * Make sure vmas are unbound before we take down the drm_mm +	 * +	 * FIXME: Proper refcounting should take care of this, this shouldn't be +	 * needed at all. +	 */ +	if (!list_empty(&vm->active_list)) { +		struct i915_vma *vma; + +		list_for_each_entry(vma, &vm->active_list, mm_list) +			if (WARN_ON(list_empty(&vma->vma_link) || +				    list_is_singular(&vma->vma_link))) +				break; + +		i915_gem_evict_vm(&ppgtt->base, true); +	} else { +		i915_gem_retire_requests(dev); +		i915_gem_evict_vm(&ppgtt->base, false); +	} + +	ppgtt->base.cleanup(&ppgtt->base); +} + +static void ppgtt_release(struct kref *kref) +{ +	struct i915_hw_ppgtt *ppgtt = +		container_of(kref, struct i915_hw_ppgtt, ref); + +	do_ppgtt_cleanup(ppgtt); +	kfree(ppgtt); +} + +static size_t get_context_alignment(struct drm_device *dev) +{ +	if (IS_GEN6(dev)) +		return GEN6_CONTEXT_ALIGN; + +	return GEN7_CONTEXT_ALIGN; +}  static int get_context_size(struct drm_device *dev)  { @@ -117,6 +166,9 @@ static int get_context_size(struct drm_device *dev)  		else  			ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;  		break; +	case 8: +		ret = GEN8_CXT_TOTAL_SIZE; +		break;  	default:  		BUG();  	} @@ -126,19 +178,52 @@ static int get_context_size(struct drm_device *dev)  void i915_gem_context_free(struct kref *ctx_ref)  { -	struct i915_hw_context *ctx = container_of(ctx_ref, +	struct intel_context *ctx = container_of(ctx_ref,  						   typeof(*ctx), ref); +	struct i915_hw_ppgtt *ppgtt = NULL; -	drm_gem_object_unreference(&ctx->obj->base); +	if (ctx->obj) { +		/* We refcount even the aliasing PPGTT to keep the code symmetric */ +		if (USES_PPGTT(ctx->obj->base.dev)) +			ppgtt = ctx_to_ppgtt(ctx); + +		/* XXX: Free up the object before tearing down the address space, in +		 * case we're bound in the PPGTT */ +		drm_gem_object_unreference(&ctx->obj->base); +	} + +	if (ppgtt) +		kref_put(&ppgtt->ref, ppgtt_release); +	list_del(&ctx->link);  	kfree(ctx);  } -static struct i915_hw_context * -create_hw_context(struct drm_device *dev, +static struct i915_hw_ppgtt * +create_vm_for_ctx(struct drm_device *dev, struct intel_context *ctx) +{ +	struct i915_hw_ppgtt *ppgtt; +	int ret; + +	ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL); +	if (!ppgtt) +		return ERR_PTR(-ENOMEM); + +	ret = i915_gem_init_ppgtt(dev, ppgtt); +	if (ret) { +		kfree(ppgtt); +		return ERR_PTR(ret); +	} + +	ppgtt->ctx = ctx; +	return ppgtt; +} + +static struct intel_context * +__create_hw_context(struct drm_device *dev,  		  struct drm_i915_file_private *file_priv)  {  	struct drm_i915_private *dev_priv = dev->dev_private; -	struct i915_hw_context *ctx; +	struct intel_context *ctx;  	int ret;  	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -146,38 +231,47 @@ create_hw_context(struct drm_device *dev,  		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); -		DRM_DEBUG_DRIVER("Context object allocated failed\n"); -		return ERR_PTR(-ENOMEM); -	} +	list_add_tail(&ctx->link, &dev_priv->context_list); -	if (INTEL_INFO(dev)->gen >= 7) { -		ret = i915_gem_object_set_cache_level(ctx->obj, -						      I915_CACHE_L3_LLC); -		/* Failure shouldn't ever happen this early */ -		if (WARN_ON(ret)) +	if (dev_priv->hw_context_size) { +		ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size); +		if (ctx->obj == NULL) { +			ret = -ENOMEM;  			goto err_out; -	} +		} -	/* The ring associated with the context object is handled by the normal -	 * object tracking code. We give an initial ring value simple to pass an -	 * assertion in the context switch code. -	 */ -	ctx->ring = &dev_priv->ring[RCS]; +		/* +		 * Try to make the context utilize L3 as well as LLC. +		 * +		 * On VLV we don't have L3 controls in the PTEs so we +		 * shouldn't touch the cache level, especially as that +		 * would make the object snooped which might have a +		 * negative performance impact. +		 */ +		if (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev)) { +			ret = i915_gem_object_set_cache_level(ctx->obj, +							      I915_CACHE_L3_LLC); +			/* Failure shouldn't ever happen this early */ +			if (WARN_ON(ret)) +				goto err_out; +		} +	}  	/* Default context will never have a file_priv */ -	if (file_priv == NULL) -		return ctx; - -	ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID + 1, 0, -			GFP_KERNEL); -	if (ret < 0) -		goto err_out; +	if (file_priv != NULL) { +		ret = idr_alloc(&file_priv->context_idr, ctx, +				DEFAULT_CONTEXT_ID, 0, GFP_KERNEL); +		if (ret < 0) +			goto err_out; +	} else +		ret = DEFAULT_CONTEXT_ID;  	ctx->file_priv = file_priv;  	ctx->id = ret; +	/* NB: Mark all slices as needing a remap so that when the context first +	 * loads it will restore whatever remap state already exists. If there +	 * is no remap info, it will be a NOP. */ +	ctx->remap_slice = (1 << NUM_L3_SLICES(dev)) - 1;  	return ctx; @@ -186,163 +280,269 @@ err_out:  	return ERR_PTR(ret);  } -static inline bool is_default_context(struct i915_hw_context *ctx) -{ -	return (ctx == ctx->ring->default_context); -} -  /**   * The default context needs to exist per ring that uses contexts. It stores the   * context state of the GPU for applications that don't utilize HW contexts, as   * well as an idle case.   */ -static int create_default_context(struct drm_i915_private *dev_priv) +static struct intel_context * +i915_gem_create_context(struct drm_device *dev, +			struct drm_i915_file_private *file_priv, +			bool create_vm)  { -	struct i915_hw_context *ctx; -	int ret; +	const bool is_global_default_ctx = file_priv == NULL; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_context *ctx; +	int ret = 0; -	BUG_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); +	BUG_ON(!mutex_is_locked(&dev->struct_mutex)); -	ctx = create_hw_context(dev_priv->dev, NULL); +	ctx = __create_hw_context(dev, file_priv);  	if (IS_ERR(ctx)) -		return PTR_ERR(ctx); +		return ctx; -	/* We may need to do things with the shrinker which require us to -	 * immediately switch back to the default context. This can cause a -	 * problem as pinning the default context also requires GTT space which -	 * may not be available. To avoid this we always pin the -	 * default context. -	 */ -	dev_priv->ring[RCS].default_context = ctx; -	ret = i915_gem_obj_ggtt_pin(ctx->obj, CONTEXT_ALIGN, false, false); -	if (ret) { -		DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret); -		goto err_destroy; +	if (is_global_default_ctx && ctx->obj) { +		/* We may need to do things with the shrinker which +		 * require us to immediately switch back to the default +		 * context. This can cause a problem as pinning the +		 * default context also requires GTT space which may not +		 * be available. To avoid this we always pin the default +		 * context. +		 */ +		ret = i915_gem_obj_ggtt_pin(ctx->obj, +					    get_context_alignment(dev), 0); +		if (ret) { +			DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret); +			goto err_destroy; +		}  	} -	ret = do_switch(ctx); -	if (ret) { -		DRM_DEBUG_DRIVER("Switch failed %d\n", ret); -		goto err_unpin; -	} +	if (create_vm) { +		struct i915_hw_ppgtt *ppgtt = create_vm_for_ctx(dev, ctx); + +		if (IS_ERR_OR_NULL(ppgtt)) { +			DRM_DEBUG_DRIVER("PPGTT setup failed (%ld)\n", +					 PTR_ERR(ppgtt)); +			ret = PTR_ERR(ppgtt); +			goto err_unpin; +		} else +			ctx->vm = &ppgtt->base; + +		/* This case is reserved for the global default context and +		 * should only happen once. */ +		if (is_global_default_ctx) { +			if (WARN_ON(dev_priv->mm.aliasing_ppgtt)) { +				ret = -EEXIST; +				goto err_unpin; +			} + +			dev_priv->mm.aliasing_ppgtt = ppgtt; +		} +	} else if (USES_PPGTT(dev)) { +		/* For platforms which only have aliasing PPGTT, we fake the +		 * address space and refcounting. */ +		ctx->vm = &dev_priv->mm.aliasing_ppgtt->base; +		kref_get(&dev_priv->mm.aliasing_ppgtt->ref); +	} else +		ctx->vm = &dev_priv->gtt.base; -	DRM_DEBUG_DRIVER("Default HW context loaded\n"); -	return 0; +	return ctx;  err_unpin: -	i915_gem_object_unpin(ctx->obj); +	if (is_global_default_ctx && ctx->obj) +		i915_gem_object_ggtt_unpin(ctx->obj);  err_destroy:  	i915_gem_context_unreference(ctx); -	return ret; +	return ERR_PTR(ret);  } -void i915_gem_context_init(struct drm_device *dev) +void i915_gem_context_reset(struct drm_device *dev)  {  	struct drm_i915_private *dev_priv = dev->dev_private; +	int i; + +	/* Prevent the hardware from restoring the last context (which hung) on +	 * the next switch */ +	for (i = 0; i < I915_NUM_RINGS; i++) { +		struct intel_engine_cs *ring = &dev_priv->ring[i]; +		struct intel_context *dctx = ring->default_context; + +		/* Do a fake switch to the default context */ +		if (ring->last_context == dctx) +			continue; + +		if (!ring->last_context) +			continue; + +		if (dctx->obj && i == RCS) { +			WARN_ON(i915_gem_obj_ggtt_pin(dctx->obj, +						      get_context_alignment(dev), 0)); +			/* Fake a finish/inactive */ +			dctx->obj->base.write_domain = 0; +			dctx->obj->active = 0; +		} -	if (!HAS_HW_CONTEXTS(dev)) { -		dev_priv->hw_contexts_disabled = true; -		DRM_DEBUG_DRIVER("Disabling HW Contexts; old hardware\n"); -		return; +		i915_gem_context_unreference(ring->last_context); +		i915_gem_context_reference(dctx); +		ring->last_context = dctx;  	} +} -	/* If called from reset, or thaw... we've been here already */ -	if (dev_priv->hw_contexts_disabled || -	    dev_priv->ring[RCS].default_context) -		return; +int i915_gem_context_init(struct drm_device *dev) +{ +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_context *ctx; +	int i; -	dev_priv->hw_context_size = round_up(get_context_size(dev), 4096); +	/* Init should only be called once per module load. Eventually the +	 * restriction on the context_disabled check can be loosened. */ +	if (WARN_ON(dev_priv->ring[RCS].default_context)) +		return 0; -	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 (HAS_HW_CONTEXTS(dev)) { +		dev_priv->hw_context_size = round_up(get_context_size(dev), 4096); +		if (dev_priv->hw_context_size > (1<<20)) { +			DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size %d\n", +					 dev_priv->hw_context_size); +			dev_priv->hw_context_size = 0; +		}  	} -	if (create_default_context(dev_priv)) { -		dev_priv->hw_contexts_disabled = true; -		DRM_DEBUG_DRIVER("Disabling HW Contexts; create failed\n"); -		return; +	ctx = i915_gem_create_context(dev, NULL, USES_PPGTT(dev)); +	if (IS_ERR(ctx)) { +		DRM_ERROR("Failed to create default global context (error %ld)\n", +			  PTR_ERR(ctx)); +		return PTR_ERR(ctx);  	} -	DRM_DEBUG_DRIVER("HW context support initialized\n"); +	/* NB: RCS will hold a ref for all rings */ +	for (i = 0; i < I915_NUM_RINGS; i++) +		dev_priv->ring[i].default_context = ctx; + +	DRM_DEBUG_DRIVER("%s context support initialized\n", dev_priv->hw_context_size ? "HW" : "fake"); +	return 0;  }  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; +	struct intel_context *dctx = dev_priv->ring[RCS].default_context; +	int i; + +	if (dctx->obj) { +		/* The only known way to stop the gpu from accessing the hw context is +		 * to reset it. Do this as the very last operation to avoid confusing +		 * other code, leading to spurious errors. */ +		intel_gpu_reset(dev); + +		/* 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. */ +		WARN_ON(!dev_priv->ring[RCS].last_context); +		if (dev_priv->ring[RCS].last_context == dctx) { +			/* Fake switch to NULL context */ +			WARN_ON(dctx->obj->active); +			i915_gem_object_ggtt_unpin(dctx->obj); +			i915_gem_context_unreference(dctx); +			dev_priv->ring[RCS].last_context = NULL; +		} -	if (dev_priv->hw_contexts_disabled) -		return; +		i915_gem_object_ggtt_unpin(dctx->obj); +	} + +	for (i = 0; i < I915_NUM_RINGS; i++) { +		struct intel_engine_cs *ring = &dev_priv->ring[i]; -	/* The only known way to stop the gpu from accessing the hw context is -	 * to reset it. Do this as the very last operation to avoid confusing -	 * other code, leading to spurious errors. */ -	intel_gpu_reset(dev); +		if (ring->last_context) +			i915_gem_context_unreference(ring->last_context); -	i915_gem_object_unpin(dctx->obj); +		ring->default_context = NULL; +		ring->last_context = NULL; +	} -	/* 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) +int i915_gem_context_enable(struct drm_i915_private *dev_priv)  { -	struct i915_hw_context *ctx = p; +	struct intel_engine_cs *ring; +	int ret, i; + +	/* This is the only place the aliasing PPGTT gets enabled, which means +	 * it has to happen before we bail on reset */ +	if (dev_priv->mm.aliasing_ppgtt) { +		struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; +		ppgtt->enable(ppgtt); +	} -	BUG_ON(id == DEFAULT_CONTEXT_ID); +	/* FIXME: We should make this work, even in reset */ +	if (i915_reset_in_progress(&dev_priv->gpu_error)) +		return 0; + +	BUG_ON(!dev_priv->ring[RCS].default_context); + +	for_each_ring(ring, dev_priv, i) { +		ret = i915_switch_context(ring, ring->default_context); +		if (ret) +			return ret; +	} + +	return 0; +} + +static int context_idr_cleanup(int id, void *p, void *data) +{ +	struct intel_context *ctx = p;  	i915_gem_context_unreference(ctx);  	return 0;  } -struct i915_ctx_hang_stats * -i915_gem_context_get_hang_stats(struct drm_device *dev, -				struct drm_file *file, -				u32 id) +int i915_gem_context_open(struct drm_device *dev, struct drm_file *file)  { -	struct drm_i915_private *dev_priv = dev->dev_private;  	struct drm_i915_file_private *file_priv = file->driver_priv; -	struct i915_hw_context *ctx; +	struct intel_context *ctx; -	if (id == DEFAULT_CONTEXT_ID) -		return &file_priv->hang_stats; +	idr_init(&file_priv->context_idr); -	ctx = NULL; -	if (!dev_priv->hw_contexts_disabled) -		ctx = i915_gem_context_get(file->driver_priv, id); -	if (ctx == NULL) -		return ERR_PTR(-ENOENT); +	mutex_lock(&dev->struct_mutex); +	ctx = i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev)); +	mutex_unlock(&dev->struct_mutex); -	return &ctx->hang_stats; +	if (IS_ERR(ctx)) { +		idr_destroy(&file_priv->context_idr); +		return PTR_ERR(ctx); +	} + +	return 0;  }  void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)  {  	struct drm_i915_file_private *file_priv = file->driver_priv; -	mutex_lock(&dev->struct_mutex);  	idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL);  	idr_destroy(&file_priv->context_idr); -	mutex_unlock(&dev->struct_mutex);  } -static struct i915_hw_context * +struct intel_context *  i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id)  { -	return (struct i915_hw_context *)idr_find(&file_priv->context_idr, id); +	struct intel_context *ctx; + +	ctx = (struct intel_context *)idr_find(&file_priv->context_idr, id); +	if (!ctx) +		return ERR_PTR(-ENOENT); + +	return ctx;  }  static inline int -mi_set_context(struct intel_ring_buffer *ring, -	       struct i915_hw_context *new_context, +mi_set_context(struct intel_engine_cs *ring, +	       struct intel_context *new_context,  	       u32 hw_flags)  {  	int ret; @@ -352,7 +552,7 @@ mi_set_context(struct intel_ring_buffer *ring,  	 * explicitly, so we rely on the value at ring init, stored in  	 * itlb_before_ctx_switch.  	 */ -	if (IS_GEN6(ring->dev) && ring->itlb_before_ctx_switch) { +	if (IS_GEN6(ring->dev)) {  		ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, 0);  		if (ret)  			return ret; @@ -362,8 +562,8 @@ mi_set_context(struct intel_ring_buffer *ring,  	if (ret)  		return ret; -	/* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw */ -	if (IS_GEN7(ring->dev)) +	/* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw,bdw,chv */ +	if (INTEL_INFO(ring->dev)->gen >= 7)  		intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE);  	else  		intel_ring_emit(ring, MI_NOOP); @@ -375,10 +575,13 @@ mi_set_context(struct intel_ring_buffer *ring,  			MI_SAVE_EXT_STATE_EN |  			MI_RESTORE_EXT_STATE_EN |  			hw_flags); -	/* w/a: MI_SET_CONTEXT must always be followed by MI_NOOP */ +	/* +	 * w/a: MI_SET_CONTEXT must always be followed by MI_NOOP +	 * WaMiSetContext_Hang:snb,ivb,vlv +	 */  	intel_ring_emit(ring, MI_NOOP); -	if (IS_GEN7(ring->dev)) +	if (INTEL_INFO(ring->dev)->gen >= 7)  		intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_ENABLE);  	else  		intel_ring_emit(ring, MI_NOOP); @@ -388,45 +591,86 @@ mi_set_context(struct intel_ring_buffer *ring,  	return ret;  } -static int do_switch(struct i915_hw_context *to) +static int do_switch(struct intel_engine_cs *ring, +		     struct intel_context *to)  { -	struct intel_ring_buffer *ring = to->ring; -	struct i915_hw_context *from = ring->last_context; +	struct drm_i915_private *dev_priv = ring->dev->dev_private; +	struct intel_context *from = ring->last_context; +	struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(to);  	u32 hw_flags = 0; -	int ret; +	bool uninitialized = false; +	int ret, i; -	BUG_ON(from != NULL && from->obj != NULL && from->obj->pin_count == 0); +	if (from != NULL && ring == &dev_priv->ring[RCS]) { +		BUG_ON(from->obj == NULL); +		BUG_ON(!i915_gem_obj_is_pinned(from->obj)); +	} -	if (from == to) +	if (from == to && from->last_ring == ring && !to->remap_slice)  		return 0; -	ret = i915_gem_obj_ggtt_pin(to->obj, CONTEXT_ALIGN, false, false); -	if (ret) -		return ret; +	/* Trying to pin first makes error handling easier. */ +	if (ring == &dev_priv->ring[RCS]) { +		ret = i915_gem_obj_ggtt_pin(to->obj, +					    get_context_alignment(ring->dev), 0); +		if (ret) +			return ret; +	} + +	/* +	 * Pin can switch back to the default context if we end up calling into +	 * evict_everything - as a last ditch gtt defrag effort that also +	 * switches to the default context. Hence we need to reload from here. +	 */ +	from = ring->last_context; + +	if (USES_FULL_PPGTT(ring->dev)) { +		ret = ppgtt->switch_mm(ppgtt, ring, false); +		if (ret) +			goto unpin_out; +	} + +	if (ring != &dev_priv->ring[RCS]) { +		if (from) +			i915_gem_context_unreference(from); +		goto done; +	} -	/* Clear this page out of any CPU caches for coherent swap-in/out. Note +	/* +	 * Clear this page out of any CPU caches for coherent swap-in/out. Note  	 * that thanks to write = false in this call and us not setting any gpu  	 * write domains when putting a context object onto the active list  	 * (when switching away from it), this won't block. -	 * XXX: We need a real interface to do this instead of trickery. */ +	 * +	 * XXX: We need a real interface to do this instead of trickery. +	 */  	ret = i915_gem_object_set_to_gtt_domain(to->obj, false); -	if (ret) { -		i915_gem_object_unpin(to->obj); -		return ret; -	} +	if (ret) +		goto unpin_out; -	if (!to->obj->has_global_gtt_mapping) -		i915_gem_gtt_bind_object(to->obj, to->obj->cache_level); +	if (!to->obj->has_global_gtt_mapping) { +		struct i915_vma *vma = i915_gem_obj_to_vma(to->obj, +							   &dev_priv->gtt.base); +		vma->bind_vma(vma, to->obj->cache_level, GLOBAL_BIND); +	} -	if (!to->is_initialized || is_default_context(to)) +	if (!to->is_initialized || i915_gem_context_is_default(to))  		hw_flags |= MI_RESTORE_INHIBIT; -	else if (WARN_ON_ONCE(from == to)) /* not yet expected */ -		hw_flags |= MI_FORCE_RESTORE;  	ret = mi_set_context(ring, to, hw_flags); -	if (ret) { -		i915_gem_object_unpin(to->obj); -		return ret; +	if (ret) +		goto unpin_out; + +	for (i = 0; i < MAX_L3_SLICES; i++) { +		if (!(to->remap_slice & (1<<i))) +			continue; + +		ret = i915_gem_l3_remap(ring, i); +		/* If it failed, try again next round */ +		if (ret) +			DRM_DEBUG_DRIVER("L3 remapping failed\n"); +		else +			to->remap_slice &= ~(1<<i);  	}  	/* The backing object for the context is done after switching to the @@ -436,11 +680,8 @@ static int do_switch(struct i915_hw_context *to)  	 * MI_SET_CONTEXT instead of when the next seqno has completed.  	 */  	if (from != NULL) { -		struct drm_i915_private *dev_priv = from->obj->base.dev->dev_private; -		struct i915_address_space *ggtt = &dev_priv->gtt.base;  		from->obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; -		list_move_tail(&i915_gem_obj_to_vma(from->obj, ggtt)->mm_list, &ggtt->active_list); -		i915_gem_object_move_to_active(from->obj, ring); +		i915_vma_move_to_active(i915_gem_obj_to_ggtt(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 @@ -451,90 +692,84 @@ static int do_switch(struct i915_hw_context *to)  		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; -		} - -		i915_gem_object_unpin(from->obj); +		/* obj is kept alive until the next request by its active ref */ +		i915_gem_object_ggtt_unpin(from->obj);  		i915_gem_context_unreference(from);  	} +	uninitialized = !to->is_initialized && from == NULL; +	to->is_initialized = true; + +done:  	i915_gem_context_reference(to);  	ring->last_context = to; -	to->is_initialized = true; +	to->last_ring = ring; + +	if (uninitialized) { +		ret = i915_gem_render_state_init(ring); +		if (ret) +			DRM_ERROR("init render state: %d\n", ret); +	}  	return 0; + +unpin_out: +	if (ring->id == RCS) +		i915_gem_object_ggtt_unpin(to->obj); +	return ret;  }  /**   * i915_switch_context() - perform a GPU context switch.   * @ring: ring for which we'll execute the context switch - * @file_priv: file_priv associated with the context, may be NULL - * @id: context id number - * @seqno: sequence number by which the new context will be switched to - * @flags: + * @to: the context to switch to   *   * The context life cycle is simple. The context refcount is incremented and   * decremented by 1 and create and destroy. If the context is in use by the GPU,   * it will have a refoucnt > 1. This allows us to destroy the context abstract   * object while letting the normal object tracking destroy the backing BO.   */ -int i915_switch_context(struct intel_ring_buffer *ring, -			struct drm_file *file, -			int to_id) +int i915_switch_context(struct intel_engine_cs *ring, +			struct intel_context *to)  {  	struct drm_i915_private *dev_priv = ring->dev->dev_private; -	struct i915_hw_context *to; - -	if (dev_priv->hw_contexts_disabled) -		return 0;  	WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); -	if (ring != &dev_priv->ring[RCS]) +	if (to->obj == NULL) { /* We have the fake context */ +		if (to != ring->last_context) { +			i915_gem_context_reference(to); +			if (ring->last_context) +				i915_gem_context_unreference(ring->last_context); +			ring->last_context = to; +		}  		return 0; - -	if (to_id == DEFAULT_CONTEXT_ID) { -		to = ring->default_context; -	} else { -		if (file == NULL) -			return -EINVAL; - -		to = i915_gem_context_get(file->driver_priv, to_id); -		if (to == NULL) -			return -ENOENT;  	} -	return do_switch(to); +	return do_switch(ring, to); +} + +static bool hw_context_enabled(struct drm_device *dev) +{ +	return to_i915(dev)->hw_context_size;  }  int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,  				  struct drm_file *file)  { -	struct drm_i915_private *dev_priv = dev->dev_private;  	struct drm_i915_gem_context_create *args = data;  	struct drm_i915_file_private *file_priv = file->driver_priv; -	struct i915_hw_context *ctx; +	struct intel_context *ctx;  	int ret; -	if (!(dev->driver->driver_features & DRIVER_GEM)) -		return -ENODEV; - -	if (dev_priv->hw_contexts_disabled) +	if (!hw_context_enabled(dev))  		return -ENODEV;  	ret = i915_mutex_lock_interruptible(dev);  	if (ret)  		return ret; -	ctx = create_hw_context(dev, file_priv); +	ctx = i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));  	mutex_unlock(&dev->struct_mutex);  	if (IS_ERR(ctx))  		return PTR_ERR(ctx); @@ -550,20 +785,20 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,  {  	struct drm_i915_gem_context_destroy *args = data;  	struct drm_i915_file_private *file_priv = file->driver_priv; -	struct i915_hw_context *ctx; +	struct intel_context *ctx;  	int ret; -	if (!(dev->driver->driver_features & DRIVER_GEM)) -		return -ENODEV; +	if (args->ctx_id == DEFAULT_CONTEXT_ID) +		return -ENOENT;  	ret = i915_mutex_lock_interruptible(dev);  	if (ret)  		return ret;  	ctx = i915_gem_context_get(file_priv, args->ctx_id); -	if (!ctx) { +	if (IS_ERR(ctx)) {  		mutex_unlock(&dev->struct_mutex); -		return -ENOENT; +		return PTR_ERR(ctx);  	}  	idr_remove(&ctx->file_priv->context_idr, ctx->id);  | 
