From a71a08532b5f06ee19ee2ff118a23cec4ecb6719 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 15 Jun 2022 11:42:50 -0500 Subject: [PATCH 02/11] nbft: added NBFT v1.0 table support Added support for parsing the contents of the NBFT table (per NVMe-oF boot specification v1.0) with the connect-all and discover commands. nvme discover/connect-all --nbft ignore /etc/nvme config and use NBFT tables nvme discover/connect-all --no-nbft ignore NBFT tables and use /etc/nvme config nvme discover/connect-all --nbft-path= user-defined path for NBFT tables Signed-off-by: Stuart Hayes Signed-off-by: Martin Belanger Use the new nbft public API. Signed-off-by: Tomas Bzatek Signed-off-by: Martin Wilck Signed-off-by: John Meneghini --- Documentation/nvme-connect-all.txt | 25 ++++ Documentation/nvme-discover.txt | 27 +++- fabrics.c | 17 +++ meson.build | 1 + nbft.c | 202 +++++++++++++++++++++++++++++ nbft.h | 19 +++ 6 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 nbft.c create mode 100644 nbft.h diff --git a/Documentation/nvme-connect-all.txt b/Documentation/nvme-connect-all.txt index 44bb4f94..cbb7ca6c 100644 --- a/Documentation/nvme-connect-all.txt +++ b/Documentation/nvme-connect-all.txt @@ -35,6 +35,9 @@ SYNOPSIS [--tls ] [--quiet | -S] [--dump-config | -O] + [--nbft] + [--no-nbft] + [--nbft-path=] DESCRIPTION ----------- @@ -198,6 +201,16 @@ OPTIONS --dump-config:: Print out resulting JSON configuration file to stdout. +--nbft:: + Only look at NBFT tables + +--no-nbft:: + Do not look at NBFT tables + +--nbft-path=:: + Use a user-defined path to the NBFT tables + + EXAMPLES -------- @@ -210,6 +223,18 @@ the RDMA network. Port 4420 is used by default: --hostnqn=host1-rogue-nqn ------------ + +* Issue a 'nvme connect-all' command using the default system defined NBFT tables: ++ +----------- +# nvme connect-all --nbft +------------ ++ +* Issue a 'nvme connect-all' command with a user-defined path for the NBFT table: ++ +----------- +# nvme connet-all --nbft-path=/sys/firmware/acpi/tables/NBFT1 +------------ ++ * Issue a 'nvme connect-all' command using a @SYSCONFDIR@/nvme/discovery.conf file: + ----------- diff --git a/Documentation/nvme-discover.txt b/Documentation/nvme-discover.txt index d4df75c2..b040c688 100644 --- a/Documentation/nvme-discover.txt +++ b/Documentation/nvme-discover.txt @@ -37,6 +37,9 @@ SYNOPSIS [--dump-config | -O] [--output-format= | -o ] [--force] + [--nbft] + [--no-nbft] + [--nbft-path=] DESCRIPTION ----------- @@ -68,7 +71,7 @@ Note that the base NVMe specification defines the NQN (NVMe Qualified Name) format which an NVMe endpoint (device, subsystem, etc) must follow to guarantee a unique name under the NVMe standard. In particular, the Host NQN uniquely identifies the NVMe Host, and -may be used by the the Discovery Controller to control what NVMe Target +may be used by the Discovery Controller to control what NVMe Target resources are allocated to the NVMe Host for a connection. A Discovery Controller has it's own NQN defined in the NVMe-over-Fabrics @@ -229,6 +232,16 @@ OPTIONS Combined with --persistent flag, always create new persistent discovery connection. +--nbft:: + Only look at NBFT tables + +--no-nbft:: + Do not look at NBFT tables + +--nbft-path=:: + Use a user-defined path to the NBFT tables + + EXAMPLES -------- * Query the Discover Controller with IP4 address 192.168.1.3 for all @@ -240,6 +253,18 @@ Port 4420 is used by default: --hostnqn=host1-rogue-nqn ------------ + +* Issue a 'nvme discover' command using the default system defined NBFT tables: ++ +----------- +# nvme discover --nbft +------------ ++ +* Issue a 'nvme discover' command with a user-defined path for the NBFT table: ++ +----------- +# nvme discover --nbft-path=/sys/firmware/acpi/tables/NBFT1 +------------ ++ * Issue a 'nvme discover' command using a @SYSCONFDIR@/nvme/discovery.conf file: + ----------- diff --git a/fabrics.c b/fabrics.c index 80827b16..0edbd299 100644 --- a/fabrics.c +++ b/fabrics.c @@ -40,6 +40,7 @@ #include "common.h" #include "nvme.h" +#include "nbft.h" #include "libnvme.h" #include "nvme-print.h" #include "nvme-print-json.h" @@ -710,6 +711,7 @@ 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; + char *hostnqn_arg, *hostid_arg; char *transport = NULL, *traddr = NULL, *trsvcid = NULL; char *config_file = PATH_NVMF_CONFIG; char *hnqn = NULL, *hid = NULL; @@ -724,6 +726,8 @@ int nvmf_discover(const char *desc, int argc, char **argv, bool connect) char *device = NULL; bool force = false; bool json_config = false; + bool nbft = false, nonbft = false; + char *nbft_path = NBFT_SYSFS_PATH; OPT_ARGS(opts) = { OPT_STRING("device", 'd', "DEV", &device, "use existing discovery controller device"), @@ -736,6 +740,9 @@ int nvmf_discover(const char *desc, int argc, char **argv, bool connect) 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_FLAG("nbft", 0, &nbft, "Only look at NBFT tables"), + OPT_FLAG("no-nbft", 0, &nonbft, "Do not look at NBFT tables"), + OPT_STRING("nbft-path", 0, "STR", &nbft_path, "user-defined path for NBFT tables"), OPT_END() }; @@ -768,6 +775,8 @@ int nvmf_discover(const char *desc, int argc, char **argv, bool connect) if (!nvme_read_config(r, config_file)) json_config = true; + hostnqn_arg = hostnqn; + hostid_arg = hostid; if (!hostnqn) hostnqn = hnqn = nvmf_hostnqn_from_file(); if (!hostnqn) @@ -789,6 +798,14 @@ int nvmf_discover(const char *desc, int argc, char **argv, bool connect) nvme_host_set_dhchap_key(h, hostkey); if (!device && !transport && !traddr) { + if (!nonbft) + discover_from_nbft(r, hostnqn_arg, hostid_arg, + hostnqn, hostid, desc, connect, + &cfg, nbft_path, flags, verbose); + + if (nbft) + goto out_free; + if (json_config) ret = discover_from_json_config_file(r, h, desc, connect, &cfg, diff --git a/meson.build b/meson.build index 43ce5f9b..7ec357a9 100644 --- a/meson.build +++ b/meson.build @@ -240,6 +240,7 @@ incdir = include_directories(['ccan']) ################################################################################ sources = [ + 'nbft.c', 'fabrics.c', 'nvme.c', 'nvme-models.c', diff --git a/nbft.c b/nbft.c new file mode 100644 index 00000000..14347287 --- /dev/null +++ b/nbft.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include "nvme.h" +#include "nbft.h" +#include "libnvme.h" +#include "fabrics.h" + +#define NBFT_SYSFS_FILENAME "NBFT*" + +static void print_connect_msg(nvme_ctrl_t c) +{ + printf("device: %s\n", nvme_ctrl_get_name(c)); +} + +static void json_connect_msg(nvme_ctrl_t c) +{ + struct json_object *root; + + root = json_create_object(); + json_object_add_value_string(root, "device", nvme_ctrl_get_name(c)); + + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +int nbft_filter(const struct dirent *dent) +{ + return !fnmatch(NBFT_SYSFS_FILENAME, dent->d_name, FNM_PATHNAME); +} + +int read_nbft_files(struct list_head *nbft_list, char *path) +{ + struct dirent **dent; + char filename[PATH_MAX]; + int i, count, ret; + struct nbft_file_entry *entry; + struct nbft_info *nbft; + + count = scandir(path, &dent, nbft_filter, NULL); + if (count < 0) { + fprintf(stderr, "Failed to open %s.\n", path); + return -1; + } + + for (i = 0; i < count; i++) { + snprintf(filename, sizeof(filename), "%s/%s", path, dent[i]->d_name); + ret = nvme_nbft_read(&nbft, filename); + if (!ret) { + entry = calloc(1, sizeof(*entry)); + entry->nbft = nbft; + list_add_tail(nbft_list, &entry->node); + } + free(dent[i]); + } + free(dent); + return 0; +} + +void free_nbfts(struct list_head *nbft_list) +{ + struct nbft_file_entry *entry; + + while ((entry = list_pop(nbft_list, struct nbft_file_entry, node))) { + nvme_nbft_free(entry->nbft); + free(entry); + } +} + +int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg, + char *hostnqn_sys, char *hostid_sys, + const char *desc, bool connect, + const struct nvme_fabrics_config *cfg, char *nbft_path, + enum nvme_print_flags flags, bool verbose) +{ + char *hostnqn = NULL, *hostid = NULL, *host_traddr = NULL; + nvme_host_t h; + nvme_ctrl_t c; + int ret, i; + struct list_head nbft_list; + struct nbft_file_entry *entry; + struct nbft_info_subsystem_ns **ss; + struct nbft_info_hfi *hfi; + + if (!connect) + /* to do: print discovery-type info from NBFT tables */ + return 0; + + list_head_init(&nbft_list); + ret = read_nbft_files(&nbft_list, nbft_path); + if (ret) + goto out_free_2; + + list_for_each(&nbft_list, entry, node) + for (ss = entry->nbft->subsystem_ns_list; ss && *ss; ss++) + for (i = 0; i < (*ss)->num_hfis; i++) { + nvme_ctrl_t cl; + + hfi = (*ss)->hfis[i]; + if (hostnqn_arg) + hostnqn = hostnqn_arg; + else { + hostnqn = entry->nbft->host.nqn; + if (!hostnqn) + hostnqn = hostnqn_sys; + } + + if (hostid_arg) + hostid = hostid_arg; + else if (*entry->nbft->host.id) { + hostid = (char *)util_uuid_to_string(entry->nbft->host.id); + if (!hostid) + hostid = hostid_sys; + } + + h = nvme_lookup_host(r, hostnqn, hostid); + if (!h) { + errno = ENOMEM; + goto out_free; + } + + if (!cfg->host_traddr) { + host_traddr = NULL; + if (!strncmp((*ss)->transport, "tcp", 3)) + host_traddr = hfi->tcp_info.ipaddr; + } + + struct tr_config trcfg = { + .subsysnqn = (*ss)->subsys_nqn, + .transport = (*ss)->transport, + .traddr = (*ss)->traddr, + .host_traddr = host_traddr, + .host_iface = NULL, + .trsvcid = (*ss)->trsvcid, + }; + + /* Already connected ? */ + cl = lookup_ctrl(r, &trcfg); + if (cl && nvme_ctrl_get_name(cl)) + continue; + + c = nvme_create_ctrl(r, (*ss)->subsys_nqn, (*ss)->transport, + (*ss)->traddr, host_traddr, NULL, + (*ss)->trsvcid); + if (!c) { + errno = ENOMEM; + goto out_free; + } + + errno = 0; + ret = nvmf_add_ctrl(h, c, cfg); + + /* + * With TCP/DHCP, it can happen that the OS + * obtains a different local IP address than the + * firmware had. Retry without host_traddr. + */ + if (ret == -1 && errno == ENVME_CONNECT_WRITE && + !strcmp((*ss)->transport, "tcp") && + strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) { + nvme_free_ctrl(c); + + trcfg.host_traddr = NULL; + cl = lookup_ctrl(r, &trcfg); + if (cl && nvme_ctrl_get_name(cl)) + continue; + + c = nvme_create_ctrl(r, (*ss)->subsys_nqn, (*ss)->transport, + (*ss)->traddr, + NULL, NULL, (*ss)->trsvcid); + if (!c) { + errno = ENOMEM; + goto out_free; + } + errno = 0; + ret = nvmf_add_ctrl(h, c, cfg); + if (ret == 0 && verbose >= 1) + fprintf(stderr, + "connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n", + host_traddr); + } + + if (ret) + fprintf(stderr, "no controller found\n"); + else { + if (flags == NORMAL) + print_connect_msg(c); + else if (flags == JSON) + json_connect_msg(c); + } +out_free: + if (errno == ENOMEM) + goto out_free_2; + } +out_free_2: + free_nbfts(&nbft_list); + return errno; +} diff --git a/nbft.h b/nbft.h new file mode 100644 index 00000000..0e09733f --- /dev/null +++ b/nbft.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +#define NBFT_SYSFS_PATH "/sys/firmware/acpi/tables" + +struct nbft_file_entry { + struct list_node node; + struct nbft_info *nbft; +}; + +int read_nbft_files(struct list_head *nbft_list, char *path); +void free_nbfts(struct list_head *nbft_list); + +extern int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg, + char *hostnqn_sys, char *hostid_sys, + const char *desc, bool connect, + const struct nvme_fabrics_config *cfg, char *nbft_path, + enum nvme_print_flags flags, bool verbose); -- 2.39.2