Hi Jakub,
Here is the first patch for adding plugins support in libgomp - could you please
take a look at it?
I changed configure.ac to add dl-library, but I am not sure if I regenerated all
related to configure files properly. I'd appreciate your help here, if I did
it wrong.
Any feedback is welcome too.
Thanks, Michael
---
libgomp/configure | 46 +++++++++++++++
libgomp/configure.ac | 2 +
libgomp/target.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 203 insertions(+)
diff --git a/libgomp/configure b/libgomp/configure
index 238b1af..2086fdb 100755
--- a/libgomp/configure
+++ b/libgomp/configure
@@ -15046,6 +15046,52 @@ fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlsym in -ldl" >&5
+$as_echo_n "checking for dlsym in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlsym+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlsym ();
+int
+main ()
+{
+return dlsym ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlsym=yes
+else
+ ac_cv_lib_dl_dlsym=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlsym" >&5
+$as_echo "$ac_cv_lib_dl_dlsym" >&6; }
+if test "x$ac_cv_lib_dl_dlsym" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBDL 1
+_ACEOF
+
+ LIBS="-ldl $LIBS"
+
+fi
+
+
# Check for functions needed.
for ac_func in getloadavg clock_gettime strtoull
do :
diff --git a/libgomp/configure.ac b/libgomp/configure.ac
index d87ed29..1c78239 100644
--- a/libgomp/configure.ac
+++ b/libgomp/configure.ac
@@ -193,6 +193,8 @@ AC_LINK_IFELSE(
[],
[AC_MSG_ERROR([Pthreads are required to build libgomp])])])
+AC_CHECK_LIB(dl, dlsym)
+
# Check for functions needed.
AC_CHECK_FUNCS(getloadavg clock_gettime strtoull)
diff --git a/libgomp/target.c b/libgomp/target.c
index 0a874d4..73f656c 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -28,6 +28,45 @@
#include "libgomp.h"
#include <stdlib.h>
#include <string.h>
+#include <dirent.h>
+
+#ifdef HAVE_DLFCN_H
+# include <dlfcn.h>
+#endif
+
+static void gomp_target_init (void);
+
+/* This structure describes accelerator device.
+ It contains name of the corresponding libgomp plugin, function handlers for
+ interaction with the device, ID-number of the device, and information about
+ mapped memory. */
+struct gomp_device_descr
+{
+ /* This is the ID number of device. It could be specified in DEVICE-clause
of
+ TARGET construct. */
+ int id;
+
+ /* Plugin file name. */
+ char plugin_name[PATH_MAX];
+
+ /* Plugin file handler. */
+ void *plugin_handle;
+
+ /* Function handlers. */
+ bool (*device_available_func) (void);
+
+ /* Information about mapping. Not implemented yet. */
+ /* SplayTree map_info; */
+};
+
+/* Array of descriptors of all available devices. */
+static struct gomp_device_descr *devices;
+
+/* Total number of available devices. */
+static int num_devices;
+
+static pthread_once_t gomp_is_initialized = PTHREAD_ONCE_INIT;
+
static int
resolve_device (int device)
@@ -49,6 +88,7 @@ GOMP_target (int device, void (*fn) (void *), const char
*fnname,
size_t mapnum, void **hostaddrs, size_t *sizes,
unsigned char *kinds)
{
+ (void) pthread_once (&gomp_is_initialized, gomp_target_init);
if (resolve_device (device) == -1)
{
fn (hostaddrs);
@@ -60,6 +100,7 @@ void
GOMP_target_data (int device, size_t mapnum, void **hostaddrs, size_t *sizes,
unsigned char *kinds)
{
+ (void) pthread_once (&gomp_is_initialized, gomp_target_init);
if (resolve_device (device) == -1)
return;
}
@@ -73,6 +114,7 @@ void
GOMP_target_update (int device, size_t mapnum, void **hostaddrs, size_t *sizes,
unsigned char *kinds)
{
+ (void) pthread_once (&gomp_is_initialized, gomp_target_init);
if (resolve_device (device) == -1)
return;
}
@@ -81,3 +123,116 @@ void
GOMP_teams (unsigned int num_teams, unsigned int thread_limit)
{
}
+
+
+#ifdef HAVE_DLFCN_H
+/* This function checks if the given string FNAME matches
+ "libgomp-plugin-*.so.1". */
+static bool
+gomp_check_plugin_file_name (const char *fname)
+{
+ const char *prefix = "libgomp-plugin-";
+ const char *suffix = ".so.1";
+ if (!fname)
+ return false;
+ if (strncmp (fname, prefix, strlen (prefix)) != 0)
+ return false;
+ if (strncmp (fname + strnlen (fname, NAME_MAX + 1) - strlen (suffix),
+ suffix,
+ strlen (suffix)) != 0)
+ return false;
+ return true;
+}
+
+/* This function tries to load plugin for DEVICE. Name of plugin should be
+ stored in PLUGIN_NAME field.
+ Plugin handle and handles of the found functions are stored in the
+ corresponding fields of DEVICE.
+ The function returns TRUE on success and FALSE otherwise. */
+static bool
+gomp_load_plugin_for_device (struct gomp_device_descr *device)
+{
+ if (!device || !device->plugin_name)
+ return false;
+
+ device->plugin_handle = dlopen (device->plugin_name, RTLD_LAZY);
+ if (!device->plugin_handle)
+ return false;
+
+ /* Clear any existing error. */
+ dlerror ();
+
+ /* Check if all required functions are available in the plugin and store
+ their handlers.
+ TODO: check for other routines as well. */
+ *(void **) (&device->device_available_func) = dlsym (device->plugin_handle,
+ "device_available");
+ if (dlerror () != NULL)
+ {
+ dlclose (device->plugin_handle);
+ return false;
+ }
+
+ return true;
+}
+
+/* This functions scans folder, specified in environment variable
+ LIBGOMP_PLUGIN_PATH, and loads all suitable libgomp plugins from this
folder.
+ For a plugin to be suitable, its name should be "libgomp-plugin-*.so.1" and
+ it should implement a certain set of functions.
+ Result of this function is properly initialized variable NUM_DEVICES and
+ array DEVICES, containing all plugins and their callback handles. */
+static void
+gomp_find_available_plugins (void)
+{
+ char *plugin_path = NULL;
+ DIR *dir = NULL;
+ struct dirent *ent;
+
+ num_devices = 0;
+ devices = NULL;
+
+ plugin_path = getenv ("LIBGOMP_PLUGIN_PATH");
+ if (!plugin_path)
+ return;
+
+ dir = opendir (plugin_path);
+ if (!dir)
+ return;
+
+ while ((ent = readdir (dir)) != NULL)
+ {
+ struct gomp_device_descr current_device;
+ if (!gomp_check_plugin_file_name (ent->d_name))
+ continue;
+ strncpy (current_device.plugin_name, plugin_path, PATH_MAX);
+ strcat (current_device.plugin_name, "/");
+ strcat (current_device.plugin_name, ent->d_name);
+ if (!gomp_load_plugin_for_device (¤t_device))
+ continue;
+ devices = realloc (devices, (num_devices + 1)
+ * sizeof (struct gomp_device_descr));
+
+ devices[num_devices] = current_device;
+ devices[num_devices].id = num_devices + 1;
+ num_devices++;
+ }
+ closedir (dir);
+}
+
+/* This function initializes runtime needed for offloading.
+ It loads plugins, sets up a connection with devices, etc. */
+static void
+gomp_target_init (void)
+{
+ gomp_find_available_plugins ();
+}
+
+#else /* HAVE_DLFCN_H */
+/* If dlfcn.h is unavailable we always fallback to host execution.
+ GOMP_target* routines are just stubs for this case. */
+static void
+gomp_target_init (void)
+{
+}
+#endif /* HAVE_DLFCN_H */
--
1.8.3.1