]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/gpu/drm/i915/i915_gem.c
i915: cleanup coding horrors in i915_gem_gtt_pwrite()
[linux-2.6-omap-h63xx.git] / drivers / gpu / drm / i915 / i915_gem.c
index 99a86249bb1b920466a798c73a7677b48f335a42..49c5a1798ac4db409a0072daa7923dee89dce277 100644 (file)
@@ -50,6 +50,9 @@ static int i915_gem_object_get_page_list(struct drm_gem_object *obj);
 static void i915_gem_object_free_page_list(struct drm_gem_object *obj);
 static int i915_gem_object_wait_rendering(struct drm_gem_object *obj);
 
+static void
+i915_gem_cleanup_ringbuffer(struct drm_device *dev);
+
 int
 i915_gem_init_ioctl(struct drm_device *dev, void *data,
                    struct drm_file *file_priv)
@@ -168,6 +171,36 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
+/*
+ * Try to write quickly with an atomic kmap. Return true on success.
+ *
+ * If this fails (which includes a partial write), we'll redo the whole
+ * thing with the slow version.
+ *
+ * This is a workaround for the low performance of iounmap (approximate
+ * 10% cpu cost on normal 3D workloads).  kmap_atomic on HIGHMEM kernels
+ * happens to let us map card memory without taking IPIs.  When the vmap
+ * rework lands we should be able to dump this hack.
+ */
+static inline int fast_user_write(unsigned long pfn, char __user *user_data, int l)
+{
+#ifdef CONFIG_HIGHMEM
+       unsigned long unwritten;
+       char *vaddr_atomic;
+
+       vaddr_atomic = kmap_atomic_pfn(pfn, KM_USER0);
+#if WATCH_PWRITE
+       DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n",
+                i, o, l, pfn, vaddr_atomic);
+#endif
+       unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + o, user_data, l);
+       kunmap_atomic(vaddr_atomic, KM_USER0);
+       return !unwritten;
+#else
+       return 0;
+#endif
+}
+
 static int
 i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
                    struct drm_i915_gem_pwrite *args,
@@ -177,12 +210,7 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
        ssize_t remain;
        loff_t offset;
        char __user *user_data;
-       char __iomem *vaddr;
-       char *vaddr_atomic;
-       int i, o, l;
        int ret = 0;
-       unsigned long pfn;
-       unsigned long unwritten;
 
        user_data = (char __user *) (uintptr_t) args->data_ptr;
        remain = args->size;
@@ -206,6 +234,9 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
        obj_priv->dirty = 1;
 
        while (remain > 0) {
+               unsigned long pfn;
+               int i, o, l;
+
                /* Operation in this page
                 *
                 * i = page number
@@ -220,25 +251,10 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
 
                pfn = (dev->agp->base >> PAGE_SHIFT) + i;
 
-#ifdef CONFIG_HIGHMEM
-               /* This is a workaround for the low performance of iounmap
-                * (approximate 10% cpu cost on normal 3D workloads).
-                * kmap_atomic on HIGHMEM kernels happens to let us map card
-                * memory without taking IPIs.  When the vmap rework lands
-                * we should be able to dump this hack.
-                */
-               vaddr_atomic = kmap_atomic_pfn(pfn, KM_USER0);
-#if WATCH_PWRITE
-               DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n",
-                        i, o, l, pfn, vaddr_atomic);
-#endif
-               unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + o,
-                                                             user_data, l);
-               kunmap_atomic(vaddr_atomic, KM_USER0);
+               if (!fast_user_write(pfn, user_data, l)) {
+                       unsigned long unwritten;
+                       char __iomem *vaddr;
 
-               if (unwritten)
-#endif /* CONFIG_HIGHMEM */
-               {
                        vaddr = ioremap_wc(pfn << PAGE_SHIFT, PAGE_SIZE);
 #if WATCH_PWRITE
                        DRM_INFO("pwrite slow i %d o %d l %d "
@@ -582,7 +598,7 @@ i915_add_request(struct drm_device *dev, uint32_t flush_domains)
        was_empty = list_empty(&dev_priv->mm.request_list);
        list_add_tail(&request->list, &dev_priv->mm.request_list);
 
-       if (was_empty)
+       if (was_empty && !dev_priv->mm.suspended)
                schedule_delayed_work(&dev_priv->mm.retire_work, HZ);
        return seqno;
 }
@@ -731,7 +747,8 @@ i915_gem_retire_work_handler(struct work_struct *work)
 
        mutex_lock(&dev->struct_mutex);
        i915_gem_retire_requests(dev);
-       if (!list_empty(&dev_priv->mm.request_list))
+       if (!dev_priv->mm.suspended &&
+           !list_empty(&dev_priv->mm.request_list))
                schedule_delayed_work(&dev_priv->mm.retire_work, HZ);
        mutex_unlock(&dev->struct_mutex);
 }
@@ -2227,14 +2244,24 @@ i915_gem_idle(struct drm_device *dev)
        uint32_t seqno, cur_seqno, last_seqno;
        int stuck, ret;
 
-       if (dev_priv->mm.suspended)
+       mutex_lock(&dev->struct_mutex);
+
+       if (dev_priv->mm.suspended || dev_priv->ring.ring_obj == NULL) {
+               mutex_unlock(&dev->struct_mutex);
                return 0;
+       }
 
        /* Hack!  Don't let anybody do execbuf while we don't control the chip.
         * We need to replace this with a semaphore, or something.
         */
        dev_priv->mm.suspended = 1;
 
+       /* Cancel the retire work handler, wait for it to finish if running
+        */
+       mutex_unlock(&dev->struct_mutex);
+       cancel_delayed_work_sync(&dev_priv->mm.retire_work);
+       mutex_lock(&dev->struct_mutex);
+
        i915_kernel_lost_context(dev);
 
        /* Flush the GPU along with all non-CPU write domains
@@ -2284,13 +2311,19 @@ i915_gem_idle(struct drm_device *dev)
 
        /* Move all buffers out of the GTT. */
        ret = i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list);
-       if (ret)
+       if (ret) {
+               mutex_unlock(&dev->struct_mutex);
                return ret;
+       }
 
        BUG_ON(!list_empty(&dev_priv->mm.active_list));
        BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
        BUG_ON(!list_empty(&dev_priv->mm.inactive_list));
        BUG_ON(!list_empty(&dev_priv->mm.request_list));
+
+       i915_gem_cleanup_ringbuffer(dev);
+       mutex_unlock(&dev->struct_mutex);
+
        return 0;
 }
 
@@ -2503,34 +2536,20 @@ i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
 {
        int ret;
 
-       mutex_lock(&dev->struct_mutex);
        ret = i915_gem_idle(dev);
-       if (ret == 0)
-               i915_gem_cleanup_ringbuffer(dev);
-       mutex_unlock(&dev->struct_mutex);
-
        drm_irq_uninstall(dev);
 
-       return 0;
+       return ret;
 }
 
 void
 i915_gem_lastclose(struct drm_device *dev)
 {
        int ret;
-       drm_i915_private_t *dev_priv = dev->dev_private;
 
-       mutex_lock(&dev->struct_mutex);
-
-       if (dev_priv->ring.ring_obj != NULL) {
-               ret = i915_gem_idle(dev);
-               if (ret)
-                       DRM_ERROR("failed to idle hardware: %d\n", ret);
-
-               i915_gem_cleanup_ringbuffer(dev);
-       }
-
-       mutex_unlock(&dev->struct_mutex);
+       ret = i915_gem_idle(dev);
+       if (ret)
+               DRM_ERROR("failed to idle hardware: %d\n", ret);
 }
 
 void