intel: make drm_intel_gem_bo_get_reloc_count() thread safe
[platform/upstream/libdrm.git] / intel / intel_bufmgr_gem.c
index a51e3f3..4e34f75 100644 (file)
@@ -57,6 +57,7 @@
 #ifndef ETIME
 #define ETIME ETIMEDOUT
 #endif
+#include "libdrm.h"
 #include "libdrm_lists.h"
 #include "intel_bufmgr.h"
 #include "intel_bufmgr_priv.h"
@@ -93,6 +94,8 @@ struct drm_intel_gem_bo_bucket {
 typedef struct _drm_intel_bufmgr_gem {
        drm_intel_bufmgr bufmgr;
 
+       atomic_t refcount;
+
        int fd;
 
        int max_relocs;
@@ -110,6 +113,8 @@ typedef struct _drm_intel_bufmgr_gem {
        int num_buckets;
        time_t time;
 
+       drmMMListHead managers;
+
        drmMMListHead named;
        drmMMListHead vma_cache;
        int vma_count, vma_open, vma_max;
@@ -149,6 +154,8 @@ struct _drm_intel_bo_gem {
 
        /**
         * Kenel-assigned global name for this object
+         *
+         * List contains both flink named and prime fd'd objects
         */
        unsigned int global_name;
        drmMMListHead name_list;
@@ -210,6 +217,15 @@ struct _drm_intel_bo_gem {
        bool reusable;
 
        /**
+        * Boolean of whether the GPU is definitely not accessing the buffer.
+        *
+        * This is only valid when reusable, since non-reusable
+        * buffers are those that have been shared wth other
+        * processes, so we don't know their state.
+        */
+       bool idle;
+
+       /**
         * Size in bytes of this buffer and its relocation descendents.
         *
         * Used to avoid costly tree walking in
@@ -380,7 +396,7 @@ drm_intel_gem_dump_validation_list(drm_intel_bufmgr_gem *bufmgr_gem)
                            (unsigned long long)bo_gem->relocs[j].offset,
                            target_gem->gem_handle,
                            target_gem->name,
-                           target_bo->offset,
+                           target_bo->offset64,
                            bo_gem->relocs[j].delta);
                }
        }
@@ -565,11 +581,19 @@ drm_intel_gem_bo_busy(drm_intel_bo *bo)
        struct drm_i915_gem_busy busy;
        int ret;
 
+       if (bo_gem->reusable && bo_gem->idle)
+               return false;
+
        VG_CLEAR(busy);
        busy.handle = bo_gem->gem_handle;
 
        ret = drmIoctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_BUSY, &busy);
-
+       if (ret == 0) {
+               bo_gem->idle = !busy.busy;
+               return busy.busy;
+       } else {
+               return false;
+       }
        return (ret == 0 && busy.busy);
 }
 
@@ -834,7 +858,7 @@ drm_intel_gem_bo_alloc_tiled(drm_intel_bufmgr *bufmgr, const char *name,
  * This can be used when one application needs to pass a buffer object
  * to another.
  */
-drm_intel_bo *
+drm_public drm_intel_bo *
 drm_intel_bo_gem_create_from_name(drm_intel_bufmgr *bufmgr,
                                  const char *name,
                                  unsigned int handle)
@@ -862,10 +886,6 @@ drm_intel_bo_gem_create_from_name(drm_intel_bufmgr *bufmgr,
                }
        }
 
-       bo_gem = calloc(1, sizeof(*bo_gem));
-       if (!bo_gem)
-               return NULL;
-
        VG_CLEAR(open_arg);
        open_arg.name = handle;
        ret = drmIoctl(bufmgr_gem->fd,
@@ -874,11 +894,29 @@ drm_intel_bo_gem_create_from_name(drm_intel_bufmgr *bufmgr,
        if (ret != 0) {
                DBG("Couldn't reference %s handle 0x%08x: %s\n",
                    name, handle, strerror(errno));
-               free(bo_gem);
                return NULL;
        }
+        /* Now see if someone has used a prime handle to get this
+         * object from the kernel before by looking through the list
+         * again for a matching gem_handle
+         */
+       for (list = bufmgr_gem->named.next;
+            list != &bufmgr_gem->named;
+            list = list->next) {
+               bo_gem = DRMLISTENTRY(drm_intel_bo_gem, list, name_list);
+               if (bo_gem->gem_handle == open_arg.handle) {
+                       drm_intel_gem_bo_reference(&bo_gem->bo);
+                       return &bo_gem->bo;
+               }
+       }
+
+       bo_gem = calloc(1, sizeof(*bo_gem));
+       if (!bo_gem)
+               return NULL;
+
        bo_gem->bo.size = open_arg.size;
        bo_gem->bo.offset = 0;
+       bo_gem->bo.offset64 = 0;
        bo_gem->bo.virtual = NULL;
        bo_gem->bo.bufmgr = bufmgr;
        bo_gem->name = name;
@@ -1119,7 +1157,8 @@ static void drm_intel_gem_bo_unreference(drm_intel_bo *bo)
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
 
        assert(atomic_read(&bo_gem->refcount) > 0);
-       if (atomic_dec_and_test(&bo_gem->refcount)) {
+
+       if (atomic_add_unless(&bo_gem->refcount, -1, 1)) {
                drm_intel_bufmgr_gem *bufmgr_gem =
                    (drm_intel_bufmgr_gem *) bo->bufmgr;
                struct timespec time;
@@ -1127,8 +1166,12 @@ static void drm_intel_gem_bo_unreference(drm_intel_bo *bo)
                clock_gettime(CLOCK_MONOTONIC, &time);
 
                pthread_mutex_lock(&bufmgr_gem->lock);
-               drm_intel_gem_bo_unreference_final(bo, time.tv_sec);
-               drm_intel_gem_cleanup_bo_cache(bufmgr_gem, time.tv_sec);
+
+               if (atomic_dec_and_test(&bo_gem->refcount)) {
+                       drm_intel_gem_bo_unreference_final(bo, time.tv_sec);
+                       drm_intel_gem_cleanup_bo_cache(bufmgr_gem, time.tv_sec);
+               }
+
                pthread_mutex_unlock(&bufmgr_gem->lock);
        }
 }
@@ -1261,7 +1304,8 @@ map_gtt(drm_intel_bo *bo)
        return 0;
 }
 
-int drm_intel_gem_bo_map_gtt(drm_intel_bo *bo)
+drm_public int
+drm_intel_gem_bo_map_gtt(drm_intel_bo *bo)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
@@ -1319,9 +1363,13 @@ int drm_intel_gem_bo_map_gtt(drm_intel_bo *bo)
  * undefined).
  */
 
-int drm_intel_gem_bo_map_unsynchronized(drm_intel_bo *bo)
+drm_public int
+drm_intel_gem_bo_map_unsynchronized(drm_intel_bo *bo)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
+#ifdef HAVE_VALGRIND
+       drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
+#endif
        int ret;
 
        /* If the CPU cache isn't coherent with the GTT, then use a
@@ -1335,7 +1383,13 @@ int drm_intel_gem_bo_map_unsynchronized(drm_intel_bo *bo)
                return drm_intel_gem_bo_map_gtt(bo);
 
        pthread_mutex_lock(&bufmgr_gem->lock);
+
        ret = map_gtt(bo);
+       if (ret == 0) {
+               drm_intel_gem_bo_mark_mmaps_incoherent(bo);
+               VG(VALGRIND_MAKE_MEM_DEFINED(bo_gem->gtt_virtual, bo->size));
+       }
+
        pthread_mutex_unlock(&bufmgr_gem->lock);
 
        return ret;
@@ -1393,7 +1447,8 @@ static int drm_intel_gem_bo_unmap(drm_intel_bo *bo)
        return ret;
 }
 
-int drm_intel_gem_bo_unmap_gtt(drm_intel_bo *bo)
+drm_public int
+drm_intel_gem_bo_unmap_gtt(drm_intel_bo *bo)
 {
        return drm_intel_gem_bo_unmap(bo);
 }
@@ -1508,7 +1563,8 @@ drm_intel_gem_bo_wait_rendering(drm_intel_bo *bo)
  * handle. Userspace must make sure this race does not occur if such precision
  * is important.
  */
-int drm_intel_gem_bo_wait(drm_intel_bo *bo, int64_t timeout_ns)
+drm_public int
+drm_intel_gem_bo_wait(drm_intel_bo *bo, int64_t timeout_ns)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
@@ -1543,7 +1599,7 @@ int drm_intel_gem_bo_wait(drm_intel_bo *bo, int64_t timeout_ns)
  * In combination with drm_intel_gem_bo_pin() and manual fence management, we
  * can do tiled pixmaps this way.
  */
-void
+drm_public void
 drm_intel_gem_bo_start_gtt_access(drm_intel_bo *bo, int write_enable)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
@@ -1665,7 +1721,7 @@ do_bo_emit_reloc(drm_intel_bo *bo, uint32_t offset,
            target_bo_gem->gem_handle;
        bo_gem->relocs[bo_gem->reloc_count].read_domains = read_domains;
        bo_gem->relocs[bo_gem->reloc_count].write_domain = write_domain;
-       bo_gem->relocs[bo_gem->reloc_count].presumed_offset = target_bo->offset;
+       bo_gem->relocs[bo_gem->reloc_count].presumed_offset = target_bo->offset64;
 
        bo_gem->reloc_target_info[bo_gem->reloc_count].bo = target_bo;
        if (target_bo != bo)
@@ -1703,7 +1759,7 @@ drm_intel_gem_bo_emit_reloc_fence(drm_intel_bo *bo, uint32_t offset,
                                read_domains, write_domain, true);
 }
 
-int
+drm_public int
 drm_intel_gem_bo_get_reloc_count(drm_intel_bo *bo)
 {
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
@@ -1724,9 +1780,10 @@ drm_intel_gem_bo_get_reloc_count(drm_intel_bo *bo)
  * Any further drm_intel_bufmgr_check_aperture_space() queries
  * involving this buffer in the tree are undefined after this call.
  */
-void
+drm_public void
 drm_intel_gem_bo_clear_relocs(drm_intel_bo *bo, int start)
 {
+       drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
        int i;
        struct timespec time;
@@ -1734,7 +1791,10 @@ drm_intel_gem_bo_clear_relocs(drm_intel_bo *bo, int start)
        clock_gettime(CLOCK_MONOTONIC, &time);
 
        assert(bo_gem->reloc_count >= start);
+
        /* Unreference the cleared target buffers */
+       pthread_mutex_lock(&bufmgr_gem->lock);
+
        for (i = start; i < bo_gem->reloc_count; i++) {
                drm_intel_bo_gem *target_bo_gem = (drm_intel_bo_gem *) bo_gem->reloc_target_info[i].bo;
                if (&target_bo_gem->bo != bo) {
@@ -1744,6 +1804,9 @@ drm_intel_gem_bo_clear_relocs(drm_intel_bo *bo, int start)
                }
        }
        bo_gem->reloc_count = start;
+
+       pthread_mutex_unlock(&bufmgr_gem->lock);
+
 }
 
 /**
@@ -1816,11 +1879,12 @@ drm_intel_update_buffer_offsets(drm_intel_bufmgr_gem *bufmgr_gem)
                drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
 
                /* Update the buffer offset */
-               if (bufmgr_gem->exec_objects[i].offset != bo->offset) {
+               if (bufmgr_gem->exec_objects[i].offset != bo->offset64) {
                        DBG("BO %d (%s) migrated: 0x%08lx -> 0x%08llx\n",
-                           bo_gem->gem_handle, bo_gem->name, bo->offset,
+                           bo_gem->gem_handle, bo_gem->name, bo->offset64,
                            (unsigned long long)bufmgr_gem->exec_objects[i].
                            offset);
+                       bo->offset64 = bufmgr_gem->exec_objects[i].offset;
                        bo->offset = bufmgr_gem->exec_objects[i].offset;
                }
        }
@@ -1836,10 +1900,11 @@ drm_intel_update_buffer_offsets2 (drm_intel_bufmgr_gem *bufmgr_gem)
                drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *)bo;
 
                /* Update the buffer offset */
-               if (bufmgr_gem->exec2_objects[i].offset != bo->offset) {
+               if (bufmgr_gem->exec2_objects[i].offset != bo->offset64) {
                        DBG("BO %d (%s) migrated: 0x%08lx -> 0x%08llx\n",
-                           bo_gem->gem_handle, bo_gem->name, bo->offset,
+                           bo_gem->gem_handle, bo_gem->name, bo->offset64,
                            (unsigned long long)bufmgr_gem->exec2_objects[i].offset);
+                       bo->offset64 = bufmgr_gem->exec2_objects[i].offset;
                        bo->offset = bufmgr_gem->exec2_objects[i].offset;
                }
        }
@@ -1937,12 +2002,14 @@ aub_write_trace_block(drm_intel_bo *bo, uint32_t type, uint32_t subtype,
 
        aub_out(bufmgr_gem,
                CMD_AUB_TRACE_HEADER_BLOCK |
-               (5 - 2));
+               ((bufmgr_gem->gen >= 8 ? 6 : 5) - 2));
        aub_out(bufmgr_gem,
                AUB_TRACE_MEMTYPE_GTT | type | AUB_TRACE_OP_DATA_WRITE);
        aub_out(bufmgr_gem, subtype);
        aub_out(bufmgr_gem, bo_gem->aub_offset + offset);
        aub_out(bufmgr_gem, size);
+       if (bufmgr_gem->gen >= 8)
+               aub_out(bufmgr_gem, 0);
        aub_write_bo_data(bo, offset, size);
 }
 
@@ -2019,20 +2086,28 @@ aub_build_dump_ringbuffer(drm_intel_bufmgr_gem *bufmgr_gem,
 
        /* Make a ring buffer to execute our batchbuffer. */
        memset(ringbuffer, 0, sizeof(ringbuffer));
-       ringbuffer[ring_count++] = AUB_MI_BATCH_BUFFER_START;
-       ringbuffer[ring_count++] = batch_buffer;
+       if (bufmgr_gem->gen >= 8) {
+               ringbuffer[ring_count++] = AUB_MI_BATCH_BUFFER_START | (3 - 2);
+               ringbuffer[ring_count++] = batch_buffer;
+               ringbuffer[ring_count++] = 0;
+       } else {
+               ringbuffer[ring_count++] = AUB_MI_BATCH_BUFFER_START;
+               ringbuffer[ring_count++] = batch_buffer;
+       }
 
        /* Write out the ring.  This appears to trigger execution of
         * the ring in the simulator.
         */
        aub_out(bufmgr_gem,
                CMD_AUB_TRACE_HEADER_BLOCK |
-               (5 - 2));
+               ((bufmgr_gem->gen >= 8 ? 6 : 5) - 2));
        aub_out(bufmgr_gem,
                AUB_TRACE_MEMTYPE_GTT | ring | AUB_TRACE_OP_COMMAND_WRITE);
        aub_out(bufmgr_gem, 0); /* general/surface subtype */
        aub_out(bufmgr_gem, bufmgr_gem->aub_offset);
        aub_out(bufmgr_gem, ring_count * 4);
+       if (bufmgr_gem->gen >= 8)
+               aub_out(bufmgr_gem, 0);
 
        /* FIXME: Need some flush operations here? */
        aub_out_data(bufmgr_gem, ringbuffer, ring_count * 4);
@@ -2041,7 +2116,7 @@ aub_build_dump_ringbuffer(drm_intel_bufmgr_gem *bufmgr_gem,
        bufmgr_gem->aub_offset += 4096;
 }
 
-void
+drm_public void
 drm_intel_gem_bo_aub_dump_bmp(drm_intel_bo *bo,
                              int x1, int y1, int width, int height,
                              enum aub_dump_bmp_format format,
@@ -2185,6 +2260,8 @@ drm_intel_gem_bo_exec(drm_intel_bo *bo, int used,
                drm_intel_bo *bo = bufmgr_gem->exec_bos[i];
                drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
 
+               bo_gem->idle = false;
+
                /* Disconnect the buffer from the validate list */
                bo_gem->validate_index = -1;
                bufmgr_gem->exec_bos[i] = NULL;
@@ -2280,6 +2357,8 @@ skip_execution:
                drm_intel_bo *bo = bufmgr_gem->exec_bos[i];
                drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *)bo;
 
+               bo_gem->idle = false;
+
                /* Disconnect the buffer from the validate list */
                bo_gem->validate_index = -1;
                bufmgr_gem->exec_bos[i] = NULL;
@@ -2308,7 +2387,7 @@ drm_intel_gem_bo_mrb_exec2(drm_intel_bo *bo, int used,
                        flags);
 }
 
-int
+drm_public int
 drm_intel_gem_bo_context_exec(drm_intel_bo *bo, drm_intel_context *ctx,
                              int used, unsigned int flags)
 {
@@ -2333,6 +2412,7 @@ drm_intel_gem_bo_pin(drm_intel_bo *bo, uint32_t alignment)
        if (ret != 0)
                return -errno;
 
+       bo->offset64 = pin.offset;
        bo->offset = pin.offset;
        return 0;
 }
@@ -2426,7 +2506,7 @@ drm_intel_gem_bo_get_tiling(drm_intel_bo *bo, uint32_t * tiling_mode,
        return 0;
 }
 
-drm_intel_bo *
+drm_public drm_intel_bo *
 drm_intel_bo_gem_create_from_prime(drm_intel_bufmgr *bufmgr, int prime_fd, int size)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bufmgr;
@@ -2434,8 +2514,25 @@ drm_intel_bo_gem_create_from_prime(drm_intel_bufmgr *bufmgr, int prime_fd, int s
        uint32_t handle;
        drm_intel_bo_gem *bo_gem;
        struct drm_i915_gem_get_tiling get_tiling;
+       drmMMListHead *list;
 
        ret = drmPrimeFDToHandle(bufmgr_gem->fd, prime_fd, &handle);
+
+       /*
+        * See if the kernel has already returned this buffer to us. Just as
+        * for named buffers, we must not create two bo's pointing at the same
+        * kernel object
+        */
+       for (list = bufmgr_gem->named.next;
+            list != &bufmgr_gem->named;
+            list = list->next) {
+               bo_gem = DRMLISTENTRY(drm_intel_bo_gem, list, name_list);
+               if (bo_gem->gem_handle == handle) {
+                       drm_intel_gem_bo_reference(&bo_gem->bo);
+                       return &bo_gem->bo;
+               }
+       }
+
        if (ret) {
          fprintf(stderr,"ret is %d %d\n", ret, errno);
                return NULL;
@@ -2445,7 +2542,17 @@ drm_intel_bo_gem_create_from_prime(drm_intel_bufmgr *bufmgr, int prime_fd, int s
        if (!bo_gem)
                return NULL;
 
-       bo_gem->bo.size = size;
+       /* Determine size of bo.  The fd-to-handle ioctl really should
+        * return the size, but it doesn't.  If we have kernel 3.12 or
+        * later, we can lseek on the prime fd to get the size.  Older
+        * kernels will just fail, in which case we fall back to the
+        * provided (estimated or guess size). */
+       ret = lseek(prime_fd, 0, SEEK_END);
+       if (ret != -1)
+               bo_gem->bo.size = ret;
+       else
+               bo_gem->bo.size = size;
+
        bo_gem->bo.handle = handle;
        bo_gem->bo.bufmgr = bufmgr;
 
@@ -2460,8 +2567,8 @@ drm_intel_bo_gem_create_from_prime(drm_intel_bufmgr *bufmgr, int prime_fd, int s
        bo_gem->has_error = false;
        bo_gem->reusable = false;
 
-       DRMINITLISTHEAD(&bo_gem->name_list);
        DRMINITLISTHEAD(&bo_gem->vma_list);
+       DRMLISTADDTAIL(&bo_gem->name_list, &bufmgr_gem->named);
 
        VG_CLEAR(get_tiling);
        get_tiling.handle = bo_gem->gem_handle;
@@ -2480,12 +2587,15 @@ drm_intel_bo_gem_create_from_prime(drm_intel_bufmgr *bufmgr, int prime_fd, int s
        return &bo_gem->bo;
 }
 
-int
+drm_public int
 drm_intel_bo_gem_export_to_prime(drm_intel_bo *bo, int *prime_fd)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bo->bufmgr;
        drm_intel_bo_gem *bo_gem = (drm_intel_bo_gem *) bo;
 
+        if (DRMLISTEMPTY(&bo_gem->name_list))
+                DRMLISTADDTAIL(&bo_gem->name_list, &bufmgr_gem->named);
+
        if (drmPrimeHandleToFD(bufmgr_gem->fd, bo_gem->gem_handle,
                               DRM_CLOEXEC, prime_fd) != 0)
                return -errno;
@@ -2515,7 +2625,8 @@ drm_intel_gem_bo_flink(drm_intel_bo *bo, uint32_t * name)
                bo_gem->global_name = flink.name;
                bo_gem->reusable = false;
 
-               DRMLISTADDTAIL(&bo_gem->name_list, &bufmgr_gem->named);
+                if (DRMLISTEMPTY(&bo_gem->name_list))
+                        DRMLISTADDTAIL(&bo_gem->name_list, &bufmgr_gem->named);
        }
 
        *name = bo_gem->global_name;
@@ -2529,7 +2640,7 @@ drm_intel_gem_bo_flink(drm_intel_bo *bo, uint32_t * name)
  * size is only bounded by how many buffers of that size we've managed to have
  * in flight at once.
  */
-void
+drm_public void
 drm_intel_bufmgr_gem_enable_reuse(drm_intel_bufmgr *bufmgr)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *) bufmgr;
@@ -2544,7 +2655,7 @@ drm_intel_bufmgr_gem_enable_reuse(drm_intel_bufmgr *bufmgr)
  * allocation.  If this option is not enabled, all relocs will have fence
  * register allocated.
  */
-void
+drm_public void
 drm_intel_bufmgr_gem_enable_fenced_relocs(drm_intel_bufmgr *bufmgr)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *)bufmgr;
@@ -2816,7 +2927,7 @@ init_cache_buckets(drm_intel_bufmgr_gem *bufmgr_gem)
        }
 }
 
-void
+drm_public void
 drm_intel_bufmgr_gem_set_vma_cache_size(drm_intel_bufmgr *bufmgr, int limit)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *)bufmgr;
@@ -2858,7 +2969,7 @@ get_pci_device_id(drm_intel_bufmgr_gem *bufmgr_gem)
        return devid;
 }
 
-int
+drm_public int
 drm_intel_bufmgr_gem_get_devid(drm_intel_bufmgr *bufmgr)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *)bufmgr;
@@ -2872,7 +2983,7 @@ drm_intel_bufmgr_gem_get_devid(drm_intel_bufmgr *bufmgr)
  * This function has to be called before drm_intel_bufmgr_gem_set_aub_dump()
  * for it to have any effect.
  */
-void
+drm_public void
 drm_intel_bufmgr_gem_set_aub_filename(drm_intel_bufmgr *bufmgr,
                                      const char *filename)
 {
@@ -2891,7 +3002,7 @@ drm_intel_bufmgr_gem_set_aub_filename(drm_intel_bufmgr *bufmgr,
  * You can set up a GTT and upload your objects into the referenced
  * space, then send off batchbuffers and get BMPs out the other end.
  */
-void
+drm_public void
 drm_intel_bufmgr_gem_set_aub_dump(drm_intel_bufmgr *bufmgr, int enable)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *)bufmgr;
@@ -2935,17 +3046,19 @@ drm_intel_bufmgr_gem_set_aub_dump(drm_intel_bufmgr *bufmgr, int enable)
        aub_out(bufmgr_gem, 0); /* comment len */
 
        /* Set up the GTT. The max we can handle is 256M */
-       aub_out(bufmgr_gem, CMD_AUB_TRACE_HEADER_BLOCK | (5 - 2));
+       aub_out(bufmgr_gem, CMD_AUB_TRACE_HEADER_BLOCK | ((bufmgr_gem->gen >= 8 ? 6 : 5) - 2));
        aub_out(bufmgr_gem, AUB_TRACE_MEMTYPE_NONLOCAL | 0 | AUB_TRACE_OP_DATA_WRITE);
        aub_out(bufmgr_gem, 0); /* subtype */
        aub_out(bufmgr_gem, 0); /* offset */
        aub_out(bufmgr_gem, gtt_size); /* size */
+       if (bufmgr_gem->gen >= 8)
+               aub_out(bufmgr_gem, 0);
        for (i = 0x000; i < gtt_size; i += 4, entry += 0x1000) {
                aub_out(bufmgr_gem, entry);
        }
 }
 
-drm_intel_context *
+drm_public drm_intel_context *
 drm_intel_gem_context_create(drm_intel_bufmgr *bufmgr)
 {
        drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *)bufmgr;
@@ -2953,22 +3066,26 @@ drm_intel_gem_context_create(drm_intel_bufmgr *bufmgr)
        drm_intel_context *context = NULL;
        int ret;
 
+       context = calloc(1, sizeof(*context));
+       if (!context)
+               return NULL;
+
        VG_CLEAR(create);
        ret = drmIoctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_CONTEXT_CREATE, &create);
        if (ret != 0) {
                DBG("DRM_IOCTL_I915_GEM_CONTEXT_CREATE failed: %s\n",
                    strerror(errno));
+               free(context);
                return NULL;
        }
 
-       context = calloc(1, sizeof(*context));
        context->ctx_id = create.ctx_id;
        context->bufmgr = bufmgr;
 
        return context;
 }
 
-void
+drm_public void
 drm_intel_gem_context_destroy(drm_intel_context *ctx)
 {
        drm_intel_bufmgr_gem *bufmgr_gem;
@@ -2991,7 +3108,41 @@ drm_intel_gem_context_destroy(drm_intel_context *ctx)
        free(ctx);
 }
 
-int
+drm_public int
+drm_intel_get_reset_stats(drm_intel_context *ctx,
+                         uint32_t *reset_count,
+                         uint32_t *active,
+                         uint32_t *pending)
+{
+       drm_intel_bufmgr_gem *bufmgr_gem;
+       struct drm_i915_reset_stats stats;
+       int ret;
+
+       if (ctx == NULL)
+               return -EINVAL;
+
+       memset(&stats, 0, sizeof(stats));
+
+       bufmgr_gem = (drm_intel_bufmgr_gem *)ctx->bufmgr;
+       stats.ctx_id = ctx->ctx_id;
+       ret = drmIoctl(bufmgr_gem->fd,
+                      DRM_IOCTL_I915_GET_RESET_STATS,
+                      &stats);
+       if (ret == 0) {
+               if (reset_count != NULL)
+                       *reset_count = stats.reset_count;
+
+               if (active != NULL)
+                       *active = stats.batch_active;
+
+               if (pending != NULL)
+                       *pending = stats.batch_pending;
+       }
+
+       return ret;
+}
+
+drm_public int
 drm_intel_reg_read(drm_intel_bufmgr *bufmgr,
                   uint32_t offset,
                   uint64_t *result)
@@ -3031,7 +3182,7 @@ drm_intel_reg_read(drm_intel_bufmgr *bufmgr,
  * default state (no annotations), call this function with a \c count
  * of zero.
  */
-void
+drm_public void
 drm_intel_bufmgr_gem_set_aub_annotations(drm_intel_bo *bo,
                                         drm_intel_aub_annotation *annotations,
                                         unsigned count)
@@ -3051,13 +3202,48 @@ drm_intel_bufmgr_gem_set_aub_annotations(drm_intel_bo *bo,
        bo_gem->aub_annotation_count = count;
 }
 
+static pthread_mutex_t bufmgr_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static drmMMListHead bufmgr_list = { &bufmgr_list, &bufmgr_list };
+
+static drm_intel_bufmgr_gem *
+drm_intel_bufmgr_gem_find(int fd)
+{
+       drm_intel_bufmgr_gem *bufmgr_gem;
+
+       DRMLISTFOREACHENTRY(bufmgr_gem, &bufmgr_list, managers) {
+               if (bufmgr_gem->fd == fd) {
+                       atomic_inc(&bufmgr_gem->refcount);
+                       return bufmgr_gem;
+               }
+       }
+
+       return NULL;
+}
+
+static void
+drm_intel_bufmgr_gem_unref(drm_intel_bufmgr *bufmgr)
+{
+       drm_intel_bufmgr_gem *bufmgr_gem = (drm_intel_bufmgr_gem *)bufmgr;
+
+       if (atomic_add_unless(&bufmgr_gem->refcount, -1, 1)) {
+               pthread_mutex_lock(&bufmgr_list_mutex);
+
+               if (atomic_dec_and_test(&bufmgr_gem->refcount)) {
+                       DRMLISTDEL(&bufmgr_gem->managers);
+                       drm_intel_bufmgr_gem_destroy(bufmgr);
+               }
+
+               pthread_mutex_unlock(&bufmgr_list_mutex);
+       }
+}
+
 /**
  * Initializes the GEM buffer manager, which uses the kernel to allocate, map,
  * and manage map buffer objections.
  *
  * \param fd File descriptor of the opened DRM device.
  */
-drm_intel_bufmgr *
+drm_public drm_intel_bufmgr *
 drm_intel_bufmgr_gem_init(int fd, int batch_size)
 {
        drm_intel_bufmgr_gem *bufmgr_gem;
@@ -3066,15 +3252,23 @@ drm_intel_bufmgr_gem_init(int fd, int batch_size)
        int ret, tmp;
        bool exec2 = false;
 
+       pthread_mutex_lock(&bufmgr_list_mutex);
+
+       bufmgr_gem = drm_intel_bufmgr_gem_find(fd);
+       if (bufmgr_gem)
+               goto exit;
+
        bufmgr_gem = calloc(1, sizeof(*bufmgr_gem));
        if (bufmgr_gem == NULL)
-               return NULL;
+               goto exit;
 
        bufmgr_gem->fd = fd;
+       atomic_set(&bufmgr_gem->refcount, 1);
 
        if (pthread_mutex_init(&bufmgr_gem->lock, NULL) != 0) {
                free(bufmgr_gem);
-               return NULL;
+               bufmgr_gem = NULL;
+               goto exit;
        }
 
        ret = drmIoctl(bufmgr_gem->fd,
@@ -3107,9 +3301,12 @@ drm_intel_bufmgr_gem_init(int fd, int batch_size)
                bufmgr_gem->gen = 6;
        else if (IS_GEN7(bufmgr_gem->pci_device))
                bufmgr_gem->gen = 7;
+       else if (IS_GEN8(bufmgr_gem->pci_device))
+               bufmgr_gem->gen = 8;
        else {
                free(bufmgr_gem);
-               return NULL;
+               bufmgr_gem = NULL;
+               goto exit;
        }
 
        if (IS_GEN3(bufmgr_gem->pci_device) &&
@@ -3220,7 +3417,7 @@ drm_intel_bufmgr_gem_init(int fd, int batch_size)
                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;
-       bufmgr_gem->bufmgr.destroy = drm_intel_bufmgr_gem_destroy;
+       bufmgr_gem->bufmgr.destroy = drm_intel_bufmgr_gem_unref;
        bufmgr_gem->bufmgr.debug = 0;
        bufmgr_gem->bufmgr.check_aperture_space =
            drm_intel_gem_check_aperture_space;
@@ -3236,5 +3433,10 @@ drm_intel_bufmgr_gem_init(int fd, int batch_size)
        DRMINITLISTHEAD(&bufmgr_gem->vma_cache);
        bufmgr_gem->vma_max = -1; /* unlimited by default */
 
-       return &bufmgr_gem->bufmgr;
+       DRMLISTADD(&bufmgr_gem->managers, &bufmgr_list);
+
+exit:
+       pthread_mutex_unlock(&bufmgr_list_mutex);
+
+       return bufmgr_gem != NULL ? &bufmgr_gem->bufmgr : NULL;
 }