[PATCH 03/25 v2] drm/i915: context basic create & destroy

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

 



TODO from irc: change create_hw_context to return the pointer and use
PTR_ERR as appropriate.

On Mon,  4 Jun 2012 14:42:43 -0700
Ben Widawsky <ben at bwidawsk.net> wrote:

> Invent an abstraction for a hw context which is passed around through
> the core functions. The main bit a hw context holds is the buffer object
> which backs the context. The rest of the members are just helper
> functions. Specifically the ring member, which could likely go away if
> we decide to never implement whatever other hw context support exists.
> 
> Of note here is the introduction of the 64k alignment constraint for the
> BO. If contexts become heavily used, we should consider tweaking this
> down to 4k. Until the contexts are merged and tested a bit though, I
> think 64k is a nice start (based on docs).
> 
> Since we don't yet switch contexts, there is really not much complexity
> here. Creation/destruction works pretty much as one would expect. An idr
> is used to generate the context id numbers which are unique per file
> descriptor.
> 
> v2: add DRM_DEBUG_DRIVERS to distinguish ENOMEM failures (ben)
> convert a BUG_ON to WARN_ON, default destruction is still fatal (ben)
> 
> Signed-off-by: Ben Widawsky <ben at bwidawsk.net>
> ---
>  drivers/gpu/drm/i915/i915_drv.h         |   11 +++
>  drivers/gpu/drm/i915/i915_gem_context.c |  142 ++++++++++++++++++++++++++++++-
>  drivers/gpu/drm/i915/intel_ringbuffer.h |    2 +
>  3 files changed, 153 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 432b44f..f543679 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -308,6 +308,16 @@ struct i915_hw_ppgtt {
>  	dma_addr_t scratch_page_dma_addr;
>  };
>  
> +
> +/* This must match up with the value previously used for execbuf2.rsvd1. */
> +#define DEFAULT_CONTEXT_ID 0
> +struct i915_hw_context {
> +	int id;
> +	struct drm_i915_file_private *file_priv;
> +	struct intel_ring_buffer *ring;
> +	struct drm_i915_gem_object *obj;
> +};
> +
>  enum no_fbc_reason {
>  	FBC_NO_OUTPUT, /* no outputs enabled to compress */
>  	FBC_STOLEN_TOO_SMALL, /* not enough space to hold compressed buffers */
> @@ -1023,6 +1033,7 @@ struct drm_i915_file_private {
>  		struct spinlock lock;
>  		struct list_head request_list;
>  	} mm;
> +	struct idr context_idr;
>  };
>  
>  #define INTEL_INFO(dev)	(((struct drm_i915_private *) (dev)->dev_private)->info)
> diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
> index e39808e..2aca002 100644
> --- a/drivers/gpu/drm/i915/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/i915_gem_context.c
> @@ -89,6 +89,15 @@
>  #include "i915_drm.h"
>  #include "i915_drv.h"
>  
> +/* This is a HW constraint. The value below is the largest known requirement
> + * 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)
> +
> +static struct i915_hw_context *
> +i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id);
> +
>  static int get_context_size(struct drm_device *dev)
>  {
>  	struct drm_i915_private *dev_priv = dev->dev_private;
> @@ -111,6 +120,76 @@ static int get_context_size(struct drm_device *dev)
>  	return ret;
>  }
>  
> +static void do_destroy(struct i915_hw_context *ctx)
> +{
> +	struct drm_device *dev = ctx->obj->base.dev;
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +
> +	if (ctx->file_priv)
> +		idr_remove(&ctx->file_priv->context_idr, ctx->id);
> +	else
> +		BUG_ON(ctx != dev_priv->ring[RCS].default_context);
> +
> +	drm_gem_object_unreference(&ctx->obj->base);
> +	kfree(ctx);
> +}
> +
> +static int
> +create_hw_context(struct drm_device *dev,
> +		  struct drm_i915_file_private *file_priv,
> +		  struct i915_hw_context **ctx_out)
> +{
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	int ret, id;
> +
> +	*ctx_out = kzalloc(sizeof(struct drm_i915_file_private), GFP_KERNEL);
> +	if (*ctx_out == NULL)
> +		return -ENOMEM;
> +
> +	(*ctx_out)->obj = i915_gem_alloc_object(dev,
> +						dev_priv->hw_context_size);
> +	if ((*ctx_out)->obj == NULL) {
> +		kfree(*ctx_out);
> +		DRM_DEBUG_DRIVER("Context object allocated failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* 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_out)->ring = &dev_priv->ring[RCS];
> +
> +	/* Default context will never have a file_priv */
> +	if (file_priv == NULL)
> +		return 0;
> +
> +	(*ctx_out)->file_priv = file_priv;
> +
> +again:
> +	if (idr_pre_get(&file_priv->context_idr, GFP_KERNEL) == 0) {
> +		ret = -ENOMEM;
> +		DRM_DEBUG_DRIVER("idr allocation failed\n");
> +		goto err_out;
> +	}
> +
> +	ret = idr_get_new_above(&file_priv->context_idr, *ctx_out,
> +				DEFAULT_CONTEXT_ID + 1, &id);
> +	if (ret == 0)
> +		(*ctx_out)->id = id;
> +
> +	if (ret == -EAGAIN)
> +		goto again;
> +	else if (ret)
> +		goto err_out;
> +
> +	return 0;
> +
> +err_out:
> +	do_destroy(*ctx_out);
> +	return ret;
> +}
> +
>  /**
>   * 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
> @@ -118,7 +197,30 @@ static int get_context_size(struct drm_device *dev)
>   */
>  static int create_default_context(struct drm_i915_private *dev_priv)
>  {
> -	return 0;
> +	struct i915_hw_context *ctx;
> +	int ret;
> +
> +	BUG_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
> +
> +	ret = create_hw_context(dev_priv->dev, NULL,
> +				&dev_priv->ring[RCS].default_context);
> +	if (ret)
> +		return ret;
> +
> +	/* 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.
> +	 */
> +	ctx = dev_priv->ring[RCS].default_context;
> +	ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false);
> +	if (ret) {
> +		do_destroy(ctx);
> +		return ret;
> +	}
> +
> +	return ret;
>  }
>  
>  void i915_gem_context_init(struct drm_device *dev)
> @@ -130,7 +232,8 @@ void i915_gem_context_init(struct drm_device *dev)
>  		return;
>  
>  	/* If called from reset, or thaw... we've been here already */
> -	if (dev_priv->hw_contexts_disabled)
> +	if (dev_priv->hw_contexts_disabled ||
> +	    dev_priv->ring[RCS].default_context)
>  		return;
>  
>  	ctx_size = get_context_size(dev);
> @@ -156,20 +259,55 @@ void i915_gem_context_fini(struct drm_device *dev)
>  
>  	if (dev_priv->hw_contexts_disabled)
>  		return;
> +
> +	i915_gem_object_unpin(dev_priv->ring[RCS].default_context->obj);
> +
> +	do_destroy(dev_priv->ring[RCS].default_context);
>  }
>  
>  void 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;
>  
>  	if (dev_priv->hw_contexts_disabled)
>  		return;
> +
> +	idr_init(&file_priv->context_idr);
> +}
> +
> +static int context_idr_cleanup(int id, void *p, void *data)
> +{
> +	struct drm_file *file = (struct drm_file *)data;
> +	struct drm_i915_file_private *file_priv = file->driver_priv;
> +	struct i915_hw_context *ctx;
> +
> +	BUG_ON(id == DEFAULT_CONTEXT_ID);
> +	ctx = i915_gem_context_get(file_priv, id);
> +	if (WARN_ON(ctx == NULL))
> +		return -ENXIO;
> +
> +	do_destroy(ctx);
> +
> +	return 0;
>  }
>  
>  void i915_gem_context_close(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;
>  
>  	if (dev_priv->hw_contexts_disabled)
>  		return;
> +
> +	mutex_lock(&dev->struct_mutex);
> +	idr_for_each(&file_priv->context_idr, context_idr_cleanup, file);
> +	idr_destroy(&file_priv->context_idr);
> +	mutex_unlock(&dev->struct_mutex);
> +}
> +
> +static __used struct i915_hw_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);
>  }
> diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
> index 55d3da2..bb19bec 100644
> --- a/drivers/gpu/drm/i915/intel_ringbuffer.h
> +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
> @@ -116,6 +116,8 @@ struct  intel_ring_buffer {
>  
>  	wait_queue_head_t irq_queue;
>  
> +	struct i915_hw_context *default_context;
> +
>  	void *private;
>  };
>  



-- 
Ben Widawsky, Intel Open Source Technology Center


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