[Intel-gfx] [RFC] [PATCH 3/8] drm/i915/context: implement load and unload

Ben Widawsky bwidawsk at gmail.com
Sat Feb 26 19:30:13 CET 2011


Loading of the context submodule context, which is called from the main
i915 driver, must create and initialize the default context for the
device. The default context is used for clients which do not set up
contexts (legacy), and as a known good state for the hardware, because
once contexts are enabled, you can't disable them. Doing this at
initialization allows later clients to determine whether or not context
support exist before actually trying to create or run them (if there was
an error at load, contexts will be disabled).

Unloading the context is much easier. The ref counts, and pin counts are
taken care of by the rest of the driver, so all this code needs to do is
free the kernel memory associated with the default context.

This commit doesn't exercise the hardware's context switch yet, but does
everything else needing to create and destroy the default context, including:
	context id creation
	context id destruction
	context allocation
	context initialization (currently a nop)
---
 drivers/gpu/drm/i915/i915_context.c     |  189 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/i915_drv.h         |    6 +-
 drivers/gpu/drm/i915/intel_ringbuffer.h |    6 +
 include/drm/i915_drm.h                  |    6 +-
 4 files changed, 205 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_context.c b/drivers/gpu/drm/i915/i915_context.c
index bae7274..490aa5a 100644
--- a/drivers/gpu/drm/i915/i915_context.c
+++ b/drivers/gpu/drm/i915/i915_context.c
@@ -26,7 +26,163 @@
 
 #include "drmP.h"
 #include "i915_drm.h"
+#include "i915_drv.h"
 
+#define DEFAULT_CONTEXT_ID 0
+
+#define CONTEXT_SIZE dev_priv->context_size
+#define CONTEXT_ALIGN 4096
+
+static int context_generate_id(struct drm_i915_gem_context *ctx)
+{
+	struct drm_i915_file_private *file_priv = ctx->file->driver_priv;
+	int ret, id;
+
+	if (WARN_ON(!mutex_is_locked(&ctx->dev->struct_mutex)))
+		return -ENOENT;
+
+again:
+	if (idr_pre_get(&file_priv->context_idr, GFP_KERNEL) == 0)
+		return -ENOMEM;
+
+	spin_lock(&file_priv->context_idr_lock);
+	ret = idr_get_new_above(&file_priv->context_idr, ctx,
+				DEFAULT_CONTEXT_ID + 1, &id);
+	if (ret == -EAGAIN) {
+		spin_unlock(&file_priv->context_idr_lock);
+		goto again;
+	}
+	spin_unlock(&file_priv->context_idr_lock);
+
+	return id;
+}
+
+static void context_destroy_id(struct drm_i915_gem_context *ctx)
+{
+	struct drm_i915_file_private *file_priv = ctx->file->driver_priv;
+
+	spin_lock(&file_priv->context_idr_lock);
+	idr_remove(&file_priv->context_idr, ctx->id);
+	spin_unlock(&file_priv->context_idr_lock);
+}
+
+/*
+ * Initialize a context for the given ring.
+ * @wait_for_switch: whether or not to wait for the context switch instruction
+ * to actually execute.
+ */
+static int context_init(struct drm_i915_gem_context *ctx,
+			struct intel_ring_buffer *ring,
+			bool wait_for_switch)
+{
+	struct drm_i915_private *dev_priv = ctx->dev->dev_private;
+	struct drm_i915_gem_context *last;
+	int ret;
+
+	if (ring->context_switch == NULL)
+		return 0;
+
+	return -ENOMEM;
+}
+
+/*
+ * Logical context is created, and initialized. The context has a backing buffer
+ * object which is referenced at the end of this function.
+ *
+ * @ring_mask: rings for which this context should be initialized (1 means don't
+ *	init)
+ * @ctx_out: output context which was created
+ *
+ * XXX this function or a wrapper to it needs to be global for power related
+ * stuff
+ */
+static int logical_context_alloc(struct drm_device *dev, struct drm_file *file,
+				 uint8_t ring_mask,
+				 struct drm_i915_gem_context **ctx_out)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_i915_gem_context *ctx;
+	int ret, i;
+#define RING_MASK (uint8_t)((1 << I915_NUM_RINGS) - 1)
+	uint8_t pass = 0;
+
+	if (WARN_ON(!mutex_is_locked(&dev->struct_mutex)))
+		return -EINVAL;
+
+	ctx = kzalloc(sizeof(struct drm_i915_gem_context), GFP_KERNEL);
+	if (ctx == NULL)
+		return -ENOMEM;
+
+	ctx->dev = dev;
+	ctx->file = file;
+
+	if (dev_priv->default_context == *ctx_out) {
+		ctx->is_default = true;
+		ctx->id = DEFAULT_CONTEXT_ID;
+	} else
+		ctx->id = context_generate_id(ctx);
+
+
+	ctx->obj = i915_gem_alloc_object(dev, CONTEXT_SIZE);
+	if (!ctx->obj) {
+		ret = -ENOMEM;
+		goto id_out;
+	}
+
+	ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false);
+	if (ret) {
+		DRM_ERROR("Failed to pin context: %d\n", ret);
+		goto err_unref;
+	}
+
+	/* XXX do we need this, we can get rid of pinning above if not? */
+	ret = i915_gem_object_set_to_gtt_domain(ctx->obj, false);
+	if (ret) {
+		DRM_ERROR("failed to set domain on context: %d", ret);
+		goto err_unpin;
+	}
+
+	i915_gem_object_unpin(ctx->obj);
+
+	for (i = 0; i < I915_NUM_RINGS; i++) {
+		if (ring_mask & (1 << i))
+			continue;
+
+		ret = context_init(ctx, &dev_priv->ring[i], true);
+		if (!ret)
+			pass |= (1 << i);
+	}
+
+	if ((ring_mask ^ pass) != RING_MASK) {
+		DRM_DEBUG_DRIVER("ring(s) couldn't initialize context "
+				 "mask = %x, passed = %x\n", ring_mask, pass);
+		if (!pass) {
+			DRM_ERROR("Couldn't initialize context for any ring");
+			ret = -EIO;
+			goto err_unref;
+		}
+	}
+
+	ctx->ring_enable = pass;
+
+	DRM_DEBUG_DRIVER("Context %d allocated, rings %x\n", ctx->id, pass);
+	*ctx_out = ctx;
+	return 0;
+
+err_unpin:
+	i915_gem_object_unpin(ctx->obj);
+err_unref:
+	drm_gem_object_unreference(&ctx->obj->base);
+id_out:
+	if (!ctx->is_default)
+		context_destroy_id(ctx);
+	kfree(ctx);
+	return ret;
+}
+
+/**
+ * i915_context_create_ioctl() - not yet supported
+ */
 int i915_context_create_ioctl(struct drm_device *dev, void *data,
 			      struct drm_file *file)
 {
@@ -35,6 +191,9 @@ int i915_context_create_ioctl(struct drm_device *dev, void *data,
 	return -EIO;
 }
 
+/**
+ * i915_context_destroy_ioctl() - not yet supported
+ */
 int i915_context_destroy_ioctl(struct drm_device *dev, void *data,
 			       struct drm_file *file)
 {
@@ -42,14 +201,44 @@ int i915_context_destroy_ioctl(struct drm_device *dev, void *data,
 	return -EINVAL;
 }
 
+/**
+ * i915_context_load() - Creates a default context.
+ * @dev: Device for which the context.
+ */
 void i915_context_load(struct drm_device *dev)
 {
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	uint32_t size;
+
+	int ret;
+	if (!HAS_HW_CONTEXTS(dev)) {
+		dev_priv->contexts_disabled = true;
+		return;
+	}
 
+	mutex_lock(&dev->struct_mutex);
+	size = I915_READ(CXT_SIZE);
+	if (size > 0x4000) /*  1MB */ {
+		DRM_ERROR("Context bo size seems invalid.");
+		size = 20;
+	}
+	size *= 64;
+	size = roundup(size, 4096);
+	dev_priv->context_size = size;
+	DRM_DEBUG_DRIVER("Logical context size = %d\n", size);
+	ret = logical_context_alloc(dev, NULL, 0, &dev_priv->default_context);
+	if (ret)
+		dev_priv->contexts_disabled = true;
+	mutex_unlock(&dev->struct_mutex);
 }
 
 void i915_context_unload(struct drm_device *dev)
 {
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	if (dev_priv->contexts_disabled)
+		return;
 
+	kfree(dev_priv->default_context);
 }
 
 void i915_context_open(struct drm_device *dev, struct drm_file *file)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 3bd9875..15f86fb 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -709,6 +709,10 @@ typedef struct drm_i915_private {
 	struct intel_fbdev *fbdev;
 
 	struct drm_property *broadcast_rgb_property;
+
+	uint32_t context_size;
+	struct drm_i915_gem_context *default_context;
+	bool contexts_disabled;
 } drm_i915_private_t;
 
 struct drm_i915_gem_object {
@@ -902,8 +906,8 @@ struct drm_i915_gem_context {
 	struct drm_file *file;
 
 	uint32_t id;
-	struct drm_gem_object *obj;
 	uint8_t ring_enable;
+	struct drm_i915_gem_object *obj;
 	bool is_default;
 
 	struct drm_gem_object **bufs;
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index bd6a5fb..a4e707c 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -77,6 +77,12 @@ struct  intel_ring_buffer {
 	int		(*dispatch_execbuffer)(struct intel_ring_buffer *ring,
 					       u32 offset, u32 length);
 	void		(*cleanup)(struct intel_ring_buffer *ring);
+	struct		drm_i915_gem_context *last_context;
+	struct drm_i915_gem_context *(*context_switch)
+				      (struct intel_ring_buffer *ring,
+				       struct drm_i915_gem_context *ctx,
+				       u32 segno,
+				       u32 flags);
 
 	/**
 	 * List of objects currently involved in rendering from the
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index 28e3c5f..49df333 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -851,8 +851,12 @@ struct drm_intel_overlay_attrs {
 struct drm_i915_gem_context_create {
 	/* input: number of slots to allocate */
 	__s32 slot_count;
-	/* output: id of new context*/
+	/* input: desired ring mask */
+	__u8 ring_mask;
+	/* output: id of new context */
 	__u32 ctx_id;
+	/* output: rings for which context is valid */
+	__u8 ring_enable;
 };
 
 struct drm_i915_gem_context_destroy {
-- 
1.7.3.4




More information about the Intel-gfx mailing list