radv: enable virtio native context support

No big code changes are needed to support virtio. The main caveat with
radv is about buffer allocations.

Allocating a cpu visible buffer requires the host process (eg qemu) to
create a dmabuf, mmap it, then map the host CPU address into the guest
application CPU address space.

The first issue is about the number of dmabuf created because we
might hit the number of open file limit. The host process limit can
be raised but we would hit the second issue - at least on qemu:
there's a limit on how many sections can be mapped and ultimately
we hit this assert:

   assert(map->sections_nb < TARGET_PAGE_SIZE);

(the third issue is a performance one: these operations have a cost,
and this increases some Vulkan app loading times)

radeonsi is not really affected because it's using pb_slab to
suballocate small buffers from larger ones.
radv on the other hand doesn't, so if an app decides to allocate
lots of cpu visible small buffers, we're likely to fail.

Earlier versions of the amdgpu nctx code had a suballoctor, but it
was removed to simplified the code. It could be restored later; or
radv could be modified to use a suballocator (like anv does AFAICT).

Acked-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/21658>
This commit is contained in:
Pierre-Eric Pelloux-Prayer 2024-07-19 10:22:06 +02:00 committed by Marge Bot
parent 612774c4a6
commit 9de728c4d0
6 changed files with 64 additions and 11 deletions

View file

@ -198,6 +198,10 @@ subdir('layers')
radv_deps = []
radv_flags = cc.get_supported_arguments(['-Wimplicit-fallthrough', '-Wshadow'])
if with_amdgpu_virtio
radv_flags += '-DHAVE_AMDGPU_VIRTIO'
endif
if with_platform_x11
radv_deps += dep_xcb_dri3
endif
@ -224,13 +228,14 @@ libvulkan_radeon = shared_library(
[libradv_files, radv_entrypoints, sha1_h, radix_sort_spv, bvh_spv, radv_annotate_layer],
vs_module_defs : vulkan_api_def,
include_directories : [
inc_include, inc_src, inc_amd, inc_amd_common, inc_amd_common_llvm, inc_util,
inc_include, inc_src, inc_amd, inc_amd_common, inc_amd_common_llvm,
inc_util, inc_virtio_gpu
],
link_with : [
libamd_common, libamd_common_llvm, libamdgpu_addrlib,
],
dependencies : [
dep_llvm, dep_libdrm_amdgpu, dep_thread, dep_elf, dep_dl, dep_m,
dep_llvm, dep_thread, dep_elf, dep_dl, dep_m,
dep_valgrind, radv_deps, idep_aco,
idep_mesautil, idep_nir, idep_vulkan_util, idep_vulkan_wsi,
idep_vulkan_runtime, idep_amdgfxregs_h, idep_xmlconfig,

View file

@ -12,6 +12,9 @@
#define RADV_CONSTANTS_H
#define ATI_VENDOR_ID 0x1002
#ifdef HAVE_AMDGPU_VIRTIO
#define VIRTGPU_PCI_VENDOR_ID 0x1af4
#endif
#define MAX_VBS 32
#define MAX_VERTEX_ATTRIBS 32

View file

@ -2018,7 +2018,15 @@ radv_physical_device_try_create(struct radv_instance *instance, drmDevicePtr drm
"Could not get the kernel driver version for device %s: %m", path);
}
if (strcmp(version->name, "amdgpu")) {
if (!strcmp(version->name, "amdgpu")) {
/* nothing to do. */
} else
#ifdef HAVE_AMDGPU_VIRTIO
if (!strcmp(version->name, "virtio_gpu")) {
is_virtio = true;
} else
#endif
{
drmFreeVersion(version);
close(fd);
@ -2054,7 +2062,8 @@ radv_physical_device_try_create(struct radv_instance *instance, drmDevicePtr drm
if (drm_device) {
bool reserve_vmid = instance->vk.trace_mode & RADV_TRACE_MODE_RGP;
pdev->ws = radv_amdgpu_winsys_create(fd, instance->debug_flags, instance->perftest_flags, reserve_vmid);
pdev->ws =
radv_amdgpu_winsys_create(fd, instance->debug_flags, instance->perftest_flags, reserve_vmid, is_virtio);
} else {
pdev->ws = radv_null_winsys_create();
}
@ -2308,8 +2317,18 @@ VkResult
create_drm_physical_device(struct vk_instance *vk_instance, struct _drmDevice *device, struct vk_physical_device **out)
{
#ifndef _WIN32
if (!(device->available_nodes & (1 << DRM_NODE_RENDER)) || device->bustype != DRM_BUS_PCI ||
device->deviceinfo.pci->vendor_id != ATI_VENDOR_ID)
bool supported_device = false;
if (!(device->available_nodes & (1 << DRM_NODE_RENDER)) || device->bustype != DRM_BUS_PCI)
return VK_ERROR_INCOMPATIBLE_DRIVER;
#ifdef HAVE_AMDGPU_VIRTIO
supported_device |= device->deviceinfo.pci->vendor_id == VIRTGPU_PCI_VENDOR_ID;
#endif
supported_device |= device->deviceinfo.pci->vendor_id == ATI_VENDOR_ID;
if (!supported_device)
return VK_ERROR_INCOMPATIBLE_DRIVER;
return radv_physical_device_try_create((struct radv_instance *)vk_instance, device,

View file

@ -492,7 +492,10 @@ radv_amdgpu_winsys_bo_create(struct radeon_winsys *_ws, uint64_t size, unsigned
request.flags |= AMDGPU_GEM_CREATE_EXPLICIT_SYNC;
if ((initial_domain & RADEON_DOMAIN_VRAM_GTT) && (flags & RADEON_FLAG_NO_INTERPROCESS_SHARING) &&
((ws->perftest & RADV_PERFTEST_LOCAL_BOS) || (flags & RADEON_FLAG_PREFER_LOCAL_BO))) {
if (request.flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) {
/* virtio needs to be able to create a dmabuf if CPU access is required but a
* dmabuf cannot be created if VM_ALWAYS_VALID is used.
*/
if (!ws->info.is_virtio || (request.flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS)) {
bo->base.is_local = true;
request.flags |= AMDGPU_GEM_CREATE_VM_ALWAYS_VALID;
}
@ -583,6 +586,22 @@ radv_amdgpu_winsys_bo_map(struct radeon_winsys *_ws, struct radeon_winsys_bo *_b
assert(!bo->cpu_map);
#if HAVE_AMDGPU_VIRTIO
struct radv_amdgpu_winsys *ws = radv_amdgpu_winsys(_ws);
if (ws->info.is_virtio) {
/* We can't use DRM_AMDGPU_GEM_MMAP directly on virtio. Instead use bo_cpu_map since
* the virtio version will map the buffer at the given address (if not NULL).
*/
void *data = NULL;
if (use_fixed_addr)
data = fixed_addr;
if (ac_drm_bo_cpu_map(ws->dev, bo->bo, &data))
return NULL;
return data;
}
#endif
union drm_amdgpu_gem_mmap args;
memset(&args, 0, sizeof(args));
args.in.handle = bo->bo_handle;
@ -613,7 +632,13 @@ radv_amdgpu_winsys_bo_unmap(struct radeon_winsys *_ws, struct radeon_winsys_bo *
if (replace) {
(void)mmap(bo->cpu_map, bo->base.size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
} else {
munmap(bo->cpu_map, bo->base.size);
#if HAVE_AMDGPU_VIRTIO
struct radv_amdgpu_winsys *ws = radv_amdgpu_winsys(_ws);
if (ws->info.is_virtio)
ac_drm_bo_cpu_unmap(ws->dev, bo->bo);
else
#endif
munmap(bo->cpu_map, bo->base.size);
}
bo->cpu_map = NULL;
}

View file

@ -196,13 +196,13 @@ radv_amdgpu_winsys_get_sync_types(struct radeon_winsys *rws)
}
struct radeon_winsys *
radv_amdgpu_winsys_create(int fd, uint64_t debug_flags, uint64_t perftest_flags, bool reserve_vmid)
radv_amdgpu_winsys_create(int fd, uint64_t debug_flags, uint64_t perftest_flags, bool reserve_vmid, bool is_virtio)
{
uint32_t drm_major, drm_minor, r;
ac_drm_device *dev;
struct radv_amdgpu_winsys *ws = NULL;
r = ac_drm_device_initialize(fd, false, &drm_major, &drm_minor, &dev);
r = ac_drm_device_initialize(fd, is_virtio, &drm_major, &drm_minor, &dev);
if (r) {
fprintf(stderr, "radv/amdgpu: failed to initialize device.\n");
return NULL;
@ -251,6 +251,7 @@ radv_amdgpu_winsys_create(int fd, uint64_t debug_flags, uint64_t perftest_flags,
ws->fd = ac_drm_device_get_fd(dev);
ws->info.drm_major = drm_major;
ws->info.drm_minor = drm_minor;
ws->info.is_virtio = is_virtio;
if (!do_winsys_init(ws, fd))
goto winsys_fail;

View file

@ -13,7 +13,7 @@
#define RADV_AMDGPU_WINSYS_PUBLIC_H
struct radeon_winsys *radv_amdgpu_winsys_create(int fd, uint64_t debug_flags, uint64_t perftest_flags,
bool reserve_vmid);
bool reserve_vmid, bool is_virtio);
struct radeon_winsys *radv_dummy_winsys_create(void);