util_sync_provider provides a wrapper to manipulate syncobjs. This allows replacing direct ioctl usages with other functions, and is going to be used to support vpipe. Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Acked-by: Rob Clark <robdclark@chromium.org> Acked-by: Marek Olšák <marek.olsak@amd.com> Acked-by: Samuel Pitoiset <samuel.pitoiset@gmail.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/34470>
196 lines
5.7 KiB
C
196 lines
5.7 KiB
C
/*
|
|
* Copyright 2024 Advanced Micro Devices, Inc.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "amdgpu_virtio_private.h"
|
|
|
|
#include "util/bitscan.h"
|
|
#include "util/log.h"
|
|
#include "util/os_file.h"
|
|
#include "util/u_debug.h"
|
|
#include "util/u_sync_provider.h"
|
|
|
|
#include <xf86drm.h>
|
|
|
|
/* amdvgpu_device manage the virtual GPU.
|
|
*
|
|
* It owns a vdrm_device instance, the rings and manage seqno.
|
|
* Since it's a drop-in replacement for libdrm_amdgpu's amdgpu_device,
|
|
* it follows its behavior: if the same device is opened multiple times,
|
|
* the same amdvgpu_device will be used.
|
|
*/
|
|
static simple_mtx_t dev_mutex = SIMPLE_MTX_INITIALIZER;
|
|
static amdvgpu_device_handle dev_list;
|
|
|
|
static int fd_compare(int fd1, int fd2)
|
|
{
|
|
char *name1 = drmGetPrimaryDeviceNameFromFd(fd1);
|
|
char *name2 = drmGetPrimaryDeviceNameFromFd(fd2);
|
|
int result;
|
|
|
|
if (name1 == NULL || name2 == NULL) {
|
|
free(name1);
|
|
free(name2);
|
|
return 0;
|
|
}
|
|
|
|
result = strcmp(name1, name2);
|
|
free(name1);
|
|
free(name2);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void amdvgpu_device_reference(struct amdvgpu_device **dst,
|
|
struct amdvgpu_device *src)
|
|
{
|
|
if (update_references(*dst ? &(*dst)->refcount : NULL,
|
|
src ? &src->refcount : NULL)) {
|
|
struct amdvgpu_device *dev, *prev = NULL;
|
|
for (dev = dev_list; dev; dev = dev->next) {
|
|
if (dev == (*dst)) {
|
|
if (prev == NULL)
|
|
dev_list = dev->next;
|
|
else
|
|
prev->next = dev->next;
|
|
break;
|
|
}
|
|
prev = dev;
|
|
}
|
|
|
|
dev = *dst;
|
|
|
|
/* Destroy BOs before closing vdrm */
|
|
hash_table_foreach(dev->handle_to_vbo, entry) {
|
|
struct amdvgpu_bo *bo = entry->data;
|
|
amdvgpu_bo_free(dev, bo);
|
|
}
|
|
_mesa_hash_table_destroy(dev->handle_to_vbo, NULL);
|
|
/* Destroy contextx. */
|
|
hash_table_foreach(&dev->contexts, entry)
|
|
amdvgpu_cs_ctx_free(dev, (uint32_t)(uintptr_t)entry->key);
|
|
_mesa_hash_table_clear(&dev->contexts, NULL);
|
|
|
|
simple_mtx_destroy(&dev->handle_to_vbo_mutex);
|
|
simple_mtx_destroy(&dev->contexts_mutex);
|
|
|
|
amdgpu_va_manager_deinit(dev->va_mgr);
|
|
|
|
vdrm_device_close(dev->vdev);
|
|
|
|
close(dev->fd);
|
|
free(dev);
|
|
}
|
|
|
|
*dst = src;
|
|
}
|
|
|
|
int amdvgpu_device_deinitialize(amdvgpu_device_handle dev) {
|
|
simple_mtx_lock(&dev_mutex);
|
|
amdvgpu_device_reference(&dev, NULL);
|
|
simple_mtx_unlock(&dev_mutex);
|
|
return 0;
|
|
}
|
|
|
|
int amdvgpu_device_initialize(int fd, uint32_t *drm_major, uint32_t *drm_minor,
|
|
amdvgpu_device_handle* dev_out,
|
|
struct util_sync_provider **p) {
|
|
simple_mtx_lock(&dev_mutex);
|
|
amdvgpu_device_handle dev;
|
|
|
|
for (dev = dev_list; dev; dev = dev->next)
|
|
if (fd_compare(dev->fd, fd) == 0)
|
|
break;
|
|
|
|
if (dev) {
|
|
*dev_out = NULL;
|
|
amdvgpu_device_reference(dev_out, dev);
|
|
*drm_major = dev->vdev->caps.version_major;
|
|
*drm_minor = dev->vdev->caps.version_minor;
|
|
simple_mtx_unlock(&dev_mutex);
|
|
goto init_sync_provider;
|
|
}
|
|
|
|
/* fd is owned by the amdgpu_screen_winsys that called this function.
|
|
* amdgpu_screen_winsys' lifetime may be shorter than the device's one,
|
|
* so dup fd to tie its lifetime to the device's one.
|
|
*/
|
|
fd = os_dupfd_cloexec(fd);
|
|
|
|
struct vdrm_device *vdev = vdrm_device_connect(fd, VIRTGPU_DRM_CONTEXT_AMDGPU);
|
|
if (vdev == NULL) {
|
|
mesa_loge("vdrm_device_connect failed\n");
|
|
simple_mtx_unlock(&dev_mutex);
|
|
return -1;
|
|
}
|
|
|
|
dev = calloc(1, sizeof(struct amdvgpu_device));
|
|
dev->refcount = 1;
|
|
dev->next = dev_list;
|
|
dev_list = dev;
|
|
dev->fd = fd;
|
|
dev->vdev = vdev;
|
|
|
|
simple_mtx_init(&dev->handle_to_vbo_mutex, mtx_plain);
|
|
simple_mtx_init(&dev->contexts_mutex, mtx_plain);
|
|
|
|
dev->handle_to_vbo = _mesa_hash_table_create_u32_keys(NULL);
|
|
|
|
p_atomic_set(&dev->next_blob_id, 1);
|
|
|
|
*dev_out = dev;
|
|
|
|
simple_mtx_unlock(&dev_mutex);
|
|
|
|
struct drm_amdgpu_info info;
|
|
info.return_pointer = (uintptr_t)&dev->dev_info;
|
|
info.query = AMDGPU_INFO_DEV_INFO;
|
|
info.return_size = sizeof(dev->dev_info);
|
|
int r = amdvgpu_query_info(dev, &info);
|
|
assert(r == 0);
|
|
|
|
/* Ring idx 0 is reserved for commands running on CPU. */
|
|
unsigned next_ring_idx = 1;
|
|
for (unsigned i = 0; i < AMD_NUM_IP_TYPES; ++i) {
|
|
struct drm_amdgpu_info_hw_ip ip_info = {0};
|
|
struct drm_amdgpu_info request = {0};
|
|
request.return_pointer = (uintptr_t)&ip_info;
|
|
request.return_size = sizeof(ip_info);
|
|
request.query = AMDGPU_INFO_HW_IP_INFO;
|
|
request.query_hw_ip.type = i;
|
|
request.query_hw_ip.ip_instance = 0;
|
|
r = amdvgpu_query_info(dev, &request);
|
|
if (r == 0 && ip_info.available_rings) {
|
|
int count = util_bitcount(ip_info.available_rings);
|
|
dev->virtio_ring_mapping[i] = next_ring_idx;
|
|
next_ring_idx += count;
|
|
}
|
|
}
|
|
/* VIRTGPU_CONTEXT_PARAM_NUM_RINGS is hardcoded for now. */
|
|
assert(next_ring_idx <= 64);
|
|
dev->num_virtio_rings = next_ring_idx - 1;
|
|
|
|
dev->va_mgr = amdgpu_va_manager_alloc();
|
|
amdgpu_va_manager_init(dev->va_mgr,
|
|
dev->dev_info.virtual_address_offset, dev->dev_info.virtual_address_max,
|
|
dev->dev_info.high_va_offset, dev->dev_info.high_va_max,
|
|
dev->dev_info.virtual_address_alignment);
|
|
|
|
_mesa_hash_table_init(&dev->contexts, NULL,
|
|
_mesa_hash_pointer, _mesa_key_pointer_equal);
|
|
dev->allow_multiple_amdgpu_ctx = debug_get_bool_option("MULTIPLE_AMDGPU_CTX", false);
|
|
dev->sync_cmd = debug_get_num_option("VIRTIO_SYNC_CMD", 0);
|
|
|
|
*drm_major = dev->vdev->caps.version_major;
|
|
*drm_minor = dev->vdev->caps.version_minor;
|
|
|
|
init_sync_provider:
|
|
*p = vdrm_vpipe_get_sync(dev->vdev);
|
|
if (!(*p))
|
|
*p = util_sync_provider_drm(fd);
|
|
|
|
return 0;
|
|
}
|