anv: Use a util_sparse_array for the GEM handle -> BO map
authorJason Ekstrand <jason@jlekstrand.net>
Fri, 25 Oct 2019 17:45:41 +0000 (12:45 -0500)
committerJason Ekstrand <jason@jlekstrand.net>
Thu, 31 Oct 2019 13:46:08 +0000 (13:46 +0000)
This lets us do less allocation because the anv_bo's are now embedded in
the sparse array and it also allows lock-free translation from GEM
handle to BO which will be useful in future commits.

Reviewed-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
src/intel/vulkan/anv_allocator.c
src/intel/vulkan/anv_private.h

index 5685687ba1d4410f567744ce7ab35d93b4217fd2..75f47744497bb91cb80758223e31fb63793582cd 100644 (file)
@@ -29,7 +29,6 @@
 
 #include "anv_private.h"
 
-#include "util/hash_table.h"
 #include "util/simple_mtx.h"
 #include "util/anon_file.h"
 
@@ -1611,12 +1610,10 @@ anv_scratch_pool_alloc(struct anv_device *device, struct anv_scratch_pool *pool,
 VkResult
 anv_bo_cache_init(struct anv_bo_cache *cache)
 {
-   cache->bo_map = _mesa_pointer_hash_table_create(NULL);
-   if (!cache->bo_map)
-      return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
+   util_sparse_array_init(&cache->bo_map, sizeof(struct anv_bo), 1024);
 
    if (pthread_mutex_init(&cache->mutex, NULL)) {
-      _mesa_hash_table_destroy(cache->bo_map, NULL);
+      util_sparse_array_finish(&cache->bo_map);
       return vk_errorf(NULL, NULL, VK_ERROR_OUT_OF_HOST_MEMORY,
                        "pthread_mutex_init failed: %m");
    }
@@ -1627,35 +1624,14 @@ anv_bo_cache_init(struct anv_bo_cache *cache)
 void
 anv_bo_cache_finish(struct anv_bo_cache *cache)
 {
-   _mesa_hash_table_destroy(cache->bo_map, NULL);
+   util_sparse_array_finish(&cache->bo_map);
    pthread_mutex_destroy(&cache->mutex);
 }
 
 static struct anv_bo *
-anv_bo_cache_lookup_locked(struct anv_bo_cache *cache, uint32_t gem_handle)
-{
-   struct hash_entry *entry =
-      _mesa_hash_table_search(cache->bo_map,
-                              (const void *)(uintptr_t)gem_handle);
-   if (!entry)
-      return NULL;
-
-   struct anv_bo *bo = (struct anv_bo *)entry->data;
-   assert(bo->gem_handle == gem_handle);
-
-   return bo;
-}
-
-UNUSED static struct anv_bo *
 anv_bo_cache_lookup(struct anv_bo_cache *cache, uint32_t gem_handle)
 {
-   pthread_mutex_lock(&cache->mutex);
-
-   struct anv_bo *bo = anv_bo_cache_lookup_locked(cache, gem_handle);
-
-   pthread_mutex_unlock(&cache->mutex);
-
-   return bo;
+   return util_sparse_array_get(&cache->bo_map, gem_handle);
 }
 
 #define ANV_BO_CACHE_SUPPORTED_FLAGS \
@@ -1673,39 +1649,30 @@ anv_bo_cache_alloc(struct anv_device *device,
 {
    assert(bo_flags == (bo_flags & ANV_BO_CACHE_SUPPORTED_FLAGS));
 
-   struct anv_bo *bo =
-      vk_alloc(&device->alloc, sizeof(struct anv_bo), 8,
-               VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
-   if (!bo)
-      return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
-
    /* The kernel is going to give us whole pages anyway */
    size = align_u64(size, 4096);
 
-   VkResult result = anv_bo_init_new(bo, device, size);
-   if (result != VK_SUCCESS) {
-      vk_free(&device->alloc, bo);
+   struct anv_bo new_bo;
+   VkResult result = anv_bo_init_new(&new_bo, device, size);
+   if (result != VK_SUCCESS)
       return result;
-   }
 
-   bo->flags = bo_flags;
+   new_bo.flags = bo_flags;
 
-   if (!anv_vma_alloc(device, bo)) {
-      anv_gem_close(device, bo->gem_handle);
-      vk_free(&device->alloc, bo);
+   if (!anv_vma_alloc(device, &new_bo)) {
+      anv_gem_close(device, new_bo.gem_handle);
       return vk_errorf(device->instance, NULL,
                        VK_ERROR_OUT_OF_DEVICE_MEMORY,
                        "failed to allocate virtual address for BO");
    }
 
-   assert(bo->gem_handle);
-
-   pthread_mutex_lock(&cache->mutex);
+   assert(new_bo.gem_handle);
 
-   _mesa_hash_table_insert(cache->bo_map,
-                           (void *)(uintptr_t)bo->gem_handle, bo);
-
-   pthread_mutex_unlock(&cache->mutex);
+   /* If we just got this gem_handle from anv_bo_init_new then we know no one
+    * else is touching this BO at the moment so we don't need to lock here.
+    */
+   struct anv_bo *bo = anv_bo_cache_lookup(cache, new_bo.gem_handle);
+   *bo = new_bo;
 
    *bo_out = bo;
 
@@ -1727,12 +1694,13 @@ anv_bo_cache_import_host_ptr(struct anv_device *device,
 
    pthread_mutex_lock(&cache->mutex);
 
-   struct anv_bo *bo = anv_bo_cache_lookup_locked(cache, gem_handle);
-   if (bo) {
+   struct anv_bo *bo = anv_bo_cache_lookup(cache, gem_handle);
+   if (bo->refcount > 0) {
       /* VK_EXT_external_memory_host doesn't require handling importing the
        * same pointer twice at the same time, but we don't get in the way.  If
        * kernel gives us the same gem_handle, only succeed if the flags match.
        */
+      assert(bo->gem_handle == gem_handle);
       if (bo_flags != bo->flags) {
          pthread_mutex_unlock(&cache->mutex);
          return vk_errorf(device->instance, NULL,
@@ -1741,27 +1709,19 @@ anv_bo_cache_import_host_ptr(struct anv_device *device,
       }
       __sync_fetch_and_add(&bo->refcount, 1);
    } else {
-      bo = vk_alloc(&device->alloc, sizeof(struct anv_bo), 8,
-                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
-      if (!bo) {
-         anv_gem_close(device, gem_handle);
-         pthread_mutex_unlock(&cache->mutex);
-         return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
-      }
-
-      anv_bo_init(bo, gem_handle, size);
-      bo->flags = bo_flags;
+      struct anv_bo new_bo;
+      anv_bo_init(&new_bo, gem_handle, size);
+      new_bo.flags = bo_flags;
 
-      if (!anv_vma_alloc(device, bo)) {
-         anv_gem_close(device, bo->gem_handle);
+      if (!anv_vma_alloc(device, &new_bo)) {
+         anv_gem_close(device, new_bo.gem_handle);
          pthread_mutex_unlock(&cache->mutex);
-         vk_free(&device->alloc, bo);
          return vk_errorf(device->instance, NULL,
                           VK_ERROR_OUT_OF_DEVICE_MEMORY,
                           "failed to allocate virtual address for BO");
       }
 
-      _mesa_hash_table_insert(cache->bo_map, (void *)(uintptr_t)gem_handle, bo);
+      *bo = new_bo;
    }
 
    pthread_mutex_unlock(&cache->mutex);
@@ -1787,8 +1747,8 @@ anv_bo_cache_import(struct anv_device *device,
       return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE);
    }
 
-   struct anv_bo *bo = anv_bo_cache_lookup_locked(cache, gem_handle);
-   if (bo) {
+   struct anv_bo *bo = anv_bo_cache_lookup(cache, gem_handle);
+   if (bo->refcount > 0) {
       /* We have to be careful how we combine flags so that it makes sense.
        * Really, though, if we get to this case and it actually matters, the
        * client has imported a BO twice in different ways and they get what
@@ -1840,27 +1800,19 @@ anv_bo_cache_import(struct anv_device *device,
          return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE);
       }
 
-      bo = vk_alloc(&device->alloc, sizeof(struct anv_bo), 8,
-                    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
-      if (!bo) {
-         anv_gem_close(device, gem_handle);
-         pthread_mutex_unlock(&cache->mutex);
-         return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
-      }
-
-      anv_bo_init(bo, gem_handle, size);
-      bo->flags = bo_flags;
+      struct anv_bo new_bo;
+      anv_bo_init(&new_bo, gem_handle, size);
+      new_bo.flags = bo_flags;
 
-      if (!anv_vma_alloc(device, bo)) {
-         anv_gem_close(device, bo->gem_handle);
+      if (!anv_vma_alloc(device, &new_bo)) {
+         anv_gem_close(device, new_bo.gem_handle);
          pthread_mutex_unlock(&cache->mutex);
-         vk_free(&device->alloc, bo);
          return vk_errorf(device->instance, NULL,
                           VK_ERROR_OUT_OF_DEVICE_MEMORY,
                           "failed to allocate virtual address for BO");
       }
 
-      _mesa_hash_table_insert(cache->bo_map, (void *)(uintptr_t)gem_handle, bo);
+      *bo = new_bo;
    }
 
    pthread_mutex_unlock(&cache->mutex);
@@ -1935,12 +1887,7 @@ anv_bo_cache_release(struct anv_device *device,
       pthread_mutex_unlock(&cache->mutex);
       return;
    }
-
-   struct hash_entry *entry =
-      _mesa_hash_table_search(cache->bo_map,
-                              (const void *)(uintptr_t)bo->gem_handle);
-   assert(entry);
-   _mesa_hash_table_remove(cache->bo_map, entry);
+   assert(bo->refcount == 0);
 
    if (bo->map)
       anv_gem_munmap(bo->map, bo->size);
@@ -1955,6 +1902,4 @@ anv_bo_cache_release(struct anv_device *device,
     * again between mutex unlock and closing the GEM handle.
     */
    pthread_mutex_unlock(&cache->mutex);
-
-   vk_free(&device->alloc, bo);
 }
index fa1a33aae6f3d77c4958b5357196de39017f9839..db00494e5a57094081a11aa0884047e32eb813e2 100644 (file)
@@ -53,6 +53,7 @@
 #include "util/hash_table.h"
 #include "util/list.h"
 #include "util/set.h"
+#include "util/sparse_array.h"
 #include "util/u_atomic.h"
 #include "util/u_vector.h"
 #include "util/u_math.h"
@@ -891,7 +892,7 @@ struct anv_bo *anv_scratch_pool_alloc(struct anv_device *device,
 
 /** Implements a BO cache that ensures a 1-1 mapping of GEM BOs to anv_bos */
 struct anv_bo_cache {
-   struct hash_table *bo_map;
+   struct util_sparse_array bo_map;
    pthread_mutex_t mutex;
 };