loader: reimplement loader_get_user_preferred_fd via libdrm
authorEmil Velikov <emil.velikov@collabora.com>
Wed, 7 Sep 2016 17:30:48 +0000 (18:30 +0100)
committerEmil Velikov <emil.l.velikov@gmail.com>
Tue, 18 Oct 2016 16:06:10 +0000 (17:06 +0100)
Currently not everyone has libudev and with follow-up patches we'll
completely remove the divergent codepaths.

Use the libdrm drm device API to construct the required ID_PATH_TAG-like
string, to preserve the current functionality for libudev users and
allow others to benefit from it as well.

v2: Drop ranty comments, pick the correct device
v3: \n -> \0 in PCI_ID_PATH_TAG_LENGTH comment (Axel).
v4: Use snprintf (Nicolai)

Signed-off-by: Emil Velikov <emil.velikov@collabora.com>
Reviewed-by: Axel Davy <axel.davy@ens.fr>
src/loader/loader.c

index 6b1273c270dd554d4ef1da0fddb01d1cc1194147..1eb90e8bba723ce07ad7016cd09f42c9653670fb 100644 (file)
 #include <sys/stat.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdbool.h>
 #include <string.h>
 #ifdef HAVE_LIBUDEV
 #include <assert.h>
 #include <dlfcn.h>
-#include <unistd.h>
-#include <stdlib.h>
-#ifdef USE_DRICONF
-#include "xmlconfig.h"
-#include "xmlpool.h"
-#endif
 #endif
 #ifdef MAJOR_IN_MKDEV
 #include <sys/mkdev.h>
 #include "loader.h"
 
 #ifdef HAVE_LIBDRM
+#include <stdlib.h>
+#include <unistd.h>
 #include <xf86drm.h>
+#ifdef USE_DRICONF
+#include "xmlconfig.h"
+#include "xmlpool.h"
+#endif
 #endif
 
 #define __IS_LOADER
@@ -203,99 +204,9 @@ udev_device_new_from_fd(struct udev *udev, int fd)
 
    return device;
 }
+#endif
 
-static char *
-get_render_node_from_id_path_tag(struct udev *udev,
-                                 char *id_path_tag,
-                                 char another_tag)
-{
-   struct udev_device *device;
-   struct udev_enumerate *e;
-   struct udev_list_entry *entry;
-   const char *path, *id_path_tag_tmp;
-   char *path_res;
-   char found = 0;
-   UDEV_SYMBOL(struct udev_enumerate *, udev_enumerate_new,
-               (struct udev *));
-   UDEV_SYMBOL(int, udev_enumerate_add_match_subsystem,
-               (struct udev_enumerate *, const char *));
-   UDEV_SYMBOL(int, udev_enumerate_add_match_sysname,
-               (struct udev_enumerate *, const char *));
-   UDEV_SYMBOL(int, udev_enumerate_scan_devices,
-               (struct udev_enumerate *));
-   UDEV_SYMBOL(struct udev_list_entry *, udev_enumerate_get_list_entry,
-               (struct udev_enumerate *));
-   UDEV_SYMBOL(void, udev_enumerate_unref,
-               (struct udev_enumerate *));
-   UDEV_SYMBOL(struct udev_list_entry *, udev_list_entry_get_next,
-               (struct udev_list_entry *));
-   UDEV_SYMBOL(const char *, udev_list_entry_get_name,
-               (struct udev_list_entry *));
-   UDEV_SYMBOL(struct udev_device *, udev_device_new_from_syspath,
-               (struct udev *, const char *));
-   UDEV_SYMBOL(const char *, udev_device_get_property_value,
-               (struct udev_device *, const char *));
-   UDEV_SYMBOL(const char *, udev_device_get_devnode,
-               (struct udev_device *));
-   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
-               (struct udev_device *));
-
-   e = udev_enumerate_new(udev);
-   udev_enumerate_add_match_subsystem(e, "drm");
-   udev_enumerate_add_match_sysname(e, "render*");
-
-   udev_enumerate_scan_devices(e);
-   udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
-      path = udev_list_entry_get_name(entry);
-      device = udev_device_new_from_syspath(udev, path);
-      if (!device)
-         continue;
-      id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG");
-      if (id_path_tag_tmp) {
-         if ((!another_tag && !strcmp(id_path_tag, id_path_tag_tmp)) ||
-             (another_tag && strcmp(id_path_tag, id_path_tag_tmp))) {
-            found = 1;
-            break;
-         }
-      }
-      udev_device_unref(device);
-   }
-
-   udev_enumerate_unref(e);
-
-   if (found) {
-      path_res = strdup(udev_device_get_devnode(device));
-      udev_device_unref(device);
-      return path_res;
-   }
-   return NULL;
-}
-
-static char *
-get_id_path_tag_from_fd(struct udev *udev, int fd)
-{
-   struct udev_device *device;
-   const char *id_path_tag_tmp;
-   char *id_path_tag;
-   UDEV_SYMBOL(const char *, udev_device_get_property_value,
-               (struct udev_device *, const char *));
-   UDEV_SYMBOL(struct udev_device *, udev_device_unref,
-               (struct udev_device *));
-
-   device = udev_device_new_from_fd(udev, fd);
-   if (!device)
-      return NULL;
-
-   id_path_tag_tmp = udev_device_get_property_value(device, "ID_PATH_TAG");
-   if (!id_path_tag_tmp)
-      return NULL;
-
-   id_path_tag = strdup(id_path_tag_tmp);
-
-   udev_device_unref(device);
-   return id_path_tag;
-}
-
+#if defined(HAVE_LIBDRM)
 #ifdef USE_DRICONF
 static const char __driConfigOptionsLoader[] =
 DRI_CONF_BEGIN
@@ -321,17 +232,60 @@ static char *loader_get_dri_config_device_id(void)
 }
 #endif
 
+static char *drm_construct_id_path_tag(drmDevicePtr device)
+{
+/* Length of "pci-xxxx_xx_xx_x\0" */
+#define PCI_ID_PATH_TAG_LENGTH 17
+   char *tag = NULL;
+
+   if (device->bustype == DRM_BUS_PCI) {
+        tag = calloc(PCI_ID_PATH_TAG_LENGTH, sizeof(char));
+        if (tag == NULL)
+            return NULL;
+
+        snprintf(tag, PCI_ID_PATH_TAG_LENGTH, "pci-%04x_%02x_%02x_%1u",
+                 device->businfo.pci->domain, device->businfo.pci->bus,
+                 device->businfo.pci->dev, device->businfo.pci->func);
+   }
+   return tag;
+}
+
+static bool drm_device_matches_tag(drmDevicePtr device, const char *prime_tag)
+{
+   char *tag = drm_construct_id_path_tag(device);
+   int ret;
+
+   if (tag == NULL)
+      return false;
+
+   ret = strcmp(tag, prime_tag);
+
+   free(tag);
+   return ret == 0;
+}
+
+static char *drm_get_id_path_tag_for_fd(int fd)
+{
+   drmDevicePtr device;
+   char *tag;
+
+   if (drmGetDevice(fd, &device) != 0)
+       return NULL;
+
+   tag = drm_construct_id_path_tag(device);
+   drmFreeDevice(&device);
+   return tag;
+}
+
 int loader_get_user_preferred_fd(int default_fd, int *different_device)
 {
-   struct udev *udev;
+/* Arbitrary "maximum" value of drm devices. */
+#define MAX_DRM_DEVICES 32
    const char *dri_prime = getenv("DRI_PRIME");
-   char *prime = NULL;
-   int is_different_device = 0, fd = default_fd;
-   char *default_device_id_path_tag;
-   char *device_name = NULL;
-   char another_tag = 0;
-   UDEV_SYMBOL(struct udev *, udev_new, (void));
-   UDEV_SYMBOL(struct udev *, udev_unref, (struct udev *));
+   char *default_tag, *prime = NULL;
+   drmDevicePtr devices[MAX_DRM_DEVICES];
+   int i, num_devices, fd;
+   bool found = false;
 
    if (dri_prime)
       prime = strdup(dri_prime);
@@ -345,55 +299,66 @@ int loader_get_user_preferred_fd(int default_fd, int *different_device)
       return default_fd;
    }
 
-   udev = udev_new();
-   if (!udev)
-      goto prime_clean;
+   default_tag = drm_get_id_path_tag_for_fd(default_fd);
+   if (default_tag == NULL)
+      goto err;
 
-   default_device_id_path_tag = get_id_path_tag_from_fd(udev, default_fd);
-   if (!default_device_id_path_tag)
-      goto udev_clean;
+   num_devices = drmGetDevices(devices, MAX_DRM_DEVICES);
+   if (num_devices < 0)
+      goto err;
 
-   is_different_device = 1;
    /* two format are supported:
     * "1": choose any other card than the card used by default.
     * id_path_tag: (for example "pci-0000_02_00_0") choose the card
     * with this id_path_tag.
     */
    if (!strcmp(prime,"1")) {
-      free(prime);
-      prime = strdup(default_device_id_path_tag);
-      /* request a card with a different card than the default card */
-      another_tag = 1;
-   } else if (!strcmp(default_device_id_path_tag, prime))
-      /* we are to get a new fd (render-node) of the same device */
-      is_different_device = 0;
-
-   device_name = get_render_node_from_id_path_tag(udev,
-                                                  prime,
-                                                  another_tag);
-   if (device_name == NULL) {
-      is_different_device = 0;
-      goto default_device_clean;
+      /* Hmm... detection for 2-7 seems to be broken. Oh well ...
+       * Pick the first render device that is not our own.
+       */
+      for (i = 0; i < num_devices; i++) {
+         if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
+             !drm_device_matches_tag(devices[i], default_tag)) {
+
+            found = true;
+            break;
+         }
+      }
+   } else {
+      for (i = 0; i < num_devices; i++) {
+         if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
+            drm_device_matches_tag(devices[i], prime)) {
+
+            found = true;
+            break;
+         }
+      }
    }
 
-   fd = loader_open_device(device_name);
-   if (fd >= 0) {
-      close(default_fd);
-   } else {
-      fd = default_fd;
-      is_different_device = 0;
+   if (!found) {
+      drmFreeDevices(devices, num_devices);
+      goto err;
    }
-   free(device_name);
 
- default_device_clean:
-   free(default_device_id_path_tag);
- udev_clean:
-   udev_unref(udev);
- prime_clean:
-   free(prime);
+   fd = loader_open_device(devices[i]->nodes[DRM_NODE_RENDER]);
+   drmFreeDevices(devices, num_devices);
+   if (fd < 0)
+      goto err;
 
-   *different_device = is_different_device;
+   close(default_fd);
+
+   *different_device = !!strcmp(default_tag, prime);
+
+   free(default_tag);
+   free(prime);
    return fd;
+
+ err:
+   *different_device = 0;
+
+   free(default_tag);
+   free(prime);
+   return default_fd;
 }
 #else
 int loader_get_user_preferred_fd(int default_fd, int *different_device)