include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / gpu / drm / i915 / i915_gem.c
index 302e096..368d726 100644 (file)
@@ -31,6 +31,7 @@
 #include "i915_drv.h"
 #include "i915_trace.h"
 #include "intel_drv.h"
+#include <linux/slab.h>
 #include <linux/swap.h>
 #include <linux/pci.h>
 
@@ -1466,9 +1467,6 @@ i915_gem_object_put_pages(struct drm_gem_object *obj)
                obj_priv->dirty = 0;
 
        for (i = 0; i < page_count; i++) {
-               if (obj_priv->pages[i] == NULL)
-                       break;
-
                if (obj_priv->dirty)
                        set_page_dirty(obj_priv->pages[i]);
 
@@ -1558,6 +1556,38 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
        i915_verify_inactive(dev, __FILE__, __LINE__);
 }
 
+static void
+i915_gem_process_flushing_list(struct drm_device *dev,
+                              uint32_t flush_domains, uint32_t seqno)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj_priv, *next;
+
+       list_for_each_entry_safe(obj_priv, next,
+                                &dev_priv->mm.gpu_write_list,
+                                gpu_write_list) {
+               struct drm_gem_object *obj = obj_priv->obj;
+
+               if ((obj->write_domain & flush_domains) ==
+                   obj->write_domain) {
+                       uint32_t old_write_domain = obj->write_domain;
+
+                       obj->write_domain = 0;
+                       list_del_init(&obj_priv->gpu_write_list);
+                       i915_gem_object_move_to_active(obj, seqno);
+
+                       /* update the fence lru list */
+                       if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
+                               list_move_tail(&obj_priv->fence_list,
+                                               &dev_priv->mm.fence_list);
+
+                       trace_i915_gem_object_change_domain(obj,
+                                                           obj->read_domains,
+                                                           old_write_domain);
+               }
+       }
+}
+
 /**
  * Creates a new sequence number, emitting a write of it to the status page
  * plus an interrupt, which will trigger i915_user_interrupt_handler.
@@ -1616,29 +1646,8 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
        /* Associate any objects on the flushing list matching the write
         * domain we're flushing with our flush.
         */
-       if (flush_domains != 0) {
-               struct drm_i915_gem_object *obj_priv, *next;
-
-               list_for_each_entry_safe(obj_priv, next,
-                                        &dev_priv->mm.gpu_write_list,
-                                        gpu_write_list) {
-                       struct drm_gem_object *obj = obj_priv->obj;
-
-                       if ((obj->write_domain & flush_domains) ==
-                           obj->write_domain) {
-                               uint32_t old_write_domain = obj->write_domain;
-
-                               obj->write_domain = 0;
-                               list_del_init(&obj_priv->gpu_write_list);
-                               i915_gem_object_move_to_active(obj, seqno);
-
-                               trace_i915_gem_object_change_domain(obj,
-                                                                   obj->read_domains,
-                                                                   old_write_domain);
-                       }
-               }
-
-       }
+       if (flush_domains != 0) 
+               i915_gem_process_flushing_list(dev, flush_domains, seqno);
 
        if (!dev_priv->mm.suspended) {
                mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
@@ -1987,6 +1996,7 @@ int
 i915_gem_object_unbind(struct drm_gem_object *obj)
 {
        struct drm_device *dev = obj->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj_priv = obj->driver_private;
        int ret = 0;
 
@@ -2042,8 +2052,10 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
        }
 
        /* Remove ourselves from the LRU list if present. */
+       spin_lock(&dev_priv->mm.active_list_lock);
        if (!list_empty(&obj_priv->list))
                list_del_init(&obj_priv->list);
+       spin_unlock(&dev_priv->mm.active_list_lock);
 
        if (i915_gem_object_is_purgeable(obj_priv))
                i915_gem_object_truncate(obj);
@@ -2081,11 +2093,34 @@ i915_gem_find_inactive_object(struct drm_device *dev, int min_size)
 }
 
 static int
+i915_gpu_idle(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       bool lists_empty;
+       uint32_t seqno;
+
+       spin_lock(&dev_priv->mm.active_list_lock);
+       lists_empty = list_empty(&dev_priv->mm.flushing_list) &&
+                     list_empty(&dev_priv->mm.active_list);
+       spin_unlock(&dev_priv->mm.active_list_lock);
+
+       if (lists_empty)
+               return 0;
+
+       /* Flush everything onto the inactive list. */
+       i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+       seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS);
+       if (seqno == 0)
+               return -ENOMEM;
+
+       return i915_wait_request(dev, seqno);
+}
+
+static int
 i915_gem_evict_everything(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        int ret;
-       uint32_t seqno;
        bool lists_empty;
 
        spin_lock(&dev_priv->mm.active_list_lock);
@@ -2098,12 +2133,7 @@ i915_gem_evict_everything(struct drm_device *dev)
                return -ENOSPC;
 
        /* Flush everything (on to the inactive lists) and evict */
-       i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
-       seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS);
-       if (seqno == 0)
-               return -ENOMEM;
-
-       ret = i915_wait_request(dev, seqno);
+       ret = i915_gpu_idle(dev);
        if (ret)
                return ret;
 
@@ -2195,11 +2225,6 @@ i915_gem_evict_something(struct drm_device *dev, int min_size)
                                seqno = i915_add_request(dev, NULL, obj->write_domain);
                                if (seqno == 0)
                                        return -ENOMEM;
-
-                               ret = i915_wait_request(dev, seqno);
-                               if (ret)
-                                       return ret;
-
                                continue;
                        }
                }
@@ -2224,7 +2249,6 @@ i915_gem_object_get_pages(struct drm_gem_object *obj,
        struct address_space *mapping;
        struct inode *inode;
        struct page *page;
-       int ret;
 
        if (obj_priv->pages_refcount++ != 0)
                return 0;
@@ -2247,11 +2271,9 @@ i915_gem_object_get_pages(struct drm_gem_object *obj,
                                           mapping_gfp_mask (mapping) |
                                           __GFP_COLD |
                                           gfpmask);
-               if (IS_ERR(page)) {
-                       ret = PTR_ERR(page);
-                       i915_gem_object_put_pages(obj);
-                       return ret;
-               }
+               if (IS_ERR(page))
+                       goto err_pages;
+
                obj_priv->pages[i] = page;
        }
 
@@ -2259,6 +2281,15 @@ i915_gem_object_get_pages(struct drm_gem_object *obj,
                i915_gem_object_do_bit_17_swizzle(obj);
 
        return 0;
+
+err_pages:
+       while (i--)
+               page_cache_release(obj_priv->pages[i]);
+
+       drm_free_large(obj_priv->pages);
+       obj_priv->pages = NULL;
+       obj_priv->pages_refcount--;
+       return PTR_ERR(page);
 }
 
 static void sandybridge_write_fence_reg(struct drm_i915_fence_reg *reg)
@@ -2379,6 +2410,58 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
        I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val);
 }
 
+static int i915_find_fence_reg(struct drm_device *dev)
+{
+       struct drm_i915_fence_reg *reg = NULL;
+       struct drm_i915_gem_object *obj_priv = NULL;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_gem_object *obj = NULL;
+       int i, avail, ret;
+
+       /* First try to find a free reg */
+       avail = 0;
+       for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) {
+               reg = &dev_priv->fence_regs[i];
+               if (!reg->obj)
+                       return i;
+
+               obj_priv = reg->obj->driver_private;
+               if (!obj_priv->pin_count)
+                   avail++;
+       }
+
+       if (avail == 0)
+               return -ENOSPC;
+
+       /* None available, try to steal one or wait for a user to finish */
+       i = I915_FENCE_REG_NONE;
+       list_for_each_entry(obj_priv, &dev_priv->mm.fence_list,
+                           fence_list) {
+               obj = obj_priv->obj;
+
+               if (obj_priv->pin_count)
+                       continue;
+
+               /* found one! */
+               i = obj_priv->fence_reg;
+               break;
+       }
+
+       BUG_ON(i == I915_FENCE_REG_NONE);
+
+       /* We only have a reference on obj from the active list. put_fence_reg
+        * might drop that one, causing a use-after-free in it. So hold a
+        * private reference to obj like the other callers of put_fence_reg
+        * (set_tiling ioctl) do. */
+       drm_gem_object_reference(obj);
+       ret = i915_gem_object_put_fence_reg(obj);
+       drm_gem_object_unreference(obj);
+       if (ret != 0)
+               return ret;
+
+       return i;
+}
+
 /**
  * i915_gem_object_get_fence_reg - set up a fence reg for an object
  * @obj: object to map through a fence reg
@@ -2399,8 +2482,7 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj_priv = obj->driver_private;
        struct drm_i915_fence_reg *reg = NULL;
-       struct drm_i915_gem_object *old_obj_priv = NULL;
-       int i, ret, avail;
+       int ret;
 
        /* Just update our place in the LRU if our fence is getting used. */
        if (obj_priv->fence_reg != I915_FENCE_REG_NONE) {
@@ -2428,51 +2510,12 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
                break;
        }
 
-       /* First try to find a free reg */
-       avail = 0;
-       for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) {
-               reg = &dev_priv->fence_regs[i];
-               if (!reg->obj)
-                       break;
-
-               old_obj_priv = reg->obj->driver_private;
-               if (!old_obj_priv->pin_count)
-                   avail++;
-       }
-
-       /* None available, try to steal one or wait for a user to finish */
-       if (i == dev_priv->num_fence_regs) {
-               struct drm_gem_object *old_obj = NULL;
-
-               if (avail == 0)
-                       return -ENOSPC;
-
-               list_for_each_entry(old_obj_priv, &dev_priv->mm.fence_list,
-                                   fence_list) {
-                       old_obj = old_obj_priv->obj;
-
-                       if (old_obj_priv->pin_count)
-                               continue;
-
-                       /* Take a reference, as otherwise the wait_rendering
-                        * below may cause the object to get freed out from
-                        * under us.
-                        */
-                       drm_gem_object_reference(old_obj);
-
-                       break;
-               }
-
-               i = old_obj_priv->fence_reg;
-               reg = &dev_priv->fence_regs[i];
-
-               ret = i915_gem_object_put_fence_reg(old_obj);
-               drm_gem_object_unreference(old_obj);
-               if (ret != 0) 
-                       return ret;
-       }
+       ret = i915_find_fence_reg(dev);
+       if (ret < 0)
+               return ret;
 
-       obj_priv->fence_reg = i;
+       obj_priv->fence_reg = ret;
+       reg = &dev_priv->fence_regs[obj_priv->fence_reg];
        list_add_tail(&obj_priv->fence_list, &dev_priv->mm.fence_list);
 
        reg->obj = obj;
@@ -2486,7 +2529,8 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
        else
                i830_write_fence_reg(reg);
 
-       trace_i915_gem_object_get_fence(obj, i, obj_priv->tiling_mode);
+       trace_i915_gem_object_get_fence(obj, obj_priv->fence_reg,
+                       obj_priv->tiling_mode);
 
        return 0;
 }
@@ -2703,7 +2747,6 @@ static void
 i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
 {
        struct drm_device *dev = obj->dev;
-       uint32_t seqno;
        uint32_t old_write_domain;
 
        if ((obj->write_domain & I915_GEM_GPU_DOMAINS) == 0)
@@ -2712,9 +2755,8 @@ i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
        /* Queue the GPU write cache flushing we need. */
        old_write_domain = obj->write_domain;
        i915_gem_flush(dev, 0, obj->write_domain);
-       seqno = i915_add_request(dev, NULL, obj->write_domain);
+       (void) i915_add_request(dev, NULL, obj->write_domain);
        BUG_ON(obj->write_domain);
-       i915_gem_object_move_to_active(obj, seqno);
 
        trace_i915_gem_object_change_domain(obj,
                                            obj->read_domains,
@@ -3324,6 +3366,16 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                }
 
                /* Validate that the target is in a valid r/w GPU domain */
+               if (reloc->write_domain & (reloc->write_domain - 1)) {
+                       DRM_ERROR("reloc with multiple write domains: "
+                                 "obj %p target %d offset %d "
+                                 "read %08x write %08x",
+                                 obj, reloc->target_handle,
+                                 (int) reloc->offset,
+                                 reloc->read_domains,
+                                 reloc->write_domain);
+                       return -EINVAL;
+               }
                if (reloc->write_domain & I915_GEM_DOMAIN_CPU ||
                    reloc->read_domains & I915_GEM_DOMAIN_CPU) {
                        DRM_ERROR("reloc with read/write CPU domains: "
@@ -4448,30 +4500,6 @@ i915_gem_evict_from_inactive_list(struct drm_device *dev)
        return 0;
 }
 
-static int
-i915_gpu_idle(struct drm_device *dev)
-{
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       bool lists_empty;
-       uint32_t seqno;
-
-       spin_lock(&dev_priv->mm.active_list_lock);
-       lists_empty = list_empty(&dev_priv->mm.flushing_list) &&
-                     list_empty(&dev_priv->mm.active_list);
-       spin_unlock(&dev_priv->mm.active_list_lock);
-
-       if (lists_empty)
-               return 0;
-
-       /* Flush everything onto the inactive list. */
-       i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
-       seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS);
-       if (seqno == 0)
-               return -ENOMEM;
-
-       return i915_wait_request(dev, seqno);
-}
-
 int
 i915_gem_idle(struct drm_device *dev)
 {
@@ -4701,6 +4729,11 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
                        ring->space += ring->Size;
        }
 
+       if (IS_I9XX(dev) && !IS_GEN3(dev)) {
+               I915_WRITE(MI_MODE,
+                          (VS_TIMER_DISPATCH) << 16 | VS_TIMER_DISPATCH);
+       }
+
        return 0;
 }