]> Pileus Git - ~andy/linux/blobdiff - drivers/gpu/drm/i915/i915_gem.c
drm/i915: Inifite timeout for wait ioctl
[~andy/linux] / drivers / gpu / drm / i915 / i915_gem.c
index c8a5b04bc8d55ed7b87e73b8591d8aecd3b243c9..deaa0d4bb456e582c01a205cc9a76f5542f7a3cb 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/slab.h>
 #include <linux/swap.h>
 #include <linux/pci.h>
+#include <linux/dma-buf.h>
 
 static __must_check int i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj);
 static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj);
@@ -538,6 +539,14 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
                goto out;
        }
 
+       /* prime objects have no backing filp to GEM pread/pwrite
+        * pages from.
+        */
+       if (!obj->base.filp) {
+               ret = -EINVAL;
+               goto out;
+       }
+
        trace_i915_gem_object_pread(obj, args->offset, args->size);
 
        ret = i915_gem_shmem_pread(dev, obj, args, file);
@@ -880,6 +889,14 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
                goto out;
        }
 
+       /* prime objects have no backing filp to GEM pread/pwrite
+        * pages from.
+        */
+       if (!obj->base.filp) {
+               ret = -EINVAL;
+               goto out;
+       }
+
        trace_i915_gem_object_pwrite(obj, args->offset, args->size);
 
        ret = -EFAULT;
@@ -1021,6 +1038,14 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
        if (obj == NULL)
                return -ENOENT;
 
+       /* prime objects have no backing filp to GEM mmap
+        * pages from.
+        */
+       if (!obj->filp) {
+               drm_gem_object_unreference_unlocked(obj);
+               return -EINVAL;
+       }
+
        addr = vm_mmap(obj->filp, 0, args->size,
                       PROT_READ | PROT_WRITE, MAP_SHARED,
                       args->offset);
@@ -1302,8 +1327,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
        return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset);
 }
 
-
-static int
+int
 i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
                              gfp_t gfpmask)
 {
@@ -1312,6 +1336,9 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
        struct inode *inode;
        struct page *page;
 
+       if (obj->pages || obj->sg_table)
+               return 0;
+
        /* Get the list of pages out of our struct file.  They'll be pinned
         * at this point until we release them.
         */
@@ -1353,6 +1380,9 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
        int page_count = obj->base.size / PAGE_SIZE;
        int i;
 
+       if (!obj->pages)
+               return;
+
        BUG_ON(obj->madv == __I915_MADV_PURGED);
 
        if (i915_gem_object_needs_bit17_swizzle(obj))
@@ -1952,8 +1982,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
  * request and object lists appropriately for that event.
  */
 int
-i915_wait_request(struct intel_ring_buffer *ring,
-                 uint32_t seqno)
+i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
 {
        drm_i915_private_t *dev_priv = ring->dev->dev_private;
        int ret = 0;
@@ -1991,7 +2020,32 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj)
         * it.
         */
        if (obj->active) {
-               ret = i915_wait_request(obj->ring, obj->last_rendering_seqno);
+               ret = i915_wait_seqno(obj->ring, obj->last_rendering_seqno);
+               if (ret)
+                       return ret;
+               i915_gem_retire_requests_ring(obj->ring);
+       }
+
+       return 0;
+}
+
+/**
+ * Ensures that an object will eventually get non-busy by flushing any required
+ * write domains, emitting any outstanding lazy request and retiring and
+ * completed requests.
+ */
+static int
+i915_gem_object_flush_active(struct drm_i915_gem_object *obj)
+{
+       int ret;
+
+       if (obj->active) {
+               ret = i915_gem_object_flush_gpu_write_domain(obj);
+               if (ret)
+                       return ret;
+
+               ret = i915_gem_check_olr(obj->ring,
+                                        obj->last_rendering_seqno);
                if (ret)
                        return ret;
                i915_gem_retire_requests_ring(obj->ring);
@@ -2000,6 +2054,90 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj)
        return 0;
 }
 
+/**
+ * i915_gem_wait_ioctl - implements DRM_IOCTL_I915_GEM_WAIT
+ * @DRM_IOCTL_ARGS: standard ioctl arguments
+ *
+ * Returns 0 if successful, else an error is returned with the remaining time in
+ * the timeout parameter.
+ *  -ETIME: object is still busy after timeout
+ *  -ERESTARTSYS: signal interrupted the wait
+ *  -ENONENT: object doesn't exist
+ * Also possible, but rare:
+ *  -EAGAIN: GPU wedged
+ *  -ENOMEM: damn
+ *  -ENODEV: Internal IRQ fail
+ *  -E?: The add request failed
+ *
+ * The wait ioctl with a timeout of 0 reimplements the busy ioctl. With any
+ * non-zero timeout parameter the wait ioctl will wait for the given number of
+ * nanoseconds on an object becoming unbusy. Since the wait itself does so
+ * without holding struct_mutex the object may become re-busied before this
+ * function completes. A similar but shorter * race condition exists in the busy
+ * ioctl
+ */
+int
+i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+       struct drm_i915_gem_wait *args = data;
+       struct drm_i915_gem_object *obj;
+       struct intel_ring_buffer *ring = NULL;
+       struct timespec timeout_stack, *timeout = NULL;
+       u32 seqno = 0;
+       int ret = 0;
+
+       if (args->timeout_ns >= 0) {
+               timeout_stack = ns_to_timespec(args->timeout_ns);
+               timeout = &timeout_stack;
+       }
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
+       obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->bo_handle));
+       if (&obj->base == NULL) {
+               mutex_unlock(&dev->struct_mutex);
+               return -ENOENT;
+       }
+
+       /* Need to make sure the object gets inactive eventually. */
+       ret = i915_gem_object_flush_active(obj);
+       if (ret)
+               goto out;
+
+       if (obj->active) {
+               seqno = obj->last_rendering_seqno;
+               ring = obj->ring;
+       }
+
+       if (seqno == 0)
+                goto out;
+
+       /* Do this after OLR check to make sure we make forward progress polling
+        * on this IOCTL with a 0 timeout (like busy ioctl)
+        */
+       if (!args->timeout_ns) {
+               ret = -ETIME;
+               goto out;
+       }
+
+       drm_gem_object_unreference(&obj->base);
+       mutex_unlock(&dev->struct_mutex);
+
+       ret = __wait_seqno(ring, seqno, true, timeout);
+       if (timeout) {
+               WARN_ON(!timespec_valid(timeout));
+               args->timeout_ns = timespec_to_ns(timeout);
+       }
+       return ret;
+
+out:
+       drm_gem_object_unreference(&obj->base);
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
+}
+
 /**
  * i915_gem_object_sync - sync an object to a ring.
  *
@@ -2079,10 +2217,8 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
        if (obj->gtt_space == NULL)
                return 0;
 
-       if (obj->pin_count != 0) {
-               DRM_ERROR("Attempting to unbind pinned buffer\n");
-               return -EINVAL;
-       }
+       if (obj->pin_count)
+               return -EBUSY;
 
        ret = i915_gem_object_finish_gpu(obj);
        if (ret)
@@ -2178,7 +2314,7 @@ static int i915_ring_idle(struct intel_ring_buffer *ring)
                        return ret;
        }
 
-       return i915_wait_request(ring, i915_gem_next_request_seqno(ring));
+       return i915_wait_seqno(ring, i915_gem_next_request_seqno(ring));
 }
 
 int i915_gpu_idle(struct drm_device *dev)
@@ -2382,7 +2518,7 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj)
        }
 
        if (obj->last_fenced_seqno) {
-               ret = i915_wait_request(obj->ring, obj->last_fenced_seqno);
+               ret = i915_wait_seqno(obj->ring, obj->last_fenced_seqno);
                if (ret)
                        return ret;
 
@@ -3217,30 +3353,9 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
         * become non-busy without any further actions, therefore emit any
         * necessary flushes here.
         */
-       args->busy = obj->active;
-       if (args->busy) {
-               /* Unconditionally flush objects, even when the gpu still uses this
-                * object. Userspace calling this function indicates that it wants to
-                * use this buffer rather sooner than later, so issuing the required
-                * flush earlier is beneficial.
-                */
-               if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) {
-                       ret = i915_gem_flush_ring(obj->ring,
-                                                 0, obj->base.write_domain);
-               } else {
-                       ret = i915_gem_check_olr(obj->ring,
-                                                obj->last_rendering_seqno);
-               }
+       ret = i915_gem_object_flush_active(obj);
 
-               /* Update the active list for the hardware's current position.
-                * Otherwise this only updates on a delayed timer or when irqs
-                * are actually unmasked, and our working set ends up being
-                * larger than required.
-                */
-               i915_gem_retire_requests_ring(obj->ring);
-
-               args->busy = obj->active;
-       }
+       args->busy = obj->active;
 
        drm_gem_object_unreference(&obj->base);
 unlock:
@@ -3309,6 +3424,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj;
        struct address_space *mapping;
+       u32 mask;
 
        obj = kzalloc(sizeof(*obj), GFP_KERNEL);
        if (obj == NULL)
@@ -3319,8 +3435,15 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
                return NULL;
        }
 
+       mask = GFP_HIGHUSER | __GFP_RECLAIMABLE;
+       if (IS_CRESTLINE(dev) || IS_BROADWATER(dev)) {
+               /* 965gm cannot relocate objects above 4GiB. */
+               mask &= ~__GFP_HIGHMEM;
+               mask |= __GFP_DMA32;
+       }
+
        mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
-       mapping_set_gfp_mask(mapping, GFP_HIGHUSER | __GFP_RECLAIMABLE);
+       mapping_set_gfp_mask(mapping, mask);
 
        i915_gem_info_add_obj(dev_priv, size);
 
@@ -3373,6 +3496,9 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
 
        trace_i915_gem_object_destroy(obj);
 
+       if (gem_obj->import_attach)
+               drm_prime_gem_destroy(gem_obj, obj->sg_table);
+
        if (obj->phys_obj)
                i915_gem_detach_phys_object(dev, obj);
 
@@ -3442,6 +3568,38 @@ i915_gem_idle(struct drm_device *dev)
        return 0;
 }
 
+void i915_gem_l3_remap(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 misccpctl;
+       int i;
+
+       if (!IS_IVYBRIDGE(dev))
+               return;
+
+       if (!dev_priv->mm.l3_remap_info)
+               return;
+
+       misccpctl = I915_READ(GEN7_MISCCPCTL);
+       I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE);
+       POSTING_READ(GEN7_MISCCPCTL);
+
+       for (i = 0; i < GEN7_L3LOG_SIZE; i += 4) {
+               u32 remap = I915_READ(GEN7_L3LOG_BASE + i);
+               if (remap && remap != dev_priv->mm.l3_remap_info[i/4])
+                       DRM_DEBUG("0x%x was already programmed to %x\n",
+                                 GEN7_L3LOG_BASE + i, remap);
+               if (remap && !dev_priv->mm.l3_remap_info[i/4])
+                       DRM_DEBUG_DRIVER("Clearing remapped register\n");
+               I915_WRITE(GEN7_L3LOG_BASE + i, dev_priv->mm.l3_remap_info[i/4]);
+       }
+
+       /* Make sure all the writes land before disabling dop clock gating */
+       POSTING_READ(GEN7_L3LOG_BASE);
+
+       I915_WRITE(GEN7_MISCCPCTL, misccpctl);
+}
+
 void i915_gem_init_swizzling(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
@@ -3531,6 +3689,8 @@ i915_gem_init_hw(struct drm_device *dev)
        drm_i915_private_t *dev_priv = dev->dev_private;
        int ret;
 
+       i915_gem_l3_remap(dev);
+
        i915_gem_init_swizzling(dev);
 
        ret = intel_init_render_ring_buffer(dev);