return gen8_canonical_addr((int)reloc->delta + target_offset);
 }
 
+struct reloc_cache {
+       void *vaddr;
+       unsigned int page;
+       enum { KMAP, IOMAP } type;
+};
+
+static void reloc_cache_init(struct reloc_cache *cache)
+{
+       cache->page = -1;
+       cache->vaddr = NULL;
+}
+
+static void reloc_cache_fini(struct reloc_cache *cache)
+{
+       if (!cache->vaddr)
+               return;
+
+       switch (cache->type) {
+       case KMAP:
+               kunmap_atomic(cache->vaddr);
+               break;
+
+       case IOMAP:
+               io_mapping_unmap_atomic(cache->vaddr);
+               break;
+       }
+}
+
+static void *reloc_kmap(struct drm_i915_gem_object *obj,
+                       struct reloc_cache *cache,
+                       int page)
+{
+       if (cache->page == page)
+               return cache->vaddr;
+
+       if (cache->vaddr)
+               kunmap_atomic(cache->vaddr);
+
+       cache->page = page;
+       cache->vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj, page));
+       cache->type = KMAP;
+
+       return cache->vaddr;
+}
+
 static int
 relocate_entry_cpu(struct drm_i915_gem_object *obj,
                   struct drm_i915_gem_relocation_entry *reloc,
+                  struct reloc_cache *cache,
                   uint64_t target_offset)
 {
        struct drm_device *dev = obj->base.dev;
        if (ret)
                return ret;
 
-       vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj,
-                               reloc->offset >> PAGE_SHIFT));
+       vaddr = reloc_kmap(obj, cache, reloc->offset >> PAGE_SHIFT);
        *(uint32_t *)(vaddr + page_offset) = lower_32_bits(delta);
 
-       if (INTEL_INFO(dev)->gen >= 8) {
-               page_offset = offset_in_page(page_offset + sizeof(uint32_t));
-
-               if (page_offset == 0) {
-                       kunmap_atomic(vaddr);
-                       vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj,
-                           (reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT));
+       if (INTEL_GEN(dev) >= 8) {
+               page_offset += sizeof(uint32_t);
+               if (page_offset == PAGE_SIZE) {
+                       vaddr = reloc_kmap(obj, cache, cache->page + 1);
+                       page_offset = 0;
                }
-
                *(uint32_t *)(vaddr + page_offset) = upper_32_bits(delta);
        }
 
-       kunmap_atomic(vaddr);
-
        return 0;
 }
 
+static void *reloc_iomap(struct drm_i915_private *i915,
+                        struct reloc_cache *cache,
+                        uint64_t offset)
+{
+       if (cache->page == offset >> PAGE_SHIFT)
+               return cache->vaddr;
+
+       if (cache->vaddr)
+               io_mapping_unmap_atomic(cache->vaddr);
+
+       cache->page = offset >> PAGE_SHIFT;
+       cache->vaddr =
+               io_mapping_map_atomic_wc(i915->ggtt.mappable,
+                                        offset & PAGE_MASK);
+       cache->type = IOMAP;
+
+       return cache->vaddr;
+}
+
 static int
 relocate_entry_gtt(struct drm_i915_gem_object *obj,
                   struct drm_i915_gem_relocation_entry *reloc,
+                  struct reloc_cache *cache,
                   uint64_t target_offset)
 {
        struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
        struct i915_vma *vma;
        uint64_t delta = relocation_target(reloc, target_offset);
        uint64_t offset;
 
        /* Map the page containing the relocation we're going to perform.  */
        offset = vma->node.start + reloc->offset;
-       reloc_page = io_mapping_map_atomic_wc(ggtt->mappable,
-                                             offset & PAGE_MASK);
+       reloc_page = reloc_iomap(dev_priv, cache, offset);
        iowrite32(lower_32_bits(delta), reloc_page + offset_in_page(offset));
 
        if (INTEL_GEN(dev_priv) >= 8) {
                offset += sizeof(uint32_t);
-
-               if (offset_in_page(offset) == 0) {
-                       io_mapping_unmap_atomic(reloc_page);
-                       reloc_page =
-                               io_mapping_map_atomic_wc(ggtt->mappable,
-                                                        offset);
-               }
-
+               if (offset_in_page(offset) == 0)
+                       reloc_page = reloc_iomap(dev_priv, cache, offset);
                iowrite32(upper_32_bits(delta),
                          reloc_page + offset_in_page(offset));
        }
 
-       io_mapping_unmap_atomic(reloc_page);
-
 unpin:
-       i915_vma_unpin(vma);
+       __i915_vma_unpin(vma);
        return ret;
 }
 
 static int
 relocate_entry_clflush(struct drm_i915_gem_object *obj,
                       struct drm_i915_gem_relocation_entry *reloc,
+                      struct reloc_cache *cache,
                       uint64_t target_offset)
 {
        struct drm_device *dev = obj->base.dev;
        if (ret)
                return ret;
 
-       vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj,
-                               reloc->offset >> PAGE_SHIFT));
+       vaddr = reloc_kmap(obj, cache, reloc->offset >> PAGE_SHIFT);
        clflush_write32(vaddr + page_offset, lower_32_bits(delta));
 
-       if (INTEL_INFO(dev)->gen >= 8) {
-               page_offset = offset_in_page(page_offset + sizeof(uint32_t));
-
-               if (page_offset == 0) {
-                       kunmap_atomic(vaddr);
-                       vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj,
-                           (reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT));
+       if (INTEL_GEN(dev) >= 8) {
+               page_offset += sizeof(uint32_t);
+               if (page_offset == PAGE_SIZE) {
+                       vaddr = reloc_kmap(obj, cache, cache->page + 1);
+                       page_offset = 0;
                }
-
                clflush_write32(vaddr + page_offset, upper_32_bits(delta));
        }
 
-       kunmap_atomic(vaddr);
-
        return 0;
 }
 
 static int
 i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
                                   struct eb_vmas *eb,
-                                  struct drm_i915_gem_relocation_entry *reloc)
+                                  struct drm_i915_gem_relocation_entry *reloc,
+                                  struct reloc_cache *cache)
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_gem_object *target_obj;
                return -EFAULT;
 
        if (use_cpu_reloc(obj))
-               ret = relocate_entry_cpu(obj, reloc, target_offset);
+               ret = relocate_entry_cpu(obj, reloc, cache, target_offset);
        else if (obj->map_and_fenceable)
-               ret = relocate_entry_gtt(obj, reloc, target_offset);
+               ret = relocate_entry_gtt(obj, reloc, cache, target_offset);
        else if (static_cpu_has(X86_FEATURE_CLFLUSH))
-               ret = relocate_entry_clflush(obj, reloc, target_offset);
+               ret = relocate_entry_clflush(obj, reloc, cache, target_offset);
        else {
                WARN_ONCE(1, "Impossible case in relocation handling\n");
                ret = -ENODEV;
        struct drm_i915_gem_relocation_entry stack_reloc[N_RELOC(512)];
        struct drm_i915_gem_relocation_entry __user *user_relocs;
        struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
-       int remain, ret;
+       struct reloc_cache cache;
+       int remain, ret = 0;
 
        user_relocs = u64_to_user_ptr(entry->relocs_ptr);
+       reloc_cache_init(&cache);
 
        remain = entry->relocation_count;
        while (remain) {
                        count = ARRAY_SIZE(stack_reloc);
                remain -= count;
 
-               if (__copy_from_user_inatomic(r, user_relocs, count*sizeof(r[0])))
-                       return -EFAULT;
+               if (__copy_from_user_inatomic(r, user_relocs, count*sizeof(r[0]))) {
+                       ret = -EFAULT;
+                       goto out;
+               }
 
                do {
                        u64 offset = r->presumed_offset;
 
-                       ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, r);
+                       ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, r, &cache);
                        if (ret)
-                               return ret;
+                               goto out;
 
                        if (r->presumed_offset != offset &&
-                           __put_user(r->presumed_offset, &user_relocs->presumed_offset)) {
-                               return -EFAULT;
+                           __put_user(r->presumed_offset,
+                                      &user_relocs->presumed_offset)) {
+                               ret = -EFAULT;
+                               goto out;
                        }
 
                        user_relocs++;
                } while (--count);
        }
 
-       return 0;
+out:
+       reloc_cache_fini(&cache);
+       return ret;
 #undef N_RELOC
 }
 
                                      struct drm_i915_gem_relocation_entry *relocs)
 {
        const struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
-       int i, ret;
+       struct reloc_cache cache;
+       int i, ret = 0;
 
+       reloc_cache_init(&cache);
        for (i = 0; i < entry->relocation_count; i++) {
-               ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, &relocs[i]);
+               ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, &relocs[i], &cache);
                if (ret)
-                       return ret;
+                       break;
        }
+       reloc_cache_fini(&cache);
 
-       return 0;
+       return ret;
 }
 
 static int