Author: imp
Date: Thu Dec  6 22:58:26 2018
New Revision: 341657
URL: https://svnweb.freebsd.org/changeset/base/341657

Log:
  Dynamically load .so modules to expand functionality
  
  o Dynamically load all the .so files found in /libexec/nvmecontrol and
    /usr/local/libexec/nvmecontrol.
  o Link nvmecontrol -rdynamic so that its symbols are visible to the
    libraries we load.
  o Create concatinated linker sets that we dynamically expand.
  o Add the linked-in top and logpage linker sets to the mirrors for them
    and add those sets to the mirrors when we load a new .so.
  o Add some macros to help hide the names of the linker sets.
  o Update the man page.
  
  Sponsored by: Netflix
  Differential Revision: https://reviews.freebsd.org/D18455
  
  fold

Modified:
  head/etc/mtree/BSD.root.dist
  head/sbin/nvmecontrol/Makefile
  head/sbin/nvmecontrol/logpage.c
  head/sbin/nvmecontrol/ns.c
  head/sbin/nvmecontrol/nvmecontrol.8
  head/sbin/nvmecontrol/nvmecontrol.c
  head/sbin/nvmecontrol/nvmecontrol.h
  head/sbin/nvmecontrol/wdc.c
  head/share/man/man7/hier.7

Modified: head/etc/mtree/BSD.root.dist
==============================================================================
--- head/etc/mtree/BSD.root.dist        Thu Dec  6 22:35:07 2018        
(r341656)
+++ head/etc/mtree/BSD.root.dist        Thu Dec  6 22:58:26 2018        
(r341657)
@@ -88,6 +88,8 @@
         ..
         geom
         ..
+        nvmecontrol
+        ..
     ..
     libexec
         resolvconf

Modified: head/sbin/nvmecontrol/Makefile
==============================================================================
--- head/sbin/nvmecontrol/Makefile      Thu Dec  6 22:35:07 2018        
(r341656)
+++ head/sbin/nvmecontrol/Makefile      Thu Dec  6 22:58:26 2018        
(r341657)
@@ -6,6 +6,7 @@ SRCS=   nvmecontrol.c devlist.c firmware.c format.c iden
        perftest.c reset.c ns.c nvme_util.c power.c nc_util.c
 SRCS+= wdc.c intel.c
 MAN=   nvmecontrol.8
+LDFLAGS+= -rdynamic
 
 .PATH: ${SRCTOP}/sys/dev/nvme
 

Modified: head/sbin/nvmecontrol/logpage.c
==============================================================================
--- head/sbin/nvmecontrol/logpage.c     Thu Dec  6 22:35:07 2018        
(r341656)
+++ head/sbin/nvmecontrol/logpage.c     Thu Dec  6 22:58:26 2018        
(r341657)
@@ -48,13 +48,13 @@ __FBSDID("$FreeBSD$");
 
 #include "nvmecontrol.h"
 
-SET_DECLARE(logpage, struct logpage_function);
-
 #define LOGPAGE_USAGE                                                         \
        "logpage <-p page_id> [-b] [-v vendor] [-x] <controller id|namespace 
id>\n"  \
 
 #define MAX_FW_SLOTS   (7)
 
+SET_CONCAT_DEF(logpage, struct logpage_function);
+
 const char *
 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
 {
@@ -332,7 +332,7 @@ logpage_help(void)
        fprintf(stderr, "\n");
        fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
        fprintf(stderr, "-------- ---------- ----------\n");
-       for (f = SET_BEGIN(logpage); f < SET_LIMIT(logpage); f++) {
+       for (f = logpage_begin(); f < logpage_limit(); f++) {
                v = (*f)->vendor == NULL ? "-" : (*f)->vendor;
                fprintf(stderr, "0x%02x     %-10s %s\n", (*f)->log_page, v, 
(*f)->name);
        }
@@ -438,7 +438,7 @@ logpage(struct nvme_function *nf, int argc, char *argv
                 * the page is vendor specific, don't match the print function
                 * unless the vendors match.
                 */
-               for (f = SET_BEGIN(logpage); f < SET_LIMIT(logpage); f++) {
+               for (f = logpage_begin(); f < logpage_limit(); f++) {
                        if ((*f)->vendor != NULL && vendor != NULL &&
                            strcmp((*f)->vendor, vendor) != 0)
                                continue;

Modified: head/sbin/nvmecontrol/ns.c
==============================================================================
--- head/sbin/nvmecontrol/ns.c  Thu Dec  6 22:35:07 2018        (r341656)
+++ head/sbin/nvmecontrol/ns.c  Thu Dec  6 22:58:26 2018        (r341657)
@@ -41,7 +41,7 @@ __FBSDID("$FreeBSD$");
 
 #include "nvmecontrol.h"
 
-SET_DECLARE(ns, struct nvme_function);
+NVME_CMD_DECLARE(ns, struct nvme_function);
 
 #define NS_USAGE                                                       \
        "ns (create|delete|attach|detach)\n"

Modified: head/sbin/nvmecontrol/nvmecontrol.8
==============================================================================
--- head/sbin/nvmecontrol/nvmecontrol.8 Thu Dec  6 22:35:07 2018        
(r341656)
+++ head/sbin/nvmecontrol/nvmecontrol.8 Thu Dec  6 22:58:26 2018        
(r341657)
@@ -34,7 +34,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd March 12, 2018
+.Dd December 7, 2018
 .Dt NVMECONTROL 8
 .Os
 .Sh NAME
@@ -230,6 +230,19 @@ Set the current power mode.
 .Dl nvmecontrol power nvme0
 .Pp
 Get the current power mode.
+.Sh DYNAMIC LOADING
+The directories
+.Pa /libexec/nvmecontrol
+and
+.Pa /usr/local/libexec/nvmecontrol
+are scanned for any .so files.
+These files are loaded.
+The members of the
+.Va top
+linker set are added to the top-level commands.
+The members of the
+.Va logpage
+linker set are added to the logpage parsers.
 .Sh HISTORY
 The
 .Nm

Modified: head/sbin/nvmecontrol/nvmecontrol.c
==============================================================================
--- head/sbin/nvmecontrol/nvmecontrol.c Thu Dec  6 22:35:07 2018        
(r341656)
+++ head/sbin/nvmecontrol/nvmecontrol.c Thu Dec  6 22:58:26 2018        
(r341657)
@@ -34,6 +34,8 @@ __FBSDID("$FreeBSD$");
 #include <sys/stat.h>
 
 #include <ctype.h>
+#include <dlfcn.h>
+#include <dirent.h>
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -47,7 +49,7 @@ __FBSDID("$FreeBSD$");
 
 #include "nvmecontrol.h"
 
-SET_DECLARE(top, struct nvme_function);
+SET_CONCAT_DEF(top, struct nvme_function);
 
 static void
 print_usage(const struct nvme_function *f)
@@ -116,6 +118,32 @@ dispatch_set(int argc, char *argv[], struct nvme_funct
        gen_usage_set(tbl, tbl_limit);
 }
 
+void
+set_concat_add(struct set_concat *m, void *b, void *e)
+{
+       void **bp, **ep;
+       int add_n, cur_n;
+
+       if (b == NULL)
+               return;
+       /*
+        * Args are really pointers to arrays of pointers, but C's
+        * casting rules kinda suck since you can't directly cast
+        * struct foo ** to a void **.
+        */
+       bp = (void **)b;
+       ep = (void **)e;
+       add_n = ep - bp;
+       cur_n = 0;
+       if (m->begin != NULL)
+               cur_n = m->limit - m->begin;
+       m->begin = reallocarray(m->begin, cur_n + add_n, sizeof(void *));
+       if (m->begin == NULL)
+               err(1, "expanding concat set");
+       memcpy(m->begin + cur_n, bp, add_n * sizeof(void *));
+       m->limit = m->begin + cur_n + add_n;
+}
+
 static void
 print_bytes(void *data, uint32_t length)
 {
@@ -260,14 +288,64 @@ parse_ns_str(const char *ns_str, char *ctrlr_str, uint
        snprintf(ctrlr_str, nsloc - ns_str + 1, "%s", ns_str);
 }
 
+/*
+ * Loads all the .so's from the specified directory.
+ */
+static void
+load_dir(const char *dir)
+{
+       DIR *d;
+       struct dirent *dent;
+       char *path = NULL;
+       void *h;
+
+       d = opendir(dir);
+       if (d == NULL)
+               return;
+       for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
+               if (strcmp(".so", dent->d_name + dent->d_namlen - 3) != 0)
+                       continue;
+               asprintf(&path, "%s/%s", dir, dent->d_name);
+               if (path == NULL)
+                       err(1, "Can't malloc for path, giving up.");
+               if ((h = dlopen(path, RTLD_NOW | RTLD_GLOBAL)) == NULL)
+                       warnx("Can't load %s: %s", path, dlerror());
+               else {
+                       /*
+                        * Add in the top (for cli commands) and logpage (for
+                        * logpage parsing) linker sets. We have to do this by
+                        * hand because linker sets aren't automatically merged.
+                        */
+                       void *begin, *limit;
+                       begin = dlsym(h, "__start_set_top");
+                       limit = dlsym(h, "__stop_set_top");
+                       if (begin)
+                               add_to_top(begin, limit);
+                       begin = dlsym(h, "__start_set_logpage");
+                       limit = dlsym(h, "__stop_set_logpage");
+                       if (begin)
+                               add_to_logpage(begin, limit);
+               }
+               free(path);
+               path = NULL;
+       }
+       closedir(d);
+}
+
 int
 main(int argc, char *argv[])
 {
 
+       add_to_top(NVME_CMD_BEGIN(top), NVME_CMD_LIMIT(top));
+       add_to_logpage(NVME_LOGPAGE_BEGIN, NVME_LOGPAGE_LIMIT);
+
+       load_dir("/lib/nvmecontrol");
+       load_dir("/usr/local/lib/nvmecontrol");
+
        if (argc < 2)
-               gen_usage_set(SET_BEGIN(top), SET_LIMIT(top));
+               gen_usage_set(top_begin(), top_limit());
 
-       DISPATCH(argc, argv, top);
+       dispatch_set(argc, argv, top_begin(), top_limit());
 
        return (0);
 }

Modified: head/sbin/nvmecontrol/nvmecontrol.h
==============================================================================
--- head/sbin/nvmecontrol/nvmecontrol.h Thu Dec  6 22:35:07 2018        
(r341656)
+++ head/sbin/nvmecontrol/nvmecontrol.h Thu Dec  6 22:58:26 2018        
(r341657)
@@ -43,11 +43,15 @@ struct nvme_function {
        const char      *usage;
 };
 
-#define NVME_CMDSET(set, sym)  DATA_SET(set, sym)
+#define NVME_SETNAME(set)      set
+#define        NVME_CMDSET(set, sym)   DATA_SET(NVME_SETNAME(set), sym)
 #define NVME_COMMAND(set, nam, function, usage_str)                    \
        static struct nvme_function function ## _nvme_cmd =             \
        { .name = #nam, .fn = function, .usage = usage_str };           \
        NVME_CMDSET(set, function ## _nvme_cmd)
+#define NVME_CMD_BEGIN(set)    SET_BEGIN(NVME_SETNAME(set))
+#define NVME_CMD_LIMIT(set)    SET_LIMIT(NVME_SETNAME(set))
+#define NVME_CMD_DECLARE(set, t) SET_DECLARE(NVME_SETNAME(set), t)
 
 typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void 
*buf, uint32_t size);
 
@@ -59,7 +63,8 @@ struct logpage_function {
        size_t          size;
 };
 
-#define NVME_LOGPAGESET(sym)           DATA_SET(logpage, sym)
+
+#define NVME_LOGPAGESET(sym)           DATA_SET(NVME_SETNAME(logpage), sym)
 #define NVME_LOGPAGE(unique, lp, vend, nam, fn, sz)                    \
        static struct logpage_function unique ## _lpf = {               \
                .log_page = lp,                                         \
@@ -69,6 +74,9 @@ struct logpage_function {
                .size = sz,                                             \
        } ;                                                             \
        NVME_LOGPAGESET(unique ## _lpf)
+#define NVME_LOGPAGE_BEGIN     SET_BEGIN(NVME_SETNAME(logpage))
+#define NVME_LOGPAGE_LIMIT     SET_LIMIT(NVME_SETNAME(logpage))
+#define NVME_LOGPAGE_DECLARE(t)                
SET_DECLARE(NVME_SETNAME(logpage), t)
 
 #define DEFAULT_SIZE   (4096)
 struct kv_name {
@@ -78,6 +86,27 @@ struct kv_name {
 
 const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key);
 
+NVME_CMD_DECLARE(top, struct nvme_function);
+NVME_LOGPAGE_DECLARE(struct logpage_function);
+
+struct set_concat {
+       void **begin;
+       void **limit;
+};
+void set_concat_add(struct set_concat *m, void *begin, void *end);
+#define SET_CONCAT_DEF(set, t)                                                 
        \
+static struct set_concat set ## _concat;                                       
\
+static inline t **set ## _begin() { return ((t **)set ## _concat.begin); }     
\
+static inline t **set ## _limit() { return ((t **)set ## _concat.limit); }     
\
+void add_to_ ## set(t **b, t **e)                                              
\
+{                                                                              
\
+       set_concat_add(&set ## _concat, b, e);                                  
\
+}
+#define SET_CONCAT_DECL(set, t)                                                
        \
+       void add_to_ ## set(t **b, t **e)
+SET_CONCAT_DECL(top, struct nvme_function);
+SET_CONCAT_DECL(logpage, struct logpage_function);
+
 #define NVME_CTRLR_PREFIX      "nvme"
 #define NVME_NS_PREFIX         "ns"
 
@@ -95,7 +124,7 @@ void dispatch_set(int argc, char *argv[], struct nvme_
     struct nvme_function **tbl_limit);
 
 #define DISPATCH(argc, argv, set)      \
-       dispatch_set(argc, argv, SET_BEGIN(set), SET_LIMIT(set))
+       dispatch_set(argc, argv, NVME_CMD_BEGIN(set), NVME_CMD_LIMIT(set))
 
 /* Utility Routines */
 /*

Modified: head/sbin/nvmecontrol/wdc.c
==============================================================================
--- head/sbin/nvmecontrol/wdc.c Thu Dec  6 22:35:07 2018        (r341656)
+++ head/sbin/nvmecontrol/wdc.c Thu Dec  6 22:58:26 2018        (r341657)
@@ -45,7 +45,7 @@ __FBSDID("$FreeBSD$");
 #define WDC_USAGE                                                             \
        "wdc (cap-diag)\n"
 
-SET_DECLARE(wdc, struct nvme_function);
+NVME_CMD_DECLARE(wdc, struct nvme_function);
 
 #define WDC_NVME_TOC_SIZE      8
 

Modified: head/share/man/man7/hier.7
==============================================================================
--- head/share/man/man7/hier.7  Thu Dec  6 22:35:07 2018        (r341656)
+++ head/share/man/man7/hier.7  Thu Dec  6 22:58:26 2018        (r341657)
@@ -152,6 +152,10 @@ Capsicum support libraries
 class-specific libraries for the
 .Xr geom 8
 utility
+.It Pa nvmecontrol/
+vendor-specific libraries to extend the
+.Xr nvmecontrol 8
+utility
 .El
 .It Pa /libexec/
 critical system utilities needed for binaries in
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to