@@ -33,6 +33,8 @@
#define CONTEXT_SIZE dev_priv->context_size
#define CONTEXT_ALIGN 4096
+static int context_idr_cleanup(int id, void *p, void *data);
+
static int context_generate_id(struct drm_i915_gem_context *ctx)
{
struct drm_i915_file_private *file_priv = ctx->file->driver_priv;
@@ -268,7 +270,50 @@ id_out:
static void logical_context_fini(struct drm_i915_gem_context *ctx)
{
+ struct drm_device *dev = ctx->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_context *last;
+ int i;
+ int ret = 0;
+
+ if (WARN_ON(!mutex_is_locked(&dev->struct_mutex)))
+ return;
+
+ if (ctx == dev_priv->default_context)
+ return;
+
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ struct intel_ring_buffer *ring = &dev_priv->ring[i];
+ if (!ring->context_switch)
+ continue;
+
+ if (!(ctx->ring_enable & (1 << i)))
+ continue;
+
+ if (ring->last_context != ctx)
+ continue;
+ /*
+ * XXX We can prevent restoring contexts, but not saving them
+ * so if we're going to take away our backing context object
+ * of the last context, we have to switch now.
+ */
+
+ DRM_DEBUG_DRIVER("Switching to default context\n");
+ last = ring->context_switch(ring, dev_priv->default_context,
+ 0, I915_CONTEXT_NORMAL_SWITCH);
+ if (!last) {
+ DRM_ERROR("Couldn't switch back to default context\n");
+ continue;
+ }
+
+ BUG_ON(last != ctx);
+ ret = wait_for_context_switch(ring);
+ if (ret)
+ DRM_ERROR("Wait for default context switch failed "
+ "ring = %d (%d)", i, ret);
+ i915_release_context(last);
+ }
}
static int logical_context_free(struct drm_file *file, uint32_t id)
@@ -304,24 +349,40 @@ static int logical_context_free(struct drm_file *file, uint32_t id)
}
/**
- * i915_context_create_ioctl() - not yet supported
+ * i915_context_create_ioctl()
*/
int i915_context_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_i915_gem_context_create *args = data;
- args->ctx_id = 0;
- return -EIO;
+ struct drm_i915_gem_context *out_ctx = NULL;
+ int ret = 0;
+
+ mutex_lock(&dev->struct_mutex);
+ ret = logical_context_alloc(dev, file, args->ring_mask, &out_ctx);
+ mutex_unlock(&dev->struct_mutex);
+ if (ret == 0) {
+ args->ctx_id = out_ctx->id;
+ if (WARN_ON(args->ctx_id == DEFAULT_CONTEXT_ID))
+ return -ENXIO;
+ args->ring_enable = out_ctx->ring_enable;
+ }
+
+ return ret;
}
/**
- * i915_context_destroy_ioctl() - not yet supported
+ * i915_context_destroy_ioctl()
*/
int i915_context_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_i915_gem_context_destroy *args = data;
- return -EINVAL;
+
+ if (args->ctx_id == DEFAULT_CONTEXT_ID)
+ return -EINVAL;
+
+ return logical_context_free(file, args->ctx_id);
}
/**
@@ -370,18 +431,55 @@ void i915_context_unload(struct drm_device *dev)
void i915_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->contexts_disabled)
+ return;
+ idr_init(&file_priv->context_idr);
+ spin_lock_init(&file_priv->context_idr_lock);
}
void i915_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->contexts_disabled)
+ return;
+ idr_for_each(&file_priv->context_idr, context_idr_cleanup, file);
+ idr_destroy(&file_priv->context_idr);
+}
+
+static int context_idr_cleanup(int id, void *p, void *data)
+{
+ struct drm_file *file = (struct drm_file *) data;
+ logical_context_free(file, id);
+ return 0;
}
struct drm_i915_gem_context *i915_get_context(struct drm_file *file,
uint32_t id)
{
- return NULL;
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+ struct drm_i915_gem_context *ctx = NULL;
+ int ret;
+
+ spin_lock(&file_priv->context_idr_lock);
+ ctx = idr_find(&file_priv->context_idr, id);
+ if (ctx) {
+ drm_gem_object_reference(&ctx->obj->base);
+ ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false);
+ if (ret) {
+ drm_gem_object_unreference(&ctx->obj->base);
+ ctx = NULL;
+ }
+ }
+ spin_unlock(&file_priv->context_idr_lock);
+
+ return ctx;
}
void i915_release_context(struct drm_i915_gem_context *ctx)