drm/i915: Avoid might_fault during pwrite whilst holding our mutex
[pandora-kernel.git] / drivers / gpu / drm / i915 / i915_gem.c
index efc6a4e..eba9b16 100644 (file)
@@ -268,9 +268,9 @@ fast_shmem_read(struct page **pages,
        char *vaddr;
        int ret;
 
-       vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT], KM_USER0);
+       vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT]);
        ret = __copy_to_user_inatomic(data, vaddr + page_offset, length);
-       kunmap_atomic(vaddr, KM_USER0);
+       kunmap_atomic(vaddr);
 
        return ret;
 }
@@ -547,16 +547,16 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
        struct drm_i915_gem_object *obj_priv;
        int ret = 0;
 
-       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
-       if (obj == NULL)
-               return -ENOENT;
-       obj_priv = to_intel_bo(obj);
-
        ret = i915_mutex_lock_interruptible(dev);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
+       if (ret)
                return ret;
+
+       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+       if (obj == NULL) {
+               ret = -ENOENT;
+               goto unlock;
        }
+       obj_priv = to_intel_bo(obj);
 
        /* Bounds check source.  */
        if (args->offset > obj->size || args->size > obj->size - args->offset) {
@@ -601,6 +601,7 @@ out_put:
        i915_gem_object_put_pages(obj);
 out:
        drm_gem_object_unreference(obj);
+unlock:
        mutex_unlock(&dev->struct_mutex);
        return ret;
 }
@@ -618,10 +619,10 @@ fast_user_write(struct io_mapping *mapping,
        char *vaddr_atomic;
        unsigned long unwritten;
 
-       vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base, KM_USER0);
+       vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base);
        unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + page_offset,
                                                      user_data, length);
-       io_mapping_unmap_atomic(vaddr_atomic, KM_USER0);
+       io_mapping_unmap_atomic(vaddr_atomic);
        return unwritten;
 }
 
@@ -658,9 +659,9 @@ fast_shmem_write(struct page **pages,
        char *vaddr;
        int ret;
 
-       vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT], KM_USER0);
+       vaddr = kmap_atomic(pages[page_base >> PAGE_SHIFT]);
        ret = __copy_from_user_inatomic(vaddr + page_offset, data, length);
-       kunmap_atomic(vaddr, KM_USER0);
+       kunmap_atomic(vaddr);
 
        return ret;
 }
@@ -982,16 +983,17 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
        struct drm_i915_gem_object *obj_priv;
        int ret = 0;
 
-       obj = drm_gem_object_lookup(dev, file, args->handle);
-       if (obj == NULL)
-               return -ENOENT;
-       obj_priv = to_intel_bo(obj);
-
        ret = i915_mutex_lock_interruptible(dev);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
+       if (ret)
                return ret;
+
+       obj = drm_gem_object_lookup(dev, file, args->handle);
+       if (obj == NULL) {
+               ret = -ENOENT;
+               goto unlock;
        }
+       obj_priv = to_intel_bo(obj);
+
 
        /* Bounds check destination. */
        if (args->offset > obj->size || args->size > obj->size - args->offset) {
@@ -1062,6 +1064,7 @@ out_put:
 
 out:
        drm_gem_object_unreference(obj);
+unlock:
        mutex_unlock(&dev->struct_mutex);
        return ret;
 }
@@ -1098,16 +1101,16 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
        if (write_domain != 0 && read_domains != write_domain)
                return -EINVAL;
 
-       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
-       if (obj == NULL)
-               return -ENOENT;
-       obj_priv = to_intel_bo(obj);
-
        ret = i915_mutex_lock_interruptible(dev);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
+       if (ret)
                return ret;
+
+       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+       if (obj == NULL) {
+               ret = -ENOENT;
+               goto unlock;
        }
+       obj_priv = to_intel_bo(obj);
 
        intel_mark_busy(dev, obj);
 
@@ -1136,9 +1139,10 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
 
        /* Maintain LRU order of "inactive" objects */
        if (ret == 0 && i915_gem_object_is_inactive(obj_priv))
-               list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+               list_move_tail(&obj_priv->mm_list, &dev_priv->mm.inactive_list);
 
        drm_gem_object_unreference(obj);
+unlock:
        mutex_unlock(&dev->struct_mutex);
        return ret;
 }
@@ -1157,14 +1161,14 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
        if (!(dev->driver->driver_features & DRIVER_GEM))
                return -ENODEV;
 
-       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
-       if (obj == NULL)
-               return -ENOENT;
-
        ret = i915_mutex_lock_interruptible(dev);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
+       if (ret)
                return ret;
+
+       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+       if (obj == NULL) {
+               ret = -ENOENT;
+               goto unlock;
        }
 
        /* Pinned buffers may be scanout, so flush the cache */
@@ -1172,6 +1176,7 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
                i915_gem_object_flush_cpu_write_domain(obj);
 
        drm_gem_object_unreference(obj);
+unlock:
        mutex_unlock(&dev->struct_mutex);
        return ret;
 }
@@ -1266,7 +1271,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        }
 
        if (i915_gem_object_is_inactive(obj_priv))
-               list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+               list_move_tail(&obj_priv->mm_list, &dev_priv->mm.inactive_list);
 
        pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) +
                page_offset;
@@ -1469,33 +1474,27 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
        if (!(dev->driver->driver_features & DRIVER_GEM))
                return -ENODEV;
 
-       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
-       if (obj == NULL)
-               return -ENOENT;
-
        ret = i915_mutex_lock_interruptible(dev);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
+       if (ret)
                return ret;
-       }
 
+       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+       if (obj == NULL) {
+               ret = -ENOENT;
+               goto unlock;
+       }
        obj_priv = to_intel_bo(obj);
 
        if (obj_priv->madv != I915_MADV_WILLNEED) {
                DRM_ERROR("Attempting to mmap a purgeable buffer\n");
-               drm_gem_object_unreference(obj);
-               mutex_unlock(&dev->struct_mutex);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
-
        if (!obj_priv->mmap_offset) {
                ret = i915_gem_create_mmap_offset(obj);
-               if (ret) {
-                       drm_gem_object_unreference(obj);
-                       mutex_unlock(&dev->struct_mutex);
-                       return ret;
-               }
+               if (ret)
+                       goto out;
        }
 
        args->offset = obj_priv->mmap_offset;
@@ -1506,17 +1505,15 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
         */
        if (!obj_priv->agp_mem) {
                ret = i915_gem_object_bind_to_gtt(obj, 0);
-               if (ret) {
-                       drm_gem_object_unreference(obj);
-                       mutex_unlock(&dev->struct_mutex);
-                       return ret;
-               }
+               if (ret)
+                       goto out;
        }
 
+out:
        drm_gem_object_unreference(obj);
+unlock:
        mutex_unlock(&dev->struct_mutex);
-
-       return 0;
+       return ret;
 }
 
 static void
@@ -1568,6 +1565,7 @@ i915_gem_object_move_to_active(struct drm_gem_object *obj,
                               struct intel_ring_buffer *ring)
 {
        struct drm_device *dev = obj->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
        uint32_t seqno = i915_gem_next_request_seqno(dev, ring);
 
@@ -1581,7 +1579,8 @@ i915_gem_object_move_to_active(struct drm_gem_object *obj,
        }
 
        /* Move from whatever list we were on to the tail of execution. */
-       list_move_tail(&obj_priv->list, &ring->active_list);
+       list_move_tail(&obj_priv->mm_list, &dev_priv->mm.active_list);
+       list_move_tail(&obj_priv->ring_list, &ring->active_list);
        obj_priv->last_rendering_seqno = seqno;
 }
 
@@ -1593,7 +1592,8 @@ i915_gem_object_move_to_flushing(struct drm_gem_object *obj)
        struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
 
        BUG_ON(!obj_priv->active);
-       list_move_tail(&obj_priv->list, &dev_priv->mm.flushing_list);
+       list_move_tail(&obj_priv->mm_list, &dev_priv->mm.flushing_list);
+       list_del_init(&obj_priv->ring_list);
        obj_priv->last_rendering_seqno = 0;
 }
 
@@ -1632,9 +1632,10 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
        struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
 
        if (obj_priv->pin_count != 0)
-               list_move_tail(&obj_priv->list, &dev_priv->mm.pinned_list);
+               list_move_tail(&obj_priv->mm_list, &dev_priv->mm.pinned_list);
        else
-               list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+               list_move_tail(&obj_priv->mm_list, &dev_priv->mm.inactive_list);
+       list_del_init(&obj_priv->ring_list);
 
        BUG_ON(!list_empty(&obj_priv->gpu_write_list));
 
@@ -1656,12 +1657,11 @@ i915_gem_process_flushing_list(struct drm_device *dev,
        struct drm_i915_gem_object *obj_priv, *next;
 
        list_for_each_entry_safe(obj_priv, next,
-                                &dev_priv->mm.gpu_write_list,
+                                &ring->gpu_write_list,
                                 gpu_write_list) {
                struct drm_gem_object *obj = &obj_priv->base;
 
-               if (obj->write_domain & flush_domains &&
-                   obj_priv->ring == ring) {
+               if (obj->write_domain & flush_domains) {
                        uint32_t old_write_domain = obj->write_domain;
 
                        obj->write_domain = 0;
@@ -1783,7 +1783,7 @@ static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv,
 
                obj_priv = list_first_entry(&ring->active_list,
                                            struct drm_i915_gem_object,
-                                           list);
+                                           ring_list);
 
                obj_priv->base.write_domain = 0;
                list_del_init(&obj_priv->gpu_write_list);
@@ -1798,8 +1798,8 @@ void i915_gem_reset(struct drm_device *dev)
        int i;
 
        i915_gem_reset_ring_lists(dev_priv, &dev_priv->render_ring);
-       if (HAS_BSD(dev))
-               i915_gem_reset_ring_lists(dev_priv, &dev_priv->bsd_ring);
+       i915_gem_reset_ring_lists(dev_priv, &dev_priv->bsd_ring);
+       i915_gem_reset_ring_lists(dev_priv, &dev_priv->blt_ring);
 
        /* Remove anything from the flushing lists. The GPU cache is likely
         * to be lost on reset along with the data, so simply move the
@@ -1808,7 +1808,7 @@ void i915_gem_reset(struct drm_device *dev)
        while (!list_empty(&dev_priv->mm.flushing_list)) {
                obj_priv = list_first_entry(&dev_priv->mm.flushing_list,
                                            struct drm_i915_gem_object,
-                                           list);
+                                           mm_list);
 
                obj_priv->base.write_domain = 0;
                list_del_init(&obj_priv->gpu_write_list);
@@ -1820,7 +1820,7 @@ void i915_gem_reset(struct drm_device *dev)
         */
        list_for_each_entry(obj_priv,
                            &dev_priv->mm.inactive_list,
-                           list)
+                           mm_list)
        {
                obj_priv->base.read_domains &= ~I915_GEM_GPU_DOMAINS;
        }
@@ -1880,7 +1880,7 @@ i915_gem_retire_requests_ring(struct drm_device *dev,
 
                obj_priv = list_first_entry(&ring->active_list,
                                            struct drm_i915_gem_object,
-                                           list);
+                                           ring_list);
 
                if (!i915_seqno_passed(seqno, obj_priv->last_rendering_seqno))
                        break;
@@ -1916,13 +1916,13 @@ i915_gem_retire_requests(struct drm_device *dev)
             */
            list_for_each_entry_safe(obj_priv, tmp,
                                     &dev_priv->mm.deferred_free_list,
-                                    list)
+                                    mm_list)
                    i915_gem_free_object_tail(&obj_priv->base);
        }
 
        i915_gem_retire_requests_ring(dev, &dev_priv->render_ring);
-       if (HAS_BSD(dev))
-               i915_gem_retire_requests_ring(dev, &dev_priv->bsd_ring);
+       i915_gem_retire_requests_ring(dev, &dev_priv->bsd_ring);
+       i915_gem_retire_requests_ring(dev, &dev_priv->blt_ring);
 }
 
 static void
@@ -1945,8 +1945,8 @@ i915_gem_retire_work_handler(struct work_struct *work)
 
        if (!dev_priv->mm.suspended &&
                (!list_empty(&dev_priv->render_ring.request_list) ||
-                       (HAS_BSD(dev) &&
-                        !list_empty(&dev_priv->bsd_ring.request_list))))
+                !list_empty(&dev_priv->bsd_ring.request_list) ||
+                !list_empty(&dev_priv->blt_ring.request_list)))
                queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ);
        mutex_unlock(&dev->struct_mutex);
 }
@@ -2065,6 +2065,10 @@ i915_gem_flush(struct drm_device *dev,
                        i915_gem_flush_ring(dev, file_priv,
                                            &dev_priv->bsd_ring,
                                            invalidate_domains, flush_domains);
+               if (flush_rings & RING_BLT)
+                       i915_gem_flush_ring(dev, file_priv,
+                                           &dev_priv->blt_ring,
+                                           invalidate_domains, flush_domains);
        }
 }
 
@@ -2151,10 +2155,11 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
        BUG_ON(obj_priv->pages_refcount);
 
        i915_gem_info_remove_gtt(dev_priv, obj->size);
-       list_del_init(&obj_priv->list);
+       list_del_init(&obj_priv->mm_list);
 
        drm_mm_put_block(obj_priv->gtt_space);
        obj_priv->gtt_space = NULL;
+       obj_priv->gtt_offset = 0;
 
        if (i915_gem_object_is_purgeable(obj_priv))
                i915_gem_object_truncate(obj);
@@ -2167,6 +2172,9 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
 static int i915_ring_idle(struct drm_device *dev,
                          struct intel_ring_buffer *ring)
 {
+       if (list_empty(&ring->gpu_write_list) && list_empty(&ring->active_list))
+               return 0;
+
        i915_gem_flush_ring(dev, NULL, ring,
                            I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
        return i915_wait_request(dev,
@@ -2182,9 +2190,7 @@ i915_gpu_idle(struct drm_device *dev)
        int ret;
 
        lists_empty = (list_empty(&dev_priv->mm.flushing_list) &&
-                      list_empty(&dev_priv->render_ring.active_list) &&
-                      (!HAS_BSD(dev) ||
-                       list_empty(&dev_priv->bsd_ring.active_list)));
+                      list_empty(&dev_priv->mm.active_list));
        if (lists_empty)
                return 0;
 
@@ -2193,11 +2199,13 @@ i915_gpu_idle(struct drm_device *dev)
        if (ret)
                return ret;
 
-       if (HAS_BSD(dev)) {
-               ret = i915_ring_idle(dev, &dev_priv->bsd_ring);
-               if (ret)
-                       return ret;
-       }
+       ret = i915_ring_idle(dev, &dev_priv->bsd_ring);
+       if (ret)
+               return ret;
+
+       ret = i915_ring_idle(dev, &dev_priv->blt_ring);
+       if (ret)
+               return ret;
 
        return 0;
 }
@@ -2648,12 +2656,9 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
  search_free:
        free_space = drm_mm_search_free(&dev_priv->mm.gtt_space,
                                        obj->size, alignment, 0);
-       if (free_space != NULL) {
+       if (free_space != NULL)
                obj_priv->gtt_space = drm_mm_get_block(free_space, obj->size,
                                                       alignment);
-               if (obj_priv->gtt_space != NULL)
-                       obj_priv->gtt_offset = obj_priv->gtt_space->start;
-       }
        if (obj_priv->gtt_space == NULL) {
                /* If the gtt is empty and we're still having trouble
                 * fitting our object in, we're out of memory.
@@ -2696,7 +2701,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
        obj_priv->agp_mem = drm_agp_bind_pages(dev,
                                               obj_priv->pages,
                                               obj->size >> PAGE_SHIFT,
-                                              obj_priv->gtt_offset,
+                                              obj_priv->gtt_space->start,
                                               obj_priv->agp_type);
        if (obj_priv->agp_mem == NULL) {
                i915_gem_object_put_pages(obj);
@@ -2711,7 +2716,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
        }
 
        /* keep track of bounds object by adding it to the inactive list */
-       list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+       list_add_tail(&obj_priv->mm_list, &dev_priv->mm.inactive_list);
        i915_gem_info_add_gtt(dev_priv, obj->size);
 
        /* Assert that the object is not currently in any GPU domain. As it
@@ -2721,6 +2726,7 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
        BUG_ON(obj->read_domains & I915_GEM_GPU_DOMAINS);
        BUG_ON(obj->write_domain & I915_GEM_GPU_DOMAINS);
 
+       obj_priv->gtt_offset = obj_priv->gtt_space->start;
        trace_i915_gem_object_bind(obj, obj_priv->gtt_offset);
 
        return 0;
@@ -3072,7 +3078,8 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write)
  *             drm_agp_chipset_flush
  */
 static void
-i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj)
+i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
+                                 struct intel_ring_buffer *ring)
 {
        struct drm_device               *dev = obj->dev;
        struct drm_i915_private         *dev_priv = dev->dev_private;
@@ -3099,7 +3106,8 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj)
         * write domain
         */
        if (obj->write_domain &&
-           obj->write_domain != obj->pending_read_domains) {
+           (obj->write_domain != obj->pending_read_domains ||
+            obj_priv->ring != ring)) {
                flush_domains |= obj->write_domain;
                invalidate_domains |=
                        obj->pending_read_domains & ~obj->write_domain;
@@ -3126,8 +3134,10 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj)
 
        dev->invalidate_domains |= invalidate_domains;
        dev->flush_domains |= flush_domains;
-       if (obj_priv->ring)
+       if (flush_domains & I915_GEM_GPU_DOMAINS)
                dev_priv->mm.flush_rings |= obj_priv->ring->id;
+       if (invalidate_domains & I915_GEM_GPU_DOMAINS)
+               dev_priv->mm.flush_rings |= ring->id;
 
        trace_i915_gem_object_change_domain(obj,
                                            old_read_domains,
@@ -3243,74 +3253,42 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
  * Pin an object to the GTT and evaluate the relocations landing in it.
  */
 static int
-i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
-                                struct drm_file *file_priv,
-                                struct drm_i915_gem_exec_object2 *entry)
+i915_gem_execbuffer_relocate(struct drm_i915_gem_object *obj,
+                            struct drm_file *file_priv,
+                            struct drm_i915_gem_exec_object2 *entry)
 {
-       struct drm_device *dev = obj->dev;
+       struct drm_device *dev = obj->base.dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
-       struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
        struct drm_i915_gem_relocation_entry __user *user_relocs;
-       int i, ret;
-       bool need_fence;
-
-       need_fence = entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
-                    obj_priv->tiling_mode != I915_TILING_NONE;
-
-       /* Check fence reg constraints and rebind if necessary */
-       if (need_fence &&
-           !i915_gem_object_fence_offset_ok(obj,
-                                            obj_priv->tiling_mode)) {
-               ret = i915_gem_object_unbind(obj);
-               if (ret)
-                       return ret;
-       }
-
-       /* Choose the GTT offset for our buffer and put it there. */
-       ret = i915_gem_object_pin(obj, (uint32_t) entry->alignment);
-       if (ret)
-               return ret;
-
-       /*
-        * Pre-965 chips need a fence register set up in order to
-        * properly handle blits to/from tiled surfaces.
-        */
-       if (need_fence) {
-               ret = i915_gem_object_get_fence_reg(obj, true);
-               if (ret != 0) {
-                       i915_gem_object_unpin(obj);
-                       return ret;
-               }
-
-               dev_priv->fence_regs[obj_priv->fence_reg].gpu = true;
-       }
-
-       entry->offset = obj_priv->gtt_offset;
+       struct drm_gem_object *target_obj = NULL;
+       uint32_t target_handle = 0;
+       int i, ret = 0;
 
-       /* Apply the relocations, using the GTT aperture to avoid cache
-        * flushing requirements.
-        */
        user_relocs = (void __user *)(uintptr_t)entry->relocs_ptr;
        for (i = 0; i < entry->relocation_count; i++) {
                struct drm_i915_gem_relocation_entry reloc;
-               struct drm_gem_object *target_obj;
-               struct drm_i915_gem_object *target_obj_priv;
+               uint32_t target_offset;
 
-               ret = __copy_from_user_inatomic(&reloc,
-                                               user_relocs+i,
-                                               sizeof(reloc));
-               if (ret) {
-                       i915_gem_object_unpin(obj);
-                       return -EFAULT;
+               if (__copy_from_user_inatomic(&reloc,
+                                             user_relocs+i,
+                                             sizeof(reloc))) {
+                       ret = -EFAULT;
+                       break;
                }
 
-               target_obj = drm_gem_object_lookup(obj->dev, file_priv,
-                                                  reloc.target_handle);
-               if (target_obj == NULL) {
-                       i915_gem_object_unpin(obj);
-                       return -ENOENT;
+               if (reloc.target_handle != target_handle) {
+                       drm_gem_object_unreference(target_obj);
+
+                       target_obj = drm_gem_object_lookup(dev, file_priv,
+                                                          reloc.target_handle);
+                       if (target_obj == NULL) {
+                               ret = -ENOENT;
+                               break;
+                       }
+
+                       target_handle = reloc.target_handle;
                }
-               target_obj_priv = to_intel_bo(target_obj);
+               target_offset = to_intel_bo(target_obj)->gtt_offset;
 
 #if WATCH_RELOC
                DRM_INFO("%s: obj %p offset %08x target %d "
@@ -3322,7 +3300,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                         (int) reloc.target_handle,
                         (int) reloc.read_domains,
                         (int) reloc.write_domain,
-                        (int) target_obj_priv->gtt_offset,
+                        (int) target_offset,
                         (int) reloc.presumed_offset,
                         reloc.delta);
 #endif
@@ -3330,12 +3308,11 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                /* The target buffer should have appeared before us in the
                 * exec_object list, so it should have a GTT space bound by now.
                 */
-               if (target_obj_priv->gtt_space == NULL) {
+               if (target_offset == 0) {
                        DRM_ERROR("No GTT space found for object %d\n",
                                  reloc.target_handle);
-                       drm_gem_object_unreference(target_obj);
-                       i915_gem_object_unpin(obj);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       break;
                }
 
                /* Validate that the target is in a valid r/w GPU domain */
@@ -3347,9 +3324,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                                  (int) reloc.offset,
                                  reloc.read_domains,
                                  reloc.write_domain);
-                       drm_gem_object_unreference(target_obj);
-                       i915_gem_object_unpin(obj);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       break;
                }
                if (reloc.write_domain & I915_GEM_DOMAIN_CPU ||
                    reloc.read_domains & I915_GEM_DOMAIN_CPU) {
@@ -3360,9 +3336,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                                  (int) reloc.offset,
                                  reloc.read_domains,
                                  reloc.write_domain);
-                       drm_gem_object_unreference(target_obj);
-                       i915_gem_object_unpin(obj);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       break;
                }
                if (reloc.write_domain && target_obj->pending_write_domain &&
                    reloc.write_domain != target_obj->pending_write_domain) {
@@ -3373,9 +3348,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                                  (int) reloc.offset,
                                  reloc.write_domain,
                                  target_obj->pending_write_domain);
-                       drm_gem_object_unreference(target_obj);
-                       i915_gem_object_unpin(obj);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       break;
                }
 
                target_obj->pending_read_domains |= reloc.read_domains;
@@ -3384,29 +3358,25 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                /* If the relocation already has the right value in it, no
                 * more work needs to be done.
                 */
-               if (target_obj_priv->gtt_offset == reloc.presumed_offset) {
-                       drm_gem_object_unreference(target_obj);
+               if (target_offset == reloc.presumed_offset)
                        continue;
-               }
 
                /* Check that the relocation address is valid... */
-               if (reloc.offset > obj->size - 4) {
+               if (reloc.offset > obj->base.size - 4) {
                        DRM_ERROR("Relocation beyond object bounds: "
                                  "obj %p target %d offset %d size %d.\n",
                                  obj, reloc.target_handle,
-                                 (int) reloc.offset, (int) obj->size);
-                       drm_gem_object_unreference(target_obj);
-                       i915_gem_object_unpin(obj);
-                       return -EINVAL;
+                                 (int) reloc.offset, (int) obj->base.size);
+                       ret = -EINVAL;
+                       break;
                }
                if (reloc.offset & 3) {
                        DRM_ERROR("Relocation not 4-byte aligned: "
                                  "obj %p target %d offset %d.\n",
                                  obj, reloc.target_handle,
                                  (int) reloc.offset);
-                       drm_gem_object_unreference(target_obj);
-                       i915_gem_object_unpin(obj);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       break;
                }
 
                /* and points to somewhere within the target object. */
@@ -3415,43 +3385,158 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                                  "obj %p target %d delta %d size %d.\n",
                                  obj, reloc.target_handle,
                                  (int) reloc.delta, (int) target_obj->size);
-                       drm_gem_object_unreference(target_obj);
-                       i915_gem_object_unpin(obj);
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       break;
                }
 
-               reloc.delta += target_obj_priv->gtt_offset;
-               if (obj->write_domain == I915_GEM_DOMAIN_CPU) {
+               reloc.delta += target_offset;
+               if (obj->base.write_domain == I915_GEM_DOMAIN_CPU) {
                        uint32_t page_offset = reloc.offset & ~PAGE_MASK;
                        char *vaddr;
 
-                       vaddr = kmap_atomic(obj_priv->pages[reloc.offset >> PAGE_SHIFT], KM_USER0);
+                       vaddr = kmap_atomic(obj->pages[reloc.offset >> PAGE_SHIFT]);
                        *(uint32_t *)(vaddr + page_offset) = reloc.delta;
-                       kunmap_atomic(vaddr, KM_USER0);
+                       kunmap_atomic(vaddr);
                } else {
                        uint32_t __iomem *reloc_entry;
                        void __iomem *reloc_page;
-                       int ret;
 
-                       ret = i915_gem_object_set_to_gtt_domain(obj, 1);
-                       if (ret) {
-                               drm_gem_object_unreference(target_obj);
-                               i915_gem_object_unpin(obj);
-                               return ret;
-                       }
+                       ret = i915_gem_object_set_to_gtt_domain(&obj->base, 1);
+                       if (ret)
+                               break;
 
                        /* Map the page containing the relocation we're going to perform.  */
-                       reloc.offset += obj_priv->gtt_offset;
+                       reloc.offset += obj->gtt_offset;
                        reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
-                                                             reloc.offset & PAGE_MASK,
-                                                             KM_USER0);
+                                                             reloc.offset & PAGE_MASK);
                        reloc_entry = (uint32_t __iomem *)
                                (reloc_page + (reloc.offset & ~PAGE_MASK));
                        iowrite32(reloc.delta, reloc_entry);
-                       io_mapping_unmap_atomic(reloc_page, KM_USER0);
+                       io_mapping_unmap_atomic(reloc_page);
+               }
+
+               /* and update the user's relocation entry */
+               reloc.presumed_offset = target_offset;
+               if (__copy_to_user_inatomic(&user_relocs[i].presumed_offset,
+                                             &reloc.presumed_offset,
+                                             sizeof(reloc.presumed_offset))) {
+                   ret = -EFAULT;
+                   break;
+               }
+       }
+
+       drm_gem_object_unreference(target_obj);
+       return ret;
+}
+
+static int
+i915_gem_execbuffer_pin(struct drm_device *dev,
+                       struct drm_file *file,
+                       struct drm_gem_object **object_list,
+                       struct drm_i915_gem_exec_object2 *exec_list,
+                       int count)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret, i, retry;
+
+       /* attempt to pin all of the buffers into the GTT */
+       for (retry = 0; retry < 2; retry++) {
+               ret = 0;
+               for (i = 0; i < count; i++) {
+                       struct drm_i915_gem_exec_object2 *entry = &exec_list[i];
+                       struct drm_i915_gem_object *obj= to_intel_bo(object_list[i]);
+                       bool need_fence =
+                               entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
+                               obj->tiling_mode != I915_TILING_NONE;
+
+                       /* Check fence reg constraints and rebind if necessary */
+                       if (need_fence &&
+                           !i915_gem_object_fence_offset_ok(&obj->base,
+                                                            obj->tiling_mode)) {
+                               ret = i915_gem_object_unbind(&obj->base);
+                               if (ret)
+                                       break;
+                       }
+
+                       ret = i915_gem_object_pin(&obj->base, entry->alignment);
+                       if (ret)
+                               break;
+
+                       /*
+                        * Pre-965 chips need a fence register set up in order
+                        * to properly handle blits to/from tiled surfaces.
+                        */
+                       if (need_fence) {
+                               ret = i915_gem_object_get_fence_reg(&obj->base, true);
+                               if (ret) {
+                                       i915_gem_object_unpin(&obj->base);
+                                       break;
+                               }
+
+                               dev_priv->fence_regs[obj->fence_reg].gpu = true;
+                       }
+
+                       entry->offset = obj->gtt_offset;
                }
 
-               drm_gem_object_unreference(target_obj);
+               while (i--)
+                       i915_gem_object_unpin(object_list[i]);
+
+               if (ret == 0)
+                       break;
+
+               if (ret != -ENOSPC || retry)
+                       return ret;
+
+               ret = i915_gem_evict_everything(dev);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int
+i915_gem_execbuffer_move_to_gpu(struct drm_device *dev,
+                               struct drm_file *file,
+                               struct intel_ring_buffer *ring,
+                               struct drm_gem_object **objects,
+                               int count)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret, i;
+
+       /* Zero the global flush/invalidate flags. These
+        * will be modified as new domains are computed
+        * for each object
+        */
+       dev->invalidate_domains = 0;
+       dev->flush_domains = 0;
+       dev_priv->mm.flush_rings = 0;
+       for (i = 0; i < count; i++)
+               i915_gem_object_set_to_gpu_domain(objects[i], ring);
+
+       if (dev->invalidate_domains | dev->flush_domains) {
+#if WATCH_EXEC
+               DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n",
+                         __func__,
+                        dev->invalidate_domains,
+                        dev->flush_domains);
+#endif
+               i915_gem_flush(dev, file,
+                              dev->invalidate_domains,
+                              dev->flush_domains,
+                              dev_priv->mm.flush_rings);
+       }
+
+       for (i = 0; i < count; i++) {
+               struct drm_i915_gem_object *obj = to_intel_bo(objects[i]);
+               /* XXX replace with semaphores */
+               if (obj->ring && ring != obj->ring) {
+                       ret = i915_gem_object_wait_rendering(&obj->base, true);
+                       if (ret)
+                               return ret;
+               }
        }
 
        return 0;
@@ -3545,6 +3630,10 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
                if (!access_ok(VERIFY_READ, ptr, length))
                        return -EFAULT;
 
+               /* we may also need to update the presumed offsets */
+               if (!access_ok(VERIFY_WRITE, ptr, length))
+                       return -EFAULT;
+
                if (fault_in_pages_readable(ptr, length))
                        return -EFAULT;
        }
@@ -3554,7 +3643,7 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
 
 static int
 i915_gem_do_execbuffer(struct drm_device *dev, void *data,
-                      struct drm_file *file_priv,
+                      struct drm_file *file,
                       struct drm_i915_gem_execbuffer2 *args,
                       struct drm_i915_gem_exec_object2 *exec_list)
 {
@@ -3564,9 +3653,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        struct drm_i915_gem_object *obj_priv;
        struct drm_clip_rect *cliprects = NULL;
        struct drm_i915_gem_request *request = NULL;
-       int ret, i, pinned = 0;
+       int ret, i, flips;
        uint64_t exec_offset;
-       int pin_tries, flips;
 
        struct intel_ring_buffer *ring = NULL;
 
@@ -3582,14 +3670,29 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
                  (int) args->buffers_ptr, args->buffer_count, args->batch_len);
 #endif
-       if (args->flags & I915_EXEC_BSD) {
+       switch (args->flags & I915_EXEC_RING_MASK) {
+       case I915_EXEC_DEFAULT:
+       case I915_EXEC_RENDER:
+               ring = &dev_priv->render_ring;
+               break;
+       case I915_EXEC_BSD:
                if (!HAS_BSD(dev)) {
-                       DRM_ERROR("execbuf with wrong flag\n");
+                       DRM_ERROR("execbuf with invalid ring (BSD)\n");
                        return -EINVAL;
                }
                ring = &dev_priv->bsd_ring;
-       } else {
-               ring = &dev_priv->render_ring;
+               break;
+       case I915_EXEC_BLT:
+               if (!HAS_BLT(dev)) {
+                       DRM_ERROR("execbuf with invalid ring (BLT)\n");
+                       return -EINVAL;
+               }
+               ring = &dev_priv->blt_ring;
+               break;
+       default:
+               DRM_ERROR("execbuf with unknown ring: %d\n",
+                         (int)(args->flags & I915_EXEC_RING_MASK));
+               return -EINVAL;
        }
 
        if (args->buffer_count < 1) {
@@ -3642,7 +3745,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
 
        /* Look up object handles */
        for (i = 0; i < args->buffer_count; i++) {
-               object_list[i] = drm_gem_object_lookup(dev, file_priv,
+               object_list[i] = drm_gem_object_lookup(dev, file,
                                                       exec_list[i].handle);
                if (object_list[i] == NULL) {
                        DRM_ERROR("Invalid object handle %d at index %d\n",
@@ -3665,63 +3768,20 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                obj_priv->in_execbuffer = true;
        }
 
-       /* Pin and relocate */
-       for (pin_tries = 0; ; pin_tries++) {
-               ret = 0;
-
-               for (i = 0; i < args->buffer_count; i++) {
-                       object_list[i]->pending_read_domains = 0;
-                       object_list[i]->pending_write_domain = 0;
-                       ret = i915_gem_object_pin_and_relocate(object_list[i],
-                                                              file_priv,
-                                                              &exec_list[i]);
-                       if (ret)
-                               break;
-                       pinned = i + 1;
-               }
-               /* success */
-               if (ret == 0)
-                       break;
-
-               /* error other than GTT full, or we've already tried again */
-               if (ret != -ENOSPC || pin_tries >= 1) {
-                       if (ret != -ERESTARTSYS) {
-                               unsigned long long total_size = 0;
-                               int num_fences = 0;
-                               for (i = 0; i < args->buffer_count; i++) {
-                                       obj_priv = to_intel_bo(object_list[i]);
-
-                                       total_size += object_list[i]->size;
-                                       num_fences +=
-                                               exec_list[i].flags & EXEC_OBJECT_NEEDS_FENCE &&
-                                               obj_priv->tiling_mode != I915_TILING_NONE;
-                               }
-                               DRM_ERROR("Failed to pin buffer %d of %d, total %llu bytes, %d fences: %d\n",
-                                         pinned+1, args->buffer_count,
-                                         total_size, num_fences,
-                                         ret);
-                               DRM_ERROR("%u objects [%u pinned, %u GTT], "
-                                         "%zu object bytes [%zu pinned], "
-                                         "%zu /%zu gtt bytes\n",
-                                         dev_priv->mm.object_count,
-                                         dev_priv->mm.pin_count,
-                                         dev_priv->mm.gtt_count,
-                                         dev_priv->mm.object_memory,
-                                         dev_priv->mm.pin_memory,
-                                         dev_priv->mm.gtt_memory,
-                                         dev_priv->mm.gtt_total);
-                       }
-                       goto err;
-               }
-
-               /* unpin all of our buffers */
-               for (i = 0; i < pinned; i++)
-                       i915_gem_object_unpin(object_list[i]);
-               pinned = 0;
+       /* Move the objects en-masse into the GTT, evicting if necessary. */
+       ret = i915_gem_execbuffer_pin(dev, file,
+                                     object_list, exec_list,
+                                     args->buffer_count);
+       if (ret)
+               goto err;
 
-               /* evict everyone we can from the aperture */
-               ret = i915_gem_evict_everything(dev);
-               if (ret && ret != -ENOSPC)
+       /* The objects are in their final locations, apply the relocations. */
+       for (i = 0; i < args->buffer_count; i++) {
+               struct drm_i915_gem_object *obj = to_intel_bo(object_list[i]);
+               obj->base.pending_read_domains = 0;
+               obj->base.pending_write_domain = 0;
+               ret = i915_gem_execbuffer_relocate(obj, file, &exec_list[i]);
+               if (ret)
                        goto err;
        }
 
@@ -3734,52 +3794,23 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        }
        batch_obj->pending_read_domains |= I915_GEM_DOMAIN_COMMAND;
 
-       /* Sanity check the batch buffer, prior to moving objects */
-       exec_offset = exec_list[args->buffer_count - 1].offset;
-       ret = i915_gem_check_execbuffer (args, exec_offset);
+       /* Sanity check the batch buffer */
+       exec_offset = to_intel_bo(batch_obj)->gtt_offset;
+       ret = i915_gem_check_execbuffer(args, exec_offset);
        if (ret != 0) {
                DRM_ERROR("execbuf with invalid offset/length\n");
                goto err;
        }
 
-       /* Zero the global flush/invalidate flags. These
-        * will be modified as new domains are computed
-        * for each object
-        */
-       dev->invalidate_domains = 0;
-       dev->flush_domains = 0;
-       dev_priv->mm.flush_rings = 0;
-
-       for (i = 0; i < args->buffer_count; i++) {
-               struct drm_gem_object *obj = object_list[i];
-
-               /* Compute new gpu domains and update invalidate/flush */
-               i915_gem_object_set_to_gpu_domain(obj);
-       }
-
-       if (dev->invalidate_domains | dev->flush_domains) {
-#if WATCH_EXEC
-               DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n",
-                         __func__,
-                        dev->invalidate_domains,
-                        dev->flush_domains);
-#endif
-               i915_gem_flush(dev, file_priv,
-                              dev->invalidate_domains,
-                              dev->flush_domains,
-                              dev_priv->mm.flush_rings);
-       }
+       ret = i915_gem_execbuffer_move_to_gpu(dev, file, ring,
+                                             object_list, args->buffer_count);
+       if (ret)
+               goto err;
 
        for (i = 0; i < args->buffer_count; i++) {
                struct drm_gem_object *obj = object_list[i];
-               struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
                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);
-
                trace_i915_gem_object_change_domain(obj,
                                                    obj->read_domains,
                                                    old_write_domain);
@@ -3844,18 +3875,17 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
 
        for (i = 0; i < args->buffer_count; i++) {
                struct drm_gem_object *obj = object_list[i];
-               obj_priv = to_intel_bo(obj);
 
                i915_gem_object_move_to_active(obj, ring);
+               if (obj->write_domain)
+                       list_move_tail(&to_intel_bo(obj)->gpu_write_list,
+                                      &ring->gpu_write_list);
        }
 
-       i915_add_request(dev, file_priv, request, ring);
+       i915_add_request(dev, file, request, ring);
        request = NULL;
 
 err:
-       for (i = 0; i < pinned; i++)
-               i915_gem_object_unpin(object_list[i]);
-
        for (i = 0; i < args->buffer_count; i++) {
                if (object_list[i]) {
                        obj_priv = to_intel_bo(object_list[i]);
@@ -4058,7 +4088,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
        if (obj_priv->pin_count == 1) {
                i915_gem_info_add_pin(dev_priv, obj->size);
                if (!obj_priv->active)
-                       list_move_tail(&obj_priv->list,
+                       list_move_tail(&obj_priv->mm_list,
                                       &dev_priv->mm.pinned_list);
        }
 
@@ -4084,7 +4114,7 @@ i915_gem_object_unpin(struct drm_gem_object *obj)
         */
        if (obj_priv->pin_count == 0) {
                if (!obj_priv->active)
-                       list_move_tail(&obj_priv->list,
+                       list_move_tail(&obj_priv->mm_list,
                                       &dev_priv->mm.inactive_list);
                i915_gem_info_remove_pin(dev_priv, obj->size);
        }
@@ -4100,44 +4130,36 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
        struct drm_i915_gem_object *obj_priv;
        int ret;
 
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (obj == NULL) {
-               DRM_ERROR("Bad handle in i915_gem_pin_ioctl(): %d\n",
-                         args->handle);
-               return -ENOENT;
+               ret = -ENOENT;
+               goto unlock;
        }
        obj_priv = to_intel_bo(obj);
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
-               return ret;
-       }
-
        if (obj_priv->madv != I915_MADV_WILLNEED) {
                DRM_ERROR("Attempting to pin a purgeable buffer\n");
-               drm_gem_object_unreference(obj);
-               mutex_unlock(&dev->struct_mutex);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != file_priv) {
                DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n",
                          args->handle);
-               drm_gem_object_unreference(obj);
-               mutex_unlock(&dev->struct_mutex);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        obj_priv->user_pin_count++;
        obj_priv->pin_filp = file_priv;
        if (obj_priv->user_pin_count == 1) {
                ret = i915_gem_object_pin(obj, args->alignment);
-               if (ret != 0) {
-                       drm_gem_object_unreference(obj);
-                       mutex_unlock(&dev->struct_mutex);
-                       return ret;
-               }
+               if (ret)
+                       goto out;
        }
 
        /* XXX - flush the CPU caches for pinned objects
@@ -4145,10 +4167,11 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
         */
        i915_gem_object_flush_cpu_write_domain(obj);
        args->offset = obj_priv->gtt_offset;
+out:
        drm_gem_object_unreference(obj);
+unlock:
        mutex_unlock(&dev->struct_mutex);
-
-       return 0;
+       return ret;
 }
 
 int
@@ -4160,27 +4183,22 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
        struct drm_i915_gem_object *obj_priv;
        int ret;
 
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (obj == NULL) {
-               DRM_ERROR("Bad handle in i915_gem_unpin_ioctl(): %d\n",
-                         args->handle);
-               return -ENOENT;
+               ret = -ENOENT;
+               goto unlock;
        }
-
        obj_priv = to_intel_bo(obj);
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
-               return ret;
-       }
-
        if (obj_priv->pin_filp != file_priv) {
                DRM_ERROR("Not pinned by caller in i915_gem_pin_ioctl(): %d\n",
                          args->handle);
-               drm_gem_object_unreference(obj);
-               mutex_unlock(&dev->struct_mutex);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
        obj_priv->user_pin_count--;
        if (obj_priv->user_pin_count == 0) {
@@ -4188,9 +4206,11 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
                i915_gem_object_unpin(obj);
        }
 
+out:
        drm_gem_object_unreference(obj);
+unlock:
        mutex_unlock(&dev->struct_mutex);
-       return 0;
+       return ret;
 }
 
 int
@@ -4202,25 +4222,22 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
        struct drm_i915_gem_object *obj_priv;
        int ret;
 
-       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
-       if (obj == NULL) {
-               DRM_ERROR("Bad handle in i915_gem_busy_ioctl(): %d\n",
-                         args->handle);
-               return -ENOENT;
-       }
-
        ret = i915_mutex_lock_interruptible(dev);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
+       if (ret)
                return ret;
+
+       obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+       if (obj == NULL) {
+               ret = -ENOENT;
+               goto unlock;
        }
+       obj_priv = to_intel_bo(obj);
 
        /* Count all active objects as busy, even if they are currently not used
         * by the gpu. Users of this interface expect objects to eventually
         * become non-busy without any further actions, therefore emit any
         * necessary flushes here.
         */
-       obj_priv = to_intel_bo(obj);
        args->busy = obj_priv->active;
        if (args->busy) {
                /* Unconditionally flush objects, even when the gpu still uses this
@@ -4244,8 +4261,9 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
        }
 
        drm_gem_object_unreference(obj);
+unlock:
        mutex_unlock(&dev->struct_mutex);
-       return 0;
+       return ret;
 }
 
 int
@@ -4272,26 +4290,20 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
            return -EINVAL;
        }
 
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ret;
+
        obj = drm_gem_object_lookup(dev, file_priv, args->handle);
        if (obj == NULL) {
-               DRM_ERROR("Bad handle in i915_gem_madvise_ioctl(): %d\n",
-                         args->handle);
-               return -ENOENT;
+               ret = -ENOENT;
+               goto unlock;
        }
        obj_priv = to_intel_bo(obj);
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
-               return ret;
-       }
-
        if (obj_priv->pin_count) {
-               drm_gem_object_unreference(obj);
-               mutex_unlock(&dev->struct_mutex);
-
-               DRM_ERROR("Attempted i915_gem_madvise_ioctl() on a pinned object\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        if (obj_priv->madv != __I915_MADV_PURGED)
@@ -4304,10 +4316,11 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
 
        args->retained = obj_priv->madv != __I915_MADV_PURGED;
 
+out:
        drm_gem_object_unreference(obj);
+unlock:
        mutex_unlock(&dev->struct_mutex);
-
-       return 0;
+       return ret;
 }
 
 struct drm_gem_object * i915_gem_alloc_object(struct drm_device *dev,
@@ -4333,7 +4346,8 @@ struct drm_gem_object * i915_gem_alloc_object(struct drm_device *dev,
        obj->agp_type = AGP_USER_MEMORY;
        obj->base.driver_private = NULL;
        obj->fence_reg = I915_FENCE_REG_NONE;
-       INIT_LIST_HEAD(&obj->list);
+       INIT_LIST_HEAD(&obj->mm_list);
+       INIT_LIST_HEAD(&obj->ring_list);
        INIT_LIST_HEAD(&obj->gpu_write_list);
        obj->madv = I915_MADV_WILLNEED;
 
@@ -4356,7 +4370,7 @@ static void i915_gem_free_object_tail(struct drm_gem_object *obj)
 
        ret = i915_gem_object_unbind(obj);
        if (ret == -ERESTARTSYS) {
-               list_move(&obj_priv->list,
+               list_move(&obj_priv->mm_list,
                          &dev_priv->mm.deferred_free_list);
                return;
        }
@@ -4396,10 +4410,7 @@ i915_gem_idle(struct drm_device *dev)
 
        mutex_lock(&dev->struct_mutex);
 
-       if (dev_priv->mm.suspended ||
-                       (dev_priv->render_ring.gem_object == NULL) ||
-                       (HAS_BSD(dev) &&
-                        dev_priv->bsd_ring.gem_object == NULL)) {
+       if (dev_priv->mm.suspended) {
                mutex_unlock(&dev->struct_mutex);
                return 0;
        }
@@ -4520,10 +4531,18 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
                        goto cleanup_render_ring;
        }
 
+       if (HAS_BLT(dev)) {
+               ret = intel_init_blt_ring_buffer(dev);
+               if (ret)
+                       goto cleanup_bsd_ring;
+       }
+
        dev_priv->next_seqno = 1;
 
        return 0;
 
+cleanup_bsd_ring:
+       intel_cleanup_ring_buffer(dev, &dev_priv->bsd_ring);
 cleanup_render_ring:
        intel_cleanup_ring_buffer(dev, &dev_priv->render_ring);
 cleanup_pipe_control:
@@ -4538,8 +4557,8 @@ i915_gem_cleanup_ringbuffer(struct drm_device *dev)
        drm_i915_private_t *dev_priv = dev->dev_private;
 
        intel_cleanup_ring_buffer(dev, &dev_priv->render_ring);
-       if (HAS_BSD(dev))
-               intel_cleanup_ring_buffer(dev, &dev_priv->bsd_ring);
+       intel_cleanup_ring_buffer(dev, &dev_priv->bsd_ring);
+       intel_cleanup_ring_buffer(dev, &dev_priv->blt_ring);
        if (HAS_PIPE_CONTROL(dev))
                i915_gem_cleanup_pipe_control(dev);
 }
@@ -4568,12 +4587,15 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
                return ret;
        }
 
+       BUG_ON(!list_empty(&dev_priv->mm.active_list));
        BUG_ON(!list_empty(&dev_priv->render_ring.active_list));
-       BUG_ON(HAS_BSD(dev) && !list_empty(&dev_priv->bsd_ring.active_list));
+       BUG_ON(!list_empty(&dev_priv->bsd_ring.active_list));
+       BUG_ON(!list_empty(&dev_priv->blt_ring.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->render_ring.request_list));
-       BUG_ON(HAS_BSD(dev) && !list_empty(&dev_priv->bsd_ring.request_list));
+       BUG_ON(!list_empty(&dev_priv->bsd_ring.request_list));
+       BUG_ON(!list_empty(&dev_priv->blt_ring.request_list));
        mutex_unlock(&dev->struct_mutex);
 
        ret = drm_irq_install(dev);
@@ -4615,24 +4637,29 @@ i915_gem_lastclose(struct drm_device *dev)
                DRM_ERROR("failed to idle hardware: %d\n", ret);
 }
 
+static void
+init_ring_lists(struct intel_ring_buffer *ring)
+{
+       INIT_LIST_HEAD(&ring->active_list);
+       INIT_LIST_HEAD(&ring->request_list);
+       INIT_LIST_HEAD(&ring->gpu_write_list);
+}
+
 void
 i915_gem_load(struct drm_device *dev)
 {
        int i;
        drm_i915_private_t *dev_priv = dev->dev_private;
 
+       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.pinned_list);
        INIT_LIST_HEAD(&dev_priv->mm.fence_list);
        INIT_LIST_HEAD(&dev_priv->mm.deferred_free_list);
-       INIT_LIST_HEAD(&dev_priv->render_ring.active_list);
-       INIT_LIST_HEAD(&dev_priv->render_ring.request_list);
-       if (HAS_BSD(dev)) {
-               INIT_LIST_HEAD(&dev_priv->bsd_ring.active_list);
-               INIT_LIST_HEAD(&dev_priv->bsd_ring.request_list);
-       }
+       init_ring_lists(&dev_priv->render_ring);
+       init_ring_lists(&dev_priv->bsd_ring);
+       init_ring_lists(&dev_priv->blt_ring);
        for (i = 0; i < 16; i++)
                INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list);
        INIT_DELAYED_WORK(&dev_priv->mm.retire_work,
@@ -4770,11 +4797,11 @@ void i915_gem_detach_phys_object(struct drm_device *dev,
        page_count = obj->size / PAGE_SIZE;
 
        for (i = 0; i < page_count; i++) {
-               char *dst = kmap_atomic(obj_priv->pages[i], KM_USER0);
+               char *dst = kmap_atomic(obj_priv->pages[i]);
                char *src = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
 
                memcpy(dst, src, PAGE_SIZE);
-               kunmap_atomic(dst, KM_USER0);
+               kunmap_atomic(dst);
        }
        drm_clflush_pages(obj_priv->pages, page_count);
        drm_agp_chipset_flush(dev);
@@ -4831,11 +4858,11 @@ i915_gem_attach_phys_object(struct drm_device *dev,
        page_count = obj->size / PAGE_SIZE;
 
        for (i = 0; i < page_count; i++) {
-               char *src = kmap_atomic(obj_priv->pages[i], KM_USER0);
+               char *src = kmap_atomic(obj_priv->pages[i]);
                char *dst = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
 
                memcpy(dst, src, PAGE_SIZE);
-               kunmap_atomic(src, KM_USER0);
+               kunmap_atomic(src);
        }
 
        i915_gem_object_put_pages(obj);
@@ -4851,17 +4878,24 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
                     struct drm_file *file_priv)
 {
        struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
-       void *obj_addr;
-       int ret;
-       char __user *user_data;
+       void *vaddr = obj_priv->phys_obj->handle->vaddr + args->offset;
+       char __user *user_data = (char __user *) (uintptr_t) args->data_ptr;
 
-       user_data = (char __user *) (uintptr_t) args->data_ptr;
-       obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset;
+       DRM_DEBUG_DRIVER("vaddr %p, %lld\n", vaddr, args->size);
 
-       DRM_DEBUG_DRIVER("obj_addr %p, %lld\n", obj_addr, args->size);
-       ret = copy_from_user(obj_addr, user_data, args->size);
-       if (ret)
-               return -EFAULT;
+       if (__copy_from_user_inatomic_nocache(vaddr, user_data, args->size)) {
+               unsigned long unwritten;
+
+               /* The physical object once assigned is fixed for the lifetime
+                * of the obj, so we can safely drop the lock and continue
+                * to access vaddr.
+                */
+               mutex_unlock(&dev->struct_mutex);
+               unwritten = copy_from_user(vaddr, user_data, args->size);
+               mutex_lock(&dev->struct_mutex);
+               if (unwritten)
+                       return -EFAULT;
+       }
 
        drm_agp_chipset_flush(dev);
        return 0;
@@ -4895,9 +4929,7 @@ i915_gpu_is_active(struct drm_device *dev)
        int lists_empty;
 
        lists_empty = list_empty(&dev_priv->mm.flushing_list) &&
-                     list_empty(&dev_priv->render_ring.active_list);
-       if (HAS_BSD(dev))
-               lists_empty &= list_empty(&dev_priv->bsd_ring.active_list);
+                     list_empty(&dev_priv->mm.active_list);
 
        return !lists_empty;
 }
@@ -4919,7 +4951,7 @@ i915_gem_shrink(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask)
                        if (mutex_trylock(&dev->struct_mutex)) {
                                list_for_each_entry(obj_priv,
                                                    &dev_priv->mm.inactive_list,
-                                                   list)
+                                                   mm_list)
                                        cnt++;
                                mutex_unlock(&dev->struct_mutex);
                        }
@@ -4945,7 +4977,7 @@ rescan:
 
                list_for_each_entry_safe(obj_priv, next_obj,
                                         &dev_priv->mm.inactive_list,
-                                        list) {
+                                        mm_list) {
                        if (i915_gem_object_is_purgeable(obj_priv)) {
                                i915_gem_object_unbind(&obj_priv->base);
                                if (--nr_to_scan <= 0)
@@ -4974,7 +5006,7 @@ rescan:
 
                list_for_each_entry_safe(obj_priv, next_obj,
                                         &dev_priv->mm.inactive_list,
-                                        list) {
+                                        mm_list) {
                        if (nr_to_scan > 0) {
                                i915_gem_object_unbind(&obj_priv->base);
                                nr_to_scan--;