winsys/radeon: Use a single buffer cache manager again
[mesa.git] / src / gallium / winsys / radeon / drm / radeon_drm_bo.c
index 79355af79953f0182e41d201efeaed27f7f1d0c2..2cfa43bc339994f8c48793986f662755cb05b432 100644 (file)
@@ -24,7 +24,6 @@
  * of the Software.
  */
 
-#define _FILE_OFFSET_BITS 64
 #include "radeon_drm_cs.h"
 
 #include "util/u_hash_table.h"
 #include "util/u_double_list.h"
 #include "os/os_thread.h"
 #include "os/os_mman.h"
+#include "os/os_time.h"
 
 #include "state_tracker/drm_driver.h"
 
 #include <sys/ioctl.h>
 #include <xf86drm.h>
 #include <errno.h>
-
-/*
- * this are copy from radeon_drm, once an updated libdrm is released
- * we should bump configure.ac requirement for it and remove the following
- * field
- */
-#define RADEON_BO_FLAGS_MACRO_TILE  1
-#define RADEON_BO_FLAGS_MICRO_TILE  2
-#define RADEON_BO_FLAGS_MICRO_TILE_SQUARE 0x20
-
-#ifndef DRM_RADEON_GEM_WAIT
-#define DRM_RADEON_GEM_WAIT     0x2b
-
-#define RADEON_GEM_NO_WAIT      0x1
-#define RADEON_GEM_USAGE_READ   0x2
-#define RADEON_GEM_USAGE_WRITE  0x4
-
-struct drm_radeon_gem_wait {
-    uint32_t    handle;
-    uint32_t    flags;  /* one of RADEON_GEM_* */
-};
-
-#endif
-
-#ifndef RADEON_VA_MAP
-
-#define RADEON_VA_MAP               1
-#define RADEON_VA_UNMAP             2
-
-#define RADEON_VA_RESULT_OK         0
-#define RADEON_VA_RESULT_ERROR      1
-#define RADEON_VA_RESULT_VA_EXIST   2
-
-#define RADEON_VM_PAGE_VALID        (1 << 0)
-#define RADEON_VM_PAGE_READABLE     (1 << 1)
-#define RADEON_VM_PAGE_WRITEABLE    (1 << 2)
-#define RADEON_VM_PAGE_SYSTEM       (1 << 3)
-#define RADEON_VM_PAGE_SNOOPED      (1 << 4)
-
-struct drm_radeon_gem_va {
-    uint32_t    handle;
-    uint32_t    operation;
-    uint32_t    vm_id;
-    uint32_t    flags;
-    uint64_t    offset;
-};
-
-#define DRM_RADEON_GEM_VA   0x2b
-#endif
-
-
+#include <fcntl.h>
+#include <stdio.h>
 
 extern const struct pb_vtbl radeon_bo_vtbl;
 
-
 static INLINE struct radeon_bo *radeon_bo(struct pb_buffer *bo)
 {
     assert(bo->vtbl == &radeon_bo_vtbl);
@@ -113,8 +63,12 @@ struct radeon_bomgr {
     /* Winsys. */
     struct radeon_drm_winsys *rws;
 
-    /* List of buffer handles and its mutex. */
+    /* List of buffer GEM names. Protected by bo_handles_mutex. */
+    struct util_hash_table *bo_names;
+    /* List of buffer handles. Protectded by bo_handles_mutex. */
     struct util_hash_table *bo_handles;
+    /* List of buffer virtual memory ranges. Protectded by bo_handles_mutex. */
+    struct util_hash_table *bo_vas;
     pipe_mutex bo_handles_mutex;
     pipe_mutex bo_va_mutex;
 
@@ -150,50 +104,62 @@ static struct radeon_bo *get_radeon_bo(struct pb_buffer *_buf)
 static void radeon_bo_wait(struct pb_buffer *_buf, enum radeon_bo_usage usage)
 {
     struct radeon_bo *bo = get_radeon_bo(_buf);
+    struct drm_radeon_gem_wait_idle args = {0};
 
     while (p_atomic_read(&bo->num_active_ioctls)) {
         sched_yield();
     }
 
-    /* XXX use this when it's ready */
-    /*if (bo->rws->info.drm_minor >= 12) {
-        struct drm_radeon_gem_wait args = {};
-        args.handle = bo->handle;
-        args.flags = usage;
-        while (drmCommandWriteRead(bo->rws->fd, DRM_RADEON_GEM_WAIT,
-                                   &args, sizeof(args)) == -EBUSY);
-    } else*/ {
-        struct drm_radeon_gem_wait_idle args;
-        memset(&args, 0, sizeof(args));
-        args.handle = bo->handle;
-        while (drmCommandWriteRead(bo->rws->fd, DRM_RADEON_GEM_WAIT_IDLE,
-                                   &args, sizeof(args)) == -EBUSY);
-    }
+    args.handle = bo->handle;
+    while (drmCommandWrite(bo->rws->fd, DRM_RADEON_GEM_WAIT_IDLE,
+                           &args, sizeof(args)) == -EBUSY);
 }
 
 static boolean radeon_bo_is_busy(struct pb_buffer *_buf,
                                  enum radeon_bo_usage usage)
 {
     struct radeon_bo *bo = get_radeon_bo(_buf);
+    struct drm_radeon_gem_busy args = {0};
 
     if (p_atomic_read(&bo->num_active_ioctls)) {
         return TRUE;
     }
 
-    /* XXX use this when it's ready */
-    /*if (bo->rws->info.drm_minor >= 12) {
-        struct drm_radeon_gem_wait args = {};
-        args.handle = bo->handle;
-        args.flags = usage | RADEON_GEM_NO_WAIT;
-        return drmCommandWriteRead(bo->rws->fd, DRM_RADEON_GEM_WAIT,
-                                   &args, sizeof(args)) != 0;
-    } else*/ {
-        struct drm_radeon_gem_busy args;
-        memset(&args, 0, sizeof(args));
-        args.handle = bo->handle;
-        return drmCommandWriteRead(bo->rws->fd, DRM_RADEON_GEM_BUSY,
-                                   &args, sizeof(args)) != 0;
-    }
+    args.handle = bo->handle;
+    return drmCommandWriteRead(bo->rws->fd, DRM_RADEON_GEM_BUSY,
+                               &args, sizeof(args)) != 0;
+}
+
+static enum radeon_bo_domain get_valid_domain(enum radeon_bo_domain domain)
+{
+    /* Zero domains the driver doesn't understand. */
+    domain &= RADEON_DOMAIN_VRAM_GTT;
+
+    /* If no domain is set, we must set something... */
+    if (!domain)
+        domain = RADEON_DOMAIN_VRAM_GTT;
+
+    return domain;
+}
+
+static enum radeon_bo_domain radeon_bo_get_initial_domain(
+               struct radeon_winsys_cs_handle *buf)
+{
+    struct radeon_bo *bo = (struct radeon_bo*)buf;
+    struct drm_radeon_gem_op args;
+
+    if (bo->rws->info.drm_minor < 38)
+        return RADEON_DOMAIN_VRAM_GTT;
+
+    memset(&args, 0, sizeof(args));
+    args.handle = bo->handle;
+    args.op = RADEON_GEM_OP_GET_INITIAL_DOMAIN;
+
+    drmCommandWriteRead(bo->rws->fd, DRM_RADEON_GEM_OP,
+                        &args, sizeof(args));
+
+    /* GEM domains and winsys domains are defined the same. */
+    return get_valid_domain(args.value);
 }
 
 static uint64_t radeon_bomgr_find_va(struct radeon_bomgr *mgr, uint64_t size, uint64_t alignment)
@@ -201,15 +167,15 @@ static uint64_t radeon_bomgr_find_va(struct radeon_bomgr *mgr, uint64_t size, ui
     struct radeon_bo_va_hole *hole, *n;
     uint64_t offset = 0, waste = 0;
 
+    alignment = MAX2(alignment, 4096);
+    size = align(size, 4096);
+
     pipe_mutex_lock(mgr->bo_va_mutex);
     /* first look for a hole */
     LIST_FOR_EACH_ENTRY_SAFE(hole, n, &mgr->va_holes, list) {
         offset = hole->offset;
-        waste = 0;
-        if (alignment) {
-            waste = offset % alignment;
-            waste = waste ? alignment - waste : 0;
-        }
+        waste = offset % alignment;
+        waste = waste ? alignment - waste : 0;
         offset += waste;
         if (offset >= (hole->offset + hole->size)) {
             continue;
@@ -241,10 +207,13 @@ static uint64_t radeon_bomgr_find_va(struct radeon_bomgr *mgr, uint64_t size, ui
     }
 
     offset = mgr->va_offset;
-    waste = 0;
-    if (alignment) {
-        waste = offset % alignment;
-        waste = waste ? alignment - waste : 0;
+    waste = offset % alignment;
+    waste = waste ? alignment - waste : 0;
+    if (waste) {
+        n = CALLOC_STRUCT(radeon_bo_va_hole);
+        n->size = waste;
+        n->offset = offset;
+        list_add(&n->list, &mgr->va_holes);
     }
     offset += waste;
     mgr->va_offset += size + waste;
@@ -252,50 +221,12 @@ static uint64_t radeon_bomgr_find_va(struct radeon_bomgr *mgr, uint64_t size, ui
     return offset;
 }
 
-static void radeon_bomgr_force_va(struct radeon_bomgr *mgr, uint64_t va, uint64_t size)
-{
-    pipe_mutex_lock(mgr->bo_va_mutex);
-    if (va >= mgr->va_offset) {
-        if (va > mgr->va_offset) {
-            struct radeon_bo_va_hole *hole;
-            hole = CALLOC_STRUCT(radeon_bo_va_hole);
-            if (hole) {
-                hole->size = va - mgr->va_offset;
-                hole->offset = mgr->va_offset;
-                list_add(&hole->list, &mgr->va_holes);
-            }
-        }
-        mgr->va_offset = va + size;
-    } else {
-        struct radeon_bo_va_hole *hole, *n;
-        uint64_t hole_end, va_end;
-
-        /* Prune/free all holes that fall into the range
-         */
-        LIST_FOR_EACH_ENTRY_SAFE(hole, n, &mgr->va_holes, list) {
-            hole_end = hole->offset + hole->size;
-            va_end = va + size;
-            if (hole->offset >= va_end || hole_end <= va)
-                continue;
-            if (hole->offset >= va && hole_end <= va_end) {
-                list_del(&hole->list);
-                FREE(hole);
-                continue;
-            }
-            if (hole->offset >= va)
-                hole->offset = va_end;
-            else
-                hole_end = va;
-            hole->size = hole_end - hole->offset;
-        }
-    }
-    pipe_mutex_unlock(mgr->bo_va_mutex);
-}
-
 static void radeon_bomgr_free_va(struct radeon_bomgr *mgr, uint64_t va, uint64_t size)
 {
     struct radeon_bo_va_hole *hole;
 
+    size = align(size, 4096);
+
     pipe_mutex_lock(mgr->bo_va_mutex);
     if ((va + size) == mgr->va_offset) {
         mgr->va_offset = va;
@@ -363,12 +294,13 @@ static void radeon_bo_destroy(struct pb_buffer *_buf)
 
     memset(&args, 0, sizeof(args));
 
-    if (bo->name) {
-        pipe_mutex_lock(bo->mgr->bo_handles_mutex);
-        util_hash_table_remove(bo->mgr->bo_handles,
-                               (void*)(uintptr_t)bo->name);
-        pipe_mutex_unlock(bo->mgr->bo_handles_mutex);
+    pipe_mutex_lock(bo->mgr->bo_handles_mutex);
+    util_hash_table_remove(bo->mgr->bo_handles, (void*)(uintptr_t)bo->handle);
+    if (bo->flink_name) {
+        util_hash_table_remove(bo->mgr->bo_names,
+                               (void*)(uintptr_t)bo->flink_name);
     }
+    pipe_mutex_unlock(bo->mgr->bo_handles_mutex);
 
     if (bo->ptr)
         os_munmap(bo->ptr, bo->base.size);
@@ -378,21 +310,66 @@ static void radeon_bo_destroy(struct pb_buffer *_buf)
     drmIoctl(bo->rws->fd, DRM_IOCTL_GEM_CLOSE, &args);
 
     if (mgr->va) {
-        radeon_bomgr_free_va(mgr, bo->va, bo->va_size);
+        radeon_bomgr_free_va(mgr, bo->va, bo->base.size);
     }
 
     pipe_mutex_destroy(bo->map_mutex);
+
+    if (bo->initial_domain & RADEON_DOMAIN_VRAM)
+        bo->rws->allocated_vram -= align(bo->base.size, 4096);
+    else if (bo->initial_domain & RADEON_DOMAIN_GTT)
+        bo->rws->allocated_gtt -= align(bo->base.size, 4096);
     FREE(bo);
 }
 
+void *radeon_bo_do_map(struct radeon_bo *bo)
+{
+    struct drm_radeon_gem_mmap args = {0};
+    void *ptr;
+
+    /* Return the pointer if it's already mapped. */
+    if (bo->ptr)
+        return bo->ptr;
+
+    /* Map the buffer. */
+    pipe_mutex_lock(bo->map_mutex);
+    /* Return the pointer if it's already mapped (in case of a race). */
+    if (bo->ptr) {
+        pipe_mutex_unlock(bo->map_mutex);
+        return bo->ptr;
+    }
+    args.handle = bo->handle;
+    args.offset = 0;
+    args.size = (uint64_t)bo->base.size;
+    if (drmCommandWriteRead(bo->rws->fd,
+                            DRM_RADEON_GEM_MMAP,
+                            &args,
+                            sizeof(args))) {
+        pipe_mutex_unlock(bo->map_mutex);
+        fprintf(stderr, "radeon: gem_mmap failed: %p 0x%08X\n",
+                bo, bo->handle);
+        return NULL;
+    }
+
+    ptr = os_mmap(0, args.size, PROT_READ|PROT_WRITE, MAP_SHARED,
+               bo->rws->fd, args.addr_ptr);
+    if (ptr == MAP_FAILED) {
+        pipe_mutex_unlock(bo->map_mutex);
+        fprintf(stderr, "radeon: mmap failed, errno: %i\n", errno);
+        return NULL;
+    }
+    bo->ptr = ptr;
+    pipe_mutex_unlock(bo->map_mutex);
+
+    return bo->ptr;
+}
+
 static void *radeon_bo_map(struct radeon_winsys_cs_handle *buf,
                            struct radeon_winsys_cs *rcs,
                            enum pipe_transfer_usage usage)
 {
     struct radeon_bo *bo = (struct radeon_bo*)buf;
     struct radeon_drm_cs *cs = (struct radeon_drm_cs*)rcs;
-    struct drm_radeon_gem_mmap args = {0};
-    void *ptr;
 
     /* If it's not unsynchronized bo_map, flush CS if needed and then wait. */
     if (!(usage & PIPE_TRANSFER_UNSYNCHRONIZED)) {
@@ -406,8 +383,8 @@ static void *radeon_bo_map(struct radeon_winsys_cs_handle *buf,
                  * (neither one is changing it).
                  *
                  * Only check whether the buffer is being used for write. */
-                if (radeon_bo_is_referenced_by_cs_for_write(cs, bo)) {
-                    cs->flush_cs(cs->flush_data, RADEON_FLUSH_ASYNC);
+                if (cs && radeon_bo_is_referenced_by_cs_for_write(cs, bo)) {
+                    cs->flush_cs(cs->flush_data, RADEON_FLUSH_ASYNC, NULL);
                     return NULL;
                 }
 
@@ -416,8 +393,8 @@ static void *radeon_bo_map(struct radeon_winsys_cs_handle *buf,
                     return NULL;
                 }
             } else {
-                if (radeon_bo_is_referenced_by_cs(cs, bo)) {
-                    cs->flush_cs(cs->flush_data, RADEON_FLUSH_ASYNC);
+                if (cs && radeon_bo_is_referenced_by_cs(cs, bo)) {
+                    cs->flush_cs(cs->flush_data, RADEON_FLUSH_ASYNC, NULL);
                     return NULL;
                 }
 
@@ -427,6 +404,8 @@ static void *radeon_bo_map(struct radeon_winsys_cs_handle *buf,
                 }
             }
         } else {
+            uint64_t time = os_time_get_nano();
+
             if (!(usage & PIPE_TRANSFER_WRITE)) {
                 /* Mapping for read.
                  *
@@ -435,61 +414,31 @@ static void *radeon_bo_map(struct radeon_winsys_cs_handle *buf,
                  * (neither one is changing it).
                  *
                  * Only check whether the buffer is being used for write. */
-                if (radeon_bo_is_referenced_by_cs_for_write(cs, bo)) {
-                    cs->flush_cs(cs->flush_data, 0);
+                if (cs && radeon_bo_is_referenced_by_cs_for_write(cs, bo)) {
+                    cs->flush_cs(cs->flush_data, 0, NULL);
                 }
                 radeon_bo_wait((struct pb_buffer*)bo,
                                RADEON_USAGE_WRITE);
             } else {
                 /* Mapping for write. */
-                if (radeon_bo_is_referenced_by_cs(cs, bo)) {
-                    cs->flush_cs(cs->flush_data, 0);
-                } else {
-                    /* Try to avoid busy-waiting in radeon_bo_wait. */
-                    if (p_atomic_read(&bo->num_active_ioctls))
-                        radeon_drm_cs_sync_flush(cs);
+                if (cs) {
+                    if (radeon_bo_is_referenced_by_cs(cs, bo)) {
+                        cs->flush_cs(cs->flush_data, 0, NULL);
+                    } else {
+                        /* Try to avoid busy-waiting in radeon_bo_wait. */
+                        if (p_atomic_read(&bo->num_active_ioctls))
+                            radeon_drm_cs_sync_flush(rcs);
+                    }
                 }
 
                 radeon_bo_wait((struct pb_buffer*)bo, RADEON_USAGE_READWRITE);
             }
-        }
-    }
-
-    /* Return the pointer if it's already mapped. */
-    if (bo->ptr)
-        return bo->ptr;
-
-    /* Map the buffer. */
-    pipe_mutex_lock(bo->map_mutex);
-    /* Return the pointer if it's already mapped (in case of a race). */
-    if (bo->ptr) {
-        pipe_mutex_unlock(bo->map_mutex);
-        return bo->ptr;
-    }
-    args.handle = bo->handle;
-    args.offset = 0;
-    args.size = (uint64_t)bo->base.size;
-    if (drmCommandWriteRead(bo->rws->fd,
-                            DRM_RADEON_GEM_MMAP,
-                            &args,
-                            sizeof(args))) {
-        pipe_mutex_unlock(bo->map_mutex);
-        fprintf(stderr, "radeon: gem_mmap failed: %p 0x%08X\n",
-                bo, bo->handle);
-        return NULL;
-    }
 
-    ptr = os_mmap(0, args.size, PROT_READ|PROT_WRITE, MAP_SHARED,
-               bo->rws->fd, args.addr_ptr);
-    if (ptr == MAP_FAILED) {
-        pipe_mutex_unlock(bo->map_mutex);
-        fprintf(stderr, "radeon: mmap failed, errno: %i\n", errno);
-        return NULL;
+            bo->mgr->rws->buffer_wait_time += os_time_get_nano() - time;
+        }
     }
-    bo->ptr = ptr;
-    pipe_mutex_unlock(bo->map_mutex);
 
-    return bo->ptr;
+    return radeon_bo_do_map(bo);
 }
 
 static void radeon_bo_unmap(struct radeon_winsys_cs_handle *_buf)
@@ -527,6 +476,18 @@ const struct pb_vtbl radeon_bo_vtbl = {
     radeon_bo_get_base_buffer,
 };
 
+#ifndef RADEON_GEM_GTT_WC
+#define RADEON_GEM_GTT_WC              (1 << 2)
+#endif
+#ifndef RADEON_GEM_CPU_ACCESS
+/* BO is expected to be accessed by the CPU */
+#define RADEON_GEM_CPU_ACCESS          (1 << 3)
+#endif
+#ifndef RADEON_GEM_NO_CPU_ACCESS
+/* CPU access is not expected to work for this BO */
+#define RADEON_GEM_NO_CPU_ACCESS       (1 << 4)
+#endif
+
 static struct pb_buffer *radeon_bomgr_create_bo(struct pb_manager *_mgr,
                                                 pb_size size,
                                                 const struct pb_desc *desc)
@@ -547,6 +508,14 @@ static struct pb_buffer *radeon_bomgr_create_bo(struct pb_manager *_mgr,
     args.size = size;
     args.alignment = desc->alignment;
     args.initial_domain = rdesc->initial_domains;
+    args.flags = 0;
+
+    if (rdesc->flags & RADEON_FLAG_GTT_WC)
+        args.flags |= RADEON_GEM_GTT_WC;
+    if (rdesc->flags & RADEON_FLAG_CPU_ACCESS)
+        args.flags |= RADEON_GEM_CPU_ACCESS;
+    if (rdesc->flags & RADEON_FLAG_NO_CPU_ACCESS)
+        args.flags |= RADEON_GEM_NO_CPU_ACCESS;
 
     if (drmCommandWriteRead(rws->fd, DRM_RADEON_GEM_CREATE,
                             &args, sizeof(args))) {
@@ -554,6 +523,7 @@ static struct pb_buffer *radeon_bomgr_create_bo(struct pb_manager *_mgr,
         fprintf(stderr, "radeon:    size      : %d bytes\n", size);
         fprintf(stderr, "radeon:    alignment : %d bytes\n", desc->alignment);
         fprintf(stderr, "radeon:    domains   : %d\n", args.initial_domain);
+        fprintf(stderr, "radeon:    flags     : %d\n", args.flags);
         return NULL;
     }
 
@@ -570,13 +540,13 @@ static struct pb_buffer *radeon_bomgr_create_bo(struct pb_manager *_mgr,
     bo->rws = mgr->rws;
     bo->handle = args.handle;
     bo->va = 0;
+    bo->initial_domain = rdesc->initial_domains;
     pipe_mutex_init(bo->map_mutex);
 
     if (mgr->va) {
         struct drm_radeon_gem_va va;
 
-        bo->va_size = align(size,  4096);
-        bo->va = radeon_bomgr_find_va(mgr, bo->va_size, desc->alignment);
+        bo->va = radeon_bomgr_find_va(mgr, size, desc->alignment);
 
         va.handle = bo->handle;
         va.vm_id = 0;
@@ -587,20 +557,34 @@ static struct pb_buffer *radeon_bomgr_create_bo(struct pb_manager *_mgr,
         va.offset = bo->va;
         r = drmCommandWriteRead(rws->fd, DRM_RADEON_GEM_VA, &va, sizeof(va));
         if (r && va.operation == RADEON_VA_RESULT_ERROR) {
-            fprintf(stderr, "radeon: Failed to allocate a buffer:\n");
+            fprintf(stderr, "radeon: Failed to allocate virtual address for buffer:\n");
             fprintf(stderr, "radeon:    size      : %d bytes\n", size);
             fprintf(stderr, "radeon:    alignment : %d bytes\n", desc->alignment);
             fprintf(stderr, "radeon:    domains   : %d\n", args.initial_domain);
+            fprintf(stderr, "radeon:    va        : 0x%016llx\n", (unsigned long long)bo->va);
             radeon_bo_destroy(&bo->base);
             return NULL;
         }
+        pipe_mutex_lock(mgr->bo_handles_mutex);
         if (va.operation == RADEON_VA_RESULT_VA_EXIST) {
-            radeon_bomgr_free_va(mgr, bo->va, bo->va_size);
-            bo->va = va.offset;
-            radeon_bomgr_force_va(mgr, bo->va, bo->va_size);
+            struct pb_buffer *b = &bo->base;
+            struct radeon_bo *old_bo =
+                util_hash_table_get(mgr->bo_vas, (void*)(uintptr_t)va.offset);
+
+            pipe_mutex_unlock(mgr->bo_handles_mutex);
+            pb_reference(&b, &old_bo->base);
+            return b;
         }
+
+        util_hash_table_set(mgr->bo_vas, (void*)(uintptr_t)bo->va, bo);
+        pipe_mutex_unlock(mgr->bo_handles_mutex);
     }
 
+    if (rdesc->initial_domains & RADEON_DOMAIN_VRAM)
+        rws->allocated_vram += align(size, 4096);
+    else if (rdesc->initial_domains & RADEON_DOMAIN_GTT)
+        rws->allocated_gtt += align(size, 4096);
+
     return &bo->base;
 }
 
@@ -629,7 +613,9 @@ static boolean radeon_bomgr_is_buffer_busy(struct pb_manager *_mgr,
 static void radeon_bomgr_destroy(struct pb_manager *_mgr)
 {
     struct radeon_bomgr *mgr = radeon_bomgr(_mgr);
+    util_hash_table_destroy(mgr->bo_names);
     util_hash_table_destroy(mgr->bo_handles);
+    util_hash_table_destroy(mgr->bo_vas);
     pipe_mutex_destroy(mgr->bo_handles_mutex);
     pipe_mutex_destroy(mgr->bo_va_mutex);
     FREE(mgr);
@@ -661,12 +647,14 @@ struct pb_manager *radeon_bomgr_create(struct radeon_drm_winsys *rws)
     mgr->base.is_buffer_busy = radeon_bomgr_is_buffer_busy;
 
     mgr->rws = rws;
+    mgr->bo_names = util_hash_table_create(handle_hash, handle_compare);
     mgr->bo_handles = util_hash_table_create(handle_hash, handle_compare);
+    mgr->bo_vas = util_hash_table_create(handle_hash, handle_compare);
     pipe_mutex_init(mgr->bo_handles_mutex);
     pipe_mutex_init(mgr->bo_va_mutex);
 
     mgr->va = rws->info.r600_virtual_address;
-    mgr->va_offset = rws->info.r600_va_start;
+    mgr->va_offset = rws->va_start;
     list_inithead(&mgr->va_holes);
 
     return &mgr->base;
@@ -707,7 +695,8 @@ static void radeon_bo_get_tiling(struct pb_buffer *_buf,
                                  unsigned *bankw, unsigned *bankh,
                                  unsigned *tile_split,
                                  unsigned *stencil_tile_split,
-                                 unsigned *mtilea)
+                                 unsigned *mtilea,
+                                 bool *scanout)
 {
     struct radeon_bo *bo = get_radeon_bo(_buf);
     struct drm_radeon_gem_set_tiling args;
@@ -723,10 +712,12 @@ static void radeon_bo_get_tiling(struct pb_buffer *_buf,
 
     *microtiled = RADEON_LAYOUT_LINEAR;
     *macrotiled = RADEON_LAYOUT_LINEAR;
-    if (args.tiling_flags & RADEON_BO_FLAGS_MICRO_TILE)
+    if (args.tiling_flags & RADEON_TILING_MICRO)
         *microtiled = RADEON_LAYOUT_TILED;
+    else if (args.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+        *microtiled = RADEON_LAYOUT_SQUARETILED;
 
-    if (args.tiling_flags & RADEON_BO_FLAGS_MACRO_TILE)
+    if (args.tiling_flags & RADEON_TILING_MACRO)
         *macrotiled = RADEON_LAYOUT_TILED;
     if (bankw && tile_split && stencil_tile_split && mtilea && tile_split) {
         *bankw = (args.tiling_flags >> RADEON_TILING_EG_BANKW_SHIFT) & RADEON_TILING_EG_BANKW_MASK;
@@ -736,6 +727,8 @@ static void radeon_bo_get_tiling(struct pb_buffer *_buf,
         *mtilea = (args.tiling_flags >> RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK;
         *tile_split = eg_tile_split(*tile_split);
     }
+    if (scanout)
+        *scanout = bo->rws->gen >= DRV_SI && !(args.tiling_flags & RADEON_TILING_R600_NO_SCANOUT);
 }
 
 static void radeon_bo_set_tiling(struct pb_buffer *_buf,
@@ -746,7 +739,8 @@ static void radeon_bo_set_tiling(struct pb_buffer *_buf,
                                  unsigned tile_split,
                                  unsigned stencil_tile_split,
                                  unsigned mtilea,
-                                 uint32_t pitch)
+                                 uint32_t pitch,
+                                 bool scanout)
 {
     struct radeon_bo *bo = get_radeon_bo(_buf);
     struct radeon_drm_cs *cs = radeon_drm_cs(rcs);
@@ -757,7 +751,7 @@ static void radeon_bo_set_tiling(struct pb_buffer *_buf,
     /* Tiling determines how DRM treats the buffer data.
      * We must flush CS when changing it if the buffer is referenced. */
     if (cs && radeon_bo_is_referenced_by_cs(cs, bo)) {
-        cs->flush_cs(cs->flush_data, 0);
+        cs->flush_cs(cs->flush_data, 0, NULL);
     }
 
     while (p_atomic_read(&bo->num_active_ioctls)) {
@@ -765,12 +759,12 @@ static void radeon_bo_set_tiling(struct pb_buffer *_buf,
     }
 
     if (microtiled == RADEON_LAYOUT_TILED)
-        args.tiling_flags |= RADEON_BO_FLAGS_MICRO_TILE;
+        args.tiling_flags |= RADEON_TILING_MICRO;
     else if (microtiled == RADEON_LAYOUT_SQUARETILED)
-        args.tiling_flags |= RADEON_BO_FLAGS_MICRO_TILE_SQUARE;
+        args.tiling_flags |= RADEON_TILING_MICRO_SQUARE;
 
     if (macrotiled == RADEON_LAYOUT_TILED)
-        args.tiling_flags |= RADEON_BO_FLAGS_MACRO_TILE;
+        args.tiling_flags |= RADEON_TILING_MACRO;
 
     args.tiling_flags |= (bankw & RADEON_TILING_EG_BANKW_MASK) <<
         RADEON_TILING_EG_BANKW_SHIFT;
@@ -787,6 +781,9 @@ static void radeon_bo_set_tiling(struct pb_buffer *_buf,
     args.tiling_flags |= (mtilea & RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK) <<
         RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT;
 
+    if (bo->rws->gen >= DRV_SI && !scanout)
+        args.tiling_flags |= RADEON_TILING_R600_NO_SCANOUT;
+
     args.handle = bo->handle;
     args.pitch = pitch;
 
@@ -796,8 +793,7 @@ static void radeon_bo_set_tiling(struct pb_buffer *_buf,
                         sizeof(args));
 }
 
-static struct radeon_winsys_cs_handle *radeon_drm_get_cs_handle(
-        struct pb_buffer *_buf)
+static struct radeon_winsys_cs_handle *radeon_drm_get_cs_handle(struct pb_buffer *_buf)
 {
     /* return radeon_bo. */
     return (struct radeon_winsys_cs_handle*)get_radeon_bo(_buf);
@@ -807,10 +803,12 @@ static struct pb_buffer *
 radeon_winsys_bo_create(struct radeon_winsys *rws,
                         unsigned size,
                         unsigned alignment,
-                        unsigned bind,
-                        enum radeon_bo_domain domain)
+                        boolean use_reusable_pool,
+                        enum radeon_bo_domain domain,
+                        enum radeon_bo_flag flags)
 {
     struct radeon_drm_winsys *ws = radeon_drm_winsys(rws);
+    struct radeon_bomgr *mgr = radeon_bomgr(ws->kman);
     struct radeon_bo_desc desc;
     struct pb_manager *provider;
     struct pb_buffer *buffer;
@@ -818,13 +816,21 @@ radeon_winsys_bo_create(struct radeon_winsys *rws,
     memset(&desc, 0, sizeof(desc));
     desc.base.alignment = alignment;
 
-    /* Additional criteria for the cache manager. */
-    desc.base.usage = domain;
+    /* Only set one usage bit each for domains and flags, or the cache manager
+     * might consider different sets of domains / flags compatible
+     */
+    if (domain == RADEON_DOMAIN_VRAM_GTT)
+        desc.base.usage = 1 << 2;
+    else
+        desc.base.usage = domain >> 1;
+    assert(flags < sizeof(desc.base.usage) * 8 - 3);
+    desc.base.usage |= 1 << (flags + 3);
+
     desc.initial_domains = domain;
+    desc.flags = flags;
 
     /* Assign a buffer manager. */
-    if (bind & (PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_INDEX_BUFFER |
-                PIPE_BIND_CONSTANT_BUFFER | PIPE_BIND_CUSTOM))
+    if (use_reusable_pool)
         provider = ws->cman;
     else
         provider = ws->kman;
@@ -833,6 +839,10 @@ radeon_winsys_bo_create(struct radeon_winsys *rws,
     if (!buffer)
         return NULL;
 
+    pipe_mutex_lock(mgr->bo_handles_mutex);
+    util_hash_table_set(mgr->bo_handles, (void*)(uintptr_t)get_radeon_bo(buffer)->handle, buffer);
+    pipe_mutex_unlock(mgr->bo_handles_mutex);
+
     return (struct pb_buffer*)buffer;
 }
 
@@ -843,10 +853,9 @@ static struct pb_buffer *radeon_winsys_bo_from_handle(struct radeon_winsys *rws,
     struct radeon_drm_winsys *ws = radeon_drm_winsys(rws);
     struct radeon_bo *bo;
     struct radeon_bomgr *mgr = radeon_bomgr(ws->kman);
-    struct drm_gem_open open_arg = {};
     int r;
-
-    memset(&open_arg, 0, sizeof(open_arg));
+    unsigned handle;
+    uint64_t size = 0;
 
     /* We must maintain a list of pairs <handle, bo>, so that we always return
      * the same BO for one particular handle. If we didn't do that and created
@@ -856,8 +865,20 @@ static struct pb_buffer *radeon_winsys_bo_from_handle(struct radeon_winsys *rws,
      * The list of pairs is guarded by a mutex, of course. */
     pipe_mutex_lock(mgr->bo_handles_mutex);
 
-    /* First check if there already is an existing bo for the handle. */
-    bo = util_hash_table_get(mgr->bo_handles, (void*)(uintptr_t)whandle->handle);
+    if (whandle->type == DRM_API_HANDLE_TYPE_SHARED) {
+        /* First check if there already is an existing bo for the handle. */
+        bo = util_hash_table_get(mgr->bo_names, (void*)(uintptr_t)whandle->handle);
+    } else if (whandle->type == DRM_API_HANDLE_TYPE_FD) {
+        /* We must first get the GEM handle, as fds are unreliable keys */
+        r = drmPrimeFDToHandle(ws->fd, whandle->handle, &handle);
+        if (r)
+            goto fail;
+        bo = util_hash_table_get(mgr->bo_handles, (void*)(uintptr_t)handle);
+    } else {
+        /* Unknown handle type */
+        goto fail;
+    }
+
     if (bo) {
         /* Increase the refcount. */
         struct pb_buffer *b = NULL;
@@ -871,27 +892,48 @@ static struct pb_buffer *radeon_winsys_bo_from_handle(struct radeon_winsys *rws,
         goto fail;
     }
 
-    /* Open the BO. */
-    open_arg.name = whandle->handle;
-    if (drmIoctl(ws->fd, DRM_IOCTL_GEM_OPEN, &open_arg)) {
-        FREE(bo);
-        goto fail;
+    if (whandle->type == DRM_API_HANDLE_TYPE_SHARED) {
+        struct drm_gem_open open_arg = {};
+        memset(&open_arg, 0, sizeof(open_arg));
+        /* Open the BO. */
+        open_arg.name = whandle->handle;
+        if (drmIoctl(ws->fd, DRM_IOCTL_GEM_OPEN, &open_arg)) {
+            FREE(bo);
+            goto fail;
+        }
+        handle = open_arg.handle;
+        size = open_arg.size;
+        bo->flink_name = whandle->handle;
+    } else if (whandle->type == DRM_API_HANDLE_TYPE_FD) {
+        size = lseek(whandle->handle, 0, SEEK_END);
+        /* 
+         * Could check errno to determine whether the kernel is new enough, but
+         * it doesn't really matter why this failed, just that it failed.
+         */
+        if (size == (off_t)-1) {
+            FREE(bo);
+            goto fail;
+        }
+        lseek(whandle->handle, 0, SEEK_SET);
     }
-    bo->handle = open_arg.handle;
-    bo->name = whandle->handle;
+
+    bo->handle = handle;
 
     /* Initialize it. */
     pipe_reference_init(&bo->base.reference, 1);
     bo->base.alignment = 0;
     bo->base.usage = PB_USAGE_GPU_WRITE | PB_USAGE_GPU_READ;
-    bo->base.size = open_arg.size;
+    bo->base.size = (unsigned) size;
     bo->base.vtbl = &radeon_bo_vtbl;
     bo->mgr = mgr;
     bo->rws = mgr->rws;
     bo->va = 0;
     pipe_mutex_init(bo->map_mutex);
 
-    util_hash_table_set(mgr->bo_handles, (void*)(uintptr_t)whandle->handle, bo);
+    if (bo->flink_name)
+        util_hash_table_set(mgr->bo_names, (void*)(uintptr_t)bo->flink_name, bo);
+
+    util_hash_table_set(mgr->bo_handles, (void*)(uintptr_t)bo->handle, bo);
 
 done:
     pipe_mutex_unlock(mgr->bo_handles_mutex);
@@ -899,11 +941,10 @@ done:
     if (stride)
         *stride = whandle->stride;
 
-    if (mgr->va) {
+    if (mgr->va && !bo->va) {
         struct drm_radeon_gem_va va;
 
-        bo->va_size = ((bo->base.size + 4095) & ~4095);
-        bo->va = radeon_bomgr_find_va(mgr, bo->va_size, 1 << 20);
+        bo->va = radeon_bomgr_find_va(mgr, bo->base.size, 1 << 20);
 
         va.handle = bo->handle;
         va.operation = RADEON_VA_MAP;
@@ -919,13 +960,28 @@ done:
             radeon_bo_destroy(&bo->base);
             return NULL;
         }
+        pipe_mutex_lock(mgr->bo_handles_mutex);
         if (va.operation == RADEON_VA_RESULT_VA_EXIST) {
-            radeon_bomgr_free_va(mgr, bo->va, bo->va_size);
-            bo->va = va.offset;
-            radeon_bomgr_force_va(mgr, bo->va, bo->va_size);
+            struct pb_buffer *b = &bo->base;
+            struct radeon_bo *old_bo =
+                util_hash_table_get(mgr->bo_vas, (void*)(uintptr_t)va.offset);
+
+            pipe_mutex_unlock(mgr->bo_handles_mutex);
+            pb_reference(&b, &old_bo->base);
+            return b;
         }
+
+        util_hash_table_set(mgr->bo_vas, (void*)(uintptr_t)bo->va, bo);
+        pipe_mutex_unlock(mgr->bo_handles_mutex);
     }
 
+    bo->initial_domain = radeon_bo_get_initial_domain((void*)bo);
+
+    if (bo->initial_domain & RADEON_DOMAIN_VRAM)
+        ws->allocated_vram += align(bo->base.size, 4096);
+    else if (bo->initial_domain & RADEON_DOMAIN_GTT)
+        ws->allocated_gtt += align(bo->base.size, 4096);
+
     return (struct pb_buffer*)bo;
 
 fail:
@@ -943,19 +999,25 @@ static boolean radeon_winsys_bo_get_handle(struct pb_buffer *buffer,
     memset(&flink, 0, sizeof(flink));
 
     if (whandle->type == DRM_API_HANDLE_TYPE_SHARED) {
-        if (!bo->flinked) {
+        if (!bo->flink_name) {
             flink.handle = bo->handle;
 
             if (ioctl(bo->rws->fd, DRM_IOCTL_GEM_FLINK, &flink)) {
                 return FALSE;
             }
 
-            bo->flinked = TRUE;
-            bo->flink = flink.name;
+            bo->flink_name = flink.name;
+
+            pipe_mutex_lock(bo->mgr->bo_handles_mutex);
+            util_hash_table_set(bo->mgr->bo_names, (void*)(uintptr_t)bo->flink_name, bo);
+            pipe_mutex_unlock(bo->mgr->bo_handles_mutex);
         }
-        whandle->handle = bo->flink;
+        whandle->handle = bo->flink_name;
     } else if (whandle->type == DRM_API_HANDLE_TYPE_KMS) {
         whandle->handle = bo->handle;
+    } else if (whandle->type == DRM_API_HANDLE_TYPE_FD) {
+        if (drmPrimeHandleToFD(bo->rws->fd, bo->handle, DRM_CLOEXEC, (int*)&whandle->handle))
+            return FALSE;
     }
 
     whandle->stride = stride;
@@ -980,4 +1042,5 @@ void radeon_bomgr_init_functions(struct radeon_drm_winsys *ws)
     ws->base.buffer_from_handle = radeon_winsys_bo_from_handle;
     ws->base.buffer_get_handle = radeon_winsys_bo_get_handle;
     ws->base.buffer_get_virtual_address = radeon_winsys_bo_va;
+    ws->base.buffer_get_initial_domain = radeon_bo_get_initial_domain;
 }