[Mesa-dev] [PATCH v2] gbm: add support for loading third-party backend (v2)

Qiang Yu Qiang.Yu at amd.com
Fri Apr 7 07:28:04 UTC 2017


V2:
  1. export gbmint.h and test backend/libgbm ABI compatible
  2. drop GBM_BACKEND_DIR, specify backend path in config file
  3. add GBM_CONFIG_DIR for config file
  4. add per backend priority
  5. take care of thread safe

Third-party can put their backend to a directory and create a
/etc/gbm.conf.d/*.conf file which contains the backend so file
path to overwrite the default builtin DRI backend.

The /etc/gbm.conf.d/*.conf will be sorted and the backends added
will be tried one-by-one until one can successfully create a gbm
device. The default DRI backend is tried at last.

/etc/gbm.conf.d/*.conf can also contain a "priority" field.
backend with bigger number will be tried first. Default priority
is 1000 inlcuding the builtin backend.

GBM config file example:
lib:/opt/amdgpu-pro/lib/gbm/amdgpu.so
priority:2000

People can still use GBM_BACKEND to overwrite the backend try
order.

Signed-off-by: Qiang Yu <Qiang.Yu at amd.com>
---
 configure.ac                   |   9 +++
 src/gbm/Makefile.am            |  11 +++-
 src/gbm/backends/dri/gbm_dri.c |   1 +
 src/gbm/main/backend.c         | 130 ++++++++++++++++++++++++++++++++++++++---
 src/gbm/main/gbmint.h          |   7 ++-
 5 files changed, 146 insertions(+), 12 deletions(-)

diff --git a/configure.ac b/configure.ac
index 70885fb..792d1b2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2221,6 +2221,15 @@ AC_ARG_WITH([d3d-libdir],
     [D3D_DRIVER_INSTALL_DIR="${libdir}/d3d"])
 AC_SUBST([D3D_DRIVER_INSTALL_DIR])
 
+dnl Directory for GBM
+
+AC_ARG_WITH([gbm-configdir],
+    [AS_HELP_STRING([--with-gbm-configdir=DIR],
+        [directory for the GBM configs @<:@/etc/gbm.conf.d@:>@])],
+    [GBM_CONFIG_DIR="$withval"],
+    [GBM_CONFIG_DIR='/etc/gbm.conf.d'])
+AC_SUBST([GBM_CONFIG_DIR])
+
 dnl
 dnl r300 doesn't strictly require LLVM, but for performance reasons we
 dnl highly recommend LLVM usage. So require it at least on x86 and x86_64
diff --git a/src/gbm/Makefile.am b/src/gbm/Makefile.am
index e34c1d4..2e29931 100644
--- a/src/gbm/Makefile.am
+++ b/src/gbm/Makefile.am
@@ -8,11 +8,17 @@ AM_CFLAGS = \
 	-I$(top_srcdir)/src/loader \
 	-I$(top_srcdir)/src/gbm/main \
 	$(DLOPEN_CFLAGS) \
+	$(PTHREAD_CFLAGS) \
 	$(DEFINES) \
 	$(VISIBILITY_CFLAGS)
 
+AM_CFLAGS += \
+	-DGBM_CONFIG_DIR='"$(GBM_CONFIG_DIR)"'
+
 lib_LTLIBRARIES = libgbm.la
-include_HEADERS = main/gbm.h
+include_HEADERS = \
+	main/gbm.h \
+	main/gbmint.h
 
 libgbm_la_SOURCES = \
 	$(gbm_core_FILES)
@@ -25,7 +31,8 @@ libgbm_la_LDFLAGS = \
 
 libgbm_la_LIBADD = \
 	$(top_builddir)/src/loader/libloader.la \
-	$(DLOPEN_LIBS)
+	$(DLOPEN_LIBS) \
+	$(PTHREAD_LIBS)
 
 if HAVE_PLATFORM_WAYLAND
 AM_CPPFLAGS = -DHAVE_WAYLAND_PLATFORM
diff --git a/src/gbm/backends/dri/gbm_dri.c b/src/gbm/backends/dri/gbm_dri.c
index 189a8fc..f8d90bd 100644
--- a/src/gbm/backends/dri/gbm_dri.c
+++ b/src/gbm/backends/dri/gbm_dri.c
@@ -1393,4 +1393,5 @@ err_dri:
 struct gbm_backend gbm_dri_backend = {
    .backend_name = "dri",
    .create_device = dri_device_create,
+   .abi_version = GBM_BACKEND_ABI_VERSION,
 };
diff --git a/src/gbm/main/backend.c b/src/gbm/main/backend.c
index 37ec9c1..687fda6 100644
--- a/src/gbm/main/backend.c
+++ b/src/gbm/main/backend.c
@@ -25,13 +25,20 @@
  *    Benjamin Franzke <benjaminfranzke at googlemail.com>
  */
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
 
+#include <dirent.h>
+#include <fnmatch.h>
+#include <dlfcn.h>
+#include <pthread.h>
+
 #include "backend.h"
+#include "gbmint.h"
 
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
 
@@ -40,34 +47,69 @@ extern const struct gbm_backend gbm_dri_backend;
 struct backend_desc {
    const char *name;
    const struct gbm_backend *builtin;
+   int priority;
+   bool bad;
 };
 
-static const struct backend_desc backends[] = {
-   { "gbm_dri.so", &gbm_dri_backend },
+#define GBM_BACKEND_DEFAULT_PRIORITY 1000
+
+static const struct backend_desc builtin_backends[] = {
+   {
+      .name = "gbm_dri.so",
+      .builtin = &gbm_dri_backend,
+      .priority = GBM_BACKEND_DEFAULT_PRIORITY,
+   },
 };
 
+#define MAX_BACKENDS 16
+static struct backend_desc backends[MAX_BACKENDS];
+static int num_backends = 0;
+
 static const void *
-load_backend(const struct backend_desc *backend)
+load_backend(struct backend_desc *backend)
 {
    const void *init = NULL;
+   static pthread_mutex_t backends_mutex = PTHREAD_MUTEX_INITIALIZER;
 
    if (backend == NULL)
       return NULL;
 
+   pthread_mutex_lock(&backends_mutex);
+
    if (backend->builtin) {
       init = backend->builtin;
    }
+   else if (!backend->bad) {
+      void *module;
+
+      module = dlopen(backend->name, RTLD_NOW | RTLD_GLOBAL);
+      if (module) {
+         backend->builtin = dlsym(module, "gbm_backend");
+         if (backend->builtin &&
+             backend->builtin->abi_version == GBM_BACKEND_ABI_VERSION)
+            init = backend->builtin;
+         else {
+            backend->builtin = NULL;
+            backend->bad = true;
+            dlclose(module);
+         }
+      }
+      else
+         backend->bad = true;
+   }
+
+   pthread_mutex_unlock(&backends_mutex);
 
    return init;
 }
 
-static const struct backend_desc *
+static struct backend_desc *
 find_backend(const char *name)
 {
-   const struct backend_desc *backend = NULL;
+   struct backend_desc *backend = NULL;
    unsigned i;
 
-   for (i = 0; i < ARRAY_SIZE(backends); ++i) {
+   for (i = 0; i < num_backends; ++i) {
       if (strcmp(backends[i].name, name) == 0) {
          backend = &backends[i];
          break;
@@ -77,6 +119,75 @@ find_backend(const char *name)
    return backend;
 }
 
+static int
+scandir_filter(const struct dirent *ent)
+{
+    if (ent->d_type != DT_REG && ent->d_type != DT_LNK &&
+        ent->d_type != DT_UNKNOWN)
+       return 0;
+
+    if (fnmatch("*.conf", ent->d_name, 0))
+       return 0;
+
+    return 1;
+}
+
+static void
+init_backends(void)
+{
+   int i, j, count;
+   struct dirent **entries = NULL;
+
+   count = scandir(GBM_CONFIG_DIR, &entries, scandir_filter, alphasort);
+   for (i = 0; i < count; i++) {
+      char line[PATH_MAX];
+      FILE *file;
+
+      snprintf(line, PATH_MAX, "%s/%s", GBM_CONFIG_DIR, entries[i]->d_name);
+      if ((file = fopen(line, "r"))) {
+         char *name = NULL;
+         int p = GBM_BACKEND_DEFAULT_PRIORITY;
+
+         while (fgets(line, PATH_MAX, file)) {
+            int n = strlen(line);
+            if (line[n - 1] == '\n')
+               line[n - 1] = '\0';
+
+            if (!strncmp("lib:", line, 4) && !fnmatch("*.so", line + 4, 0))
+               name = strdup(line + 4);
+            else if (!strncmp("priority:", line, 9)) {
+               int v = atoi(line + 9);
+               if (v > 0)
+                  p = v;
+            }
+         }
+         fclose(file);
+
+         if (name && num_backends < MAX_BACKENDS - ARRAY_SIZE(builtin_backends)) {
+            backends[num_backends].name = name;
+            backends[num_backends].builtin = NULL;
+            backends[num_backends].priority = p;
+            backends[num_backends].bad = false;
+            num_backends++;
+         }
+      }
+   }
+
+   memcpy(backends + num_backends, builtin_backends, sizeof(builtin_backends));
+   num_backends += ARRAY_SIZE(builtin_backends);
+
+   /* sort backends by priority */
+   for (i = num_backends - 1; i > 0; i--) {
+      for (j = i; j > 0; j--) {
+         if (backends[j].priority > backends[j - 1].priority) {
+            struct backend_desc t = backends[j - 1];
+            backends[j - 1] = backends[j];
+            backends[j] = t;
+         }
+      }
+   }
+}
+
 struct gbm_device *
 _gbm_create_device(int fd)
 {
@@ -84,6 +195,9 @@ _gbm_create_device(int fd)
    struct gbm_device *dev = NULL;
    unsigned i;
    const char *b;
+   static pthread_once_t backends_is_initialized = PTHREAD_ONCE_INIT;
+
+   pthread_once(&backends_is_initialized, init_backends);
 
    b = getenv("GBM_BACKEND");
    if (b)
@@ -92,13 +206,13 @@ _gbm_create_device(int fd)
    if (backend)
       dev = backend->create_device(fd);
 
-   for (i = 0; i < ARRAY_SIZE(backends) && dev == NULL; ++i) {
+   for (i = 0; i < num_backends && dev == NULL; ++i) {
       backend = load_backend(&backends[i]);
       if (backend == NULL)
          continue;
 
       dev = backend->create_device(fd);
    }
-   
+
    return dev;
 }
diff --git a/src/gbm/main/gbmint.h b/src/gbm/main/gbmint.h
index c27a7a5..0fde5f6 100644
--- a/src/gbm/main/gbmint.h
+++ b/src/gbm/main/gbmint.h
@@ -25,8 +25,8 @@
  *    Benjamin Franzke <benjaminfranzke at googlemail.com>
  */
 
-#ifndef INTERNAL_H_
-#define INTERNAL_H_
+#ifndef _GBM_INTERNAL_H_
+#define _GBM_INTERNAL_H_
 
 #include "gbm.h"
 #include <sys/stat.h>
@@ -125,7 +125,10 @@ struct gbm_surface {
    };
 };
 
+#define GBM_BACKEND_ABI_VERSION 1
+
 struct gbm_backend {
+   uint64_t abi_version;
    const char *backend_name;
    struct gbm_device *(*create_device)(int fd);
 };
-- 
2.7.4



More information about the mesa-dev mailing list