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