Index: head/etc/mtree/BSD.root.dist =================================================================== --- head/etc/mtree/BSD.root.dist +++ head/etc/mtree/BSD.root.dist @@ -88,6 +88,8 @@ .. geom .. + nvmecontrol + .. .. libexec resolvconf Index: head/sbin/nvmecontrol/Makefile =================================================================== --- head/sbin/nvmecontrol/Makefile +++ head/sbin/nvmecontrol/Makefile @@ -6,6 +6,7 @@ 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 Index: head/sbin/nvmecontrol/logpage.c =================================================================== --- head/sbin/nvmecontrol/logpage.c +++ head/sbin/nvmecontrol/logpage.c @@ -48,13 +48,13 @@ #include "nvmecontrol.h" -SET_DECLARE(logpage, struct logpage_function); - #define LOGPAGE_USAGE \ "logpage <-p page_id> [-b] [-v vendor] [-x] \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 @@ 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 @@ * 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; Index: head/sbin/nvmecontrol/ns.c =================================================================== --- head/sbin/nvmecontrol/ns.c +++ head/sbin/nvmecontrol/ns.c @@ -41,7 +41,7 @@ #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" Index: head/sbin/nvmecontrol/nvmecontrol.h =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.h +++ head/sbin/nvmecontrol/nvmecontrol.h @@ -43,11 +43,15 @@ 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 @@ 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 @@ .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 @@ 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 @@ 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 */ /* Index: head/sbin/nvmecontrol/nvmecontrol.8 =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.8 +++ head/sbin/nvmecontrol/nvmecontrol.8 @@ -34,7 +34,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 12, 2018 +.Dd December 7, 2018 .Dt NVMECONTROL 8 .Os .Sh NAME @@ -230,6 +230,19 @@ .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 Index: head/sbin/nvmecontrol/nvmecontrol.c =================================================================== --- head/sbin/nvmecontrol/nvmecontrol.c +++ head/sbin/nvmecontrol/nvmecontrol.c @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -47,7 +49,7 @@ #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 @@ 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 @@ 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); } Index: head/sbin/nvmecontrol/wdc.c =================================================================== --- head/sbin/nvmecontrol/wdc.c +++ head/sbin/nvmecontrol/wdc.c @@ -45,7 +45,7 @@ #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 Index: head/share/man/man7/hier.7 =================================================================== --- head/share/man/man7/hier.7 +++ head/share/man/man7/hier.7 @@ -152,6 +152,10 @@ 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