drm/i915: Remove dead code
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / i915 / i915_gem_context.c
index 7a5311c3225af1eb5ba7144ccd25787c9cfe9816..da74522f377da13e339352ddd3d3c400265d04aa 100644 (file)
@@ -96,8 +96,6 @@
 #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 intel_ring_buffer *ring,
                     struct i915_hw_context *to);
 
@@ -141,14 +139,43 @@ void i915_gem_context_free(struct kref *ctx_ref)
 {
        struct i915_hw_context *ctx = container_of(ctx_ref,
                                                   typeof(*ctx), ref);
+       struct i915_hw_ppgtt *ppgtt = NULL;
 
-       list_del(&ctx->link);
+       /* 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_ppgtt *
+create_vm_for_ctx(struct drm_device *dev, struct i915_hw_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);
+       }
+
+       return ppgtt;
+}
+
 static struct i915_hw_context *
-create_hw_context(struct drm_device *dev,
+__create_hw_context(struct drm_device *dev,
                  struct drm_i915_file_private *file_priv)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -176,18 +203,13 @@ create_hw_context(struct drm_device *dev,
                        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];
        list_add_tail(&ctx->link, &dev_priv->context_list);
 
        /* 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,
+       ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID, 0,
                        GFP_KERNEL);
        if (ret < 0)
                goto err_out;
@@ -206,70 +228,136 @@ 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_device *dev)
+static struct i915_hw_context *
+i915_gem_create_context(struct drm_device *dev,
+                       struct drm_i915_file_private *file_priv,
+                       bool create_vm)
 {
+       const bool is_global_default_ctx = file_priv == NULL;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct i915_hw_context *ctx;
-       int ret;
+       int ret = 0;
 
        BUG_ON(!mutex_is_locked(&dev->struct_mutex));
 
-       ctx = create_hw_context(dev, NULL);
+       ctx = __create_hw_context(dev, file_priv);
        if (IS_ERR(ctx))
-               return PTR_ERR(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.
-        */
-       ret = i915_gem_obj_ggtt_pin(ctx->obj, get_context_alignment(dev),
-                                   false, false);
-       if (ret) {
-               DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret);
-               goto err_destroy;
-       }
+               return ctx;
 
-       ret = do_switch(&dev_priv->ring[RCS], ctx);
-       if (ret) {
-               DRM_DEBUG_DRIVER("Switch failed %d\n", ret);
-               goto err_unpin;
+       if (is_global_default_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.
+                */
+               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;
+               }
        }
 
-       dev_priv->ring[RCS].default_context = ctx;
+       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_ggtt_unpin(ctx->obj);
+       if (is_global_default_ctx)
+               i915_gem_object_ggtt_unpin(ctx->obj);
 err_destroy:
        i915_gem_context_unreference(ctx);
-       return ret;
+       return ERR_PTR(ret);
+}
+
+void i915_gem_context_reset(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
+       int i;
+
+       if (!HAS_HW_CONTEXTS(dev))
+               return;
+
+       /* Prevent the hardware from restoring the last context (which hung) on
+        * the next switch */
+       for (i = 0; i < I915_NUM_RINGS; i++) {
+               struct i915_hw_context *dctx;
+               if (!(INTEL_INFO(dev)->ring_mask & (1<<i)))
+                       continue;
+
+               /* Do a fake switch to the default context */
+               ring = &dev_priv->ring[i];
+               dctx = ring->default_context;
+               if (WARN_ON(!dctx))
+                       continue;
+
+               if (!ring->last_context)
+                       continue;
+
+               if (ring->last_context == dctx)
+                       continue;
+
+               if (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;
+               }
+
+               i915_gem_context_unreference(ring->last_context);
+               i915_gem_context_reference(dctx);
+               ring->last_context = dctx;
+       }
 }
 
 int i915_gem_context_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_ring_buffer *ring;
-       int i, ret;
+       int i;
 
        if (!HAS_HW_CONTEXTS(dev))
                return 0;
 
-       /* If called from reset, or thaw... we've been here already */
-       if (dev_priv->ring[RCS].default_context)
+       /* 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;
 
        dev_priv->hw_context_size = round_up(get_context_size(dev), 4096);
@@ -279,11 +367,13 @@ int i915_gem_context_init(struct drm_device *dev)
                return -E2BIG;
        }
 
-       ret = create_default_context(dev);
-       if (ret) {
-               DRM_DEBUG_DRIVER("Disabling HW Contexts; create failed %d\n",
-                                ret);
-               return ret;
+       dev_priv->ring[RCS].default_context =
+               i915_gem_create_context(dev, NULL, USES_PPGTT(dev));
+
+       if (IS_ERR_OR_NULL(dev_priv->ring[RCS].default_context)) {
+               DRM_DEBUG_DRIVER("Disabling HW Contexts; create failed %ld\n",
+                                PTR_ERR(dev_priv->ring[RCS].default_context));
+               return PTR_ERR(dev_priv->ring[RCS].default_context);
        }
 
        for (i = RCS + 1; i < I915_NUM_RINGS; i++) {
@@ -338,52 +428,85 @@ void i915_gem_context_fini(struct drm_device *dev)
                        i915_gem_context_unreference(ring->last_context);
 
                ring->default_context = NULL;
+               ring->last_context = NULL;
        }
 
        i915_gem_object_ggtt_unpin(dctx->obj);
        i915_gem_context_unreference(dctx);
+       dev_priv->mm.aliasing_ppgtt = NULL;
 }
 
-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_ring_buffer *ring;
+       int ret, i;
 
-       BUG_ON(id == DEFAULT_CONTEXT_ID);
+       if (!HAS_HW_CONTEXTS(dev_priv->dev))
+               return 0;
+
+       /* 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);
+       }
+
+       /* 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 = do_switch(ring, ring->default_context);
+               if (ret)
+                       return ret;
+       }
 
-       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)
+static int context_idr_cleanup(int id, void *p, void *data)
 {
-       struct drm_i915_file_private *file_priv = file->driver_priv;
-       struct i915_hw_context *ctx;
-
-       if (id == DEFAULT_CONTEXT_ID)
-               return &file_priv->hang_stats;
-
-       if (!HAS_HW_CONTEXTS(dev))
-               return ERR_PTR(-ENOENT);
+       struct i915_hw_context *ctx = p;
 
-       ctx = i915_gem_context_get(file->driver_priv, id);
-       if (ctx == NULL)
-               return ERR_PTR(-ENOENT);
+       /* Ignore the default context because close will handle it */
+       if (i915_gem_context_is_default(ctx))
+               return 0;
 
-       return &ctx->hang_stats;
+       i915_gem_context_unreference(ctx);
+       return 0;
 }
 
 int i915_gem_context_open(struct drm_device *dev, struct drm_file *file)
 {
        struct drm_i915_file_private *file_priv = file->driver_priv;
+       struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (!HAS_HW_CONTEXTS(dev))
+       if (!HAS_HW_CONTEXTS(dev)) {
+               /* Cheat for hang stats */
+               file_priv->private_default_ctx =
+                       kzalloc(sizeof(struct i915_hw_context), GFP_KERNEL);
+
+               if (file_priv->private_default_ctx == NULL)
+                       return -ENOMEM;
+
+               file_priv->private_default_ctx->vm = &dev_priv->gtt.base;
                return 0;
+       }
 
        idr_init(&file_priv->context_idr);
 
+       mutex_lock(&dev->struct_mutex);
+       file_priv->private_default_ctx =
+               i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));
+       mutex_unlock(&dev->struct_mutex);
+
+       if (IS_ERR(file_priv->private_default_ctx)) {
+               idr_destroy(&file_priv->context_idr);
+               return PTR_ERR(file_priv->private_default_ctx);
+       }
+
        return 0;
 }
 
@@ -391,19 +514,29 @@ void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)
 {
        struct drm_i915_file_private *file_priv = file->driver_priv;
 
-       if (!HAS_HW_CONTEXTS(dev))
+       if (!HAS_HW_CONTEXTS(dev)) {
+               kfree(file_priv->private_default_ctx);
                return;
+       }
 
-       mutex_lock(&dev->struct_mutex);
        idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL);
+       i915_gem_context_unreference(file_priv->private_default_ctx);
        idr_destroy(&file_priv->context_idr);
-       mutex_unlock(&dev->struct_mutex);
 }
 
-static struct i915_hw_context *
+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);
+       struct i915_hw_context *ctx;
+
+       if (!HAS_HW_CONTEXTS(file_priv->dev_priv->dev))
+               return file_priv->private_default_ctx;
+
+       ctx = (struct i915_hw_context *)idr_find(&file_priv->context_idr, id);
+       if (!ctx)
+               return ERR_PTR(-ENOENT);
+
+       return ctx;
 }
 
 static inline int
@@ -441,7 +574,10 @@ 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))
@@ -459,6 +595,7 @@ static int do_switch(struct intel_ring_buffer *ring,
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
        struct i915_hw_context *from = ring->last_context;
+       struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(to);
        u32 hw_flags = 0;
        int ret, i;
 
@@ -467,30 +604,47 @@ static int do_switch(struct intel_ring_buffer *ring,
                BUG_ON(!i915_gem_obj_is_pinned(from->obj));
        }
 
-       if (from == to && !to->remap_slice)
+       if (from == to && from->last_ring == ring && !to->remap_slice)
                return 0;
 
+       /* 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;
        }
 
-       ret = i915_gem_obj_ggtt_pin(to->obj, get_context_alignment(ring->dev),
-                                   false, false);
-       if (ret)
-               return ret;
-
-       /* 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_ggtt_unpin(to->obj);
-               return ret;
-       }
+       if (ret)
+               goto unpin_out;
 
        if (!to->obj->has_global_gtt_mapping) {
                struct i915_vma *vma = i915_gem_obj_to_vma(to->obj,
@@ -498,14 +652,12 @@ static int do_switch(struct intel_ring_buffer *ring,
                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;
 
        ret = mi_set_context(ring, to, hw_flags);
-       if (ret) {
-               i915_gem_object_ggtt_unpin(to->obj);
-               return ret;
-       }
+       if (ret)
+               goto unpin_out;
 
        for (i = 0; i < MAX_L3_SLICES; i++) {
                if (!(to->remap_slice & (1<<i)))
@@ -543,12 +695,19 @@ static int do_switch(struct intel_ring_buffer *ring,
                i915_gem_context_unreference(from);
        }
 
+       to->is_initialized = true;
+
 done:
        i915_gem_context_reference(to);
        ring->last_context = to;
-       to->is_initialized = true;
+       to->last_ring = ring;
 
        return 0;
+
+unpin_out:
+       if (ring->id == RCS)
+               i915_gem_object_ggtt_unpin(to->obj);
+       return ret;
 }
 
 /**
@@ -564,26 +723,17 @@ done:
  */
 int i915_switch_context(struct intel_ring_buffer *ring,
                        struct drm_file *file,
-                       int to_id)
+                       struct i915_hw_context *to)
 {
        struct drm_i915_private *dev_priv = ring->dev->dev_private;
-       struct i915_hw_context *to;
-
-       if (!HAS_HW_CONTEXTS(ring->dev))
-               return 0;
 
        WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
 
-       if (to_id == DEFAULT_CONTEXT_ID) {
-               to = ring->default_context;
-       } else {
-               if (file == NULL)
-                       return -EINVAL;
+       BUG_ON(file && to == NULL);
 
-               to = i915_gem_context_get(file->driver_priv, to_id);
-               if (to == NULL)
-                       return -ENOENT;
-       }
+       /* We have the fake context, but don't supports switching. */
+       if (!HAS_HW_CONTEXTS(ring->dev))
+               return 0;
 
        return do_switch(ring, to);
 }
@@ -596,9 +746,6 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
        struct i915_hw_context *ctx;
        int ret;
 
-       if (!(dev->driver->driver_features & DRIVER_GEM))
-               return -ENODEV;
-
        if (!HAS_HW_CONTEXTS(dev))
                return -ENODEV;
 
@@ -606,7 +753,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
        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);
@@ -625,17 +772,17 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
        struct i915_hw_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);