intel: Defer tiling change to allocation.
[profile/ivi/libdrm.git] / intel / intel_bufmgr_gem.c
index d40e95b..b4be1af 100644 (file)
@@ -39,6 +39,7 @@
 #endif
 
 #include <xf86drm.h>
+#include <xf86atomic.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -53,7 +54,6 @@
 
 #include "errno.h"
 #include "libdrm_lists.h"
-#include "intel_atomic.h"
 #include "intel_bufmgr.h"
 #include "intel_bufmgr_priv.h"
 #include "intel_chipset.h"
@@ -66,6 +66,8 @@
                fprintf(stderr, __VA_ARGS__);           \
 } while (0)
 
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
 typedef struct _drm_intel_bo_gem drm_intel_bo_gem;
 
 struct drm_intel_gem_bo_bucket {
@@ -73,10 +75,6 @@ struct drm_intel_gem_bo_bucket {
        unsigned long size;
 };
 
-/* Only cache objects up to 64MB.  Bigger than that, and the rounding of the
- * size makes many operations fail that wouldn't otherwise.
- */
-#define DRM_INTEL_GEM_BO_BUCKETS       14
 typedef struct _drm_intel_bufmgr_gem {
        drm_intel_bufmgr bufmgr;
 
@@ -93,7 +91,8 @@ typedef struct _drm_intel_bufmgr_gem {
        int exec_count;
 
        /** Array of lists of cached gem objects of power-of-two sizes */
-       struct drm_intel_gem_bo_bucket cache_bucket[DRM_INTEL_GEM_BO_BUCKETS];
+       struct drm_intel_gem_bo_bucket cache_bucket[14 * 4];
+       int num_buckets;
 
        uint64_t gtt_size;
        int available_fences;
@@ -133,6 +132,7 @@ struct _drm_intel_bo_gem {
         */
        uint32_t tiling_mode;
        uint32_t swizzle_mode;
+       unsigned long stride;
 
        time_t free_time;
 
@@ -201,8 +201,9 @@ drm_intel_gem_bo_get_tiling(drm_intel_bo *bo, uint32_t * tiling_mode,
                            uint32_t * swizzle_mode);
 
 static int
-drm_intel_gem_bo_set_tiling(drm_intel_bo *bo, uint32_t * tiling_mode,
-                           uint32_t stride);
+drm_intel_gem_bo_set_tiling_internal(drm_intel_bo *bo,
+                                    uint32_t tiling_mode,
+                                    uint32_t stride);
 
 static void drm_intel_gem_bo_unreference_locked_timed(drm_intel_bo *bo,
                                                      time_t time);
@@ -254,11 +255,19 @@ static unsigned long
 drm_intel_gem_bo_tile_pitch(drm_intel_bufmgr_gem *bufmgr_gem,
                            unsigned long pitch, uint32_t tiling_mode)
 {
-       unsigned long tile_width = 512;
+       unsigned long tile_width;
        unsigned long i;
 
+       /* If untiled, then just align it so that we can do rendering
+        * to it with the 3D engine.
+        */
        if (tiling_mode == I915_TILING_NONE)
-               return pitch;
+               return ALIGN(pitch, 64);
+
+       if (tiling_mode == I915_TILING_X)
+               tile_width = 512;
+       else
+               tile_width = 128;
 
        /* 965 is flexible */
        if (bufmgr_gem->gen >= 4)
@@ -277,7 +286,7 @@ drm_intel_gem_bo_bucket_for_size(drm_intel_bufmgr_gem *bufmgr_gem,
 {
        int i;
 
-       for (i = 0; i < DRM_INTEL_GEM_BO_BUCKETS; i++) {
+       for (i = 0; i < bufmgr_gem->num_buckets; i++) {
                struct drm_intel_gem_bo_bucket *bucket =
                    &bufmgr_gem->cache_bucket[i];
                if (bucket->size >= size) {
@@ -383,8 +392,12 @@ drm_intel_add_validate_buffer2(drm_intel_bo *bo, int need_fence)
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *)bo;
        int index;
 
-       if (bo_gem->validate_index != -1)
+       if (bo_gem->validate_index != -1) {
+               if (need_fence)
+                       bufmgr_gem->exec2_objects[bo_gem->validate_index].flags |=
+                               EXEC_OBJECT_NEEDS_FENCE;
                return;
+       }
 
        /* Extend the array of validation entries as necessary. */
        if (bufmgr_gem->exec_count == bufmgr_gem->exec_size) {
@@ -458,7 +471,7 @@ drm_intel_setup_reloc_list(drm_intel_bo *bo)
        bo_gem->relocs = malloc(max_relocs *
                                sizeof(struct drm_i915_gem_relocation_entry));
        bo_gem->reloc_target_info = malloc(max_relocs *
-                                          sizeof(drm_intel_reloc_target *));
+                                          sizeof(drm_intel_reloc_target));
        if (bo_gem->relocs == NULL || bo_gem->reloc_target_info == NULL) {
                bo_gem->has_error = 1;
 
@@ -538,7 +551,9 @@ static drm_intel_bo *
 drm_intel_gem_bo_alloc_internal(drm_intel_bufmgr *bufmgr,
                                const char *name,
                                unsigned long size,
-                               unsigned long flags)
+                               unsigned long flags,
+                               uint32_t tiling_mode,
+                               unsigned long stride)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bufmgr;
        drm_intel_bo_gem *bo_gem;
@@ -604,6 +619,13 @@ retry:
                                                                    bucket);
                                goto retry;
                        }
+
+                       if (drm_intel_gem_bo_set_tiling_internal(&bo_gem->bo,
+                                                                tiling_mode,
+                                                                stride)) {
+                               drm_intel_gem_bo_free(&bo_gem->bo);
+                               goto retry;
+                       }
                }
        }
        pthread_mutex_unlock(&bufmgr_gem->lock);
@@ -631,6 +653,17 @@ retry:
                        return NULL;
                }
                bo_gem->bo.bufmgr = bufmgr;
+
+               bo_gem->tiling_mode = I915_TILING_NONE;
+               bo_gem->swizzle_mode = I915_BIT_6_SWIZZLE_NONE;
+               bo_gem->stride = 0;
+
+               if (drm_intel_gem_bo_set_tiling_internal(&bo_gem->bo,
+                                                        tiling_mode,
+                                                        stride)) {
+                   drm_intel_gem_bo_free(&bo_gem->bo);
+                   return NULL;
+               }
        }
 
        bo_gem->name = name;
@@ -639,8 +672,6 @@ retry:
        bo_gem->reloc_tree_fences = 0;
        bo_gem->used_as_reloc_target = 0;
        bo_gem->has_error = 0;
-       bo_gem->tiling_mode = I915_TILING_NONE;
-       bo_gem->swizzle_mode = I915_BIT_6_SWIZZLE_NONE;
        bo_gem->reusable = 1;
 
        drm_intel_bo_gem_set_in_aperture_size(bufmgr_gem, bo_gem);
@@ -658,7 +689,8 @@ drm_intel_gem_bo_alloc_for_render(drm_intel_bufmgr *bufmgr,
                                  unsigned int alignment)
 {
        return drm_intel_gem_bo_alloc_internal(bufmgr, name, size,
-                                              BO_ALLOC_FOR_RENDER);
+                                              BO_ALLOC_FOR_RENDER,
+                                              I915_TILING_NONE, 0);
 }
 
 static drm_intel_bo *
@@ -667,7 +699,8 @@ drm_intel_gem_bo_alloc(drm_intel_bufmgr *bufmgr,
                       unsigned long size,
                       unsigned int alignment)
 {
-       return drm_intel_gem_bo_alloc_internal(bufmgr, name, size, 0);
+       return drm_intel_gem_bo_alloc_internal(bufmgr, name, size, 0,
+                                              I915_TILING_NONE, 0);
 }
 
 static drm_intel_bo *
@@ -677,44 +710,45 @@ drm_intel_gem_bo_alloc_tiled(drm_intel_bufmgr *bufmgr, const char *name,
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *)bufmgr;
        drm_intel_bo *bo;
-       unsigned long size, stride, aligned_y = y;
-       int ret;
+       unsigned long size, stride;
+       uint32_t tiling;
 
-       /* If we're tiled, our allocations are in 8 or 32-row blocks,
-        * so failure to align our height means that we won't allocate
-        * enough pages.
-        *
-        * If we're untiled, we still have to align to 2 rows high
-        * because the data port accesses 2x2 blocks even if the
-        * bottom row isn't to be rendered, so failure to align means
-        * we could walk off the end of the GTT and fault.  This is
-        * documented on 965, and may be the case on older chipsets
-        * too so we try to be careful.
-        */
-       if (*tiling_mode == I915_TILING_NONE)
-               aligned_y = ALIGN(y, 2);
-       else if (*tiling_mode == I915_TILING_X)
-               aligned_y = ALIGN(y, 8);
-       else if (*tiling_mode == I915_TILING_Y)
-               aligned_y = ALIGN(y, 32);
-
-       stride = x * cpp;
-       stride = drm_intel_gem_bo_tile_pitch(bufmgr_gem, stride, *tiling_mode);
-       size = stride * aligned_y;
-       size = drm_intel_gem_bo_tile_size(bufmgr_gem, size, tiling_mode);
-
-       bo = drm_intel_gem_bo_alloc_internal(bufmgr, name, size, flags);
+       do {
+               unsigned long aligned_y;
+
+               tiling = *tiling_mode;
+
+               /* If we're tiled, our allocations are in 8 or 32-row blocks,
+                * so failure to align our height means that we won't allocate
+                * enough pages.
+                *
+                * If we're untiled, we still have to align to 2 rows high
+                * because the data port accesses 2x2 blocks even if the
+                * bottom row isn't to be rendered, so failure to align means
+                * we could walk off the end of the GTT and fault.  This is
+                * documented on 965, and may be the case on older chipsets
+                * too so we try to be careful.
+                */
+               aligned_y = y;
+               if (tiling == I915_TILING_NONE)
+                       aligned_y = ALIGN(y, 2);
+               else if (tiling == I915_TILING_X)
+                       aligned_y = ALIGN(y, 8);
+               else if (tiling == I915_TILING_Y)
+                       aligned_y = ALIGN(y, 32);
+
+               stride = x * cpp;
+               stride = drm_intel_gem_bo_tile_pitch(bufmgr_gem, stride, tiling);
+               size = stride * aligned_y;
+               size = drm_intel_gem_bo_tile_size(bufmgr_gem, size, tiling_mode);
+       } while (*tiling_mode != tiling);
+
+       bo = drm_intel_gem_bo_alloc_internal(bufmgr, name, size, flags,
+                                            *tiling_mode, stride);
        if (!bo)
                return NULL;
 
-       ret = drm_intel_gem_bo_set_tiling(bo, tiling_mode, stride);
-       if (ret != 0) {
-               drm_intel_gem_bo_unreference(bo);
-               return NULL;
-       }
-
        *pitch = stride;
-
        return bo;
 }
 
@@ -772,6 +806,7 @@ drm_intel_bo_gem_create_from_name(drm_intel_bufmgr *bufmgr,
        }
        bo_gem->tiling_mode = get_tiling.tiling_mode;
        bo_gem->swizzle_mode = get_tiling.swizzle_mode;
+       /* XXX stride is unknown */
        drm_intel_bo_gem_set_in_aperture_size(bufmgr_gem, bo_gem);
 
        DBG("bo_create_from_handle: %d (%s)\n", handle, bo_gem->name);
@@ -810,7 +845,7 @@ drm_intel_gem_cleanup_bo_cache(drm_intel_bufmgr_gem *bufmgr_gem, time_t time)
 {
        int i;
 
-       for (i = 0; i < DRM_INTEL_GEM_BO_BUCKETS; i++) {
+       for (i = 0; i < bufmgr_gem->num_buckets; i++) {
                struct drm_intel_gem_bo_bucket *bucket =
                    &bufmgr_gem->cache_bucket[i];
 
@@ -835,14 +870,15 @@ drm_intel_gem_bo_unreference_final(drm_intel_bo *bo, time_t time)
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
        struct drm_intel_gem_bo_bucket *bucket;
-       uint32_t tiling_mode;
        int i;
 
        /* Unreference all the target buffers */
        for (i = 0; i < bo_gem->reloc_count; i++) {
-               drm_intel_gem_bo_unreference_locked_timed(bo_gem->
-                                                         reloc_target_info[i].bo,
-                                                         time);
+               if (bo_gem->reloc_target_info[i].bo != bo) {
+                       drm_intel_gem_bo_unreference_locked_timed(bo_gem->
+                                                                 reloc_target_info[i].bo,
+                                                                 time);
+               }
        }
        bo_gem->reloc_count = 0;
        bo_gem->used_as_reloc_target = 0;
@@ -862,9 +898,7 @@ drm_intel_gem_bo_unreference_final(drm_intel_bo *bo, time_t time)
 
        bucket = drm_intel_gem_bo_bucket_for_size(bufmgr_gem, bo->size);
        /* Put the buffer into our internal cache for reuse if we can. */
-       tiling_mode = I915_TILING_NONE;
        if (bufmgr_gem->bo_reuse && bo_gem->reusable && bucket != NULL &&
-           drm_intel_gem_bo_set_tiling(bo, &tiling_mode, 0) == 0 &&
            drm_intel_gem_bo_madvise_internal(bufmgr_gem, bo_gem,
                                              I915_MADV_DONTNEED)) {
                bo_gem->free_time = time;
@@ -1095,10 +1129,12 @@ static int drm_intel_gem_bo_unmap(drm_intel_bo *bo)
                            DRM_IOCTL_I915_GEM_SW_FINISH,
                            &sw_finish);
        } while (ret == -1 && errno == EINTR);
+       ret = ret == -1 ? -errno : 0;
 
        bo->virtual = NULL;
        pthread_mutex_unlock(&bufmgr_gem->lock);
-       return 0;
+
+       return ret;
 }
 
 static int
@@ -1121,12 +1157,14 @@ drm_intel_gem_bo_subdata(drm_intel_bo *bo, unsigned long offset,
                            &pwrite);
        } while (ret == -1 && errno == EINTR);
        if (ret != 0) {
+               ret = -errno;
                fprintf(stderr,
                        "%s:%d: Error writing data to buffer %d: (%d %d) %s .\n",
                        __FILE__, __LINE__, bo_gem->gem_handle, (int)offset,
                        (int)size, strerror(errno));
        }
-       return 0;
+
+       return ret;
 }
 
 static int
@@ -1178,6 +1216,7 @@ drm_intel_gem_bo_get_subdata(drm_intel_bo *bo, unsigned long offset,
                        __FILE__, __LINE__, bo_gem->gem_handle, (int)offset,
                        (int)size, strerror(errno));
        }
+
        return ret;
 }
 
@@ -1233,7 +1272,7 @@ drm_intel_bufmgr_gem_destroy(drm_intel_bufmgr *bufmgr)
        pthread_mutex_destroy(&bufmgr_gem->lock);
 
        /* Free any cached buffer objects we were going to reuse */
-       for (i = 0; i < DRM_INTEL_GEM_BO_BUCKETS; i++) {
+       for (i = 0; i < bufmgr_gem->num_buckets; i++) {
                struct drm_intel_gem_bo_bucket *bucket =
                    &bufmgr_gem->cache_bucket[i];
                drm_intel_bo_gem *bo_gem;
@@ -1299,7 +1338,10 @@ do_bo_emit_reloc(drm_intel_bo *bo, uint32_t offset,
         * already been accounted for.
         */
        assert(!bo_gem->used_as_reloc_target);
-       bo_gem->reloc_tree_size += target_bo_gem->reloc_tree_size;
+       if (target_bo_gem != bo_gem) {
+               target_bo_gem->used_as_reloc_target = 1;
+               bo_gem->reloc_tree_size += target_bo_gem->reloc_tree_size;
+       }
        /* An object needing a fence is a tiled buffer, so it won't have
         * relocs to other buffers.
         */
@@ -1308,7 +1350,6 @@ do_bo_emit_reloc(drm_intel_bo *bo, uint32_t offset,
        bo_gem->reloc_tree_fences += target_bo_gem->reloc_tree_fences;
 
        /* Flag the target to disallow further relocations in it. */
-       target_bo_gem->used_as_reloc_target = 1;
 
        bo_gem->relocs[bo_gem->reloc_count].offset = offset;
        bo_gem->relocs[bo_gem->reloc_count].delta = target_offset;
@@ -1319,7 +1360,8 @@ do_bo_emit_reloc(drm_intel_bo *bo, uint32_t offset,
        bo_gem->relocs[bo_gem->reloc_count].presumed_offset = target_bo->offset;
 
        bo_gem->reloc_target_info[bo_gem->reloc_count].bo = target_bo;
-       drm_intel_gem_bo_reference(target_bo);
+       if (target_bo != bo)
+               drm_intel_gem_bo_reference(target_bo);
        if (need_fence)
                bo_gem->reloc_target_info[bo_gem->reloc_count].flags =
                        DRM_INTEL_RELOC_FENCE;
@@ -1370,6 +1412,9 @@ drm_intel_gem_bo_process_reloc(drm_intel_bo *bo)
        for (i = 0; i < bo_gem->reloc_count; i++) {
                drm_intel_bo *target_bo = bo_gem->reloc_target_info[i].bo;
 
+               if (target_bo == bo)
+                       continue;
+
                /* Continue walking the tree depth-first. */
                drm_intel_gem_bo_process_reloc(target_bo);
 
@@ -1391,6 +1436,9 @@ drm_intel_gem_bo_process_reloc2(drm_intel_bo *bo)
                drm_intel_bo *target_bo = bo_gem->reloc_target_info[i].bo;
                int need_fence;
 
+               if (target_bo == bo)
+                       continue;
+
                /* Continue walking the tree depth-first. */
                drm_intel_gem_bo_process_reloc2(target_bo);
 
@@ -1513,14 +1561,17 @@ drm_intel_gem_bo_exec(drm_intel_bo *bo, int used,
 }
 
 static int
-drm_intel_gem_bo_exec2(drm_intel_bo *bo, int used,
-                      drm_clip_rect_t *cliprects, int num_cliprects,
-                      int DR4)
+drm_intel_gem_bo_mrb_exec2(drm_intel_bo *bo, int used,
+                       drm_clip_rect_t *cliprects, int num_cliprects, int DR4,
+                       int ring_flag)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *)bo->bufmgr;
        struct drm_i915_gem_execbuffer2 execbuf;
        int ret, i;
 
+       if ((ring_flag != I915_EXEC_RENDER) && (ring_flag != I915_EXEC_BSD))
+               return -EINVAL;
+
        pthread_mutex_lock(&bufmgr_gem->lock);
        /* Update indices and set up the validate list. */
        drm_intel_gem_bo_process_reloc2(bo);
@@ -1538,24 +1589,27 @@ drm_intel_gem_bo_exec2(drm_intel_bo *bo, int used,
        execbuf.num_cliprects = num_cliprects;
        execbuf.DR1 = 0;
        execbuf.DR4 = DR4;
-       execbuf.flags = 0;
+       execbuf.flags = ring_flag;
        execbuf.rsvd1 = 0;
        execbuf.rsvd2 = 0;
 
        do {
                ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_EXECBUFFER2,
                            &execbuf);
-       } while (ret != 0 && errno == EAGAIN);
+       } while (ret != 0 && errno == EINTR);
 
-       if (ret != 0 && errno == ENOMEM) {
-               fprintf(stderr,
-                       "Execbuffer fails to pin. "
-                       "Estimate: %u. Actual: %u. Available: %u\n",
-                       drm_intel_gem_estimate_batch_space(bufmgr_gem->exec_bos,
-                                                          bufmgr_gem->exec_count),
-                       drm_intel_gem_compute_batch_space(bufmgr_gem->exec_bos,
-                                                         bufmgr_gem->exec_count),
-                       (unsigned int) bufmgr_gem->gtt_size);
+       if (ret != 0) {
+               ret = -errno;
+               if (ret == -ENOMEM) {
+                       fprintf(stderr,
+                               "Execbuffer fails to pin. "
+                               "Estimate: %u. Actual: %u. Available: %u\n",
+                               drm_intel_gem_estimate_batch_space(bufmgr_gem->exec_bos,
+                                                                  bufmgr_gem->exec_count),
+                               drm_intel_gem_compute_batch_space(bufmgr_gem->exec_bos,
+                                                                 bufmgr_gem->exec_count),
+                               (unsigned int) bufmgr_gem->gtt_size);
+               }
        }
        drm_intel_update_buffer_offsets2(bufmgr_gem);
 
@@ -1573,7 +1627,17 @@ drm_intel_gem_bo_exec2(drm_intel_bo *bo, int used,
        bufmgr_gem->exec_count = 0;
        pthread_mutex_unlock(&bufmgr_gem->lock);
 
-       return 0;
+       return ret;
+}
+
+static int
+drm_intel_gem_bo_exec2(drm_intel_bo *bo, int used,
+                      drm_clip_rect_t *cliprects, int num_cliprects,
+                      int DR4)
+{
+       return drm_intel_gem_bo_mrb_exec2(bo, used,
+                                       cliprects, num_cliprects, DR4,
+                                       I915_EXEC_RENDER);
 }
 
 static int
@@ -1620,35 +1684,55 @@ drm_intel_gem_bo_unpin(drm_intel_bo *bo)
 }
 
 static int
-drm_intel_gem_bo_set_tiling(drm_intel_bo *bo, uint32_t * tiling_mode,
-                           uint32_t stride)
+drm_intel_gem_bo_set_tiling_internal(drm_intel_bo *bo,
+                                    uint32_t tiling_mode,
+                                    uint32_t stride)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
        struct drm_i915_gem_set_tiling set_tiling;
        int ret;
 
-       if (bo_gem->global_name == 0 && *tiling_mode == bo_gem->tiling_mode)
+       if (tiling_mode == bo_gem->tiling_mode &&
+           stride == bo_gem->stride)
                return 0;
 
        memset(&set_tiling, 0, sizeof(set_tiling));
-       set_tiling.handle = bo_gem->gem_handle;
-
        do {
-               set_tiling.tiling_mode = *tiling_mode;
+               set_tiling.handle = bo_gem->gem_handle;
+               set_tiling.tiling_mode = tiling_mode;
                set_tiling.stride = stride;
 
                ret = ioctl(bufmgr_gem->fd,
                            DRM_IOCTL_I915_GEM_SET_TILING,
                            &set_tiling);
        } while (ret == -1 && errno == EINTR);
+       if (ret == -1)
+               return -errno;
+
        bo_gem->tiling_mode = set_tiling.tiling_mode;
        bo_gem->swizzle_mode = set_tiling.swizzle_mode;
+       bo_gem->stride = stride;
+       return 0;
+}
 
-       drm_intel_bo_gem_set_in_aperture_size(bufmgr_gem, bo_gem);
+static int
+drm_intel_gem_bo_set_tiling(drm_intel_bo *bo, uint32_t * tiling_mode,
+                           uint32_t stride)
+{
+       drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
+       drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
+       int ret;
+
+       if (bo_gem->global_name == 0)
+               return 0;
+
+       ret = drm_intel_gem_bo_set_tiling_internal(bo, *tiling_mode, stride);
+       if (ret == 0)
+               drm_intel_bo_gem_set_in_aperture_size(bufmgr_gem, bo_gem);
 
        *tiling_mode = bo_gem->tiling_mode;
-       return ret == 0 ? 0 : -errno;
+       return ret;
 }
 
 static int
@@ -1901,6 +1985,14 @@ drm_intel_gem_bo_disable_reuse(drm_intel_bo *bo)
 }
 
 static int
+drm_intel_gem_bo_is_reusable(drm_intel_bo *bo)
+{
+       drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
+
+       return bo_gem->reusable;
+}
+
+static int
 _drm_intel_gem_bo_references(drm_intel_bo *bo, drm_intel_bo *target_bo)
 {
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
@@ -1909,6 +2001,8 @@ _drm_intel_gem_bo_references(drm_intel_bo *bo, drm_intel_bo *target_bo)
        for (i = 0; i < bo_gem->reloc_count; i++) {
                if (bo_gem->reloc_target_info[i].bo == target_bo)
                        return 1;
+               if (bo == bo_gem->reloc_target_info[i].bo)
+                       continue;
                if (_drm_intel_gem_bo_references(bo_gem->reloc_target_info[i].bo,
                                                target_bo))
                        return 1;
@@ -1930,6 +2024,45 @@ drm_intel_gem_bo_references(drm_intel_bo *bo, drm_intel_bo *target_bo)
        return 0;
 }
 
+static void
+add_bucket(drm_intel_bufmgr_gem *bufmgr_gem, int size)
+{
+       unsigned int i = bufmgr_gem->num_buckets;
+
+       assert(i < ARRAY_SIZE(bufmgr_gem->cache_bucket));
+
+       DRMINITLISTHEAD(&bufmgr_gem->cache_bucket[i].head);
+       bufmgr_gem->cache_bucket[i].size = size;
+       bufmgr_gem->num_buckets++;
+}
+
+static void
+init_cache_buckets(drm_intel_bufmgr_gem *bufmgr_gem)
+{
+       unsigned long size, cache_max_size = 64 * 1024 * 1024;
+
+       /* OK, so power of two buckets was too wasteful of memory.
+        * Give 3 other sizes between each power of two, to hopefully
+        * cover things accurately enough.  (The alternative is
+        * probably to just go for exact matching of sizes, and assume
+        * that for things like composited window resize the tiled
+        * width/height alignment and rounding of sizes to pages will
+        * get us useful cache hit rates anyway)
+        */
+       add_bucket(bufmgr_gem, 4096);
+       add_bucket(bufmgr_gem, 4096 * 2);
+       add_bucket(bufmgr_gem, 4096 * 3);
+
+       /* Initialize the linked lists for BO reuse cache. */
+       for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
+               add_bucket(bufmgr_gem, size);
+
+               add_bucket(bufmgr_gem, size + size * 1 / 4);
+               add_bucket(bufmgr_gem, size + size * 2 / 4);
+               add_bucket(bufmgr_gem, size + size * 3 / 4);
+       }
+}
+
 /**
  * Initializes the GEM buffer manager, which uses the kernel to allocate, map,
  * and manage map buffer objections.
@@ -1942,9 +2075,8 @@ drm_intel_bufmgr_gem_init(int fd, int batch_size)
        drm_intel_bufmgr_gem *bufmgr_gem;
        struct drm_i915_gem_get_aperture aperture;
        drm_i915_getparam_t gp;
-       int ret, i;
-       unsigned long size;
-       int exec2 = 0;
+       int ret;
+       int exec2 = 0, has_bsd = 0;
 
        bufmgr_gem = calloc(1, sizeof(*bufmgr_gem));
        if (bufmgr_gem == NULL)
@@ -1993,6 +2125,11 @@ drm_intel_bufmgr_gem_init(int fd, int batch_size)
        if (!ret)
                exec2 = 1;
 
+       gp.param = I915_PARAM_HAS_BSD;
+       ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GETPARAM, &gp);
+       if (!ret)
+               has_bsd = 1;
+
        if (bufmgr_gem->gen < 4) {
                gp.param = I915_PARAM_NUM_FENCES_AVAIL;
                gp.value = &bufmgr_gem->available_fences;
@@ -2046,9 +2183,11 @@ drm_intel_bufmgr_gem_init(int fd, int batch_size)
        bufmgr_gem->bufmgr.bo_set_tiling = drm_intel_gem_bo_set_tiling;
        bufmgr_gem->bufmgr.bo_flink = drm_intel_gem_bo_flink;
        /* Use the new one if available */
-       if (exec2)
+       if (exec2) {
                bufmgr_gem->bufmgr.bo_exec = drm_intel_gem_bo_exec2;
-       else
+               if (has_bsd)
+                       bufmgr_gem->bufmgr.bo_mrb_exec = drm_intel_gem_bo_mrb_exec2;
+       } else
                bufmgr_gem->bufmgr.bo_exec = drm_intel_gem_bo_exec;
        bufmgr_gem->bufmgr.bo_busy = drm_intel_gem_bo_busy;
        bufmgr_gem->bufmgr.bo_madvise = drm_intel_gem_bo_madvise;
@@ -2057,15 +2196,12 @@ drm_intel_bufmgr_gem_init(int fd, int batch_size)
        bufmgr_gem->bufmgr.check_aperture_space =
            drm_intel_gem_check_aperture_space;
        bufmgr_gem->bufmgr.bo_disable_reuse = drm_intel_gem_bo_disable_reuse;
+       bufmgr_gem->bufmgr.bo_is_reusable = drm_intel_gem_bo_is_reusable;
        bufmgr_gem->bufmgr.get_pipe_from_crtc_id =
            drm_intel_gem_get_pipe_from_crtc_id;
        bufmgr_gem->bufmgr.bo_references = drm_intel_gem_bo_references;
 
-       /* Initialize the linked lists for BO reuse cache. */
-       for (i = 0, size = 4096; i < DRM_INTEL_GEM_BO_BUCKETS; i++, size *= 2) {
-               DRMINITLISTHEAD(&bufmgr_gem->cache_bucket[i].head);
-               bufmgr_gem->cache_bucket[i].size = size;
-       }
+       init_cache_buckets(bufmgr_gem);
 
        return &bufmgr_gem->bufmgr;
 }