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 0330c3a..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>
 
@@ -128,9 +129,7 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
                return -ENOMEM;
 
        ret = drm_gem_handle_create(file_priv, obj, &handle);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_handle_unreference(obj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_handle_unreference_unlocked(obj);
 
        if (ret)
                return ret;
@@ -277,7 +276,7 @@ i915_gem_shmem_pread_fast(struct drm_device *dev, struct drm_gem_object *obj,
 
        mutex_lock(&dev->struct_mutex);
 
-       ret = i915_gem_object_get_pages(obj);
+       ret = i915_gem_object_get_pages(obj, 0);
        if (ret != 0)
                goto fail_unlock;
 
@@ -321,40 +320,24 @@ fail_unlock:
        return ret;
 }
 
-static inline gfp_t
-i915_gem_object_get_page_gfp_mask (struct drm_gem_object *obj)
-{
-       return mapping_gfp_mask(obj->filp->f_path.dentry->d_inode->i_mapping);
-}
-
-static inline void
-i915_gem_object_set_page_gfp_mask (struct drm_gem_object *obj, gfp_t gfp)
-{
-       mapping_set_gfp_mask(obj->filp->f_path.dentry->d_inode->i_mapping, gfp);
-}
-
 static int
 i915_gem_object_get_pages_or_evict(struct drm_gem_object *obj)
 {
        int ret;
 
-       ret = i915_gem_object_get_pages(obj);
+       ret = i915_gem_object_get_pages(obj, __GFP_NORETRY | __GFP_NOWARN);
 
        /* If we've insufficient memory to map in the pages, attempt
         * to make some space by throwing out some old buffers.
         */
        if (ret == -ENOMEM) {
                struct drm_device *dev = obj->dev;
-               gfp_t gfp;
 
                ret = i915_gem_evict_something(dev, obj->size);
                if (ret)
                        return ret;
 
-               gfp = i915_gem_object_get_page_gfp_mask(obj);
-               i915_gem_object_set_page_gfp_mask(obj, gfp & ~__GFP_NORETRY);
-               ret = i915_gem_object_get_pages(obj);
-               i915_gem_object_set_page_gfp_mask (obj, gfp);
+               ret = i915_gem_object_get_pages(obj, 0);
        }
 
        return ret;
@@ -504,7 +487,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
         */
        if (args->offset > obj->size || args->size > obj->size ||
            args->offset + args->size > obj->size) {
-               drm_gem_object_unreference(obj);
+               drm_gem_object_unreference_unlocked(obj);
                return -EINVAL;
        }
 
@@ -517,7 +500,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
                                                        file_priv);
        }
 
-       drm_gem_object_unreference(obj);
+       drm_gem_object_unreference_unlocked(obj);
 
        return ret;
 }
@@ -790,7 +773,7 @@ i915_gem_shmem_pwrite_fast(struct drm_device *dev, struct drm_gem_object *obj,
 
        mutex_lock(&dev->struct_mutex);
 
-       ret = i915_gem_object_get_pages(obj);
+       ret = i915_gem_object_get_pages(obj, 0);
        if (ret != 0)
                goto fail_unlock;
 
@@ -977,7 +960,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
         */
        if (args->offset > obj->size || args->size > obj->size ||
            args->offset + args->size > obj->size) {
-               drm_gem_object_unreference(obj);
+               drm_gem_object_unreference_unlocked(obj);
                return -EINVAL;
        }
 
@@ -1011,7 +994,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
                DRM_INFO("pwrite failed %d\n", ret);
 #endif
 
-       drm_gem_object_unreference(obj);
+       drm_gem_object_unreference_unlocked(obj);
 
        return ret;
 }
@@ -1154,9 +1137,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
                       PROT_READ | PROT_WRITE, MAP_SHARED,
                       args->offset);
        up_write(&current->mm->mmap_sem);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(obj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(obj);
        if (IS_ERR((void *)addr))
                return addr;
 
@@ -1486,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]);
 
@@ -1568,6 +1546,8 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
        else
                list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
 
+       BUG_ON(!list_empty(&obj_priv->gpu_write_list));
+
        obj_priv->last_rendering_seqno = 0;
        if (obj_priv->active) {
                obj_priv->active = 0;
@@ -1576,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.
@@ -1634,27 +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.flushing_list, 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;
-                               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);
@@ -1834,7 +1827,7 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible)
                return -EIO;
 
        if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
-               if (IS_IRONLAKE(dev))
+               if (HAS_PCH_SPLIT(dev))
                        ier = I915_READ(DEIER) | I915_READ(GTIER);
                else
                        ier = I915_READ(IER);
@@ -2003,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;
 
@@ -2058,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);
@@ -2097,10 +2093,33 @@ i915_gem_find_inactive_object(struct drm_device *dev, int min_size)
 }
 
 static int
-i915_gem_evict_everything(struct drm_device *dev)
+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;
        bool lists_empty;
 
@@ -2114,15 +2133,12 @@ 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;
 
+       BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
+
        ret = i915_gem_evict_from_inactive_list(dev);
        if (ret)
                return ret;
@@ -2209,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;
                        }
                }
@@ -2230,14 +2241,14 @@ i915_gem_evict_something(struct drm_device *dev, int min_size)
 }
 
 int
-i915_gem_object_get_pages(struct drm_gem_object *obj)
+i915_gem_object_get_pages(struct drm_gem_object *obj,
+                         gfp_t gfpmask)
 {
        struct drm_i915_gem_object *obj_priv = obj->driver_private;
        int page_count, i;
        struct address_space *mapping;
        struct inode *inode;
        struct page *page;
-       int ret;
 
        if (obj_priv->pages_refcount++ != 0)
                return 0;
@@ -2256,12 +2267,13 @@ i915_gem_object_get_pages(struct drm_gem_object *obj)
        inode = obj->filp->f_path.dentry->d_inode;
        mapping = inode->i_mapping;
        for (i = 0; i < page_count; i++) {
-               page = read_mapping_page(mapping, i, NULL);
-               if (IS_ERR(page)) {
-                       ret = PTR_ERR(page);
-                       i915_gem_object_put_pages(obj);
-                       return ret;
-               }
+               page = read_cache_page_gfp(mapping, i,
+                                          mapping_gfp_mask (mapping) |
+                                          __GFP_COLD |
+                                          gfpmask);
+               if (IS_ERR(page))
+                       goto err_pages;
+
                obj_priv->pages[i] = page;
        }
 
@@ -2269,6 +2281,37 @@ 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)
+{
+       struct drm_gem_object *obj = reg->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 regnum = obj_priv->fence_reg;
+       uint64_t val;
+
+       val = (uint64_t)((obj_priv->gtt_offset + obj->size - 4096) &
+                   0xfffff000) << 32;
+       val |= obj_priv->gtt_offset & 0xfffff000;
+       val |= (uint64_t)((obj_priv->stride / 128) - 1) <<
+               SANDYBRIDGE_FENCE_PITCH_SHIFT;
+
+       if (obj_priv->tiling_mode == I915_TILING_Y)
+               val |= 1 << I965_FENCE_TILING_Y_SHIFT;
+       val |= I965_FENCE_REG_VALID;
+
+       I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (regnum * 8), val);
 }
 
 static void i965_write_fence_reg(struct drm_i915_fence_reg *reg)
@@ -2367,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
@@ -2387,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) {
@@ -2416,86 +2510,27 @@ 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);
-
-                       /* i915 uses fences for GPU access to tiled buffers */
-                       if (IS_I965G(dev) || !old_obj_priv->active)
-                               break;
-
-                       /* This brings the object to the head of the LRU if it
-                        * had been written to.  The only way this should
-                        * result in us waiting longer than the expected
-                        * optimal amount of time is if there was a
-                        * fence-using buffer later that was read-only.
-                        */
-                       i915_gem_object_flush_gpu_write_domain(old_obj);
-                       ret = i915_gem_object_wait_rendering(old_obj);
-                       if (ret != 0) {
-                               drm_gem_object_unreference(old_obj);
-                               return ret;
-                       }
-
-                       break;
-               }
-
-               /*
-                * Zap this virtual mapping so we can set up a fence again
-                * for this object next time we need it.
-                */
-               i915_gem_release_mmap(old_obj);
-
-               i = old_obj_priv->fence_reg;
-               reg = &dev_priv->fence_regs[i];
-
-               old_obj_priv->fence_reg = I915_FENCE_REG_NONE;
-               list_del_init(&old_obj_priv->fence_list);
-
-               drm_gem_object_unreference(old_obj);
-       }
+       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;
 
-       if (IS_I965G(dev))
+       if (IS_GEN6(dev))
+               sandybridge_write_fence_reg(reg);
+       else if (IS_I965G(dev))
                i965_write_fence_reg(reg);
        else if (IS_I9XX(dev))
                i915_write_fence_reg(reg);
        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;
 }
@@ -2514,9 +2549,12 @@ i915_gem_clear_fence_reg(struct drm_gem_object *obj)
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj_priv = obj->driver_private;
 
-       if (IS_I965G(dev))
+       if (IS_GEN6(dev)) {
+               I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 +
+                            (obj_priv->fence_reg * 8), 0);
+       } else if (IS_I965G(dev)) {
                I915_WRITE64(FENCE_REG_965_0 + (obj_priv->fence_reg * 8), 0);
-       else {
+       else {
                uint32_t fence_reg;
 
                if (obj_priv->fence_reg < 8)
@@ -2550,6 +2588,12 @@ i915_gem_object_put_fence_reg(struct drm_gem_object *obj)
        if (obj_priv->fence_reg == I915_FENCE_REG_NONE)
                return 0;
 
+       /* If we've changed tiling, GTT-mappings of the object
+        * need to re-fault to ensure that the correct fence register
+        * setup is in place.
+        */
+       i915_gem_release_mmap(obj);
+
        /* On the i915, GPU access to tiled buffers is via a fence,
         * therefore we must wait for any outstanding access to complete
         * before clearing the fence.
@@ -2558,12 +2602,12 @@ i915_gem_object_put_fence_reg(struct drm_gem_object *obj)
                int ret;
 
                i915_gem_object_flush_gpu_write_domain(obj);
-               i915_gem_object_flush_gtt_write_domain(obj);
                ret = i915_gem_object_wait_rendering(obj);
                if (ret != 0)
                        return ret;
        }
 
+       i915_gem_object_flush_gtt_write_domain(obj);
        i915_gem_clear_fence_reg (obj);
 
        return 0;
@@ -2579,12 +2623,9 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj_priv = obj->driver_private;
        struct drm_mm_node *free_space;
-       bool retry_alloc = false;
+       gfp_t gfpmask =  __GFP_NORETRY | __GFP_NOWARN;
        int ret;
 
-       if (dev_priv->mm.suspended)
-               return -EBUSY;
-
        if (obj_priv->madv != I915_MADV_WILLNEED) {
                DRM_ERROR("Attempting to bind a purgeable object\n");
                return -EINVAL;
@@ -2626,15 +2667,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
        DRM_INFO("Binding object of size %zd at 0x%08x\n",
                 obj->size, obj_priv->gtt_offset);
 #endif
-       if (retry_alloc) {
-               i915_gem_object_set_page_gfp_mask (obj,
-                                                  i915_gem_object_get_page_gfp_mask (obj) & ~__GFP_NORETRY);
-       }
-       ret = i915_gem_object_get_pages(obj);
-       if (retry_alloc) {
-               i915_gem_object_set_page_gfp_mask (obj,
-                                                  i915_gem_object_get_page_gfp_mask (obj) | __GFP_NORETRY);
-       }
+       ret = i915_gem_object_get_pages(obj, gfpmask);
        if (ret) {
                drm_mm_put_block(obj_priv->gtt_space);
                obj_priv->gtt_space = NULL;
@@ -2644,9 +2677,9 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
                        ret = i915_gem_evict_something(dev, obj->size);
                        if (ret) {
                                /* now try to shrink everyone else */
-                               if (! retry_alloc) {
-                                   retry_alloc = true;
-                                   goto search_free;
+                               if (gfpmask) {
+                                       gfpmask = 0;
+                                       goto search_free;
                                }
 
                                return ret;
@@ -2714,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)
@@ -2723,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);
-       obj->write_domain = 0;
-       i915_gem_object_move_to_active(obj, seqno);
+       (void) i915_add_request(dev, NULL, obj->write_domain);
+       BUG_ON(obj->write_domain);
 
        trace_i915_gem_object_change_domain(obj,
                                            obj->read_domains,
@@ -2840,6 +2871,57 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write)
        return 0;
 }
 
+/*
+ * Prepare buffer for display plane. Use uninterruptible for possible flush
+ * wait, as in modesetting process we're not supposed to be interrupted.
+ */
+int
+i915_gem_object_set_to_display_plane(struct drm_gem_object *obj)
+{
+       struct drm_device *dev = obj->dev;
+       struct drm_i915_gem_object *obj_priv = obj->driver_private;
+       uint32_t old_write_domain, old_read_domains;
+       int ret;
+
+       /* Not valid to be called on unbound objects. */
+       if (obj_priv->gtt_space == NULL)
+               return -EINVAL;
+
+       i915_gem_object_flush_gpu_write_domain(obj);
+
+       /* Wait on any GPU rendering and flushing to occur. */
+       if (obj_priv->active) {
+#if WATCH_BUF
+               DRM_INFO("%s: object %p wait for seqno %08x\n",
+                         __func__, obj, obj_priv->last_rendering_seqno);
+#endif
+               ret = i915_do_wait_request(dev, obj_priv->last_rendering_seqno, 0);
+               if (ret != 0)
+                       return ret;
+       }
+
+       old_write_domain = obj->write_domain;
+       old_read_domains = obj->read_domains;
+
+       obj->read_domains &= I915_GEM_DOMAIN_GTT;
+
+       i915_gem_object_flush_cpu_write_domain(obj);
+
+       /* It should now be out of any other write domains, and we can update
+        * the domain values for our changes.
+        */
+       BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0);
+       obj->read_domains |= I915_GEM_DOMAIN_GTT;
+       obj->write_domain = I915_GEM_DOMAIN_GTT;
+       obj_priv->dirty = 1;
+
+       trace_i915_gem_object_change_domain(obj,
+                                           old_read_domains,
+                                           old_write_domain);
+
+       return 0;
+}
+
 /**
  * Moves a single object to the CPU read, and possibly write domain.
  *
@@ -3213,7 +3295,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                     obj_priv->tiling_mode != I915_TILING_NONE;
 
        /* Check fence reg constraints and rebind if necessary */
-       if (need_fence && !i915_obj_fenceable(dev, obj))
+       if (need_fence && !i915_gem_object_fence_offset_ok(obj,
+           obj_priv->tiling_mode))
                i915_gem_object_unbind(obj);
 
        /* Choose the GTT offset for our buffer and put it there. */
@@ -3283,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: "
@@ -3536,6 +3629,9 @@ i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object2 *exec_list,
        uint32_t reloc_count = 0, i;
        int ret = 0;
 
+       if (relocs == NULL)
+           return 0;
+
        for (i = 0; i < buffer_count; i++) {
                struct drm_i915_gem_relocation_entry __user *user_relocs;
                int unwritten;
@@ -3625,7 +3721,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        struct drm_gem_object *batch_obj;
        struct drm_i915_gem_object *obj_priv;
        struct drm_clip_rect *cliprects = NULL;
-       struct drm_i915_gem_relocation_entry *relocs;
+       struct drm_i915_gem_relocation_entry *relocs = NULL;
        int ret = 0, ret2, i, pinned = 0;
        uint64_t exec_offset;
        uint32_t seqno, flush_domains, reloc_index;
@@ -3651,8 +3747,10 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        if (args->num_cliprects != 0) {
                cliprects = kcalloc(args->num_cliprects, sizeof(*cliprects),
                                    GFP_KERNEL);
-               if (cliprects == NULL)
+               if (cliprects == NULL) {
+                       ret = -ENOMEM;
                        goto pre_mutex_err;
+               }
 
                ret = copy_from_user(cliprects,
                                     (struct drm_clip_rect __user *)
@@ -3694,6 +3792,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                if (object_list[i] == NULL) {
                        DRM_ERROR("Invalid object handle %d at index %d\n",
                                   exec_list[i].handle, i);
+                       /* prevent error path from reading uninitialized data */
+                       args->buffer_count = i + 1;
                        ret = -EBADF;
                        goto err;
                }
@@ -3702,6 +3802,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                if (obj_priv->in_execbuffer) {
                        DRM_ERROR("Object %p appears more than once in object list\n",
                                   object_list[i]);
+                       /* prevent error path from reading uninitialized data */
+                       args->buffer_count = i + 1;
                        ret = -EBADF;
                        goto err;
                }
@@ -3815,16 +3917,23 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                i915_gem_flush(dev,
                               dev->invalidate_domains,
                               dev->flush_domains);
-               if (dev->flush_domains)
+               if (dev->flush_domains & I915_GEM_GPU_DOMAINS)
                        (void)i915_add_request(dev, file_priv,
                                               dev->flush_domains);
        }
 
        for (i = 0; i < args->buffer_count; i++) {
                struct drm_gem_object *obj = object_list[i];
+               struct drm_i915_gem_object *obj_priv = obj->driver_private;
                uint32_t old_write_domain = obj->write_domain;
 
                obj->write_domain = obj->pending_write_domain;
+               if (obj->write_domain)
+                       list_move_tail(&obj_priv->gpu_write_list,
+                                      &dev_priv->mm.gpu_write_list);
+               else
+                       list_del_init(&obj_priv->gpu_write_list);
+
                trace_i915_gem_object_change_domain(obj,
                                                    obj->read_domains,
                                                    old_write_domain);
@@ -3898,6 +4007,7 @@ err:
 
        mutex_unlock(&dev->struct_mutex);
 
+pre_mutex_err:
        /* Copy the updated relocations out regardless of current error
         * state.  Failure to update the relocs would mean that the next
         * time userland calls execbuf, it would do so with presumed offset
@@ -3912,7 +4022,6 @@ err:
                        ret = ret2;
        }
 
-pre_mutex_err:
        drm_free_large(object_list);
        kfree(cliprects);
 
@@ -4003,8 +4112,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
                                  "back to user (%d)\n",
                                  args->buffer_count, ret);
                }
-       } else {
-               DRM_ERROR("i915_gem_do_execbuffer returns %d\n", ret);
        }
 
        drm_free_large(exec_list);
@@ -4337,6 +4444,7 @@ int i915_gem_init_object(struct drm_gem_object *obj)
        obj_priv->obj = obj;
        obj_priv->fence_reg = I915_FENCE_REG_NONE;
        INIT_LIST_HEAD(&obj_priv->list);
+       INIT_LIST_HEAD(&obj_priv->gpu_write_list);
        INIT_LIST_HEAD(&obj_priv->fence_list);
        obj_priv->madv = I915_MADV_WILLNEED;
 
@@ -4396,8 +4504,7 @@ int
 i915_gem_idle(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       uint32_t seqno, cur_seqno, last_seqno;
-       int stuck, ret;
+       int ret;
 
        mutex_lock(&dev->struct_mutex);
 
@@ -4406,115 +4513,36 @@ i915_gem_idle(struct drm_device *dev)
                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;
-       del_timer(&dev_priv->hangcheck_timer);
-
-       /* 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
-        */
-       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) {
+       ret = i915_gpu_idle(dev);
+       if (ret) {
                mutex_unlock(&dev->struct_mutex);
-               return -ENOMEM;
+               return ret;
        }
 
-       dev_priv->mm.waiting_gem_seqno = seqno;
-       last_seqno = 0;
-       stuck = 0;
-       for (;;) {
-               cur_seqno = i915_get_gem_seqno(dev);
-               if (i915_seqno_passed(cur_seqno, seqno))
-                       break;
-               if (last_seqno == cur_seqno) {
-                       if (stuck++ > 100) {
-                               DRM_ERROR("hardware wedged\n");
-                               atomic_set(&dev_priv->mm.wedged, 1);
-                               DRM_WAKEUP(&dev_priv->irq_queue);
-                               break;
-                       }
+       /* Under UMS, be paranoid and evict. */
+       if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
+               ret = i915_gem_evict_from_inactive_list(dev);
+               if (ret) {
+                       mutex_unlock(&dev->struct_mutex);
+                       return ret;
                }
-               msleep(10);
-               last_seqno = cur_seqno;
        }
-       dev_priv->mm.waiting_gem_seqno = 0;
 
-       i915_gem_retire_requests(dev);
-
-       spin_lock(&dev_priv->mm.active_list_lock);
-       if (!atomic_read(&dev_priv->mm.wedged)) {
-               /* Active and flushing should now be empty as we've
-                * waited for a sequence higher than any pending execbuffer
-                */
-               WARN_ON(!list_empty(&dev_priv->mm.active_list));
-               WARN_ON(!list_empty(&dev_priv->mm.flushing_list));
-               /* Request should now be empty as we've also waited
-                * for the last request in the list
-                */
-               WARN_ON(!list_empty(&dev_priv->mm.request_list));
-       }
-
-       /* Empty the active and flushing lists to inactive.  If there's
-        * anything left at this point, it means that we're wedged and
-        * nothing good's going to happen by leaving them there.  So strip
-        * the GPU domains and just stuff them onto inactive.
+       /* Hack!  Don't let anybody do execbuf while we don't control the chip.
+        * We need to replace this with a semaphore, or something.
+        * And not confound mm.suspended!
         */
-       while (!list_empty(&dev_priv->mm.active_list)) {
-               struct drm_gem_object *obj;
-               uint32_t old_write_domain;
-
-               obj = list_first_entry(&dev_priv->mm.active_list,
-                                      struct drm_i915_gem_object,
-                                      list)->obj;
-               old_write_domain = obj->write_domain;
-               obj->write_domain &= ~I915_GEM_GPU_DOMAINS;
-               i915_gem_object_move_to_inactive(obj);
-
-               trace_i915_gem_object_change_domain(obj,
-                                                   obj->read_domains,
-                                                   old_write_domain);
-       }
-       spin_unlock(&dev_priv->mm.active_list_lock);
-
-       while (!list_empty(&dev_priv->mm.flushing_list)) {
-               struct drm_gem_object *obj;
-               uint32_t old_write_domain;
-
-               obj = list_first_entry(&dev_priv->mm.flushing_list,
-                                      struct drm_i915_gem_object,
-                                      list)->obj;
-               old_write_domain = obj->write_domain;
-               obj->write_domain &= ~I915_GEM_GPU_DOMAINS;
-               i915_gem_object_move_to_inactive(obj);
-
-               trace_i915_gem_object_change_domain(obj,
-                                                   obj->read_domains,
-                                                   old_write_domain);
-       }
-
-
-       /* Move all inactive buffers out of the GTT. */
-       ret = i915_gem_evict_from_inactive_list(dev);
-       WARN_ON(!list_empty(&dev_priv->mm.inactive_list));
-       if (ret) {
-               mutex_unlock(&dev->struct_mutex);
-               return ret;
-       }
+       dev_priv->mm.suspended = 1;
+       del_timer(&dev_priv->hangcheck_timer);
 
+       i915_kernel_lost_context(dev);
        i915_gem_cleanup_ringbuffer(dev);
+
        mutex_unlock(&dev->struct_mutex);
 
+       /* Cancel the retire work handler, which should be idle now. */
+       cancel_delayed_work_sync(&dev_priv->mm.retire_work);
+
        return 0;
 }
 
@@ -4558,8 +4586,13 @@ i915_gem_init_hws(struct drm_device *dev)
        }
        dev_priv->hws_obj = obj;
        memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
-       I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
-       I915_READ(HWS_PGA); /* posting read */
+       if (IS_GEN6(dev)) {
+               I915_WRITE(HWS_PGA_GEN6, dev_priv->status_gfx_addr);
+               I915_READ(HWS_PGA_GEN6); /* posting read */
+       } else {
+               I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
+               I915_READ(HWS_PGA); /* posting read */
+       }
        DRM_DEBUG_DRIVER("hws offset: 0x%08x\n", dev_priv->status_gfx_addr);
 
        return 0;
@@ -4696,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;
 }
 
@@ -4788,6 +4826,7 @@ i915_gem_load(struct drm_device *dev)
        spin_lock_init(&dev_priv->mm.active_list_lock);
        INIT_LIST_HEAD(&dev_priv->mm.active_list);
        INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
+       INIT_LIST_HEAD(&dev_priv->mm.gpu_write_list);
        INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
        INIT_LIST_HEAD(&dev_priv->mm.request_list);
        INIT_LIST_HEAD(&dev_priv->mm.fence_list);
@@ -4800,7 +4839,8 @@ i915_gem_load(struct drm_device *dev)
        spin_unlock(&shrink_list_lock);
 
        /* Old X drivers will take 0-2 for front, back, depth buffers */
-       dev_priv->fence_reg_start = 3;
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               dev_priv->fence_reg_start = 3;
 
        if (IS_I965G(dev) || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
                dev_priv->num_fence_regs = 16;
@@ -4842,7 +4882,7 @@ int i915_gem_init_phys_object(struct drm_device *dev,
 
        phys_obj->id = id;
 
-       phys_obj->handle = drm_pci_alloc(dev, size, 0, 0xffffffff);
+       phys_obj->handle = drm_pci_alloc(dev, size, 0);
        if (!phys_obj->handle) {
                ret = -ENOMEM;
                goto kfree_obj;
@@ -4900,7 +4940,7 @@ void i915_gem_detach_phys_object(struct drm_device *dev,
        if (!obj_priv->phys_obj)
                return;
 
-       ret = i915_gem_object_get_pages(obj);
+       ret = i915_gem_object_get_pages(obj, 0);
        if (ret)
                goto out;
 
@@ -4958,7 +4998,7 @@ i915_gem_attach_phys_object(struct drm_device *dev,
        obj_priv->phys_obj = dev_priv->mm.phys_objs[id - 1];
        obj_priv->phys_obj->cur_obj = obj;
 
-       ret = i915_gem_object_get_pages(obj);
+       ret = i915_gem_object_get_pages(obj, 0);
        if (ret) {
                DRM_ERROR("failed to get page list\n");
                goto out;