From 85b6f36ca5238dd3fec7c5fcacb8b7074ce53c8e Mon Sep 17 00:00:00 2001 From: Gary Wong Date: Wed, 21 May 2014 21:07:42 -0600 Subject: loader: add optional /sys filesystem method for PCI identification. Introduce a simple PCI identification method of looking up the answer the /sys filesystem (available on Linux). Attempted after libudev, but before DRM. Disabled by default (available only when the --enable-sysfs configure option is specified). Signed-off-by: Gary Wong Acked-by: Emil Velikov Backported to mesa-10.2.8 by Ian Stakenvicius --- a/configure.ac +++ b/configure.ac @@ -870,14 +870,21 @@ fi case "$host_os" in linux*) - need_libudev=yes ;; + need_pci_id=yes ;; *) - need_libudev=no ;; + need_pci_id=no ;; esac PKG_CHECK_MODULES([LIBUDEV], [libudev >= $LIBUDEV_REQUIRED], have_libudev=yes, have_libudev=no) +AC_ARG_ENABLE([sysfs], + [AS_HELP_STRING([--enable-sysfs], + [enable /sys PCI identification @<:@default=disabled@:>@])], + [have_sysfs="$enableval"], + [have_sysfs=no] +) + if test "x$enable_dri" = xyes; then if test "$enable_static" = yes; then AC_MSG_ERROR([Cannot use static libraries for DRI drivers]) @@ -973,8 +980,15 @@ xyesno) ;; esac +have_pci_id=no if test "$have_libudev" = yes; then DEFINES="$DEFINES -DHAVE_LIBUDEV" + have_pci_id=yes +fi + +if test "$have_sysfs" = yes; then + DEFINES="$DEFINES -DHAVE_SYSFS" + have_pci_id=yes fi # This is outside the case (above) so that it is invoked even for non-GLX @@ -1076,8 +1090,8 @@ if test "x$enable_dri" = xyes; then DEFINES="$DEFINES -DHAVE_DRI3" fi - if test "x$have_libudev" != xyes; then - AC_MSG_ERROR([libudev-dev required for building DRI]) + if test "x$have_pci_id" != xyes; then + AC_MSG_ERROR([libudev-dev or sysfs required for building DRI]) fi case "$host_cpu" in @@ -1252,8 +1266,8 @@ if test "x$enable_gbm" = xauto; then esac fi if test "x$enable_gbm" = xyes; then - if test "x$need_libudev$have_libudev" = xyesno; then - AC_MSG_ERROR([gbm requires udev >= $LIBUDEV_REQUIRED]) + if test "x$need_pci_id$have_pci_id" = xyesno; then + AC_MSG_ERROR([gbm requires udev >= $LIBUDEV_REQUIRED or sysfs]) fi if test "x$enable_dri" = xyes; then @@ -1271,7 +1285,7 @@ if test "x$enable_gbm" = xyes; then fi fi AM_CONDITIONAL(HAVE_GBM, test "x$enable_gbm" = xyes) -if test "x$need_libudev" = xyes; then +if test "x$need_pci_id$have_libudev" = xyesyes; then GBM_PC_REQ_PRIV="libudev >= $LIBUDEV_REQUIRED" else GBM_PC_REQ_PRIV="" @@ -1560,9 +1574,9 @@ for plat in $egl_platforms; do ;; esac - case "$plat$need_libudev$have_libudev" in + case "$plat$need_pci_id$have_pci_id" in waylandyesno|drmyesno) - AC_MSG_ERROR([cannot build $plat platform without udev >= $LIBUDEV_REQUIRED]) ;; + AC_MSG_ERROR([cannot build $plat platform without udev >= $LIBUDEV_REQUIRED or sysfs]) ;; esac done @@ -1843,8 +1857,8 @@ gallium_require_llvm() { gallium_require_drm_loader() { if test "x$enable_gallium_loader" = xyes; then - if test "x$need_libudev$have_libudev" = xyesno; then - AC_MSG_ERROR([Gallium drm loader requires libudev >= $LIBUDEV_REQUIRED]) + if test "x$need_pci_id$have_pci_id" = xyesno; then + AC_MSG_ERROR([Gallium drm loader requires libudev >= $LIBUDEV_REQUIRED or sysfs]) fi if test "x$have_libdrm" != xyes; then AC_MSG_ERROR([Gallium drm loader requires libdrm >= $LIBDRM_REQUIRED]) --- a/src/loader/loader.c 2014-09-19 13:03:22.000000000 -0400 +++ b/src/loader/loader.c 2014-11-25 12:46:51.412249717 -0500 @@ -71,6 +71,10 @@ #include #include #endif +#ifdef HAVE_SYSFS +#include +#include +#endif #include "loader.h" #ifndef __NOT_HAVE_DRM_H @@ -113,8 +117,8 @@ udev_handle = dlopen("libudev.so.0", RTLD_LOCAL | RTLD_LAZY); if (!udev_handle) { - log_(_LOADER_FATAL, "Couldn't dlopen libudev.so.1 or libudev.so.0, " - "driver detection may be broken.\n"); + log_(_LOADER_WARNING, "Couldn't dlopen libudev.so.1 or " + "libudev.so.0, driver detection may be broken.\n"); } } } @@ -122,16 +126,19 @@ return udev_handle; } +static int dlsym_failed = 0; + static void * -asserted_dlsym(void *dlopen_handle, const char *name) +checked_dlsym(void *dlopen_handle, const char *name) { void *result = dlsym(dlopen_handle, name); - assert(result); + if (!result) + dlsym_failed = 1; return result; } #define UDEV_SYMBOL(ret, name, args) \ - ret (*name) args = asserted_dlsym(udev_dlopen_handle(), #name); + ret (*name) args = checked_dlsym(udev_dlopen_handle(), #name); static inline struct udev_device * @@ -142,6 +149,9 @@ UDEV_SYMBOL(struct udev_device *, udev_device_new_from_devnum, (struct udev *udev, char type, dev_t devnum)); + if (dlsym_failed) + return NULL; + if (fstat(fd, &buf) < 0) { log_(_LOADER_WARNING, "MESA-LOADER: failed to stat fd %d\n", fd); return NULL; @@ -157,8 +167,8 @@ return device; } -int -loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) +static int +libudev_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) { struct udev *udev = NULL; struct udev_device *device = NULL, *parent; @@ -174,6 +184,9 @@ *chip_id = -1; + if (dlsym_failed) + return 0; + udev = udev_new(); device = udev_device_new_from_fd(udev, fd); if (!device) @@ -201,16 +214,76 @@ return (*chip_id >= 0); } +#endif + +#if defined(HAVE_SYSFS) +static int +dev_node_from_fd(int fd, unsigned int *maj, unsigned int *min) +{ + struct stat buf; + + if (fstat(fd, &buf) < 0) { + log_(_LOADER_WARNING, "MESA-LOADER: failed to stat fd %d\n", fd); + return -1; + } + + if (!S_ISCHR(buf.st_mode)) { + log_(_LOADER_WARNING, "MESA-LOADER: fd %d not a character device\n", fd); + return -1; + } + + *maj = major(buf.st_rdev); + *min = minor(buf.st_rdev); + + return 0; +} + +static int +sysfs_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) +{ + unsigned int maj, min; + FILE *f; + char buf[0x40]; -#elif !defined(__NOT_HAVE_DRM_H) + if (dev_node_from_fd(fd, &maj, &min) < 0) { + *chip_id = -1; + return 0; + } + snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/vendor", maj, min); + if (!(f = fopen(buf, "r"))) { + *chip_id = -1; + return 0; + } + if (fscanf(f, "%x", vendor_id) != 1) { + *chip_id = -1; + fclose(f); + return 0; + } + fclose(f); + snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/device/device", maj, min); + if (!(f = fopen(buf, "r"))) { + *chip_id = -1; + return 0; + } + if (fscanf(f, "%x", chip_id) != 1) { + *chip_id = -1; + fclose(f); + return 0; + } + fclose(f); + return 1; +} +#endif + +#if !defined(__NOT_HAVE_DRM_H) /* for i915 */ #include /* for radeon */ #include -int -loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) +static int +drm_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) { drmVersionPtr version; @@ -272,23 +345,33 @@ return (*chip_id >= 0); } +#endif -#else int loader_get_pci_id_for_fd(int fd, int *vendor_id, int *chip_id) { +#if HAVE_LIBUDEV + if (libudev_get_pci_id_for_fd(fd, vendor_id, chip_id)) + return 1; +#endif +#if HAVE_SYSFS + if (sysfs_get_pci_id_for_fd(fd, vendor_id, chip_id)) + return 1; +#endif +#if !defined(__NOT_HAVE_DRM_H) + if (drm_get_pci_id_for_fd(fd, vendor_id, chip_id)) + return 1; +#endif return 0; } -#endif - -char * -loader_get_device_name_for_fd(int fd) +#ifdef HAVE_LIBUDEV +static char * +libudev_get_device_name_for_fd(int fd) { char *device_name = NULL; -#ifdef HAVE_LIBUDEV struct udev *udev; struct udev_device *device; const char *const_device_name; @@ -312,9 +395,66 @@ out: udev_device_unref(device); udev_unref(udev); + return device_name; +} #endif + + +#if HAVE_SYSFS +static char * +sysfs_get_device_name_for_fd(int fd) +{ + char *device_name = NULL; + unsigned int maj, min; + FILE *f; + char buf[0x40]; + static const char match[9] = "\0DEVNAME="; + int expected = 1; + + if (dev_node_from_fd(fd, &maj, &min) < 0) + return NULL; + + snprintf(buf, sizeof(buf), "/sys/dev/char/%d:%d/uevent", maj, min); + if (!(f = fopen(buf, "r"))) + return NULL; + + while (expected < sizeof(match)) { + int c = getc(f); + + if (c == EOF) { + fclose(f); + return NULL; + } else if (c == match[expected] ) + expected++; + else + expected = 0; + } + + strcpy(buf, "/dev/"); + if (fgets(buf + 5, sizeof(buf) - 5, f)) + device_name = strdup(buf); + + fclose(f); return device_name; } +#endif + + +char * +loader_get_device_name_for_fd(int fd) +{ + char *result = NULL; + +#if HAVE_LIBUDEV + if ((result = libudev_get_device_name_for_fd(fd))) + return result; +#endif +#if HAVE_SYSFS + if ((result = sysfs_get_device_name_for_fd(fd))) + return result; +#endif + return result; +} char * loader_get_driver_for_fd(int fd, unsigned driver_types)