[PATCH] gfxstream: Make the virtgpu device discovery for LinuxVirtGpu more robust
authorAaron Ruby <aruby@qnx.com>
Fri, 10 Jan 2025 22:17:56 +0000 (17:17 -0500)
committerTimo Aaltonen <tjaalton@debian.org>
Thu, 20 Mar 2025 14:52:21 +0000 (16:52 +0200)
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33363>

Gbp-Pq: Name gfxstream-make-virtgpu-discovery-more-robust.diff

src/gfxstream/guest/platform/linux/LinuxVirtGpu.h
src/gfxstream/guest/platform/linux/LinuxVirtGpuDevice.cpp
src/gfxstream/guest/vulkan/gfxstream_vk_device.cpp

index 46daa0d59150c99616b739585667322bb7e1a891..a92a67a962551317f23d371023c80cfaea84f188 100644 (file)
@@ -6,6 +6,7 @@
 #pragma once
 
 #include "VirtGpu.h"
+#include <xf86drm.h>
 
 class LinuxVirtGpuResource : public std::enable_shared_from_this<LinuxVirtGpuResource>,
                              public VirtGpuResource {
@@ -69,4 +70,15 @@ class LinuxVirtGpuDevice : public VirtGpuDevice {
    private:
     int64_t mDeviceHandle;
     struct VirtGpuCaps mCaps;
+
+    int openDevice(const drmDevicePtr dev);
+
+    bool mHasPrimary;
+    int mPrimaryMajor;
+    int mPrimaryMinor;
+    int mRenderMajor;
+    int mRenderMinor;
+
+    int mBusType;
+    drmPciBusInfo mPciBusInfo;
 };
index 9eb6b64ec6acb084b92bc4911bfd87b5b9f65ad7..d085a3a91fffb50e14dddfb12a925ac868b1fb17 100644 (file)
@@ -9,6 +9,7 @@
 #include <sys/mman.h>
 #include <unistd.h>
 #include <xf86drm.h>
+#include <sys/stat.h>
 
 #include <cerrno>
 #include <cstring>
 #include "util/log.h"
 #include "virtgpu_gfxstream_protocol.h"
 
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#endif
+#ifdef MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#endif
+
+#define VIRTGPU_PCI_VENDOR_ID 0x1af4
+#define VIRTGPU_PCI_DEVICE_ID 0x1050
+
 #define VIRTGPU_PARAM_CREATE_FENCE_PASSING 9  /* Fence passing */
 #define VIRTGPU_PARAM_CREATE_GUEST_HANDLE 10  /* Host OS handle can be created from guest memory. */
 
 
 static inline uint32_t align_up(uint32_t n, uint32_t a) { return ((n + a - 1) / a) * a; }
 
+int
+LinuxVirtGpuDevice::openDevice(const drmDevicePtr dev)
+{
+    bool supported_bus = false;
+
+    switch (dev->bustype) {
+    case DRM_BUS_PCI:
+        if (dev->deviceinfo.pci->vendor_id == VIRTGPU_PCI_VENDOR_ID &&
+            dev->deviceinfo.pci->device_id == VIRTGPU_PCI_DEVICE_ID)
+            supported_bus = true;
+        break;
+    case DRM_BUS_PLATFORM:
+        supported_bus = true;
+        break;
+    default:
+        break;
+    }
+
+    if (!supported_bus || !(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
+        const char *name = "unknown";
+        for (uint32_t i = 0; i < DRM_NODE_MAX; i++) {
+            if (dev->available_nodes & (1 << i)) {
+                name = dev->nodes[i];
+                break;
+            }
+        }
+        mesa_logd("skipping DRM device %s", name);
+        return -1;
+    }
+
+    const char *primary_path = dev->nodes[DRM_NODE_PRIMARY];
+    const char *node_path = dev->nodes[DRM_NODE_RENDER];
+
+    int fd = open(node_path, O_RDWR | O_CLOEXEC);
+    if (fd < 0) {
+        mesa_logd("failed to open %s", node_path);
+        return -1;
+    }
+
+    drmVersionPtr version = drmGetVersion(fd);
+    if (!version || strcmp(version->name, "virtio_gpu") ||
+        version->version_major != 0) {
+        if (version) {
+            mesa_logd("unknown DRM driver %s version %d",
+                    version->name, version->version_major);
+        } else {
+            mesa_logd("failed to get DRM driver version");
+        }
+        if (version)
+            drmFreeVersion(version);
+        close(fd);
+        return -1;
+    }
+
+    struct stat st;
+    if (stat(primary_path, &st) == 0) {
+        mHasPrimary = true;
+        mPrimaryMajor = major(st.st_rdev);
+        mPrimaryMinor = minor(st.st_rdev);
+    } else {
+        mHasPrimary = false;
+        mPrimaryMajor = 0;
+        mPrimaryMinor = 0;
+    }
+    stat(node_path, &st);
+    mRenderMajor = major(st.st_rdev);
+    mRenderMinor = minor(st.st_rdev);
+
+    mBusType = dev->bustype;
+    if (dev->bustype == DRM_BUS_PCI)
+        mPciBusInfo = *dev->businfo.pci;
+
+    drmFreeVersion(version);
+
+    mesa_logd("using DRM device %s", node_path);
+
+    return fd;
+}
+
 LinuxVirtGpuDevice::LinuxVirtGpuDevice(enum VirtGpuCapset capset, int32_t descriptor)
     : VirtGpuDevice(capset) {
     struct VirtGpuParam params[] = {
@@ -52,11 +142,27 @@ LinuxVirtGpuDevice::LinuxVirtGpuDevice(enum VirtGpuCapset capset, int32_t descri
 #endif
 
     if (descriptor < 0) {
-        mDeviceHandle = static_cast<int64_t>(drmOpenRender(128));
-        if (mDeviceHandle < 0) {
-            mesa_loge("Failed to open rendernode: %s", strerror(errno));
+        drmDevicePtr devs[8];
+        int count = drmGetDevices2(0, devs, ARRAY_SIZE(devs));
+        if (count < 0) {
+            mesa_loge("failed to enumerate DRM devices");
             return;
         }
+
+        int fd = -1;
+        for (int i = 0; i < count; i++) {
+            fd = openDevice(devs[i]);
+            if (fd >= 0) break;
+        }
+
+        drmFreeDevices(devs, count);
+
+        if (fd < 0) {
+            mesa_loge("Failed to open the virtio_gpu device.");
+            return;
+        }
+
+        mDeviceHandle = static_cast<int64_t>(fd);
     } else {
         mDeviceHandle = dup(descriptor);
         if (mDeviceHandle < 0) {
index 9716114458a92fa69df7d3c206956cb12b42cd0b..f5e6a685fd2ac923a05a4f90a7d75cb7547b2a37 100644 (file)
@@ -368,9 +368,10 @@ VkResult gfxstream_vk_CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
         return vk_error(NULL, result);
     }
 
+    // Note: Do not support try_create_for_drm. virtio_gpu DRM device opened in
+    // init_renderer above, which can still enumerate multiple physical devices on the host.
     instance->vk.physical_devices.enumerate = gfxstream_vk_enumerate_devices;
     instance->vk.physical_devices.destroy = gfxstream_vk_destroy_physical_device;
-    // TODO: instance->vk.physical_devices.try_create_for_drm (?)
 
     *pInstance = gfxstream_vk_instance_to_handle(instance);
     return VK_SUCCESS;