tu: Switch to userspace iova allocations if kernel supports it

With MSM_INFO_SET_IOVA it's now possible for userspace to manually
set iova instead of relying on kernel.

In preparation to support bufferDeviceAddressCaptureReplay.

Signed-off-by: Danylo Piliaiev <dpiliaiev@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15676>
This commit is contained in:
Danylo Piliaiev 2022-08-18 12:15:35 +03:00
parent 3e5aac8fbc
commit e23c4fbd9b
3 changed files with 112 additions and 11 deletions

View file

@ -281,6 +281,12 @@ tu_physical_device_init(struct tu_physical_device *device,
goto fail_free_name; goto fail_free_name;
} }
if (device->has_set_iova) {
mtx_init(&device->vma_mutex, mtx_plain);
util_vma_heap_init(&device->vma, device->va_start,
ROUND_DOWN_TO(device->va_size, 4096));
}
fd_get_driver_uuid(device->driver_uuid); fd_get_driver_uuid(device->driver_uuid);
fd_get_device_uuid(device->device_uuid, &device->dev_id); fd_get_device_uuid(device->device_uuid, &device->dev_id);
@ -297,7 +303,7 @@ tu_physical_device_init(struct tu_physical_device *device,
&supported_extensions, &supported_extensions,
&dispatch_table); &dispatch_table);
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
goto fail_free_name; goto fail_free_vma;
device->vk.supported_sync_types = device->sync_types; device->vk.supported_sync_types = device->sync_types;
@ -306,7 +312,7 @@ tu_physical_device_init(struct tu_physical_device *device,
if (result != VK_SUCCESS) { if (result != VK_SUCCESS) {
vk_startup_errorf(instance, result, "WSI init failure"); vk_startup_errorf(instance, result, "WSI init failure");
vk_physical_device_finish(&device->vk); vk_physical_device_finish(&device->vk);
goto fail_free_name; goto fail_free_vma;
} }
#endif #endif
@ -321,6 +327,9 @@ tu_physical_device_init(struct tu_physical_device *device,
return VK_SUCCESS; return VK_SUCCESS;
fail_free_vma:
if (device->has_set_iova)
util_vma_heap_finish(&device->vma);
fail_free_name: fail_free_name:
vk_free(&instance->vk.alloc, (void *)device->name); vk_free(&instance->vk.alloc, (void *)device->name);
return result; return result;
@ -337,6 +346,9 @@ tu_physical_device_finish(struct tu_physical_device *device)
if (device->master_fd != -1) if (device->master_fd != -1)
close(device->master_fd); close(device->master_fd);
if (device->has_set_iova)
util_vma_heap_finish(&device->vma);
vk_free(&device->instance->vk.alloc, (void *)device->name); vk_free(&device->instance->vk.alloc, (void *)device->name);
vk_physical_device_finish(&device->vk); vk_physical_device_finish(&device->vk);

View file

@ -18,6 +18,8 @@
#include "tu_suballoc.h" #include "tu_suballoc.h"
#include "tu_util.h" #include "tu_util.h"
#include "util/vma.h"
/* queue types */ /* queue types */
#define TU_QUEUE_GENERAL 0 #define TU_QUEUE_GENERAL 0
@ -107,6 +109,10 @@ struct tu_physical_device
uint32_t ccu_offset_gmem; uint32_t ccu_offset_gmem;
uint32_t ccu_offset_bypass; uint32_t ccu_offset_bypass;
bool has_set_iova;
uint64_t va_start;
uint64_t va_size;
struct fd_dev_id dev_id; struct fd_dev_id dev_id;
const struct fd_dev_info *info; const struct fd_dev_info *info;
@ -117,6 +123,8 @@ struct tu_physical_device
uint64_t fault_count; uint64_t fault_count;
struct tu_memory_heap heap; struct tu_memory_heap heap;
mtx_t vma_mutex;
struct util_vma_heap vma;
struct vk_sync_type syncobj_type; struct vk_sync_type syncobj_type;
struct vk_sync_timeline_type timeline_type; struct vk_sync_timeline_type timeline_type;

View file

@ -109,6 +109,26 @@ tu_drm_get_gmem_base(const struct tu_physical_device *dev, uint64_t *base)
return tu_drm_get_param(dev, MSM_PARAM_GMEM_BASE, base); return tu_drm_get_param(dev, MSM_PARAM_GMEM_BASE, base);
} }
static int
tu_drm_get_va_prop(const struct tu_physical_device *dev,
uint64_t *va_start, uint64_t *va_size)
{
uint64_t value;
int ret = tu_drm_get_param(dev, MSM_PARAM_VA_START, &value);
if (ret)
return ret;
*va_start = value;
ret = tu_drm_get_param(dev, MSM_PARAM_VA_SIZE, &value);
if (ret)
return ret;
*va_size = value;
return 0;
}
int int
tu_device_get_gpu_timestamp(struct tu_device *dev, uint64_t *ts) tu_device_get_gpu_timestamp(struct tu_device *dev, uint64_t *ts)
{ {
@ -192,19 +212,67 @@ tu_gem_info(const struct tu_device *dev, uint32_t gem_handle, uint32_t info)
return req.value; return req.value;
} }
static VkResult
tu_allocate_userspace_iova(struct tu_device *dev,
uint32_t gem_handle,
uint64_t size,
uint64_t *iova)
{
mtx_lock(&dev->physical_device->vma_mutex);
dev->physical_device->vma.alloc_high = false;
*iova = util_vma_heap_alloc(&dev->physical_device->vma, size, 0x1000);
mtx_unlock(&dev->physical_device->vma_mutex);
if (!*iova)
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
struct drm_msm_gem_info req = {
.handle = gem_handle,
.info = MSM_INFO_SET_IOVA,
.value = *iova,
};
int ret =
drmCommandWriteRead(dev->fd, DRM_MSM_GEM_INFO, &req, sizeof(req));
if (ret < 0)
return VK_ERROR_OUT_OF_HOST_MEMORY;
return VK_SUCCESS;
}
static VkResult
tu_allocate_kernel_iova(struct tu_device *dev,
uint32_t gem_handle,
uint64_t *iova)
{
*iova = tu_gem_info(dev, gem_handle, MSM_INFO_GET_IOVA);
if (!*iova)
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
return VK_SUCCESS;
}
static VkResult static VkResult
tu_bo_init(struct tu_device *dev, tu_bo_init(struct tu_device *dev,
struct tu_bo *bo, struct tu_bo *bo,
uint32_t gem_handle, uint32_t gem_handle,
uint64_t size, uint64_t size,
bool dump) enum tu_bo_alloc_flags flags)
{ {
uint64_t iova = tu_gem_info(dev, gem_handle, MSM_INFO_GET_IOVA); VkResult result = VK_SUCCESS;
if (!iova) { uint64_t iova = 0;
tu_gem_close(dev, gem_handle);
return VK_ERROR_OUT_OF_DEVICE_MEMORY; if (dev->physical_device->has_set_iova) {
result = tu_allocate_userspace_iova(dev, gem_handle, size, &iova);
} else {
result = tu_allocate_kernel_iova(dev, gem_handle, &iova);
} }
if (result != VK_SUCCESS)
goto fail_bo_list;
mtx_lock(&dev->bo_mutex); mtx_lock(&dev->bo_mutex);
uint32_t idx = dev->bo_count++; uint32_t idx = dev->bo_count++;
@ -214,13 +282,16 @@ tu_bo_init(struct tu_device *dev,
struct drm_msm_gem_submit_bo *new_ptr = struct drm_msm_gem_submit_bo *new_ptr =
vk_realloc(&dev->vk.alloc, dev->bo_list, new_len * sizeof(*dev->bo_list), vk_realloc(&dev->vk.alloc, dev->bo_list, new_len * sizeof(*dev->bo_list),
8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
if (!new_ptr) if (!new_ptr) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail_bo_list; goto fail_bo_list;
}
dev->bo_list = new_ptr; dev->bo_list = new_ptr;
dev->bo_list_size = new_len; dev->bo_list_size = new_len;
} }
bool dump = flags & TU_BO_ALLOC_ALLOW_DUMP;
dev->bo_list[idx] = (struct drm_msm_gem_submit_bo) { dev->bo_list[idx] = (struct drm_msm_gem_submit_bo) {
.flags = MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE | .flags = MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE |
COND(dump, MSM_SUBMIT_BO_DUMP), COND(dump, MSM_SUBMIT_BO_DUMP),
@ -242,7 +313,7 @@ tu_bo_init(struct tu_device *dev,
fail_bo_list: fail_bo_list:
tu_gem_close(dev, gem_handle); tu_gem_close(dev, gem_handle);
return VK_ERROR_OUT_OF_HOST_MEMORY; return result;
} }
VkResult VkResult
@ -269,7 +340,7 @@ tu_bo_init_new(struct tu_device *dev, struct tu_bo **out_bo, uint64_t size,
assert(bo && bo->gem_handle == 0); assert(bo && bo->gem_handle == 0);
VkResult result = VkResult result =
tu_bo_init(dev, bo, req.handle, size, flags & TU_BO_ALLOC_ALLOW_DUMP); tu_bo_init(dev, bo, req.handle, size, flags);
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
memset(bo, 0, sizeof(*bo)); memset(bo, 0, sizeof(*bo));
@ -317,7 +388,8 @@ tu_bo_init_dmabuf(struct tu_device *dev,
return VK_SUCCESS; return VK_SUCCESS;
} }
VkResult result = tu_bo_init(dev, bo, gem_handle, size, false); VkResult result =
tu_bo_init(dev, bo, gem_handle, size, TU_BO_ALLOC_NO_FLAGS);
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
memset(bo, 0, sizeof(*bo)); memset(bo, 0, sizeof(*bo));
@ -386,6 +458,12 @@ tu_bo_finish(struct tu_device *dev, struct tu_bo *bo)
mtx_unlock(&dev->bo_mutex); mtx_unlock(&dev->bo_mutex);
if (dev->physical_device->has_set_iova) {
mtx_lock(&dev->physical_device->vma_mutex);
util_vma_heap_free(&dev->physical_device->vma, bo->iova, bo->size);
mtx_unlock(&dev->physical_device->vma_mutex);
}
/* Our BO structs are stored in a sparse array in the physical device, /* Our BO structs are stored in a sparse array in the physical device,
* so we don't want to free the BO pointer, instead we want to reset it * so we don't want to free the BO pointer, instead we want to reset it
* to 0, to signal that array entry as being free. * to 0, to signal that array entry as being free.
@ -709,6 +787,9 @@ tu_drm_device_init(struct tu_physical_device *device,
goto fail; goto fail;
} }
device->has_set_iova = !tu_drm_get_va_prop(device, &device->va_start,
&device->va_size);
struct stat st; struct stat st;
if (stat(primary_path, &st) == 0) { if (stat(primary_path, &st) == 0) {