aaa06e1609
* libhsakmt/virtio: change shmem size to 80 Some DGPU props have a lot of information, so it is necessary to increase the size of shmem. Signed-off-by: Honglei Huang <honghuan@amd.com> * libhsakmt/virtio: use BO handle instead of pointer in memory registration Change vhsakmt_map_to_gpu() return type from void* to vhsakmt_bo_handle to properly handle buffer object information. This allows access to both the host address and resource ID needed for memory registration. Signed-off-by: Honglei Huang <honghuan@amd.com> * libhsakmt/virtio: Improve memory mapping logic - Update vhsakmt_mappable() to check NoAddress flag and require HostAccess - Remove mappable checks in cpu_map/unmap to allow all BOs to be mapped - Set BO flags properly in vhsakmt_alloc_memory and scratch memory creation - Ensure scratch memory is correctly flagged for proper handling Signed-off-by: Honglei Huang <honghuan@amd.com> * libhsakmt/virtio: add no svm mode for libhsakmt virtio Add no svm mode for libhsakmt virtio driver, in no svm mode userptrs need UMD to manage, so add interval tree to manage them. New Features: - Add augmented red-black tree based interval tree implementation * Implement RB-tree insertion, deletion, and color balancing * Provide interval query for fast overlapping range lookup * Based on Linux kernel's augmented rbtree implementation - Improve userptr memory management * Use interval tree to efficiently track userptr memory regions * Support finding registered memory within given address ranges * Optimize memory mapping and unmapping performance Signed-off-by: Honglei Huang <honghuan@amd.com> --------- Signed-off-by: Honglei Huang <honghuan@amd.com>
325 line
9.1 KiB
C
325 line
9.1 KiB
C
/*
|
|
* Copyright 2025 Advanced Micro Devices, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <libsync.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "virtio_gpu.h"
|
|
|
|
#define SHMEM_SZ (80 * 0x1000)
|
|
|
|
static int set_context(int fd) {
|
|
struct drm_virtgpu_context_set_param params[] = {
|
|
{VIRTGPU_CONTEXT_PARAM_CAPSET_ID, VIRGL_RENDERER_CAPSET_HSAKMT},
|
|
{VIRTGPU_CONTEXT_PARAM_NUM_RINGS, 64},
|
|
};
|
|
struct drm_virtgpu_context_init args = {
|
|
.num_params = ARRAY_SIZE(params),
|
|
.ctx_set_params = (uintptr_t)(params),
|
|
};
|
|
|
|
return virtio_gpu_ioctl(fd, VIRTGPU_CONTEXT_INIT, &args);
|
|
}
|
|
|
|
int virtio_gpu_map_handle(struct virtio_gpu_device* vgdev, uint32_t handle, uint64_t size,
|
|
void** addr, void* fixed_map) {
|
|
struct drm_virtgpu_map args = {
|
|
.handle = handle,
|
|
};
|
|
int r;
|
|
|
|
r = virtio_gpu_ioctl(vgdev->fd, VIRTGPU_MAP, &args);
|
|
if (r) return r;
|
|
|
|
*addr = mmap(fixed_map, size, PROT_READ | PROT_WRITE, MAP_SHARED | (fixed_map ? MAP_FIXED : 0),
|
|
vgdev->fd, args.offset);
|
|
|
|
if (*addr == MAP_FAILED) return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void virtio_gpu_unmap(void* addr, uint64_t size) { munmap(addr, size); }
|
|
|
|
static void virtio_gpu_bo_close(struct virtio_gpu_device* vgdev, uint32_t handle) {
|
|
struct drm_gem_close args = {
|
|
.handle = handle,
|
|
};
|
|
|
|
virtio_gpu_ioctl(vgdev->fd, GEM_CLOSE, &args);
|
|
}
|
|
|
|
static int virtio_gpu_shmem_init(struct virtio_gpu_device* vgdev, size_t size) {
|
|
struct drm_virtgpu_resource_create_blob args = {
|
|
.blob_mem = VIRTGPU_BLOB_MEM_HOST3D,
|
|
.blob_flags = VIRTGPU_BLOB_FLAG_USE_MAPPABLE,
|
|
.size = size,
|
|
.blob_id = 0,
|
|
};
|
|
|
|
int r = virtio_gpu_ioctl(vgdev->fd, VIRTGPU_RESOURCE_CREATE_BLOB, &args);
|
|
if (r) return r;
|
|
|
|
r = virtio_gpu_map_handle(vgdev, args.bo_handle, size, (void**)&vgdev->shmem, NULL);
|
|
if (r) {
|
|
virtio_gpu_bo_close(vgdev, args.bo_handle);
|
|
return r;
|
|
}
|
|
|
|
vgdev->shmem_handle = args.bo_handle;
|
|
|
|
uint32_t offset = vgdev->shmem->base.rsp_mem_offset;
|
|
vgdev->rsp_mem_len = size - offset;
|
|
vgdev->rsp_mem = &((uint8_t*)vgdev->shmem)[offset];
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct virtio_gpu_device* virtio_gpu_init(int fd, uint32_t context_id) {
|
|
struct virtio_gpu_device* vgdev;
|
|
int r;
|
|
|
|
r = set_context(fd);
|
|
|
|
if (r) return NULL;
|
|
|
|
vgdev = calloc(1, sizeof(*vgdev));
|
|
if (!vgdev) return NULL;
|
|
|
|
vgdev->fd = fd;
|
|
|
|
vgdev->reqbuf = calloc(1, SHMEM_SZ);
|
|
if (!vgdev->reqbuf) {
|
|
free(vgdev);
|
|
return NULL;
|
|
}
|
|
|
|
r = virtio_gpu_shmem_init(vgdev, SHMEM_SZ);
|
|
if (r) {
|
|
free(vgdev);
|
|
return NULL;
|
|
}
|
|
|
|
pthread_mutex_init(&vgdev->rsp_lock, NULL);
|
|
pthread_mutex_init(&vgdev->eb_lock, NULL);
|
|
|
|
return vgdev;
|
|
}
|
|
|
|
void virtio_gpu_close(struct virtio_gpu_device* vgdev) {
|
|
virtio_gpu_unmap(vgdev->shmem, SHMEM_SZ);
|
|
virtio_gpu_bo_close(vgdev, vgdev->shmem_handle);
|
|
|
|
pthread_mutex_destroy(&vgdev->rsp_lock);
|
|
pthread_mutex_destroy(&vgdev->eb_lock);
|
|
|
|
close(vgdev->fd);
|
|
free(vgdev->reqbuf);
|
|
free(vgdev);
|
|
}
|
|
|
|
void* virtio_gpu_alloc_rsp(struct virtio_gpu_device* vgdev, struct virtio_gpu_ccmd_req* req,
|
|
uint32_t size) {
|
|
uint32_t off;
|
|
|
|
pthread_mutex_lock(&vgdev->rsp_lock);
|
|
|
|
size = VHSA_ALIGN_UP(size, 8);
|
|
|
|
if ((vgdev->next_rsp_off + size) >= vgdev->rsp_mem_len) vgdev->next_rsp_off = 0;
|
|
|
|
off = vgdev->next_rsp_off;
|
|
vgdev->next_rsp_off += size;
|
|
|
|
pthread_mutex_unlock(&vgdev->rsp_lock);
|
|
|
|
req->rsp_off = off;
|
|
struct virtio_gpu_ccmd_rsp* rsp = (void*)&vgdev->rsp_mem[off];
|
|
rsp->len = size;
|
|
|
|
return rsp;
|
|
}
|
|
|
|
static int virtio_gpu_execbuffer_locked(struct virtio_gpu_device* vgdev, void* cmd,
|
|
uint32_t cmd_size, uint32_t* handles, uint32_t num_handles,
|
|
int* fence_fd, int ring_idx, uint32_t num_in_syncobjs,
|
|
uint32_t num_out_syncobjs,
|
|
struct drm_virtgpu_execbuffer_syncobj* in_syncobjs,
|
|
struct drm_virtgpu_execbuffer_syncobj* out_syncobjs,
|
|
bool in_fence, bool out_fence) {
|
|
struct drm_virtgpu_execbuffer eb = {
|
|
.flags = (out_fence ? VIRTGPU_EXECBUF_FENCE_FD_OUT : 0) |
|
|
(in_fence ? VIRTGPU_EXECBUF_FENCE_FD_IN : 0) | VIRTGPU_EXECBUF_RING_IDX,
|
|
.size = cmd_size,
|
|
.command = (uintptr_t)cmd,
|
|
.bo_handles = (uintptr_t)handles,
|
|
.num_bo_handles = num_handles,
|
|
.fence_fd = *fence_fd,
|
|
.ring_idx = ring_idx,
|
|
.syncobj_stride = sizeof(struct drm_virtgpu_execbuffer_syncobj),
|
|
.num_in_syncobjs = num_in_syncobjs,
|
|
.num_out_syncobjs = num_out_syncobjs,
|
|
.in_syncobjs = (uintptr_t)in_syncobjs,
|
|
.out_syncobjs = (uintptr_t)out_syncobjs,
|
|
};
|
|
int r = virtio_gpu_ioctl(vgdev->fd, VIRTGPU_EXECBUFFER, &eb);
|
|
if (r) return r;
|
|
|
|
if (out_fence) *fence_fd = eb.fence_fd;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int virtio_gpu_flush_locked(struct virtio_gpu_device* vgdev, int* fence) {
|
|
int r;
|
|
|
|
if (!vgdev->reqbuf_len) return 0;
|
|
|
|
r = virtio_gpu_execbuffer_locked(vgdev, vgdev->reqbuf, vgdev->reqbuf_len, NULL, 0, fence, 0, 0, 0,
|
|
NULL, NULL, false, !!fence);
|
|
if (r) return r;
|
|
|
|
vgdev->reqbuf_len = 0;
|
|
vgdev->reqbuf_cnt = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int virtio_gpu_add_cmd(struct virtio_gpu_device* vgdev, struct virtio_gpu_ccmd_req* req) {
|
|
req->seqno = ++vgdev->next_seqno;
|
|
int r;
|
|
|
|
if (vgdev->reqbuf_len + req->len > sizeof(vgdev->reqbuf)) {
|
|
r = virtio_gpu_flush_locked(vgdev, NULL);
|
|
if (r) return r;
|
|
}
|
|
|
|
memcpy(&vgdev->reqbuf[vgdev->reqbuf_len], req, req->len);
|
|
vgdev->reqbuf_len += req->len;
|
|
vgdev->reqbuf_cnt++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline bool fence_before(uint32_t a, uint32_t b) { return (int32_t)(a - b) < 0; }
|
|
|
|
static void virtio_gpu_seqno_sync(struct virtio_gpu_device* vgdev,
|
|
struct virtio_gpu_ccmd_req* req) {
|
|
while (fence_before(vgdev->shmem->base.seqno, req->seqno)) sched_yield();
|
|
}
|
|
|
|
int virtio_gpu_exec_cmd(struct virtio_gpu_device* vgdev, struct virtio_gpu_ccmd_req* req,
|
|
bool sync) {
|
|
int r = 0;
|
|
int fence;
|
|
|
|
pthread_mutex_lock(&vgdev->eb_lock);
|
|
|
|
r = virtio_gpu_add_cmd(vgdev, req);
|
|
|
|
if (r || !sync) goto out;
|
|
|
|
r = virtio_gpu_flush_locked(vgdev, &fence);
|
|
|
|
out:
|
|
pthread_mutex_unlock(&vgdev->eb_lock);
|
|
if (r) return r;
|
|
|
|
if (sync) {
|
|
sync_wait(fence, -1);
|
|
close(fence);
|
|
virtio_gpu_seqno_sync(vgdev, req);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int virtio_gpu_create_blob(struct virtio_gpu_device* vgdev,
|
|
struct drm_virtgpu_resource_create_blob* args) {
|
|
return virtio_gpu_ioctl(vgdev->fd, VIRTGPU_RESOURCE_CREATE_BLOB, args);
|
|
}
|
|
|
|
int virtio_gpu_destroy_handle(struct virtio_gpu_device* vgdev, uint32_t bo_handle) {
|
|
struct drm_gem_close args = {
|
|
.handle = bo_handle,
|
|
};
|
|
|
|
return virtio_gpu_ioctl(vgdev->fd, GEM_CLOSE, &args);
|
|
}
|
|
|
|
int virtio_gpu_res_id(struct virtio_gpu_device* vgdev, uint32_t handle, uint32_t* res_id) {
|
|
struct drm_virtgpu_resource_info args = {
|
|
.bo_handle = handle,
|
|
};
|
|
int r = virtio_gpu_ioctl(vgdev->fd, VIRTGPU_RESOURCE_INFO, &args);
|
|
if (r) return r;
|
|
|
|
*res_id = args.res_handle;
|
|
return 0;
|
|
}
|
|
|
|
static int virtio_gpu_get_capset(int fd, struct virgl_renderer_capset_hsakmt* caps) {
|
|
struct drm_virtgpu_get_caps args = {
|
|
.cap_set_id = VIRGL_RENDERER_CAPSET_HSAKMT,
|
|
.cap_set_ver = 0,
|
|
.addr = (uintptr_t)caps,
|
|
.size = sizeof(*caps),
|
|
};
|
|
|
|
memset(caps, 0, sizeof(*caps));
|
|
|
|
return virtio_gpu_ioctl(fd, VIRTGPU_GET_CAPS, &args);
|
|
}
|
|
|
|
int virtio_gpu_kfd_open(void) {
|
|
drmDevicePtr devices[VHSA_MAX_DEVICES];
|
|
int num_devices = 0;
|
|
int i, fd, ret;
|
|
|
|
num_devices = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
|
|
if (num_devices <= 0) return -1;
|
|
|
|
for (i = 0; i < num_devices; i++) {
|
|
fd = open(devices[i]->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC);
|
|
if (fd < 0) continue;
|
|
|
|
struct virgl_renderer_capset_hsakmt caps;
|
|
ret = virtio_gpu_get_capset(fd, &caps);
|
|
if (ret || caps.context_type != VIRTGPU_DRM_CONTEXT_AMDGPU) {
|
|
close(fd);
|
|
fd = -1;
|
|
continue;
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
drmFreeDevices(devices, num_devices);
|
|
return fd;
|
|
}
|