summaryrefslogtreecommitdiffstats
path: root/fabrics.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--fabrics.c2554
1 files changed, 944 insertions, 1610 deletions
diff --git a/fabrics.c b/fabrics.c
index d691191..92c5312 100644
--- a/fabrics.c
+++ b/fabrics.c
@@ -27,7 +27,6 @@
#include <stdint.h>
#include <unistd.h>
#include <dirent.h>
-#include <sys/ioctl.h>
#include <inttypes.h>
#include <libgen.h>
#include <sys/stat.h>
@@ -36,720 +35,231 @@
#include <time.h>
#include <sys/types.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#include "util/parser.h"
-#include "nvme-ioctl.h"
-#include "nvme-status.h"
-#include "fabrics.h"
+#include <linux/types.h>
+#include "common.h"
#include "nvme.h"
-#include "util/argconfig.h"
+#include "libnvme.h"
+#include "nvme-print.h"
-#include "common.h"
-#include "util/log.h"
-#include "util/cleanup.h"
-
-#ifdef HAVE_SYSTEMD
-#include <systemd/sd-id128.h>
-#define NVME_HOSTNQN_ID SD_ID128_MAKE(c7,f4,61,81,12,be,49,32,8c,83,10,6f,9d,dd,d8,6b)
-#endif
-
-#define NVMF_HOSTID_SIZE 36
-
-/* default to 600 seconds of reconnect attempts before giving up */
-#define NVMF_DEF_CTRL_LOSS_TMO 600
-
-const char *conarg_nqn = "nqn";
-const char *conarg_transport = "transport";
-const char *conarg_traddr = "traddr";
-const char *conarg_trsvcid = "trsvcid";
-const char *conarg_host_traddr = "host_traddr";
-const char *conarg_host_iface = "host_iface";
-
-struct fabrics_config fabrics_cfg = {
- .ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO,
- .fast_io_fail_tmo = -1,
- .output_format = "normal",
-};
+#define PATH_NVMF_DISC SYSCONFDIR "/nvme/discovery.conf"
+#define PATH_NVMF_CONFIG SYSCONFDIR "/nvme/config.json"
+#define MAX_DISC_ARGS 32
+#define MAX_DISC_RETRIES 10
-struct connect_args {
+#define NVMF_DEF_DISC_TMO 30
+
+/* Name of file to output log pages in their raw format */
+static char *raw;
+static bool persistent;
+static bool quiet;
+static bool dump_config;
+
+static const char *nvmf_tport = "transport type";
+static const char *nvmf_traddr = "transport address";
+static const char *nvmf_nqn = "subsystem nqn";
+static const char *nvmf_trsvcid = "transport service id (e.g. IP port)";
+static const char *nvmf_htraddr = "host traddr (e.g. FC WWN's)";
+static const char *nvmf_hiface = "host interface (for tcp transport)";
+static const char *nvmf_hostnqn = "user-defined hostnqn";
+static const char *nvmf_hostid = "user-defined hostid (if default not used)";
+static const char *nvmf_hostkey = "user-defined dhchap key (if default not used)";
+static const char *nvmf_ctrlkey = "user-defined dhchap controller key (for bi-directional authentication)";
+static const char *nvmf_nr_io_queues = "number of io queues to use (default is core count)";
+static const char *nvmf_nr_write_queues = "number of write queues to use (default 0)";
+static const char *nvmf_nr_poll_queues = "number of poll queues to use (default 0)";
+static const char *nvmf_queue_size = "number of io queue elements to use (default 128)";
+static const char *nvmf_keep_alive_tmo = "keep alive timeout period in seconds";
+static const char *nvmf_reconnect_delay = "reconnect timeout period in seconds";
+static const char *nvmf_ctrl_loss_tmo = "controller loss timeout period in seconds";
+static const char *nvmf_tos = "type of service";
+static const char *nvmf_dup_connect = "allow duplicate connections between same transport host and subsystem port";
+static const char *nvmf_disable_sqflow = "disable controller sq flow control (default false)";
+static const char *nvmf_hdr_digest = "enable transport protocol header digest (TCP transport)";
+static const char *nvmf_data_digest = "enable transport protocol data digest (TCP transport)";
+static const char *nvmf_config_file = "Use specified JSON configuration file or 'none' to disable";
+
+#define NVMF_OPTS(c) \
+ OPT_STRING("transport", 't', "STR", &transport, nvmf_tport), \
+ OPT_STRING("traddr", 'a', "STR", &traddr, nvmf_traddr), \
+ OPT_STRING("trsvcid", 's', "STR", &trsvcid, nvmf_trsvcid), \
+ OPT_STRING("host-traddr", 'w', "STR", &c.host_traddr, nvmf_htraddr), \
+ OPT_STRING("host-iface", 'f', "STR", &c.host_iface, nvmf_hiface), \
+ OPT_STRING("hostnqn", 'q', "STR", &hostnqn, nvmf_hostnqn), \
+ OPT_STRING("hostid", 'I', "STR", &hostid, nvmf_hostid), \
+ OPT_STRING("nqn", 'n', "STR", &subsysnqn, nvmf_nqn), \
+ OPT_STRING("dhchap-secret", 'S', "STR", &hostkey, nvmf_hostkey), \
+ OPT_STRING("dhchap-ctrl-secret", 'C', "STR", &ctrlkey, nvmf_ctrlkey), \
+ OPT_INT("nr-io-queues", 'i', &c.nr_io_queues, nvmf_nr_io_queues), \
+ OPT_INT("nr-write-queues", 'W', &c.nr_write_queues, nvmf_nr_write_queues),\
+ OPT_INT("nr-poll-queues", 'P', &c.nr_poll_queues, nvmf_nr_poll_queues), \
+ OPT_INT("queue-size", 'Q', &c.queue_size, nvmf_queue_size), \
+ OPT_INT("keep-alive-tmo", 'k', &c.keep_alive_tmo, nvmf_keep_alive_tmo), \
+ OPT_INT("reconnect-delay", 'c', &c.reconnect_delay, nvmf_reconnect_delay),\
+ OPT_INT("ctrl-loss-tmo", 'l', &c.ctrl_loss_tmo, nvmf_ctrl_loss_tmo), \
+ OPT_INT("tos", 'T', &c.tos, nvmf_tos), \
+ OPT_FLAG("duplicate-connect", 'D', &c.duplicate_connect, nvmf_dup_connect), \
+ OPT_FLAG("disable-sqflow", 'd', &c.disable_sqflow, nvmf_disable_sqflow), \
+ OPT_FLAG("hdr-digest", 'g', &c.hdr_digest, nvmf_hdr_digest), \
+ OPT_FLAG("data-digest", 'G', &c.data_digest, nvmf_data_digest) \
+
+struct tr_config {
char *subsysnqn;
char *transport;
char *traddr;
- char *trsvcid;
char *host_traddr;
char *host_iface;
- struct connect_args *next;
- struct connect_args *tail;
-};
-
-struct connect_args *tracked_ctrls;
-
-#define PATH_NVME_FABRICS "/dev/nvme-fabrics"
-#define PATH_NVMF_DISC "/etc/nvme/discovery.conf"
-#define PATH_NVMF_HOSTNQN "/etc/nvme/hostnqn"
-#define PATH_NVMF_HOSTID "/etc/nvme/hostid"
-#define MAX_DISC_ARGS 10
-#define MAX_DISC_RETRIES 10
-
-enum {
- OPT_INSTANCE,
- OPT_CNTLID,
- OPT_ERR
-};
-
-static const match_table_t opt_tokens = {
- { OPT_INSTANCE, "instance=%d" },
- { OPT_CNTLID, "cntlid=%d" },
- { OPT_ERR, NULL },
-};
-
-const char *arg_str(const char * const *strings,
- size_t array_size, size_t idx)
-{
- if (idx < array_size && strings[idx])
- return strings[idx];
- return "unrecognized";
-}
-
-const char * const trtypes[] = {
- [NVMF_TRTYPE_RDMA] = "rdma",
- [NVMF_TRTYPE_FC] = "fc",
- [NVMF_TRTYPE_TCP] = "tcp",
- [NVMF_TRTYPE_LOOP] = "loop",
-};
-
-static const char *trtype_str(__u8 trtype)
-{
- return arg_str(trtypes, ARRAY_SIZE(trtypes), trtype);
-}
-
-static const char * const adrfams[] = {
- [NVMF_ADDR_FAMILY_PCI] = "pci",
- [NVMF_ADDR_FAMILY_IP4] = "ipv4",
- [NVMF_ADDR_FAMILY_IP6] = "ipv6",
- [NVMF_ADDR_FAMILY_IB] = "infiniband",
- [NVMF_ADDR_FAMILY_FC] = "fibre-channel",
- [NVMF_ADDR_FAMILY_LOOP] = "loop",
-};
-
-static inline const char *adrfam_str(__u8 adrfam)
-{
- return arg_str(adrfams, ARRAY_SIZE(adrfams), adrfam);
-}
-
-static const char * const subtypes[] = {
- [NVME_NQN_DISC] = "discovery subsystem",
- [NVME_NQN_NVME] = "nvme subsystem",
-};
-
-static inline const char *subtype_str(__u8 subtype)
-{
- return arg_str(subtypes, ARRAY_SIZE(subtypes), subtype);
-}
-
-static const char * const treqs[] = {
- [NVMF_TREQ_NOT_SPECIFIED] = "not specified",
- [NVMF_TREQ_REQUIRED] = "required",
- [NVMF_TREQ_NOT_REQUIRED] = "not required",
- [NVMF_TREQ_DISABLE_SQFLOW] = "not specified, "
- "sq flow control disable supported",
-};
-
-static inline const char *treq_str(__u8 treq)
-{
- return arg_str(treqs, ARRAY_SIZE(treqs), treq);
-}
-
-static const char * const sectypes[] = {
- [NVMF_TCP_SECTYPE_NONE] = "none",
- [NVMF_TCP_SECTYPE_TLS] = "tls",
-};
-
-static inline const char *sectype_str(__u8 sectype)
-{
- return arg_str(sectypes, ARRAY_SIZE(sectypes), sectype);
-}
-
-static const char * const prtypes[] = {
- [NVMF_RDMA_PRTYPE_NOT_SPECIFIED] = "not specified",
- [NVMF_RDMA_PRTYPE_IB] = "infiniband",
- [NVMF_RDMA_PRTYPE_ROCE] = "roce",
- [NVMF_RDMA_PRTYPE_ROCEV2] = "roce-v2",
- [NVMF_RDMA_PRTYPE_IWARP] = "iwarp",
-};
-
-static inline const char *prtype_str(__u8 prtype)
-{
- return arg_str(prtypes, ARRAY_SIZE(prtypes), prtype);
-}
-
-static const char * const qptypes[] = {
- [NVMF_RDMA_QPTYPE_CONNECTED] = "connected",
- [NVMF_RDMA_QPTYPE_DATAGRAM] = "datagram",
-};
-
-static inline const char *qptype_str(__u8 qptype)
-{
- return arg_str(qptypes, ARRAY_SIZE(qptypes), qptype);
-}
-
-static const char * const cms[] = {
- [NVMF_RDMA_CMS_RDMA_CM] = "rdma-cm",
+ char *trsvcid;
};
-static const char *cms_str(__u8 cm)
+static void space_strip_len(int max, char *str)
{
- return arg_str(cms, ARRAY_SIZE(cms), cm);
-}
-
-/*
- * parse strings with connect arguments to find a particular field.
- * If field found, return string containing field value. If field
- * not found, return an empty string.
- */
-char *parse_conn_arg(const char *conargs, const char delim, const char *field)
-{
- char *s, *e;
- size_t cnt;
-
- /*
- * There are field name overlaps: traddr and host_traddr.
- * By chance, both connect arg strings are set up to
- * have traddr field followed by host_traddr field. Thus field
- * name matching doesn't overlap in the searches. Technically,
- * as is, the loop and delimiter checking isn't necessary.
- * However, better to be prepared.
- */
- do {
- s = strstr(conargs, field);
- if (!s)
- goto empty_field;
- /* validate prior character is delimiter */
- if (s == conargs || *(s - 1) == delim) {
- /* match requires next character to be assignment */
- s += strlen(field);
- if (*s == '=')
- /* match */
- break;
- }
- /* field overlap: seek to delimiter and keep looking */
- conargs = strchr(s, delim);
- if (!conargs)
- goto empty_field;
- conargs++; /* skip delimiter */
- } while (1);
- s++; /* skip assignment character */
- e = strchr(s, delim);
- if (e)
- cnt = e - s;
- else
- cnt = strlen(s);
-
- return strndup(s, cnt);
-
-empty_field:
- return strdup("\0");
-}
-
-int ctrl_instance(const char *device)
-{
- char d[64];
- const char *p;
- int ret, instance;
-
- p = strrchr(device, '/');
- if (p == NULL)
- p = device;
- else
- p++;
- ret = sscanf(p, "nvme%d", &instance);
- if (ret <= 0)
- return -EINVAL;
- if (snprintf(d, sizeof(d), "nvme%d", instance) <= 0 ||
- strcmp(p, d))
- return -EINVAL;
- return instance;
-}
-
-/*
- * Given a controller name, create a connect_args with its
- * attributes and compare the attributes against the connect args
- * given.
- * Return true/false based on whether it matches
- */
-static bool ctrl_matches_connectargs(const char *name, struct connect_args *args)
-{
- struct connect_args cargs;
- bool found = false;
- char *path = NULL, *addr;
- int ret;
- bool persistent = true;
-
- ret = asprintf(&path, "%s/%s", SYS_NVME, name);
- if (ret < 0)
- return found;
-
- addr = nvme_get_ctrl_attr(path, "address");
- cargs.subsysnqn = nvme_get_ctrl_attr(path, "subsysnqn");
- cargs.transport = nvme_get_ctrl_attr(path, "transport");
-
- if (!addr || !cargs.subsysnqn || !cargs.transport) {
- fprintf(stderr, "nvme_get_ctrl_attr failed\n");
- goto out;
- }
+ int i;
- cargs.traddr = parse_conn_arg(addr, ' ', conarg_traddr);
- cargs.trsvcid = parse_conn_arg(addr, ' ', conarg_trsvcid);
- cargs.host_traddr = parse_conn_arg(addr, ' ', conarg_host_traddr);
- cargs.host_iface = parse_conn_arg(addr, ' ', conarg_host_iface);
-
- if (!strcmp(cargs.subsysnqn, NVME_DISC_SUBSYS_NAME)) {
- char *kato_str = nvme_get_ctrl_attr(path, "kato"), *p;
- unsigned int kato = 0;
-
- /*
- * When looking up discovery controllers we have to skip
- * any non-persistent controllers (ie those with a zero
- * kato value). Otherwise the controller will vanish from
- * underneath us as they are owned by another program.
- *
- * On older kernels, the 'kato' attribute isn't present.
- * Assume a persistent controller for these installations.
- */
- if (kato_str) {
- kato = strtoul(kato_str, &p, 0);
- if (p == kato_str)
- kato = 0;
- free(kato_str);
- persistent = (kato != 0);
- }
+ for (i = max - 1; i >= 0; i--) {
+ if (str[i] != '\0' && str[i] != ' ')
+ return;
+ else
+ str[i] = '\0';
}
-
- if (persistent &&
- !strcmp(cargs.subsysnqn, args->subsysnqn) &&
- !strcmp(cargs.transport, args->transport) &&
- (!strcmp(cargs.traddr, args->traddr) ||
- !strcmp(args->traddr, "none")) &&
- (!strcmp(cargs.trsvcid, args->trsvcid) ||
- !strcmp(args->trsvcid, "none")) &&
- (!strcmp(cargs.host_traddr, args->host_traddr) ||
- !strcmp(args->host_traddr, "none")) &&
- (!strcmp(cargs.host_iface, args->host_iface) ||
- !strcmp(args->host_iface, "none")))
- found = true;
-
- free(cargs.traddr);
- free(cargs.trsvcid);
- free(cargs.host_traddr);
- free(cargs.host_iface);
-out:
- free(cargs.subsysnqn);
- free(cargs.transport);
- free(addr);
- free(path);
-
- return found;
}
-/*
- * Look through the system to find an existing controller whose
- * attributes match the connect arguments specified
- * If found, a string containing the controller name (ex: "nvme?")
- * is returned.
- * If not found, a NULL is returned.
- */
-static char *find_ctrl_with_connectargs(struct connect_args *args)
+static int set_discovery_kato(struct nvme_fabrics_config *cfg)
{
- struct dirent **devices;
- char *devname = NULL;
- int i, n;
-
- n = scandir(SYS_NVME, &devices, scan_ctrls_filter, alphasort);
- if (n < 0) {
- msg(LOG_ERR, "no NVMe controller(s) detected.\n");
- return NULL;
- }
+ int tmo = cfg->keep_alive_tmo;
- for (i = 0; i < n; i++) {
- if (ctrl_matches_connectargs(devices[i]->d_name, args)) {
- devname = strdup(devices[i]->d_name);
- if (devname == NULL)
- msg(LOG_ERR, "no memory for ctrl name %s\n",
- devices[i]->d_name);
- goto cleanup_devices;
- }
- }
-
-cleanup_devices:
- for (i = 0; i < n; i++)
- free(devices[i]);
- free(devices);
+ /* Set kato to NVMF_DEF_DISC_TMO for persistent controllers */
+ if (persistent && !cfg->keep_alive_tmo)
+ cfg->keep_alive_tmo = NVMF_DEF_DISC_TMO;
+ /* Set kato to zero for non-persistent controllers */
+ else if (!persistent && (cfg->keep_alive_tmo > 0))
+ cfg->keep_alive_tmo = 0;
- return devname;
+ return tmo;
}
-static struct connect_args *extract_connect_args(char *argstr)
+static nvme_ctrl_t __create_discover_ctrl(nvme_root_t r, nvme_host_t h,
+ struct nvme_fabrics_config *cfg,
+ struct tr_config *trcfg)
{
- struct connect_args *cargs;
+ nvme_ctrl_t c;
+ int tmo, ret;
- cargs = calloc(1, sizeof(*cargs));
- if (!cargs)
+ c = nvme_create_ctrl(r, trcfg->subsysnqn, trcfg->transport,
+ trcfg->traddr, trcfg->host_traddr,
+ trcfg->host_iface, trcfg->trsvcid);
+ if (!c)
return NULL;
- cargs->subsysnqn = parse_conn_arg(argstr, ',', conarg_nqn);
- cargs->transport = parse_conn_arg(argstr, ',', conarg_transport);
- cargs->traddr = parse_conn_arg(argstr, ',', conarg_traddr);
- cargs->trsvcid = parse_conn_arg(argstr, ',', conarg_trsvcid);
- cargs->host_traddr = parse_conn_arg(argstr, ',', conarg_host_traddr);
- cargs->host_iface = parse_conn_arg(argstr, ',', conarg_host_iface);
- return cargs;
-}
-
-static void destruct_connect_args(struct connect_args *cargs)
-{
- free(cargs->subsysnqn);
- free(cargs->transport);
- free(cargs->traddr);
- free(cargs->trsvcid);
- free(cargs->host_traddr);
- free(cargs->host_iface);
-}
-
-static void free_connect_args(struct connect_args *cargs)
-{
- destruct_connect_args(cargs);
- free(cargs);
-}
-
-static void track_ctrl(char *argstr)
-{
- struct connect_args *cargs;
-
- cargs = extract_connect_args(argstr);
- if (!cargs)
- return;
-
- if (!tracked_ctrls)
- tracked_ctrls = cargs;
- else
- tracked_ctrls->tail->next = cargs;
- tracked_ctrls->tail = cargs;
-}
-
-static int add_ctrl(const char *argstr)
-{
- substring_t args[MAX_OPT_ARGS];
- char buf[BUF_SIZE], *options, *p;
- int token, ret, fd, len = strlen(argstr);
-
- fd = open(PATH_NVME_FABRICS, O_RDWR);
- if (fd < 0) {
- msg(LOG_ERR, "Failed to open %s: %s\n",
- PATH_NVME_FABRICS, strerror(errno));
- ret = -errno;
- goto out;
- }
-
- ret = write(fd, argstr, len);
- if (ret != len) {
- if (errno != EALREADY)
- msg(LOG_NOTICE, "Failed to write to %s: %s\n",
- PATH_NVME_FABRICS, strerror(errno));
- ret = -errno;
- goto out_close;
- }
-
- len = read(fd, buf, BUF_SIZE);
- if (len < 0) {
- msg(LOG_ERR, "Failed to read from %s: %s\n",
- PATH_NVME_FABRICS, strerror(errno));
- ret = -errno;
- goto out_close;
- }
-
- buf[len] = '\0';
- options = buf;
- while ((p = strsep(&options, ",\n")) != NULL) {
- if (!*p)
- continue;
-
- token = match_token(p, opt_tokens, args);
- switch (token) {
- case OPT_INSTANCE:
- if (match_int(args, &token))
- goto out_fail;
- ret = token;
- track_ctrl((char *)argstr);
- goto out_close;
- default:
- /* ignore */
- break;
- }
- }
-
-out_fail:
- msg(LOG_ERR, "Failed to parse ctrl info for \"%s\"\n", argstr);
- ret = -EINVAL;
-out_close:
- close(fd);
-out:
- return ret;
-}
-
-static int remove_ctrl_by_path(char *sysfs_path)
-{
- int ret, fd;
-
- fd = open(sysfs_path, O_WRONLY);
- if (fd < 0) {
- ret = -errno;
- msg(LOG_ERR, "Failed to open %s: %s\n", sysfs_path,
- strerror(errno));
- goto out;
- }
- if (write(fd, "1", 1) != 1) {
- ret = -errno;
- goto out_close;
- }
+ nvme_ctrl_set_discovery_ctrl(c, true);
+ tmo = set_discovery_kato(cfg);
- ret = 0;
-out_close:
- close(fd);
-out:
- return ret;
-}
+ errno = 0;
+ ret = nvmf_add_ctrl(h, c, cfg);
-int remove_ctrl(int instance)
-{
- char *sysfs_path;
- int ret;
-
- if (asprintf(&sysfs_path, "/sys/class/nvme/nvme%d/delete_controller",
- instance) < 0) {
- ret = -errno;
- goto out;
+ cfg->keep_alive_tmo = tmo;
+ if (ret) {
+ errno = ret;
+ nvme_free_ctrl(c);
+ return NULL;
}
- ret = remove_ctrl_by_path(sysfs_path);
- free(sysfs_path);
-out:
- return ret;
+ return c;
}
-enum {
- DISC_OK,
- DISC_NO_LOG,
- DISC_GET_NUMRECS,
- DISC_GET_LOG,
- DISC_RETRY_EXHAUSTED,
- DISC_NOT_EQUAL,
-};
-
-static int nvmf_get_log_page_discovery(const char *dev_path,
- struct nvmf_disc_rsp_page_hdr **logp, int *numrec, int *status)
+static nvme_ctrl_t create_discover_ctrl(nvme_root_t r, nvme_host_t h,
+ struct nvme_fabrics_config *cfg,
+ struct tr_config *trcfg)
{
- struct nvmf_disc_rsp_page_hdr *log;
- unsigned int hdr_size;
- unsigned long genctr;
- int error, fd, max_retries = MAX_DISC_RETRIES, retries = 0;
-
- fd = open(dev_path, O_RDWR);
- if (fd < 0) {
- error = -errno;
- msg(LOG_ERR, "Failed to open %s: %s\n",
- dev_path, strerror(errno));
- goto out;
- }
-
- /* first get_log_page we just need numrec entry from discovery hdr.
- * host supplies its desired bytes via dwords, per NVMe spec.
- */
- hdr_size = round_up((offsetof(struct nvmf_disc_rsp_page_hdr, numrec) +
- sizeof(log->numrec)), sizeof(__u32));
-
- /*
- * Issue first get log page w/numdl small enough to retrieve numrec.
- * We just want to know how many records to retrieve.
- */
- log = calloc(1, hdr_size);
- if (!log) {
- perror("could not alloc memory for discovery log header");
- error = -ENOMEM;
- goto out_close;
- }
-
- error = nvme_discovery_log(fd, log, hdr_size);
- if (error) {
- error = DISC_GET_NUMRECS;
- goto out_free_log;
+ nvme_ctrl_t c;
+
+ c = __create_discover_ctrl(r, h, cfg, trcfg);
+ if (!persistent)
+ return c;
+
+ /* Find out the name of discovery controller */
+ struct nvme_id_ctrl id = { 0 };
+ if (nvme_ctrl_identify(c, &id)) {
+ fprintf(stderr, "failed to identify controller, error %s\n",
+ nvme_strerror(errno));
+ nvme_disconnect_ctrl(c);
+ nvme_free_ctrl(c);
+ return NULL;
}
- do {
- unsigned int log_size;
-
- /* check numrec limits */
- *numrec = le64_to_cpu(log->numrec);
- genctr = le64_to_cpu(log->genctr);
- free(log);
-
- if (*numrec == 0) {
- error = DISC_NO_LOG;
- goto out_close;
- }
-
- /* we are actually retrieving the entire discovery tables
- * for the second get_log_page(), per
- * NVMe spec so no need to round_up(), or there is something
- * seriously wrong with the standard
- */
- log_size = sizeof(struct nvmf_disc_rsp_page_hdr) +
- sizeof(struct nvmf_disc_rsp_page_entry) * *numrec;
-
- /* allocate discovery log pages based on page_hdr->numrec */
- log = calloc(1, log_size);
- if (!log) {
- perror("could not alloc memory for discovery log page");
- error = -ENOMEM;
- goto out_close;
- }
-
- /*
- * issue new get_log_page w/numdl+numdh set to get all records,
- * up to MAX_DISC_LOGS.
- */
- error = nvme_discovery_log(fd, log, log_size);
- if (error) {
- error = DISC_GET_LOG;
- goto out_free_log;
- }
-
- /*
- * The above call to nvme_discovery_log() might result
- * in several calls (with different offsets), so we need
- * to fetch the header again to have the most up-to-date
- * value for the generation counter
- */
- genctr = le64_to_cpu(log->genctr);
- error = nvme_discovery_log(fd, log, hdr_size);
- if (error) {
- error = DISC_GET_LOG;
- goto out_free_log;
- }
- } while (genctr != le64_to_cpu(log->genctr) &&
- ++retries < max_retries);
+ if (!strcmp(id.subnqn, NVME_DISC_SUBSYS_NAME))
+ return c;
/*
- * If genctr is still different with the one in the log entry, it
- * means the retires have been exhausted to max_retries. Then it
- * should be retried by the caller or the user.
+ * The subsysnqn is not the well-known name. Prefer the unique
+ * subsysnqn over the well-known one.
*/
- if (genctr != le64_to_cpu(log->genctr)) {
- error = DISC_RETRY_EXHAUSTED;
- goto out_free_log;
- }
-
- if (*numrec != le64_to_cpu(log->numrec)) {
- error = DISC_NOT_EQUAL;
- goto out_free_log;
- }
-
- /* needs to be freed by the caller */
- *logp = log;
- error = DISC_OK;
- goto out_close;
-
-out_free_log:
- free(log);
-out_close:
- close(fd);
-out:
- *status = nvme_status_to_errno(error, true);
- return error;
-}
+ nvme_disconnect_ctrl(c);
+ nvme_free_ctrl(c);
-static int space_strip_len(int max, const char *str)
-{
- int i;
-
- for (i = max - 1; i >= 0; i--)
- if (str[i] != '\0' && str[i] != ' ')
- break;
-
- return i + 1;
+ trcfg->subsysnqn = id.subnqn;
+ return __create_discover_ctrl(r, h, cfg, trcfg);
}
-static void print_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec,
- int instance)
+static void print_discovery_log(struct nvmf_discovery_log *log, int numrec)
{
int i;
- printf("\n");
-
- if (fabrics_cfg.persistent)
- printf("Persistent device: nvme%d\n", instance);
-
- printf("Discovery Log Number of Records %d, "
+ printf("\nDiscovery Log Number of Records %d, "
"Generation counter %"PRIu64"\n",
numrec, le64_to_cpu(log->genctr));
for (i = 0; i < numrec; i++) {
- struct nvmf_disc_rsp_page_entry *e = &log->entries[i];
+ struct nvmf_disc_log_entry *e = &log->entries[i];
+
+ space_strip_len(NVMF_TRSVCID_SIZE, e->trsvcid);
+ space_strip_len(NVMF_TRADDR_SIZE, e->traddr);
printf("=====Discovery Log Entry %d======\n", i);
- printf("trtype: %s\n", trtype_str(e->trtype));
- printf("adrfam: %s\n", adrfam_str(e->adrfam));
- printf("subtype: %s\n", subtype_str(e->subtype));
- printf("treq: %s\n", treq_str(e->treq));
+ printf("trtype: %s\n", nvmf_trtype_str(e->trtype));
+ printf("adrfam: %s\n",
+ e->traddr && strlen(e->traddr) ?
+ nvmf_adrfam_str(e->adrfam): "");
+ printf("subtype: %s\n", nvmf_subtype_str(e->subtype));
+ printf("treq: %s\n", nvmf_treq_str(e->treq));
printf("portid: %d\n", e->portid);
- printf("trsvcid: %.*s\n",
- space_strip_len(NVMF_TRSVCID_SIZE, e->trsvcid),
- e->trsvcid);
+ printf("trsvcid: %s\n", e->trsvcid);
printf("subnqn: %s\n", e->subnqn);
- printf("traddr: %.*s\n",
- space_strip_len(NVMF_TRADDR_SIZE, e->traddr),
- e->traddr);
+ printf("traddr: %s\n", e->traddr);
+ printf("eflags: %s\n", nvmf_eflags_str(e->eflags));
switch (e->trtype) {
case NVMF_TRTYPE_RDMA:
printf("rdma_prtype: %s\n",
- prtype_str(e->tsas.rdma.prtype));
+ nvmf_prtype_str(e->tsas.rdma.prtype));
printf("rdma_qptype: %s\n",
- qptype_str(e->tsas.rdma.qptype));
+ nvmf_qptype_str(e->tsas.rdma.qptype));
printf("rdma_cms: %s\n",
- cms_str(e->tsas.rdma.cms));
+ nvmf_cms_str(e->tsas.rdma.cms));
printf("rdma_pkey: 0x%04x\n",
le16_to_cpu(e->tsas.rdma.pkey));
break;
case NVMF_TRTYPE_TCP:
printf("sectype: %s\n",
- sectype_str(e->tsas.tcp.sectype));
+ nvmf_sectype_str(e->tsas.tcp.sectype));
break;
}
}
}
-static void json_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec,
- int instance)
+static void json_discovery_log(struct nvmf_discovery_log *log, int numrec)
{
struct json_object *root;
struct json_object *entries;
- char *dev_name = NULL;
int i;
- if (asprintf(&dev_name, "nvme%d", instance) < 0)
- return;
-
root = json_create_object();
entries = json_create_array();
- json_object_add_value_string(root, "device", dev_name);
- json_object_add_value_uint(root, "genctr", le64_to_cpu(log->genctr));
+ json_object_add_value_uint64(root, "genctr", le64_to_cpu(log->genctr));
json_object_add_value_array(root, "records", entries);
for (i = 0; i < numrec; i++) {
- struct nvmf_disc_rsp_page_entry *e = &log->entries[i];
+ struct nvmf_disc_log_entry *e = &log->entries[i];
struct json_object *entry = json_create_object();
nvme_strip_spaces(e->trsvcid, NVMF_TRSVCID_SIZE);
@@ -757,32 +267,34 @@ static void json_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec,
nvme_strip_spaces(e->traddr, NVMF_TRADDR_SIZE);
json_object_add_value_string(entry, "trtype",
- trtype_str(e->trtype));
+ nvmf_trtype_str(e->trtype));
json_object_add_value_string(entry, "adrfam",
- adrfam_str(e->adrfam));
+ nvmf_adrfam_str(e->adrfam));
json_object_add_value_string(entry, "subtype",
- subtype_str(e->subtype));
+ nvmf_subtype_str(e->subtype));
json_object_add_value_string(entry,"treq",
- treq_str(e->treq));
- json_object_add_value_uint(entry, "portid", e->portid);
+ nvmf_treq_str(e->treq));
+ json_object_add_value_uint(entry, "portid",
+ le16_to_cpu(e->portid));
json_object_add_value_string(entry, "trsvcid", e->trsvcid);
json_object_add_value_string(entry, "subnqn", e->subnqn);
json_object_add_value_string(entry, "traddr", e->traddr);
+ json_object_add_value_uint(entry, "eflags", e->eflags);
switch (e->trtype) {
case NVMF_TRTYPE_RDMA:
json_object_add_value_string(entry, "rdma_prtype",
- prtype_str(e->tsas.rdma.prtype));
+ nvmf_prtype_str(e->tsas.rdma.prtype));
json_object_add_value_string(entry, "rdma_qptype",
- qptype_str(e->tsas.rdma.qptype));
+ nvmf_qptype_str(e->tsas.rdma.qptype));
json_object_add_value_string(entry, "rdma_cms",
- cms_str(e->tsas.rdma.cms));
+ nvmf_cms_str(e->tsas.rdma.cms));
json_object_add_value_uint(entry, "rdma_pkey",
le16_to_cpu(e->tsas.rdma.pkey));
break;
case NVMF_TRTYPE_TCP:
json_object_add_value_string(entry, "sectype",
- sectype_str(e->tsas.tcp.sectype));
+ nvmf_sectype_str(e->tsas.tcp.sectype));
break;
}
json_array_add_value_object(entries, entry);
@@ -790,1158 +302,980 @@ static void json_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec,
json_print_object(root, NULL);
printf("\n");
json_free_object(root);
- free(dev_name);
}
-static void save_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec)
+static void save_discovery_log(char *raw, struct nvmf_discovery_log *log)
{
- int fd;
- int len, ret;
+ uint64_t numrec = le64_to_cpu(log->numrec);
+ int fd, len, ret;
- fd = open(fabrics_cfg.raw, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
+ fd = open(raw, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
if (fd < 0) {
- msg(LOG_ERR, "failed to open %s: %s\n",
- fabrics_cfg.raw, strerror(errno));
+ fprintf(stderr, "failed to open %s: %s\n",
+ raw, strerror(errno));
return;
}
- len = sizeof(struct nvmf_disc_rsp_page_hdr) +
- numrec * sizeof(struct nvmf_disc_rsp_page_entry);
+ len = sizeof(struct nvmf_discovery_log) +
+ numrec * sizeof(struct nvmf_disc_log_entry);
ret = write(fd, log, len);
if (ret < 0)
- msg(LOG_ERR, "failed to write to %s: %s\n",
- fabrics_cfg.raw, strerror(errno));
+ fprintf(stderr, "failed to write to %s: %s\n",
+ raw, strerror(errno));
else
- printf("Discovery log is saved to %s\n", fabrics_cfg.raw);
+ printf("Discovery log is saved to %s\n", raw);
close(fd);
}
-static void print_connect_msg(int instance)
+static void print_connect_msg(nvme_ctrl_t c)
{
- printf("device: nvme%d\n", instance);
+ printf("device: %s\n", nvme_ctrl_get_name(c));
}
-static void json_connect_msg(int instance)
+static void json_connect_msg(nvme_ctrl_t c)
{
struct json_object *root;
- char *dev_name = NULL;
-
- if (asprintf(&dev_name, "nvme%d", instance) < 0)
- return;
root = json_create_object();
- json_object_add_value_string(root, "device", dev_name);
+ json_object_add_value_string(root, "device", nvme_ctrl_get_name(c));
json_print_object(root, NULL);
printf("\n");
json_free_object(root);
- free(dev_name);
}
-static char *hostnqn_read_file(void)
+static int __discover(nvme_ctrl_t c, struct nvme_fabrics_config *defcfg,
+ char *raw, bool connect, bool persistent,
+ enum nvme_print_flags flags)
{
- FILE *f;
- char hostnqn[NVMF_NQN_SIZE];
- char *ret = NULL;
-
- f = fopen(PATH_NVMF_HOSTNQN, "r");
- if (f == NULL)
- return false;
-
- if (fgets(hostnqn, sizeof(hostnqn), f) == NULL ||
- !strlen(hostnqn))
- goto out;
+ struct nvmf_discovery_log *log = NULL;
+ nvme_subsystem_t s = nvme_ctrl_get_subsystem(c);
+ nvme_host_t h = nvme_subsystem_get_host(s);
+ uint64_t numrec;
+ int err;
- ret = strndup(hostnqn, strcspn(hostnqn, "\n"));
-out:
- fclose(f);
- if (ret && strcmp(ret, "") == 0) {
- free(ret);
- ret = NULL;
+ err = nvmf_get_discovery_log(c, &log, MAX_DISC_RETRIES);
+ if (err) {
+ if (err > 0)
+ nvme_show_status(err);
+ else
+ fprintf(stderr, "failed to get discovery log: %s\n",
+ nvme_strerror(errno));
+ return err;
}
- return ret;
-}
-
-static char *hostnqn_generate_systemd(void)
-{
-#ifdef HAVE_SYSTEMD
- sd_id128_t id;
- char *ret;
- if (sd_id128_get_machine_app_specific(NVME_HOSTNQN_ID, &id) < 0)
- return NULL;
- if (asprintf(&ret, "nqn.2014-08.org.nvmexpress:uuid:" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(id)) == -1)
- ret = NULL;
-
- return ret;
-#else
- return NULL;
-#endif
-}
-
-static char *hostnqn_read_dmi(void)
-{
- char uuid[16];
- char *ret = NULL;
-
- if (uuid_from_dmi(uuid) < 0)
- return NULL;
- if (asprintf(&ret, "nqn.2014-08.org.nvmexpress:uuid:%s", uuid) == -1)
- return NULL;
- return ret;
-}
-
-/* returns an allocated string or NULL */
-char *hostnqn_read(void)
-{
- char *ret;
-
- ret = hostnqn_read_file();
- if (ret)
- return ret;
-
- ret = hostnqn_read_dmi();
- if (ret)
- return ret;
-
- ret = hostnqn_generate_systemd();
- if (ret)
- return ret;
-
- return NULL;
-}
-
-static int nvmf_hostnqn_file(void)
-{
- fabrics_cfg.hostnqn = hostnqn_read();
-
- return fabrics_cfg.hostnqn != NULL;
-}
-
-static int nvmf_hostid_file(void)
-{
- FILE *f;
- char hostid[NVMF_HOSTID_SIZE + 1];
- int ret = false;
-
- f = fopen(PATH_NVMF_HOSTID, "r");
- if (f == NULL)
- return false;
-
- if (fgets(hostid, sizeof(hostid), f) == NULL)
- goto out;
-
- fabrics_cfg.hostid = strdup(hostid);
- if (!fabrics_cfg.hostid)
- goto out;
-
- ret = true;
-out:
- fclose(f);
- return ret;
-}
-
-static int
-add_bool_argument(char **argstr, int *max_len, char *arg_str, bool arg)
-{
- int len;
-
- if (arg) {
- len = snprintf(*argstr, *max_len, ",%s", arg_str);
- if (len < 0)
- return -EINVAL;
- *argstr += len;
- *max_len -= len;
+ numrec = le64_to_cpu(log->numrec);
+ if (raw)
+ save_discovery_log(raw, log);
+ else if (!connect) {
+ if (flags == JSON)
+ json_discovery_log(log, numrec);
+ else
+ print_discovery_log(log, numrec);
+ } else if (connect) {
+ int i;
+
+ for (i = 0; i < numrec; i++) {
+ struct nvmf_disc_log_entry *e = &log->entries[i];
+ bool discover = false;
+ nvme_ctrl_t child;
+ int tmo = defcfg->keep_alive_tmo;
+
+ if (e->subtype == NVME_NQN_DISC)
+ set_discovery_kato(defcfg);
+
+ errno = 0;
+ child = nvmf_connect_disc_entry(h, e, defcfg,
+ &discover);
+
+ defcfg->keep_alive_tmo = tmo;
+
+ if (child) {
+ if (discover)
+ __discover(child, defcfg, raw,
+ true, persistent, flags);
+ if (e->subtype != NVME_NQN_NVME &&
+ !persistent) {
+ nvme_disconnect_ctrl(child);
+ nvme_free_ctrl(child);
+ }
+ } else if (errno == EALREADY && !quiet) {
+ char *traddr = log->entries[i].traddr;
+
+ space_strip_len(NVMF_TRADDR_SIZE, traddr);
+ fprintf(stderr,
+ "traddr=%s is already connected\n",
+ traddr);
+ }
+ }
}
+ free(log);
return 0;
}
-static int
-add_int_argument(char **argstr, int *max_len, char *arg_str, int arg,
- bool allow_zero)
+/*
+ * Compare two C strings and handle NULL pointers gracefully.
+ * If either of the two strings is NULL, return 0
+ * to let caller ignore the compare.
+ */
+static inline int strcmp0(const char *s1, const char *s2)
{
- int len;
-
- if (arg || allow_zero) {
- len = snprintf(*argstr, *max_len, ",%s=%d", arg_str, arg);
- if (len < 0)
- return -EINVAL;
- *argstr += len;
- *max_len -= len;
- }
-
- return 0;
+ if (!s1 || !s2)
+ return 0;
+ return strcmp(s1, s2);
}
-static int
-add_argument(char **argstr, int *max_len, char *arg_str, const char *arg)
+/*
+ * Compare two C strings and handle NULL pointers gracefully.
+ * If either of the two strings is NULL, return 0
+ * to let caller ignore the compare.
+ */
+static inline int strcasecmp0(const char *s1, const char *s2)
{
- int len;
-
- if (arg && strcmp(arg, "none")) {
- len = snprintf(*argstr, *max_len, ",%s=%s", arg_str, arg);
- if (len < 0)
- return -EINVAL;
- *argstr += len;
- *max_len -= len;
- }
-
- return 0;
+ if (!s1 || !s2)
+ return 0;
+ return strcasecmp(s1, s2);
}
-int build_options(char *argstr, int max_len, bool discover)
+static bool ctrl_config_match(nvme_ctrl_t c, struct tr_config *trcfg)
{
- int len;
+ if (!strcmp0(nvme_ctrl_get_transport(c), trcfg->transport) &&
+ !strcasecmp0(nvme_ctrl_get_traddr(c), trcfg->traddr) &&
+ !strcmp0(nvme_ctrl_get_trsvcid(c), trcfg->trsvcid) &&
+ !strcmp0(nvme_ctrl_get_host_traddr(c), trcfg->host_traddr) &&
+ !strcmp0(nvme_ctrl_get_host_iface(c), trcfg->host_iface))
+ return true;
- if (!fabrics_cfg.transport) {
- msg(LOG_ERR, "need a transport (-t) argument\n");
- return -EINVAL;
- }
+ return false;
+}
- if (strncmp(fabrics_cfg.transport, "loop", 4)) {
- if (!fabrics_cfg.traddr) {
- msg(LOG_ERR, "need a address (-a) argument\n");
- return -EINVAL;
+static nvme_ctrl_t lookup_discover_ctrl(nvme_root_t r, struct tr_config *trcfg)
+{
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ nvme_subsystem_for_each_ctrl(s, c) {
+ if (!nvme_ctrl_is_discovery_ctrl(c))
+ continue;
+ if (ctrl_config_match(c, trcfg))
+ return c;
+ }
}
}
- /* always specify nqn as first arg - this will init the string */
- len = snprintf(argstr, max_len, "nqn=%s", fabrics_cfg.nqn);
- if (len < 0)
- return -EINVAL;
- argstr += len;
- max_len -= len;
-
- if (add_argument(&argstr, &max_len, "transport", fabrics_cfg.transport) ||
- add_argument(&argstr, &max_len, "traddr", fabrics_cfg.traddr) ||
- add_argument(&argstr, &max_len, "host_traddr", fabrics_cfg.host_traddr) ||
- add_argument(&argstr, &max_len, "host_iface", fabrics_cfg.host_iface) ||
- add_argument(&argstr, &max_len, "trsvcid", fabrics_cfg.trsvcid) ||
- ((fabrics_cfg.hostnqn || nvmf_hostnqn_file()) &&
- add_argument(&argstr, &max_len, "hostnqn", fabrics_cfg.hostnqn)) ||
- ((fabrics_cfg.hostid || nvmf_hostid_file()) &&
- add_argument(&argstr, &max_len, "hostid", fabrics_cfg.hostid)) ||
- (!discover &&
- add_int_argument(&argstr, &max_len, "nr_io_queues",
- fabrics_cfg.nr_io_queues, false)) ||
- add_int_argument(&argstr, &max_len, "nr_write_queues",
- fabrics_cfg.nr_write_queues, false) ||
- add_int_argument(&argstr, &max_len, "nr_poll_queues",
- fabrics_cfg.nr_poll_queues, false) ||
- (!discover &&
- add_int_argument(&argstr, &max_len, "queue_size",
- fabrics_cfg.queue_size, false)) ||
- add_int_argument(&argstr, &max_len, "keep_alive_tmo",
- fabrics_cfg.keep_alive_tmo, false) ||
- add_int_argument(&argstr, &max_len, "reconnect_delay",
- fabrics_cfg.reconnect_delay, false) ||
- (strncmp(fabrics_cfg.transport, "loop", 4) &&
- add_int_argument(&argstr, &max_len, "ctrl_loss_tmo",
- fabrics_cfg.ctrl_loss_tmo, true)) ||
- (fabrics_cfg.fast_io_fail_tmo != -1 &&
- add_int_argument(&argstr, &max_len, "fast_io_fail_tmo",
- fabrics_cfg.fast_io_fail_tmo, true)) ||
- (fabrics_cfg.tos != -1 &&
- add_int_argument(&argstr, &max_len, "tos",
- fabrics_cfg.tos, true)) ||
- add_bool_argument(&argstr, &max_len, "duplicate_connect",
- fabrics_cfg.duplicate_connect) ||
- add_bool_argument(&argstr, &max_len, "disable_sqflow",
- fabrics_cfg.disable_sqflow) ||
- add_bool_argument(&argstr, &max_len, "hdr_digest", fabrics_cfg.hdr_digest) ||
- add_bool_argument(&argstr, &max_len, "data_digest", fabrics_cfg.data_digest))
- return -EINVAL;
-
- return 0;
-}
-
-static void set_discovery_kato(struct fabrics_config *cfg)
-{
- /* Set kato to NVMF_DEF_DISC_TMO for persistent controllers */
- if (cfg->persistent && !cfg->keep_alive_tmo)
- cfg->keep_alive_tmo = NVMF_DEF_DISC_TMO;
- /* Set kato to zero for non-persistent controllers */
- else if (!cfg->persistent && (cfg->keep_alive_tmo > 0))
- cfg->keep_alive_tmo = 0;
+ return NULL;
}
-static void discovery_trsvcid(struct fabrics_config *fabrics_cfg, bool discover)
+static char *get_default_trsvcid(const char *transport,
+ bool discovery_ctrl)
{
- if (!strcmp(fabrics_cfg->transport, "tcp")) {
- if (discover) {
+ if (!strcmp(transport, "tcp")) {
+ if (discovery_ctrl) {
/* Default port for NVMe/TCP discovery controllers */
- fabrics_cfg->trsvcid = __stringify(NVME_DISC_IP_PORT);
+ return stringify(NVME_DISC_IP_PORT);
} else {
/* Default port for NVMe/TCP io controllers */
- fabrics_cfg->trsvcid = __stringify(NVME_RDMA_IP_PORT);
+ return stringify(NVME_RDMA_IP_PORT);
}
- } else if (!strcmp(fabrics_cfg->transport, "rdma")) {
+ } else if (!strcmp(transport, "rdma")) {
/* Default port for NVMe/RDMA controllers */
- fabrics_cfg->trsvcid = __stringify(NVME_RDMA_IP_PORT);
+ return stringify(NVME_RDMA_IP_PORT);
}
-}
-static bool traddr_is_hostname(struct fabrics_config *fabrics_cfg)
-{
- char addrstr[NVMF_TRADDR_SIZE];
-
- if (!fabrics_cfg->traddr || !fabrics_cfg->transport)
- return false;
- if (strcmp(fabrics_cfg->transport, "tcp") && strcmp(fabrics_cfg->transport, "rdma"))
- return false;
- if (inet_pton(AF_INET, fabrics_cfg->traddr, addrstr) > 0 ||
- inet_pton(AF_INET6, fabrics_cfg->traddr, addrstr) > 0)
- return false;
- return true;
+ return NULL;
}
-static int hostname2traddr(struct fabrics_config *fabrics_cfg)
+static int discover_from_conf_file(nvme_root_t r, nvme_host_t h,
+ const char *desc, bool connect,
+ const struct nvme_fabrics_config *defcfg)
{
- struct addrinfo *host_info, hints = {.ai_family = AF_UNSPEC};
- char addrstr[NVMF_TRADDR_SIZE];
- const char *p;
- int ret;
-
- ret = getaddrinfo(fabrics_cfg->traddr, NULL, &hints, &host_info);
- if (ret) {
- msg(LOG_ERR, "failed to resolve host %s info\n", fabrics_cfg->traddr);
- return ret;
- }
-
- switch (host_info->ai_family) {
- case AF_INET:
- p = inet_ntop(host_info->ai_family,
- &(((struct sockaddr_in *)host_info->ai_addr)->sin_addr),
- addrstr, NVMF_TRADDR_SIZE);
- break;
- case AF_INET6:
- p = inet_ntop(host_info->ai_family,
- &(((struct sockaddr_in6 *)host_info->ai_addr)->sin6_addr),
- addrstr, NVMF_TRADDR_SIZE);
- break;
- default:
- msg(LOG_ERR, "unrecognized address family (%d) %s\n",
- host_info->ai_family, fabrics_cfg->traddr);
- ret = -EINVAL;
- goto free_addrinfo;
- }
-
- if (!p) {
- msg(LOG_ERR, "failed to get traddr for %s\n", fabrics_cfg->traddr);
- ret = -errno;
- goto free_addrinfo;
- }
- fabrics_cfg->traddr = strdup(addrstr);
-
-free_addrinfo:
- freeaddrinfo(host_info);
- return ret;
-}
+ char *transport = NULL, *traddr = NULL, *trsvcid = NULL;
+ char *hostnqn = NULL, *hostid = NULL, *hostkey = NULL, *ctrlkey = NULL;
+ char *subsysnqn = NULL;
+ char *ptr, **argv, *p, line[4096];
+ int argc, ret = 0;
+ unsigned int verbose = 0;
+ FILE *f;
+ enum nvme_print_flags flags;
+ char *format = "normal";
+ struct nvme_fabrics_config cfg;
+ bool force = false;
-static int connect_ctrl(struct nvmf_disc_rsp_page_entry *e)
-{
- char argstr[BUF_SIZE], *p;
- const char *transport;
- bool discover, disable_sqflow = true;
- int len, ret;
-
-retry:
- p = argstr;
- discover = false;
-
- switch (e->subtype) {
- case NVME_NQN_DISC:
- discover = true;
- case NVME_NQN_NVME:
- break;
- default:
- msg(LOG_ERR, "skipping unsupported subtype %d\n",
- e->subtype);
- return -EINVAL;
- }
+ OPT_ARGS(opts) = {
+ NVMF_OPTS(cfg),
+ OPT_FMT("output-format", 'o', &format, output_format),
+ OPT_FILE("raw", 'r', &raw, "save raw output to file"),
+ OPT_FLAG("persistent", 'p', &persistent, "persistent discovery connection"),
+ OPT_FLAG("quiet", 'S', &quiet, "suppress already connected errors"),
+ OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"),
+ OPT_FLAG("force", 0, &force, "Force persistent discovery controller creation"),
+ OPT_END()
+ };
- len = sprintf(p, "nqn=%s", e->subnqn);
- if (len < 0)
- return -EINVAL;
- p += len;
+ nvmf_default_config(&cfg);
- if (fabrics_cfg.hostnqn && strcmp(fabrics_cfg.hostnqn, "none")) {
- len = sprintf(p, ",hostnqn=%s", fabrics_cfg.hostnqn);
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+ ret = flags = validate_output_format(format);
+ if (ret < 0)
+ return ret;
- if (fabrics_cfg.hostid && strcmp(fabrics_cfg.hostid, "none")) {
- len = sprintf(p, ",hostid=%s", fabrics_cfg.hostid);
- if (len < 0)
- return -EINVAL;
- p += len;
+ f = fopen(PATH_NVMF_DISC, "r");
+ if (f == NULL) {
+ errno = ENOENT;
+ return -1;
}
- if (fabrics_cfg.queue_size && !discover) {
- len = sprintf(p, ",queue_size=%d", fabrics_cfg.queue_size);
- if (len < 0)
- return -EINVAL;
- p += len;
+ argv = calloc(MAX_DISC_ARGS, sizeof(char *));
+ if (!argv) {
+ ret = -1;
+ goto out;
}
- if (fabrics_cfg.nr_io_queues && !discover) {
- len = sprintf(p, ",nr_io_queues=%d", fabrics_cfg.nr_io_queues);
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+ argv[0] = "discover";
+ memset(line, 0, sizeof(line));
+ while (fgets(line, sizeof(line), f) != NULL) {
+ nvme_ctrl_t c;
- if (fabrics_cfg.nr_write_queues) {
- len = sprintf(p, ",nr_write_queues=%d", fabrics_cfg.nr_write_queues);
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
- if (fabrics_cfg.nr_poll_queues) {
- len = sprintf(p, ",nr_poll_queues=%d", fabrics_cfg.nr_poll_queues);
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+ argc = 1;
+ p = line;
+ while ((ptr = strsep(&p, " =\n")) != NULL)
+ argv[argc++] = ptr;
+ argv[argc] = NULL;
- if (fabrics_cfg.host_traddr && strcmp(fabrics_cfg.host_traddr, "none")) {
- len = sprintf(p, ",host_traddr=%s", fabrics_cfg.host_traddr);
- if (len < 0)
- return -EINVAL;
- p+= len;
- }
+ memcpy(&cfg, defcfg, sizeof(cfg));
+ subsysnqn = NVME_DISC_SUBSYS_NAME;
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret)
+ goto next;
+ if (!transport && !traddr)
+ goto next;
+
+ if (!trsvcid)
+ trsvcid = get_default_trsvcid(transport, true);
+
+ struct tr_config trcfg = {
+ .subsysnqn = subsysnqn,
+ .transport = transport,
+ .traddr = traddr,
+ .host_traddr = cfg.host_traddr,
+ .host_iface = cfg.host_iface,
+ .trsvcid = trsvcid,
+ };
+
+ if (!force) {
+ c = lookup_discover_ctrl(r, &trcfg);
+ if (c) {
+ __discover(c, &cfg, raw, connect,
+ true, flags);
+ goto next;
+ }
+ }
- if (fabrics_cfg.host_iface && strcmp(fabrics_cfg.host_iface, "none")) {
- len = sprintf(p, ",host_iface=%s", fabrics_cfg.host_iface);
- if (len < 0)
- return -EINVAL;
- p+= len;
- }
+ c = create_discover_ctrl(r, h, &cfg, &trcfg);
+ if (!c)
+ goto next;
- if (fabrics_cfg.reconnect_delay) {
- len = sprintf(p, ",reconnect_delay=%d", fabrics_cfg.reconnect_delay);
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+ __discover(c, &cfg, raw, connect, persistent, flags);
+ if (!persistent)
+ ret = nvme_disconnect_ctrl(c);
+ nvme_free_ctrl(c);
- if ((e->trtype != NVMF_TRTYPE_LOOP) && (fabrics_cfg.ctrl_loss_tmo >= -1)) {
- len = sprintf(p, ",ctrl_loss_tmo=%d", fabrics_cfg.ctrl_loss_tmo);
- if (len < 0)
- return -EINVAL;
- p += len;
+next:
+ memset(&cfg, 0, sizeof(cfg));
}
+ free(argv);
+out:
+ fclose(f);
+ return ret;
+}
- if (fabrics_cfg.fast_io_fail_tmo) {
- len = sprintf(p, ",fast_io_fail_tmo=%d", fabrics_cfg.fast_io_fail_tmo);
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+int nvmf_discover(const char *desc, int argc, char **argv, bool connect)
+{
+ char *subsysnqn = NVME_DISC_SUBSYS_NAME;
+ char *hostnqn = NULL, *hostid = NULL, *hostkey = NULL, *ctrlkey = NULL;
+ char *transport = NULL, *traddr = NULL, *trsvcid = NULL;
+ char *config_file = PATH_NVMF_CONFIG;
+ char *hnqn = NULL, *hid = NULL;
+ enum nvme_print_flags flags;
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_ctrl_t c = NULL;
+ unsigned int verbose = 0;
+ int ret;
+ char *format = "normal";
+ struct nvme_fabrics_config cfg;
+ char *device = NULL;
+ bool force = false;
- if (fabrics_cfg.tos != -1) {
- len = sprintf(p, ",tos=%d", fabrics_cfg.tos);
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+ OPT_ARGS(opts) = {
+ OPT_STRING("device", 'd', "DEV", &device, "use existing discovery controller device"),
+ NVMF_OPTS(cfg),
+ OPT_FMT("output-format", 'o', &format, output_format),
+ OPT_FILE("raw", 'r', &raw, "save raw output to file"),
+ OPT_FLAG("persistent", 'p', &persistent, "persistent discovery connection"),
+ OPT_FLAG("quiet", 'S', &quiet, "suppress already connected errors"),
+ OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file),
+ OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"),
+ OPT_FLAG("dump-config", 'O', &dump_config, "Dump configuration file to stdout"),
+ OPT_FLAG("force", 0, &force, "Force persistent discovery controller creation"),
+ OPT_END()
+ };
- if (fabrics_cfg.keep_alive_tmo) {
- len = sprintf(p, ",keep_alive_tmo=%d", fabrics_cfg.keep_alive_tmo);
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+ nvmf_default_config(&cfg);
- transport = trtype_str(e->trtype);
- if (!strcmp(transport, "unrecognized")) {
- msg(LOG_ERR, "skipping unsupported transport %d\n",
- e->trtype);
- return -EINVAL;
- }
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret)
+ return ret;
- len = sprintf(p, ",transport=%s", transport);
- if (len < 0)
- return -EINVAL;
- p += len;
+ ret = flags = validate_output_format(format);
+ if (ret < 0)
+ return ret;
- if (fabrics_cfg.hdr_digest) {
- len = sprintf(p, ",hdr_digest");
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+ if (!strcmp(config_file, "none"))
+ config_file = NULL;
- if (fabrics_cfg.data_digest) {
- len = sprintf(p, ",data_digest");
- if (len < 0)
- return -EINVAL;
- p += len;
+ r = nvme_create_root(stderr, map_log_level(verbose, quiet));
+ if (!r) {
+ fprintf(stderr, "Failed to create topology root: %s\n",
+ nvme_strerror(errno));
+ return -errno;
}
-
- switch (e->trtype) {
- case NVMF_TRTYPE_RDMA:
- case NVMF_TRTYPE_TCP:
- switch (e->adrfam) {
- case NVMF_ADDR_FAMILY_IP4:
- case NVMF_ADDR_FAMILY_IP6:
- /* FALLTHRU */
- len = sprintf(p, ",traddr=%.*s",
- space_strip_len(NVMF_TRADDR_SIZE, e->traddr),
- e->traddr);
- if (len < 0)
- return -EINVAL;
- p += len;
-
- len = sprintf(p, ",trsvcid=%.*s",
- space_strip_len(NVMF_TRSVCID_SIZE, e->trsvcid),
- e->trsvcid);
- if (len < 0)
- return -EINVAL;
- p += len;
- break;
- default:
- msg(LOG_ERR, "skipping unsupported adrfam\n");
- return -EINVAL;
- }
- break;
- case NVMF_TRTYPE_FC:
- switch (e->adrfam) {
- case NVMF_ADDR_FAMILY_FC:
- len = sprintf(p, ",traddr=%.*s",
- space_strip_len(NVMF_TRADDR_SIZE, e->traddr),
- e->traddr);
- if (len < 0)
- return -EINVAL;
- p += len;
- break;
- default:
- msg(LOG_ERR, "skipping unsupported adrfam\n");
- return -EINVAL;
- }
- break;
+ ret = nvme_scan_topology(r, NULL, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to scan topoplogy: %s\n",
+ nvme_strerror(errno));
+ nvme_free_tree(r);
+ return ret;
}
- if (e->treq & NVMF_TREQ_DISABLE_SQFLOW && disable_sqflow) {
- len = sprintf(p, ",disable_sqflow");
- if (len < 0)
- return -EINVAL;
- p += len;
- }
+ if (!hostnqn)
+ hostnqn = hnqn = nvmf_hostnqn_from_file();
+ if (!hostnqn)
+ hostnqn = hnqn = nvmf_hostnqn_generate();
+ if (!hostid)
+ hostid = hid = nvmf_hostid_from_file();
+ h = nvme_lookup_host(r, hostnqn, hostid);
+ if (!h) {
+ ret = ENOMEM;
+ goto out_free;
+ }
+ if (device) {
+ if (!strcmp(device, "none"))
+ device = NULL;
+ else if (!strncmp(device, "/dev/", 5))
+ device += 5;
+ }
+ if (hostkey)
+ nvme_host_set_dhchap_key(h, hostkey);
+
+ if (!device && !transport && !traddr) {
+ ret = discover_from_conf_file(r, h, desc, connect, &cfg);
+ goto out_free;
+ }
+
+ if (!trsvcid)
+ trsvcid = get_default_trsvcid(transport, true);
+
+ struct tr_config trcfg = {
+ .subsysnqn = subsysnqn,
+ .transport = transport,
+ .traddr = traddr,
+ .host_traddr = cfg.host_traddr,
+ .host_iface = cfg.host_iface,
+ .trsvcid = trsvcid,
+ };
- if (discover) {
- enum nvme_print_flags flags;
-
- flags = validate_output_format(fabrics_cfg.output_format);
- if (flags < 0)
- flags = NORMAL;
- ret = do_discover(argstr, true, flags);
- } else
- ret = add_ctrl(argstr);
- if (ret == -EINVAL && disable_sqflow &&
- e->treq & NVMF_TREQ_DISABLE_SQFLOW) {
- /* disable_sqflow param might not be supported, try without it */
- disable_sqflow = false;
- goto retry;
+ if (device && !force) {
+ c = nvme_scan_ctrl(r, device);
+ if (c) {
+ /* Check if device matches command-line options */
+ if (!ctrl_config_match(c, &trcfg)) {
+ fprintf(stderr,
+ "ctrl device %s found, ignoring "
+ "non matching command-line options\n",
+ device);
+ }
+
+ if (!nvme_ctrl_is_discovery_ctrl(c)) {
+ fprintf(stderr,
+ "ctrl device %s found, ignoring "
+ "non discovery controller\n",
+ device);
+
+ nvme_free_ctrl(c);
+ c = NULL;
+ persistent = false;
+ } else {
+ /*
+ * If the controller device is found it must
+ * be persistent, and shouldn't be disconnected
+ * on exit.
+ */
+ persistent = true;
+ }
+ } else {
+ /*
+ * No controller found, fall back to create one.
+ * But that controller cannot be persistent.
+ */
+ fprintf(stderr,
+ "ctrl device %s not found%s\n", device,
+ persistent ? ", ignoring --persistent" : "");
+ persistent = false;
+ }
}
- return ret;
-}
-
-static bool cargs_match_found(struct nvmf_disc_rsp_page_entry *entry)
-{
- struct connect_args cargs __cleanup__(destruct_connect_args) = { NULL, };
- struct connect_args *c = tracked_ctrls;
-
- cargs.traddr = strdup(entry->traddr);
- cargs.transport = strdup(trtype_str(entry->trtype));
- cargs.subsysnqn = strdup(entry->subnqn);
- cargs.trsvcid = strdup(entry->trsvcid);
- cargs.host_traddr = strdup(fabrics_cfg.host_traddr ?: "\0");
- cargs.host_iface = strdup(fabrics_cfg.host_iface ?: "\0");
-
- /* check if we have a match in the discovery recursion */
- while (c) {
- if (!strcmp(cargs.subsysnqn, c->subsysnqn) &&
- !strcmp(cargs.transport, c->transport) &&
- !strcmp(cargs.traddr, c->traddr) &&
- !strcmp(cargs.trsvcid, c->trsvcid) &&
- !strcmp(cargs.host_traddr, c->host_traddr) &&
- !strcmp(cargs.host_iface, c->host_iface))
- return true;
- c = c->next;
+ if (!c && !force) {
+ c = lookup_discover_ctrl(r, &trcfg);
+ if (c)
+ persistent = true;
+ }
+ if (!c) {
+ /* No device or non-matching device, create a new controller */
+ c = create_discover_ctrl(r, h, &cfg, &trcfg);
+ if (!c) {
+ fprintf(stderr,
+ "failed to add controller, error %s\n",
+ nvme_strerror(errno));
+ ret = errno;
+ goto out_free;
+ }
}
- /* check if we have a matching existing controller */
- return find_ctrl_with_connectargs(&cargs) != NULL;
-}
-
-static bool should_connect(struct nvmf_disc_rsp_page_entry *entry)
-{
- int len;
-
- if (cargs_match_found(entry))
- return false;
+ ret = __discover(c, &cfg, raw, connect,
+ persistent, flags);
+ if (!persistent)
+ nvme_disconnect_ctrl(c);
+ nvme_free_ctrl(c);
- /* skip connect if the transport type doesn't match */
- if (strcmp(fabrics_cfg.transport, trtype_str(entry->trtype)))
- return false;
+out_free:
+ free(hnqn);
+ free(hid);
+ if (dump_config)
+ nvme_dump_config(r);
+ nvme_free_tree(r);
- if (!fabrics_cfg.matching_only || !fabrics_cfg.traddr)
- return true;
-
- len = space_strip_len(NVMF_TRADDR_SIZE, entry->traddr);
- return !strncmp(fabrics_cfg.traddr, entry->traddr, len);
+ return ret;
}
-static int connect_ctrls(struct nvmf_disc_rsp_page_hdr *log, int numrec)
+int nvmf_connect(const char *desc, int argc, char **argv)
{
- int i;
- int instance;
- int ret = 0;
-
- for (i = 0; i < numrec; i++) {
- if (!should_connect(&log->entries[i]))
- continue;
+ char *subsysnqn = NULL;
+ char *transport = NULL, *traddr = NULL;
+ char *trsvcid = NULL, *hostnqn = NULL, *hostid = NULL;
+ char *hostkey = NULL, *ctrlkey = NULL;
+ char *hnqn = NULL, *hid = NULL;
+ char *config_file = PATH_NVMF_CONFIG;
+ unsigned int verbose = 0;
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_ctrl_t c;
+ int ret;
+ struct nvme_fabrics_config cfg;
+ enum nvme_print_flags flags = -1;
+ char *format = "";
- instance = connect_ctrl(&log->entries[i]);
+ OPT_ARGS(opts) = {
+ NVMF_OPTS(cfg),
+ OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file),
+ OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"),
+ OPT_FLAG("dump-config", 'O', &dump_config, "Dump JSON configuration to stdout"),
+ OPT_FMT("output-format", 'o', &format, "Output format: normal|json"),
+ OPT_END()
+ };
- /* clean success */
- if (instance >= 0)
- continue;
+ nvmf_default_config(&cfg);
- /* already connected print message */
- if (instance == -EALREADY) {
- const char *traddr = log->entries[i].traddr;
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret)
+ return ret;
- msg(LOG_NOTICE, "traddr=%.*s is already connected\n",
- space_strip_len(NVMF_TRADDR_SIZE,
- traddr),
- traddr);
- continue;
- }
+ if (!strcmp(format, ""))
+ flags = -1;
+ else if (!strcmp(format, "normal"))
+ flags = NORMAL;
+ else if (!strcmp(format, "json"))
+ flags = JSON;
+ else
+ return EINVAL;
- /*
- * don't error out. The Discovery Log may contain
- * devices that aren't necessarily connectable via
- * the system/host transport port. Let those items
- * fail and continue on to the next log element.
- */
+ if (!subsysnqn) {
+ fprintf(stderr,
+ "required argument [--nqn | -n] not specified\n");
+ return EINVAL;
}
- return ret;
-}
-
-static void nvmf_get_host_identifiers(int ctrl_instance)
-{
- char *path;
-
- if (asprintf(&path, "%s/nvme%d", SYS_NVME, ctrl_instance) < 0)
- return;
- fabrics_cfg.hostnqn = nvme_get_ctrl_attr(path, "hostnqn");
- fabrics_cfg.hostid = nvme_get_ctrl_attr(path, "hostid");
-}
-
-static DEFINE_CLEANUP_FUNC(cleanup_log, struct nvmf_disc_rsp_page_hdr *, free);
-
-int do_discover(char *argstr, bool connect, enum nvme_print_flags flags)
-{
- struct nvmf_disc_rsp_page_hdr *log __cleanup__(cleanup_log) = NULL;
- char *dev_name;
- int instance, numrec = 0, ret, err;
- int status = 0;
- struct connect_args *cargs;
-
- cargs = extract_connect_args(argstr);
- if (!cargs)
- return -ENOMEM;
-
- if (fabrics_cfg.device &&
- !ctrl_matches_connectargs(fabrics_cfg.device, cargs)) {
- free(fabrics_cfg.device);
- fabrics_cfg.device = NULL;
+ if (!transport) {
+ fprintf(stderr,
+ "required argument [--transport | -t] not specified\n");
+ return EINVAL;
}
- if (!fabrics_cfg.device)
- fabrics_cfg.device = find_ctrl_with_connectargs(cargs);
- free_connect_args(cargs);
- if (!fabrics_cfg.device) {
- instance = add_ctrl(argstr);
- } else {
- instance = ctrl_instance(fabrics_cfg.device);
- nvmf_get_host_identifiers(instance);
+ if (strcmp(transport, "loop")) {
+ if (!traddr) {
+ fprintf(stderr,
+ "required argument [--address | -a] not specified for transport %s\n",
+ transport);
+ return EINVAL;
+ }
}
- if (instance < 0)
- return instance;
- if (asprintf(&dev_name, "/dev/nvme%d", instance) < 0)
+ if (!strcmp(config_file, "none"))
+ config_file = NULL;
+
+ r = nvme_create_root(stderr, map_log_level(verbose, quiet));
+ if (!r) {
+ fprintf(stderr, "Failed to create topology root: %s\n",
+ nvme_strerror(errno));
return -errno;
- ret = nvmf_get_log_page_discovery(dev_name, &log, &numrec, &status);
- free(dev_name);
- if (!fabrics_cfg.device && !fabrics_cfg.persistent) {
- err = remove_ctrl(instance);
- if (err)
- return err;
}
-
- switch (ret) {
- case DISC_OK:
- if (connect)
- ret = connect_ctrls(log, numrec);
- else if (fabrics_cfg.raw || flags == BINARY)
- save_discovery_log(log, numrec);
+ ret = nvme_scan_topology(r, NULL, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to scan topoplogy: %s\n",
+ nvme_strerror(errno));
+ nvme_free_tree(r);
+ return ret;
+ }
+ nvme_read_config(r, config_file);
+
+ if (!hostnqn)
+ hostnqn = hnqn = nvmf_hostnqn_from_file();
+ if (!hostnqn)
+ hostnqn = hnqn = nvmf_hostnqn_generate();
+ if (!hostid)
+ hostid = hid = nvmf_hostid_from_file();
+ h = nvme_lookup_host(r, hostnqn, hostid);
+ if (!h) {
+ errno = ENOMEM;
+ goto out_free;
+ }
+ if (hostkey)
+ nvme_host_set_dhchap_key(h, hostkey);
+ if (!trsvcid)
+ trsvcid = get_default_trsvcid(transport, false);
+ c = nvme_create_ctrl(r, subsysnqn, transport, traddr,
+ cfg.host_traddr, cfg.host_iface, trsvcid);
+ if (!c) {
+ errno = ENOMEM;
+ goto out_free;
+ }
+ if (ctrlkey)
+ nvme_ctrl_set_dhchap_key(c, ctrlkey);
+
+ errno = 0;
+ ret = nvmf_add_ctrl(h, c, &cfg);
+ if (ret)
+ fprintf(stderr, "no controller found: %s\n",
+ nvme_strerror(errno));
+ else {
+ errno = 0;
+ if (flags == NORMAL)
+ print_connect_msg(c);
else if (flags == JSON)
- json_discovery_log(log, numrec, instance);
- else
- print_discovery_log(log, numrec, instance);
- break;
- case DISC_GET_NUMRECS:
- msg(LOG_ERR,
- "Get number of discovery log entries failed.\n");
- ret = status;
- break;
- case DISC_GET_LOG:
- msg(LOG_ERR, "Get discovery log entries failed.\n");
- ret = status;
- break;
- case DISC_NO_LOG:
- fprintf(stdout, "No discovery log entries to fetch.\n");
- ret = DISC_OK;
- break;
- case DISC_RETRY_EXHAUSTED:
- fprintf(stdout, "Discovery retries exhausted.\n");
- ret = -EAGAIN;
- break;
- case DISC_NOT_EQUAL:
- msg(LOG_ERR,
- "Numrec values of last two get discovery log page not equal\n");
- ret = -EBADSLT;
- break;
- default:
- msg(LOG_ERR, "Get discovery log page failed: %d\n", ret);
- break;
+ json_connect_msg(c);
}
- return ret;
+out_free:
+ free(hnqn);
+ free(hid);
+ if (dump_config)
+ nvme_dump_config(r);
+ nvme_free_tree(r);
+ return errno;
}
-static int discover_from_conf_file(const char *desc, char *argstr,
- const struct argconfig_commandline_options *opts, bool connect)
+int nvmf_disconnect(const char *desc, int argc, char **argv)
{
- FILE *f;
- char line[256], *ptr, *all_args, *args, **argv;
- int argc, err, ret = 0;
-
- f = fopen(PATH_NVMF_DISC, "r");
- if (f == NULL) {
- msg(LOG_ERR, "No discover params given and no %s\n",
- PATH_NVMF_DISC);
- return -EINVAL;
- }
-
- while (fgets(line, sizeof(line), f) != NULL) {
- enum nvme_print_flags flags;
+ const char *device = "nvme device handle";
+ nvme_root_t r;
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+ char *p;
+ int ret;
- if (line[0] == '#' || line[0] == '\n')
- continue;
+ struct config {
+ char *nqn;
+ char *device;
+ unsigned int verbose;
+ };
- args = strdup(line);
- if (!args) {
- msg(LOG_ERR, "failed to strdup args\n");
- ret = -ENOMEM;
- goto out;
- }
- all_args = args;
+ struct config cfg = { 0 };
- argv = calloc(MAX_DISC_ARGS, BUF_SIZE);
- if (!argv) {
- msg(LOG_ERR, "failed to allocate argv vector: %m\n");
- free(args);
- ret = -ENOMEM;
- goto out;
- }
+ OPT_ARGS(opts) = {
+ OPT_STRING("nqn", 'n', "NAME", &cfg.nqn, nvmf_nqn),
+ OPT_STRING("device", 'd', "DEV", &cfg.device, device),
+ OPT_INCR("verbose", 'v', &cfg.verbose, "Increase logging verbosity"),
+ OPT_END()
+ };
- argc = 0;
- argv[argc++] = "discover";
- while ((ptr = strsep(&args, " =\n")) != NULL)
- argv[argc++] = ptr;
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret)
+ return ret;
- err = argconfig_parse(argc, argv, desc, opts);
- if (err)
- goto free_and_continue;
+ if (!cfg.nqn && !cfg.device) {
+ fprintf(stderr,
+ "Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n");
+ return EINVAL;
+ }
- if (!fabrics_cfg.transport || !fabrics_cfg.traddr)
- goto free_and_continue;
+ r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
+ if (!r) {
+ fprintf(stderr, "Failed to create topology root: %s\n",
+ nvme_strerror(errno));
+ return -errno;
+ }
+ ret = nvme_scan_topology(r, NULL, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to scan topoplogy: %s\n",
+ nvme_strerror(errno));
+ nvme_free_tree(r);
+ return ret;
+ }
- err = flags = validate_output_format(fabrics_cfg.output_format);
- if (err < 0)
- goto free_and_continue;
- set_discovery_kato(&fabrics_cfg);
+ if (cfg.nqn) {
+ int i = 0;
+ char *n = cfg.nqn;
- if (traddr_is_hostname(&fabrics_cfg)) {
- ret = hostname2traddr(&fabrics_cfg);
- if (ret)
- goto out;
+ while ((p = strsep(&n, ",")) != NULL) {
+ if (!strlen(p))
+ continue;
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ if (strcmp(nvme_subsystem_get_nqn(s), p))
+ continue;
+ nvme_subsystem_for_each_ctrl(s, c) {
+ if (!nvme_disconnect_ctrl(c))
+ i++;
+ }
+ }
+ }
}
-
- if (!fabrics_cfg.trsvcid)
- discovery_trsvcid(&fabrics_cfg, true);
-
- err = build_options(argstr, BUF_SIZE, true);
- if (err) {
- ret = err;
- goto free_and_continue;
+ printf("NQN:%s disconnected %d controller(s)\n", cfg.nqn, i);
+ }
+
+ if (cfg.device) {
+ char *d;
+
+ d = cfg.device;
+ while ((p = strsep(&d, ",")) != NULL) {
+ if (!strncmp(p, "/dev/", 5))
+ p += 5;
+ c = nvme_scan_ctrl(r, p);
+ if (!c) {
+ fprintf(stderr,
+ "Did not find device %s: %s\n",
+ p, nvme_strerror(errno));
+ nvme_free_tree(r);
+ return errno;
+ }
+ ret = nvme_disconnect_ctrl(c);
+ if (ret)
+ fprintf(stderr,
+ "Failed to disconnect %s: %s\n",
+ p, nvme_strerror(errno));
}
-
- err = do_discover(argstr, connect, flags);
- if (err)
- ret = err;
-
-free_and_continue:
- free(all_args);
- free(argv);
- fabrics_cfg.transport = fabrics_cfg.traddr =
- fabrics_cfg.trsvcid = fabrics_cfg.host_traddr =
- fabrics_cfg.host_iface = NULL;
}
+ nvme_free_tree(r);
-out:
- fclose(f);
- return ret;
+ return 0;
}
-int fabrics_discover(const char *desc, int argc, char **argv, bool connect)
+int nvmf_disconnect_all(const char *desc, int argc, char **argv)
{
- char argstr[BUF_SIZE];
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_root_t r;
+ nvme_ctrl_t c;
int ret;
- enum nvme_print_flags flags;
- bool quiet = false;
+
+ struct config {
+ char *transport;
+ unsigned int verbose;
+ };
+
+ struct config cfg = { 0 };
OPT_ARGS(opts) = {
- OPT_LIST("transport", 't', &fabrics_cfg.transport, "transport type"),
- OPT_LIST("traddr", 'a', &fabrics_cfg.traddr, "transport address"),
- OPT_LIST("trsvcid", 's', &fabrics_cfg.trsvcid, "transport service id (e.g. IP port)"),
- OPT_LIST("host-traddr", 'w', &fabrics_cfg.host_traddr, "host traddr (e.g. FC WWN's or IP source address)"),
- OPT_LIST("host-iface", 'f', &fabrics_cfg.host_iface, "host transport interface (e.g. IP eth1, enp2s0)"),
- OPT_LIST("hostnqn", 'q', &fabrics_cfg.hostnqn, "user-defined hostnqn (if default not used)"),
- OPT_LIST("hostid", 'I', &fabrics_cfg.hostid, "user-defined hostid (if default not used)"),
- OPT_LIST("raw", 'r', &fabrics_cfg.raw, "raw output file"),
- OPT_LIST("device", 'd', &fabrics_cfg.device, "existing discovery controller device"),
- OPT_INT("keep-alive-tmo", 'k', &fabrics_cfg.keep_alive_tmo, "keep alive timeout period in seconds"),
- OPT_INT("reconnect-delay", 'c', &fabrics_cfg.reconnect_delay, "reconnect timeout period in seconds"),
- OPT_INT("ctrl-loss-tmo", 'l', &fabrics_cfg.ctrl_loss_tmo, "controller loss timeout period in seconds"),
- OPT_INT("fast_io_fail_tmo",'f',&fabrics_cfg.fast_io_fail_tmo, "fast I/O fail timeout (default off)"),
- OPT_INT("tos", 'T', &fabrics_cfg.tos, "type of service"),
- OPT_FLAG("hdr_digest", 'g', &fabrics_cfg.hdr_digest, "enable transport protocol header digest (TCP transport)"),
- OPT_FLAG("data_digest", 'G', &fabrics_cfg.data_digest, "enable transport protocol data digest (TCP transport)"),
- OPT_INT("nr-io-queues", 'i', &fabrics_cfg.nr_io_queues, "number of io queues to use (default is core count)"),
- OPT_INT("nr-write-queues", 'W', &fabrics_cfg.nr_write_queues, "number of write queues to use (default 0)"),
- OPT_INT("nr-poll-queues", 'P', &fabrics_cfg.nr_poll_queues, "number of poll queues to use (default 0)"),
- OPT_INT("queue-size", 'Q', &fabrics_cfg.queue_size, "number of io queue elements to use (default 128)"),
- OPT_FLAG("persistent", 'p', &fabrics_cfg.persistent, "persistent discovery connection"),
- OPT_FLAG("quiet", 'S', &quiet, "suppress already connected errors"),
- OPT_FLAG("matching", 'm', &fabrics_cfg.matching_only, "connect only records matching the traddr"),
- OPT_FMT("output-format", 'o', &fabrics_cfg.output_format, output_format),
+ OPT_STRING("transport", 'r', "STR", (char *)&cfg.transport, nvmf_tport),
+ OPT_INCR("verbose", 'v', &cfg.verbose, "Increase logging verbosity"),
OPT_END()
};
- fabrics_cfg.tos = -1;
ret = argconfig_parse(argc, argv, desc, opts);
if (ret)
- goto out;
+ return ret;
- ret = flags = validate_output_format(fabrics_cfg.output_format);
- if (ret < 0)
- goto out;
- if (fabrics_cfg.device && strcmp(fabrics_cfg.device, "none")) {
- fabrics_cfg.device = strdup(fabrics_cfg.device);
- if (!fabrics_cfg.device) {
- ret = -ENOMEM;
- goto out;
- }
+ r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
+ if (!r) {
+ fprintf(stderr, "Failed to create topology root: %s\n",
+ nvme_strerror(errno));
+ return -errno;
+ }
+ ret = nvme_scan_topology(r, NULL, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to scan topoplogy: %s\n",
+ nvme_strerror(errno));
+ nvme_free_tree(r);
+ return ret;
}
- if (quiet)
- log_level = LOG_WARNING;
-
- if (fabrics_cfg.device && !strcmp(fabrics_cfg.device, "none"))
- fabrics_cfg.device = NULL;
-
- fabrics_cfg.nqn = NVME_DISC_SUBSYS_NAME;
-
- if (!fabrics_cfg.transport && !fabrics_cfg.traddr) {
- ret = discover_from_conf_file(desc, argstr, opts, connect);
- } else {
- set_discovery_kato(&fabrics_cfg);
-
- if (traddr_is_hostname(&fabrics_cfg)) {
- ret = hostname2traddr(&fabrics_cfg);
- if (ret)
- goto out;
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ nvme_subsystem_for_each_ctrl(s, c) {
+ if (cfg.transport &&
+ strcmp(cfg.transport,
+ nvme_ctrl_get_transport(c)))
+ continue;
+ else if (!strcmp(nvme_ctrl_get_transport(c),
+ "pcie"))
+ continue;
+ if (nvme_disconnect_ctrl(c))
+ fprintf(stderr,
+ "failed to disconnect %s\n",
+ nvme_ctrl_get_name(c));
+ }
}
-
- if (!fabrics_cfg.trsvcid)
- discovery_trsvcid(&fabrics_cfg, true);
-
- ret = build_options(argstr, BUF_SIZE, true);
- if (ret)
- goto out;
-
- ret = do_discover(argstr, connect, flags);
}
+ nvme_free_tree(r);
-out:
- return nvme_status_to_errno(ret, true);
+ return 0;
}
-int fabrics_connect(const char *desc, int argc, char **argv)
+int nvmf_config(const char *desc, int argc, char **argv)
{
- char argstr[BUF_SIZE];
- int instance, ret;
- enum nvme_print_flags flags = -1;
+ char *subsysnqn = NULL;
+ char *transport = NULL, *traddr = NULL;
+ char *trsvcid = NULL, *hostnqn = NULL, *hostid = NULL;
+ char *hnqn = NULL, *hid = NULL;
+ char *hostkey = NULL, *ctrlkey = NULL;
+ char *config_file = PATH_NVMF_CONFIG;
+ unsigned int verbose = 0;
+ nvme_root_t r;
+ int ret;
+ struct nvme_fabrics_config cfg;
+ bool scan_tree = false, modify_config = false, update_config = false;
OPT_ARGS(opts) = {
- OPT_LIST("transport", 't', &fabrics_cfg.transport, "transport type"),
- OPT_LIST("nqn", 'n', &fabrics_cfg.nqn, "nqn name"),
- OPT_LIST("traddr", 'a', &fabrics_cfg.traddr, "transport address"),
- OPT_LIST("trsvcid", 's', &fabrics_cfg.trsvcid, "transport service id (e.g. IP port)"),
- OPT_LIST("host-traddr", 'w', &fabrics_cfg.host_traddr, "host traddr (e.g. FC WWN's or IP source address)"),
- OPT_LIST("host-iface", 'f', &fabrics_cfg.host_iface, "host transport interface (e.g. IP eth1, enp2s0)"),
- OPT_LIST("hostnqn", 'q', &fabrics_cfg.hostnqn, "user-defined hostnqn"),
- OPT_LIST("hostid", 'I', &fabrics_cfg.hostid, "user-defined hostid (if default not used)"),
- OPT_INT("nr-io-queues", 'i', &fabrics_cfg.nr_io_queues, "number of io queues to use (default is core count)"),
- OPT_INT("nr-write-queues", 'W', &fabrics_cfg.nr_write_queues, "number of write queues to use (default 0)"),
- OPT_INT("nr-poll-queues", 'P', &fabrics_cfg.nr_poll_queues, "number of poll queues to use (default 0)"),
- OPT_INT("queue-size", 'Q', &fabrics_cfg.queue_size, "number of io queue elements to use (default 128)"),
- OPT_INT("keep-alive-tmo", 'k', &fabrics_cfg.keep_alive_tmo, "keep alive timeout period in seconds"),
- OPT_INT("reconnect-delay", 'c', &fabrics_cfg.reconnect_delay, "reconnect timeout period in seconds"),
- OPT_INT("ctrl-loss-tmo", 'l', &fabrics_cfg.ctrl_loss_tmo, "controller loss timeout period in seconds"),
- OPT_INT("fast_io_fail_tmo", 'f', &fabrics_cfg.fast_io_fail_tmo, "fast I/O fail timeout (default off)"),
- OPT_INT("tos", 'T', &fabrics_cfg.tos, "type of service"),
- OPT_FLAG("duplicate-connect", 'D', &fabrics_cfg.duplicate_connect, "allow duplicate connections between same transport host and subsystem port"),
- OPT_FLAG("disable-sqflow", 'd', &fabrics_cfg.disable_sqflow, "disable controller sq flow control (default false)"),
- OPT_FLAG("hdr-digest", 'g', &fabrics_cfg.hdr_digest, "enable transport protocol header digest (TCP transport)"),
- OPT_FLAG("data-digest", 'G', &fabrics_cfg.data_digest, "enable transport protocol data digest (TCP transport)"),
- OPT_FMT("output-format", 'o', &fabrics_cfg.output_format, "Output format: normal|json"),
+ NVMF_OPTS(cfg),
+ OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file),
+ OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"),
+ OPT_FLAG("scan", 'R', &scan_tree, "Scan current NVMeoF topology"),
+ OPT_FLAG("modify", 'M', &modify_config, "Modify JSON configuration file"),
+ OPT_FLAG("dump", 'O', &dump_config, "Dump JSON configuration to stdout"),
+ OPT_FLAG("update", 'U', &update_config, "Update JSON configuration file"),
OPT_END()
};
- fabrics_cfg.output_format = "";
- fabrics_cfg.tos = -1;
+ nvmf_default_config(&cfg);
+
ret = argconfig_parse(argc, argv, desc, opts);
if (ret)
- goto out;
+ return ret;
- if (!strcmp(fabrics_cfg.output_format, ""))
- flags = -1;
- else if (!strcmp(fabrics_cfg.output_format, "normal"))
- flags = NORMAL;
- else if (!strcmp(fabrics_cfg.output_format, "json"))
- flags = JSON;
- else {
- ret = -EINVAL;
- goto out;
- }
+ if (!strcmp(config_file, "none"))
+ config_file = NULL;
- if (traddr_is_hostname(&fabrics_cfg)) {
- ret = hostname2traddr(&fabrics_cfg);
- if (ret)
+ r = nvme_create_root(stderr, map_log_level(verbose, quiet));
+ if (!r) {
+ fprintf(stderr, "Failed to create topology root: %s\n",
+ nvme_strerror(errno));
+ return -errno;
+ }
+ if (scan_tree) {
+ ret = nvme_scan_topology(r, NULL, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to scan topoplogy: %s\n",
+ nvme_strerror(errno));
+ nvme_free_tree(r);
+ return ret;
+ }
+ }
+ nvme_read_config(r, config_file);
+
+ if (modify_config) {
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+
+ if (!hostnqn)
+ hostnqn = hnqn = nvmf_hostnqn_from_file();
+ if (!hostid && hnqn)
+ hostid = hid = nvmf_hostid_from_file();
+ h = nvme_lookup_host(r, hostnqn, hostid);
+ if (!h) {
+ fprintf(stderr, "Failed to lookup host '%s': %s\n",
+ hostnqn, nvme_strerror(errno));
+ goto out;
+ }
+ if (hostkey)
+ nvme_host_set_dhchap_key(h, hostkey);
+ s = nvme_lookup_subsystem(h, NULL, subsysnqn);
+ if (!s) {
+ fprintf(stderr, "Failed to lookup subsystem '%s': %s\n",
+ subsysnqn, nvme_strerror(errno));
+ goto out;
+ }
+ c = nvme_lookup_ctrl(s, transport, traddr,
+ cfg.host_traddr, cfg.host_iface,
+ trsvcid, NULL);
+ if (!c) {
+ fprintf(stderr, "Failed to lookup controller: %s\n",
+ nvme_strerror(errno));
goto out;
+ }
+ nvmf_update_config(c, &cfg);
+ if (ctrlkey)
+ nvme_ctrl_set_dhchap_key(c, ctrlkey);
}
- if (!fabrics_cfg.trsvcid)
- discovery_trsvcid(&fabrics_cfg, false);
-
- ret = build_options(argstr, BUF_SIZE, false);
- if (ret)
- goto out;
-
- if (!fabrics_cfg.nqn) {
- msg(LOG_ERR, "need a -n argument\n");
- ret = -EINVAL;
- goto out;
- }
+ if (update_config)
+ nvme_update_config(r);
- instance = add_ctrl(argstr);
- if (instance < 0)
- ret = instance;
- else {
- if (flags == NORMAL)
- print_connect_msg(instance);
- else if (flags == JSON)
- json_connect_msg(instance);
- }
+ if (dump_config)
+ nvme_dump_config(r);
out:
- return nvme_status_to_errno(ret, true);
-}
-
-static int scan_sys_nvme_filter(const struct dirent *d)
-{
- if (!strcmp(d->d_name, "."))
- return 0;
- if (!strcmp(d->d_name, ".."))
- return 0;
- return 1;
+ if (hid)
+ free(hid);
+ if (hnqn)
+ free(hnqn);
+ nvme_free_tree(r);
+ return errno;
}
-/*
- * Returns 1 if disconnect occurred, 0 otherwise.
- */
-static int disconnect_subsys(const char *nqn, char *ctrl)
+static void dim_operation(nvme_ctrl_t c, enum nvmf_dim_tas tas, const char * name)
{
- char *sysfs_nqn_path = NULL, *sysfs_del_path = NULL;
- char subsysnqn[NVMF_NQN_SIZE] = {};
- int fd, ret = 0;
-
- if (asprintf(&sysfs_nqn_path, "%s/%s/subsysnqn", SYS_NVME, ctrl) < 0)
- goto free;
- if (asprintf(&sysfs_del_path, "%s/%s/delete_controller", SYS_NVME, ctrl) < 0)
- goto free;
-
- fd = open(sysfs_nqn_path, O_RDONLY);
- if (fd < 0) {
- msg(LOG_ERR, "Failed to open %s: %s\n",
- sysfs_nqn_path, strerror(errno));
- goto free;
+ static const char * const task[] = {
+ [NVMF_DIM_TAS_REGISTER] = "register",
+ [NVMF_DIM_TAS_DEREGISTER] = "deregister",
+ };
+ const char * t;
+ int status;
+ __u32 result;
+
+ t = (tas > NVMF_DIM_TAS_DEREGISTER || !task[tas]) ? "reserved" : task[tas];
+ status = nvmf_register_ctrl(c, tas, &result);
+ if (status == NVME_SC_SUCCESS) {
+ printf("%s DIM %s command success\n", name, t);
+ } else if (status < NVME_SC_SUCCESS) {
+ fprintf(stderr, "%s DIM %s command error. Status:0x%04x - %s\n",
+ name, t, status, nvme_status_to_string(status, false));
+ } else {
+ fprintf(stderr, "%s DIM %s command error. Result:0x%04x, Status:0x%04x - %s\n",
+ name, t, result, status, nvme_status_to_string(status, false));
}
-
- if (read(fd, subsysnqn, NVMF_NQN_SIZE) < 0)
- goto close;
-
- subsysnqn[strcspn(subsysnqn, "\n")] = '\0';
- if (strcmp(subsysnqn, nqn))
- goto close;
-
- if (!remove_ctrl_by_path(sysfs_del_path))
- ret = 1;
- close:
- close(fd);
- free:
- free(sysfs_del_path);
- free(sysfs_nqn_path);
- return ret;
-}
-
-/*
- * Returns the number of controllers successfully disconnected.
- */
-static int disconnect_by_nqn(const char *nqn)
-{
- struct dirent **devices = NULL;
- int i, n, ret = 0;
-
- if (strlen(nqn) > NVMF_NQN_SIZE)
- return -EINVAL;
-
- n = scandir(SYS_NVME, &devices, scan_sys_nvme_filter, alphasort);
- if (n < 0)
- return n;
-
- for (i = 0; i < n; i++)
- ret += disconnect_subsys(nqn, devices[i]->d_name);
-
- for (i = 0; i < n; i++)
- free(devices[i]);
- free(devices);
-
- return ret;
}
-static int disconnect_by_device(const char *device)
+int nvmf_dim(const char *desc, int argc, char **argv)
{
- int instance;
-
- instance = ctrl_instance(device);
- if (instance < 0)
- return instance;
- return remove_ctrl(instance);
-}
-
-int fabrics_disconnect(const char *desc, int argc, char **argv)
-{
- const char *nqn = "nqn name";
- const char *device = "nvme device";
+ enum nvmf_dim_tas tas;
+ nvme_root_t r;
+ nvme_ctrl_t c;
+ char *p;
int ret;
+ struct {
+ char *nqn;
+ char *device;
+ char *tas;
+ unsigned int verbose;
+ } cfg = { 0 };
+
OPT_ARGS(opts) = {
- OPT_LIST("nqn", 'n', &fabrics_cfg.nqn, nqn),
- OPT_LIST("device", 'd', &fabrics_cfg.device, device),
+ OPT_STRING("nqn", 'n', "NAME", &cfg.nqn, "Comma-separated list of DC nqn"),
+ OPT_STRING("device", 'd', "DEV", &cfg.device, "Comma-separated list of DC nvme device handle."),
+ OPT_STRING("task", 't', "TASK", &cfg.tas, "[register|deregister]"),
+ OPT_INCR("verbose", 'v', &cfg.verbose, "Increase logging verbosity"),
OPT_END()
};
ret = argconfig_parse(argc, argv, desc, opts);
if (ret)
- goto out;
+ return ret;
- if (!fabrics_cfg.nqn && !fabrics_cfg.device) {
- msg(LOG_ERR, "need a -n or -d argument\n");
- ret = -EINVAL;
- goto out;
+ if (!cfg.nqn && !cfg.device) {
+ fprintf(stderr,
+ "Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n");
+ return EINVAL;
}
- if (fabrics_cfg.nqn) {
- ret = disconnect_by_nqn(fabrics_cfg.nqn);
- if (ret < 0)
- msg(LOG_ERR, "Failed to disconnect by NQN: %s\n",
- fabrics_cfg.nqn);
- else {
- printf("NQN:%s disconnected %d controller(s)\n", fabrics_cfg.nqn, ret);
- ret = 0;
- }
+ if (!cfg.tas) {
+ fprintf(stderr,
+ "Task [--task | -t] must be specified\n");
+ return EINVAL;
}
- if (fabrics_cfg.device) {
- ret = disconnect_by_device(fabrics_cfg.device);
- if (ret)
- msg(LOG_ERR,
- "Failed to disconnect by device name: %s\n",
- fabrics_cfg.device);
+ /* Allow partial name (e.g. "reg" for "register" */
+ if (strstarts("register", cfg.tas)) {
+ tas = NVMF_DIM_TAS_REGISTER;
+ } else if (strstarts("deregister", cfg.tas)) {
+ tas = NVMF_DIM_TAS_DEREGISTER;
+ } else {
+ fprintf(stderr, "Invalid --task: %s\n", cfg.tas);
+ return EINVAL;
}
-out:
- return nvme_status_to_errno(ret, true);
-}
-
-int fabrics_disconnect_all(const char *desc, int argc, char **argv)
-{
- struct nvme_topology t = { };
- int i, j, err;
-
- OPT_ARGS(opts) = {
- OPT_END()
- };
-
- err = argconfig_parse(argc, argv, desc, opts);
- if (err)
- goto out;
-
- err = scan_subsystems(&t, NULL, 0, 0, NULL);
- if (err) {
- msg(LOG_ERR, "Failed to scan namespaces\n");
- goto out;
+ r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
+ if (!r) {
+ fprintf(stderr, "Failed to create topology root: %s\n",
+ nvme_strerror(errno));
+ return -errno;
+ }
+ ret = nvme_scan_topology(r, NULL, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to scan topoplogy: %s\n",
+ nvme_strerror(errno));
+ nvme_free_tree(r);
+ return ret;
}
- for (i = 0; i < t.nr_subsystems; i++) {
- struct nvme_subsystem *s = &t.subsystems[i];
-
- for (j = 0; j < s->nr_ctrls; j++) {
- struct nvme_ctrl *c = &s->ctrls[j];
+ if (cfg.nqn) {
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ char *n = cfg.nqn;
- if (!c->transport || !strcmp(c->transport, "pcie"))
+ while ((p = strsep(&n, ",")) != NULL) {
+ if (!strlen(p))
continue;
- err = disconnect_by_device(c->name);
- if (err)
- goto free;
+ nvme_for_each_host(r, h) {
+ nvme_for_each_subsystem(h, s) {
+ if (strcmp(nvme_subsystem_get_nqn(s), p))
+ continue;
+ nvme_subsystem_for_each_ctrl(s, c) {
+ dim_operation(c, tas, p);
+ }
+ }
+ }
}
}
-free:
- free_topology(&t);
-out:
- return nvme_status_to_errno(err, true);
+
+ if (cfg.device) {
+ char *d = cfg.device;
+
+ while ((p = strsep(&d, ",")) != NULL) {
+ if (!strncmp(p, "/dev/", 5))
+ p += 5;
+ c = nvme_scan_ctrl(r, p);
+ if (!c) {
+ fprintf(stderr,
+ "Did not find device %s: %s\n",
+ p, nvme_strerror(errno));
+ nvme_free_tree(r);
+ return errno;
+ }
+ dim_operation(c, tas, p);
+ }
+ }
+
+ nvme_free_tree(r);
+
+ return 0;
}