From: Jason Gunthorpe <jgg@xxxxxxxxxxxx> This is preparation for removing this code when statically linking. Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxxxx> --- libibverbs/CMakeLists.txt | 1 + libibverbs/dynamic_driver.c | 239 ++++++++++++++++++++++++++++++++++++ libibverbs/ibverbs.h | 1 + libibverbs/init.c | 206 ------------------------------- 4 files changed, 241 insertions(+), 206 deletions(-) create mode 100644 libibverbs/dynamic_driver.c diff --git a/libibverbs/CMakeLists.txt b/libibverbs/CMakeLists.txt index 738e5e15aaf00e..33e86a409708b0 100644 --- a/libibverbs/CMakeLists.txt +++ b/libibverbs/CMakeLists.txt @@ -38,6 +38,7 @@ rdma_library(ibverbs "${CMAKE_CURRENT_BINARY_DIR}/libibverbs.map" compat-1_0.c device.c dummy_ops.c + dynamic_driver.c enum_strs.c init.c marshall.c diff --git a/libibverbs/dynamic_driver.c b/libibverbs/dynamic_driver.c new file mode 100644 index 00000000000000..7a8f95b9e74f32 --- /dev/null +++ b/libibverbs/dynamic_driver.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2006 Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2018 Mellanox Technologies, Ltd. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#define _GNU_SOURCE + +#include <dlfcn.h> +#include <stdio.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <ccan/list.h> + +#include "ibverbs.h" + +struct ibv_driver_name { + struct list_node entry; + char *name; +}; + +static LIST_HEAD(driver_name_list); + +static void read_config_file(const char *path) +{ + FILE *conf; + char *line = NULL; + char *config; + char *field; + size_t buflen = 0; + ssize_t len; + + conf = fopen(path, "r" STREAM_CLOEXEC); + if (!conf) { + fprintf(stderr, PFX "Warning: couldn't read config file %s.\n", + path); + return; + } + + while ((len = getline(&line, &buflen, conf)) != -1) { + config = line + strspn(line, "\t "); + if (config[0] == '\n' || config[0] == '#') + continue; + + field = strsep(&config, "\n\t "); + + if (strcmp(field, "driver") == 0 && config != NULL) { + struct ibv_driver_name *driver_name; + + config += strspn(config, "\t "); + field = strsep(&config, "\n\t "); + + driver_name = malloc(sizeof(*driver_name)); + if (!driver_name) { + fprintf(stderr, + PFX + "Warning: couldn't allocate driver name '%s'.\n", + field); + continue; + } + + driver_name->name = strdup(field); + if (!driver_name->name) { + fprintf(stderr, + PFX + "Warning: couldn't allocate driver name '%s'.\n", + field); + free(driver_name); + continue; + } + + list_add(&driver_name_list, &driver_name->entry); + } else + fprintf(stderr, + PFX + "Warning: ignoring bad config directive '%s' in file '%s'.\n", + field, path); + } + + if (line) + free(line); + fclose(conf); +} + +static void read_config(void) +{ + DIR *conf_dir; + struct dirent *dent; + char *path; + + conf_dir = opendir(IBV_CONFIG_DIR); + if (!conf_dir) { + fprintf(stderr, + PFX "Warning: couldn't open config directory '%s'.\n", + IBV_CONFIG_DIR); + return; + } + + while ((dent = readdir(conf_dir))) { + struct stat buf; + + if (asprintf(&path, "%s/%s", IBV_CONFIG_DIR, dent->d_name) < + 0) { + fprintf(stderr, + PFX + "Warning: couldn't read config file %s/%s.\n", + IBV_CONFIG_DIR, dent->d_name); + goto out; + } + + if (stat(path, &buf)) { + fprintf(stderr, + PFX + "Warning: couldn't stat config file '%s'.\n", + path); + goto next; + } + + if (!S_ISREG(buf.st_mode)) + goto next; + + read_config_file(path); +next: + free(path); + } + +out: + closedir(conf_dir); +} + +static void load_driver(const char *name) +{ + char *so_name; + void *dlhandle; + + /* If the name is an absolute path then open that path after appending + * the trailer suffix + */ + if (name[0] == '/') { + if (asprintf(&so_name, "%s" VERBS_PROVIDER_SUFFIX, name) < 0) + goto out_asprintf; + dlhandle = dlopen(so_name, RTLD_NOW); + if (!dlhandle) + goto out_dlopen; + free(so_name); + return; + } + + /* If configured with a provider plugin path then try that next */ + if (sizeof(VERBS_PROVIDER_DIR) > 1) { + if (asprintf(&so_name, + VERBS_PROVIDER_DIR "/lib%s" VERBS_PROVIDER_SUFFIX, + name) < 0) + goto out_asprintf; + dlhandle = dlopen(so_name, RTLD_NOW); + free(so_name); + if (dlhandle) + return; + } + + /* Otherwise use the system library search path. This is the historical + * behavior of libibverbs + */ + if (asprintf(&so_name, "lib%s" VERBS_PROVIDER_SUFFIX, name) < 0) + goto out_asprintf; + dlhandle = dlopen(so_name, RTLD_NOW); + if (!dlhandle) + goto out_dlopen; + free(so_name); + return; + +out_asprintf: + fprintf(stderr, PFX "Warning: couldn't load driver '%s'.\n", name); + return; +out_dlopen: + fprintf(stderr, PFX "Warning: couldn't load driver '%s': %s\n", so_name, + dlerror()); + free(so_name); +} + +void load_drivers(void) +{ + struct ibv_driver_name *name, *next_name; + const char *env; + char *list, *env_name; + + read_config(); + + /* Only use drivers passed in through the calling user's environment + * if we're not running setuid. + */ + if (getuid() == geteuid()) { + if ((env = getenv("RDMAV_DRIVERS"))) { + list = strdupa(env); + while ((env_name = strsep(&list, ":;"))) + load_driver(env_name); + } else if ((env = getenv("IBV_DRIVERS"))) { + list = strdupa(env); + while ((env_name = strsep(&list, ":;"))) + load_driver(env_name); + } + } + + list_for_each_safe (&driver_name_list, name, next_name, entry) { + load_driver(name->name); + free(name->name); + free(name); + } +} diff --git a/libibverbs/ibverbs.h b/libibverbs/ibverbs.h index 56be3627c81bdd..4ff4dc588410e6 100644 --- a/libibverbs/ibverbs.h +++ b/libibverbs/ibverbs.h @@ -61,6 +61,7 @@ int ibverbs_get_device_list(struct list_head *list); int ibverbs_init(void); void ibverbs_device_put(struct ibv_device *dev); void ibverbs_device_hold(struct ibv_device *dev); +void load_drivers(void); struct verbs_ex_private { BITMAP_DECLARE(unsupported_ioctls, VERBS_OPS_NUM); diff --git a/libibverbs/init.c b/libibverbs/init.c index a4c5f9e6e65d40..930d91811ca978 100644 --- a/libibverbs/init.c +++ b/libibverbs/init.c @@ -37,7 +37,6 @@ #include <string.h> #include <glob.h> #include <stdio.h> -#include <dlfcn.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> @@ -54,17 +53,11 @@ int abi_ver; -struct ibv_driver_name { - struct list_node entry; - char *name; -}; - struct ibv_driver { struct list_node entry; const struct verbs_device_ops *ops; }; -static LIST_HEAD(driver_name_list); static LIST_HEAD(driver_list); static int try_access_device(const struct verbs_sysfs_dev *sysfs_dev) @@ -192,179 +185,6 @@ void verbs_register_driver(const struct verbs_device_ops *ops) list_add_tail(&driver_list, &driver->entry); } -static void load_driver(const char *name) -{ - char *so_name; - void *dlhandle; - - /* If the name is an absolute path then open that path after appending - the trailer suffix */ - if (name[0] == '/') { - if (asprintf(&so_name, "%s" VERBS_PROVIDER_SUFFIX, name) < 0) - goto out_asprintf; - dlhandle = dlopen(so_name, RTLD_NOW); - if (!dlhandle) - goto out_dlopen; - free(so_name); - return; - } - - /* If configured with a provider plugin path then try that next */ - if (sizeof(VERBS_PROVIDER_DIR) > 1) { - if (asprintf(&so_name, - VERBS_PROVIDER_DIR "/lib%s" VERBS_PROVIDER_SUFFIX, - name) < 0) - goto out_asprintf; - dlhandle = dlopen(so_name, RTLD_NOW); - free(so_name); - if (dlhandle) - return; - } - - /* Otherwise use the system libary search path. This is the historical - behavior of libibverbs */ - if (asprintf(&so_name, "lib%s" VERBS_PROVIDER_SUFFIX, name) < 0) - goto out_asprintf; - dlhandle = dlopen(so_name, RTLD_NOW); - if (!dlhandle) - goto out_dlopen; - free(so_name); - return; - -out_asprintf: - fprintf(stderr, PFX "Warning: couldn't load driver '%s'.\n", name); - return; -out_dlopen: - fprintf(stderr, PFX "Warning: couldn't load driver '%s': %s\n", so_name, - dlerror()); - free(so_name); - return; -} - -static void load_drivers(void) -{ - struct ibv_driver_name *name, *next_name; - const char *env; - char *list, *env_name; - - /* - * Only use drivers passed in through the calling user's - * environment if we're not running setuid. - */ - if (getuid() == geteuid()) { - if ((env = getenv("RDMAV_DRIVERS"))) { - list = strdupa(env); - while ((env_name = strsep(&list, ":;"))) - load_driver(env_name); - } else if ((env = getenv("IBV_DRIVERS"))) { - list = strdupa(env); - while ((env_name = strsep(&list, ":;"))) - load_driver(env_name); - } - } - - list_for_each_safe(&driver_name_list, name, next_name, entry) { - load_driver(name->name); - free(name->name); - free(name); - } -} - -static void read_config_file(const char *path) -{ - FILE *conf; - char *line = NULL; - char *config; - char *field; - size_t buflen = 0; - ssize_t len; - - conf = fopen(path, "r" STREAM_CLOEXEC); - if (!conf) { - fprintf(stderr, PFX "Warning: couldn't read config file %s.\n", - path); - return; - } - - while ((len = getline(&line, &buflen, conf)) != -1) { - config = line + strspn(line, "\t "); - if (config[0] == '\n' || config[0] == '#') - continue; - - field = strsep(&config, "\n\t "); - - if (strcmp(field, "driver") == 0 && config != NULL) { - struct ibv_driver_name *driver_name; - - config += strspn(config, "\t "); - field = strsep(&config, "\n\t "); - - driver_name = malloc(sizeof *driver_name); - if (!driver_name) { - fprintf(stderr, PFX "Warning: couldn't allocate " - "driver name '%s'.\n", field); - continue; - } - - driver_name->name = strdup(field); - if (!driver_name->name) { - fprintf(stderr, PFX "Warning: couldn't allocate " - "driver name '%s'.\n", field); - free(driver_name); - continue; - } - - list_add(&driver_name_list, &driver_name->entry); - } else - fprintf(stderr, PFX "Warning: ignoring bad config directive " - "'%s' in file '%s'.\n", field, path); - } - - if (line) - free(line); - fclose(conf); -} - -static void read_config(void) -{ - DIR *conf_dir; - struct dirent *dent; - char *path; - - conf_dir = opendir(IBV_CONFIG_DIR); - if (!conf_dir) { - fprintf(stderr, PFX "Warning: couldn't open config directory '%s'.\n", - IBV_CONFIG_DIR); - return; - } - - while ((dent = readdir(conf_dir))) { - struct stat buf; - - if (asprintf(&path, "%s/%s", IBV_CONFIG_DIR, dent->d_name) < 0) { - fprintf(stderr, PFX "Warning: couldn't read config file %s/%s.\n", - IBV_CONFIG_DIR, dent->d_name); - goto out; - } - - if (stat(path, &buf)) { - fprintf(stderr, PFX "Warning: couldn't stat config file '%s'.\n", - path); - goto next; - } - - if (!S_ISREG(buf.st_mode)) - goto next; - - read_config_file(path); -next: - free(path); - } - -out: - closedir(conf_dir); -} - /* Match a single modalias value */ static bool match_modalias(const struct verbs_match_ent *ent, const char *value) { @@ -616,7 +436,6 @@ int ibverbs_get_device_list(struct list_head *device_list) struct verbs_device *vdev, *tmp; static int drivers_loaded; unsigned int num_devices = 0; - int statically_linked = 0; int ret; ret = find_sysfs_devs(&sysfs_list); @@ -652,26 +471,6 @@ int ibverbs_get_device_list(struct list_head *device_list) if (list_empty(&sysfs_list) || drivers_loaded) goto out; - /* - * Check if we can dlopen() ourselves. If this fails, - * libibverbs is probably statically linked into the - * executable, and we should just give up, since trying to - * dlopen() a driver module will fail spectacularly (loading a - * driver .so will bring in dynamic copies of libibverbs and - * libdl to go along with the static copies the executable - * has, which quickly leads to a crash. - */ - { - void *hand = dlopen(NULL, RTLD_NOW); - if (!hand) { - fprintf(stderr, PFX "Warning: dlopen(NULL) failed, " - "assuming static linking.\n"); - statically_linked = 1; - goto out; - } - dlclose(hand); - } - load_drivers(); drivers_loaded = 1; @@ -686,9 +485,6 @@ out: fprintf(stderr, PFX "Warning: no userspace device-specific driver found for %s\n", sysfs_dev->sysfs_path); - if (statically_linked) - fprintf(stderr, - " When linking libibverbs statically, driver must be statically linked too.\n"); } free(sysfs_dev); } @@ -725,8 +521,6 @@ int ibverbs_init(void) check_memlock_limit(); - read_config(); - return 0; } -- 2.19.1