[PATCH 07/11] Implement discover command
Christoph Hellwig
hch at lst.de
Tue Jun 7 08:19:23 PDT 2016
From: Ming Lin <ming.l at ssi.samsung.com>
Example output:
root at host:~# nvme discover -t rdma -a 192.168.2.2 -p 1023
Discovery Log Number of Records 1, Generation counter 3
=====Discovery Log Entry 0======
trtype: 1
adrfam: 1
nqntype: 2
treq: 0
portid: 1
trsvcid: 1023
subnqn: nqn.testiqn
traddr: 192.168.2.2
rdma_prtype: 0
rdma_qptype: 0
rdma_cms: 0
rdma_pkey: 0x0000
Signed-off-by: Ming Lin <ming.l at ssi.samsung.com>
Tested-by: Armen Baloyan <armenx.baloyan at intel.com>
Tested-by: Sagi Grimberg <sagi at grimberg.me>
Reviewed-by: Jay Freyensee <james.p.freyensee at intel.com>
---
Makefile | 6 +-
fabrics.c | 393 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
fabrics.h | 6 +
nvme.c | 8 ++
4 files changed, 412 insertions(+), 1 deletion(-)
create mode 100644 fabrics.c
create mode 100644 fabrics.h
diff --git a/Makefile b/Makefile
index c125756..e43c65d 100644
--- a/Makefile
+++ b/Makefile
@@ -31,7 +31,8 @@ override CFLAGS += -DNVME_VERSION='"$(NVME_VERSION)"'
NVME_DPKG_VERSION=1~`lsb_release -sc`
-OBJS := argconfig.o suffix.o parser.o nvme-print.o nvme-ioctl.o nvme-lightnvm.o
+OBJS := argconfig.o suffix.o parser.o nvme-print.o nvme-ioctl.o \
+ nvme-lightnvm.o fabrics.o
nvme: nvme.c ./linux/nvme.h $(OBJS) NVME-VERSION-FILE
$(CC) $(CPPFLAGS) $(CFLAGS) nvme.c $(LDFLAGS) -o $(NVME) $(OBJS)
@@ -39,6 +40,9 @@ nvme: nvme.c ./linux/nvme.h $(OBJS) NVME-VERSION-FILE
nvme-ioctl.o: nvme-ioctl.c nvme-ioctl.h
$(CC) $(CPPFLAGS) $(CFLAGS) -c nvme-ioctl.c
+fabrics.o: fabrics.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c fabrics.c
+
parser.o: parser.c
$(CC) $(CPPFLAGS) $(CFLAGS) -c parser.c
diff --git a/fabrics.c b/fabrics.c
new file mode 100644
index 0000000..ad79017
--- /dev/null
+++ b/fabrics.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ * Copyright (c) 2016 HGST, a Western Digital Company.
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This file implements the discovery controller feature of NVMe over
+ * Fabrics specification standard.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <libudev.h>
+
+#include <linux/types.h>
+
+#include "parser.h"
+#include "nvme-ioctl.h"
+#include "fabrics.h"
+
+#include "linux/nvme.h"
+#include "src/argconfig.h"
+
+#include "common.h"
+
+struct config {
+ char *nqn;
+ char *transport;
+ char *traddr;
+ char *trsvcid;
+} cfg = { 0 };
+
+#define BUF_SIZE 4096
+#define PATH_NVME_FABRICS "/dev/nvme-fabrics"
+
+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 },
+};
+
+static int add_ctrl(const char *argstr)
+{
+ substring_t args[MAX_OPT_ARGS];
+ char buf[BUF_SIZE], *options, *p;
+ size_t len = strlen(argstr);
+ int token, ret, fd;
+
+ fd = open(PATH_NVME_FABRICS, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s: %s\n",
+ PATH_NVME_FABRICS, strerror(errno));
+ ret = -errno;
+ goto out;
+ }
+
+ if (write(fd, argstr, len) != len) {
+ fprintf(stderr, "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) {
+ fprintf(stderr, "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;
+ goto out_close;
+ default:
+ /* ignore */
+ break;
+ }
+ }
+
+out_fail:
+ fprintf(stderr, "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;
+ goto out;
+ }
+
+ if (write(fd, "1", 1) != 1) {
+ ret = errno;
+ goto out_close;
+ }
+
+ ret = 0;
+out_close:
+ close(fd);
+out:
+ return ret;
+}
+
+static 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;
+ }
+
+ ret = remove_ctrl_by_path(sysfs_path);
+ free(sysfs_path);
+out:
+ return ret;
+}
+
+enum {
+ DISC_OK,
+ DISC_NO_LOG,
+ DISC_GET_NUMRECS,
+ DISC_GET_LOG,
+ DISC_NOT_EQUAL,
+};
+
+static int nvmf_get_log_page_discovery(const char *dev_path,
+ struct nvmf_disc_rsp_page_hdr **logp, int *numrec)
+{
+ struct nvmf_disc_rsp_page_hdr *log;
+ unsigned log_size = 0;
+ unsigned long genctr;
+ int error, fd;
+
+ fd = open(dev_path, O_RDWR);
+ if (fd < 0) {
+ error = -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.
+ */
+ log_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, log_size);
+ if (!log) {
+ error = -ENOMEM;
+ goto out_close;
+ }
+
+ error = nvme_discovery_log(fd, log, log_size);
+ if (error) {
+ error = DISC_GET_NUMRECS;
+ goto out_free_log;
+ }
+
+ /* check numrec limits */
+ *numrec = le64toh(log->numrec);
+ genctr = le64toh(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) {
+ 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;
+ }
+
+ if (*numrec != le32toh(log->numrec) || genctr != le64toh(log->genctr)) {
+ error = DISC_NOT_EQUAL;
+ goto out_free_log;
+ }
+
+ /* needs to be freed by the caller */
+ *logp = log;
+ goto out_close;
+
+ error = DISC_OK;
+out_free_log:
+ free(log);
+out_close:
+ close(fd);
+out:
+ return error;
+}
+
+static void print_discovery_log(struct nvmf_disc_rsp_page_hdr *log, int numrec)
+{
+ int i;
+
+ printf("Discovery Log Number of Records %d, Generation counter %lld\n",
+ numrec, log->genctr);
+
+ for (i = 0; i < numrec; i++) {
+ struct nvmf_disc_rsp_page_entry *e = &log->entries[i];
+
+ printf("=====Discovery Log Entry %d======\n", i);
+ printf("trtype: %d\n", e->trtype);
+ printf("adrfam: %d\n", e->adrfam);
+ printf("nqntype: %d\n", e->nqntype);
+ printf("treq: %d\n", e->treq);
+ printf("portid: %d\n", e->portid);
+ printf("trsvcid: %s\n", e->trsvcid);
+ printf("subnqn: %s\n", e->subnqn);
+ printf("traddr: %s\n", e->traddr);
+
+ switch (e->trtype) {
+ case NVMF_TRTYPE_RDMA:
+ printf("rdma_prtype: %d\n", e->tsas.rdma.prtype);
+ printf("rdma_qptype: %d\n", e->tsas.rdma.qptype);
+ printf("rdma_cms: %d\n", e->tsas.rdma.cms);
+ printf("rdma_pkey: 0x%04x\n", e->tsas.rdma.pkey);
+ break;
+ }
+ }
+}
+
+static int build_options(char *argstr, int max_len)
+{
+ int len;
+
+ if (!cfg.transport) {
+ fprintf(stderr, "need a transport (-t) argument\n");
+ return -EINVAL;
+ }
+
+ if (strncmp(cfg.transport, "loop", 4)) {
+ if (!cfg.traddr) {
+ fprintf(stderr, "need a address (-a) argument\n");
+ return -EINVAL;
+ }
+ }
+
+ len = snprintf(argstr, max_len, "nqn=%s", cfg.nqn);
+ if (len < 0)
+ return -EINVAL;
+ argstr += len;
+ max_len -= len;
+
+ len = snprintf(argstr, max_len, ",transport=%s", cfg.transport);
+ if (len < 0)
+ return -EINVAL;
+ argstr += len;
+ max_len -= len;
+
+ if (cfg.traddr) {
+ len = snprintf(argstr, max_len, ",traddr=%s", cfg.traddr);
+ if (len < 0)
+ return -EINVAL;
+ argstr += len;
+ max_len -= len;
+ }
+
+ if (cfg.trsvcid) {
+ len = snprintf(argstr, max_len, ",trsvcid=%s", cfg.trsvcid);
+ if (len < 0)
+ return -EINVAL;
+ argstr += len;
+ max_len -= len;
+ }
+
+ return 0;
+}
+
+int discover(const char *desc, int argc, char **argv)
+{
+ char argstr[BUF_SIZE];
+ struct nvmf_disc_rsp_page_hdr *log = NULL;
+ char *dev_name;
+ int instance, numrec = 0, ret;
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"transport", 't', "LIST", CFG_STRING, &cfg.transport, required_argument,
+ "transport type" },
+ {"traddr", 'a', "LIST", CFG_STRING, &cfg.traddr, required_argument,
+ "transport address" },
+ {"trsvcid", 's', "LIST", CFG_STRING, &cfg.trsvcid, required_argument,
+ "transport service id (e.g. IP port)" },
+ {0},
+ };
+
+ argconfig_parse(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+
+ cfg.nqn = NVME_DISC_SUBSYS_NAME;
+
+ ret = build_options(argstr, BUF_SIZE);
+ if (ret)
+ return ret;
+
+ instance = add_ctrl(argstr);
+ if (instance < 0)
+ return instance;
+
+ if (asprintf(&dev_name, "/dev/nvme%d", instance) < 0)
+ return errno;
+ ret = nvmf_get_log_page_discovery(dev_name, &log, &numrec);
+ free(dev_name);
+ remove_ctrl(instance);
+
+ switch (ret) {
+ case DISC_OK:
+ print_discovery_log(log, numrec);
+ break;
+ case DISC_GET_NUMRECS:
+ fprintf(stderr, "Get number of discovery log entries failed.\n");
+ break;
+ case DISC_GET_LOG:
+ fprintf(stderr, "Get discovery log entries failed.\n");
+ break;
+ case DISC_NO_LOG:
+ fprintf(stderr, "No discovery log entries to fetch.\n");
+ break;
+ case DISC_NOT_EQUAL:
+ fprintf(stderr, "Numrec values of last two get dicovery log page not equal\n");
+ break;
+ default:
+ fprintf(stderr, "Get dicovery log page failed: %d\n", ret);
+ break;
+ }
+
+ return ret;
+}
diff --git a/fabrics.h b/fabrics.h
new file mode 100644
index 0000000..c80b016
--- /dev/null
+++ b/fabrics.h
@@ -0,0 +1,6 @@
+#ifndef _DISCOVER_H
+#define _DISCOVER_H
+
+extern int discover(const char *desc, int argc, char **argv);
+
+#endif
diff --git a/nvme.c b/nvme.c
index 72a85a4..2dd2f3a 100644
--- a/nvme.c
+++ b/nvme.c
@@ -54,6 +54,8 @@
#include "src/argconfig.h"
#include "src/suffix.h"
+#include "fabrics.h"
+
#define array_len(x) ((size_t)(sizeof(x) / sizeof(x[0])))
#define min(x, y) (x) > (y) ? (y) : (x)
#define max(x, y) (x) > (y) ? (x) : (y)
@@ -112,6 +114,7 @@ static const char nvme_version_string[] = NVME_VERSION;
ENTRY(LNVM_FACTORY, "lnvm-factory", "Reset device to factory state", lnvm_factory_init) \
ENTRY(LNVM_BBTBL_GET, "lnvm-diag-bbtbl", "Diagnose bad block table", lnvm_get_bbtbl) \
ENTRY(LNVM_BBTBL_SET, "lnvm-diag-set-bbtbl", "Update bad block table", lnvm_set_bbtbl) \
+ ENTRY(DISCOVER, "discover", "Discover NVMeoF subsystems", discover_cmd) \
ENTRY(VERSION, "version", "Shows the program version", version) \
ENTRY(HELP, "help", "Display this help", help)
@@ -2837,6 +2840,11 @@ static int lnvm_set_bbtbl(int argc, char **argv)
cfg.value);
}
+static int discover_cmd(int argc, char **argv)
+{
+ const char *desc = "Send command to discovery service.";
+ return discover(desc, argc, argv);
+}
static void usage()
{
--
2.1.4
More information about the Linux-nvme
mailing list