From 0f13cf08d090ba42630faeb3e83a8d94b747cf43 Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Tue, 16 May 2023 15:37:49 +0200 Subject: [PATCH] nvme-cli: Add support to NBFT Resolves: #2188518 Signed-off-by: Maurizio Lombardi --- ...bft-make-lookup_ctrl-function-public.patch | 75 ++ 0002-nbft-added-NBFT-v1.0-table-support.patch | 451 ++++++++++++ 0003-nbft-add-the-nbft-show-plugin.patch | 640 ++++++++++++++++++ nvme-cli.spec | 13 +- 4 files changed, 1178 insertions(+), 1 deletion(-) create mode 100644 0001-nbft-make-lookup_ctrl-function-public.patch create mode 100644 0002-nbft-added-NBFT-v1.0-table-support.patch create mode 100644 0003-nbft-add-the-nbft-show-plugin.patch diff --git a/0001-nbft-make-lookup_ctrl-function-public.patch b/0001-nbft-make-lookup_ctrl-function-public.patch new file mode 100644 index 0000000..b0ff508 --- /dev/null +++ b/0001-nbft-make-lookup_ctrl-function-public.patch @@ -0,0 +1,75 @@ +From 34c197e0ac2d57734ab775f00b2d0758aca7b82f Mon Sep 17 00:00:00 2001 +From: John Meneghini +Date: Fri, 14 Apr 2023 10:53:58 -0400 +Subject: [PATCH] nbft: make lookup_ctrl function public + +To prepare for the addition of nbft functionality fixup the fabrics +declarations. + +Signed-off-by: John Meneghini +--- + fabrics.c | 12 ++---------- + fabrics.h | 10 ++++++++++ + 2 files changed, 12 insertions(+), 10 deletions(-) + +diff --git a/fabrics.c b/fabrics.c +index f8078e59..80827b16 100644 +--- a/fabrics.c ++++ b/fabrics.c +@@ -43,6 +43,7 @@ + #include "libnvme.h" + #include "nvme-print.h" + #include "nvme-print-json.h" ++#include "fabrics.h" + + #define PATH_NVMF_DISC SYSCONFDIR "/nvme/discovery.conf" + #define PATH_NVMF_CONFIG SYSCONFDIR "/nvme/config.json" +@@ -110,15 +111,6 @@ static const char *nvmf_config_file = "Use specified JSON configuration file or + OPT_FLAG("data-digest", 'G', &c.data_digest, nvmf_data_digest), \ + OPT_FLAG("tls", 0, &c.tls, nvmf_tls) \ + +-struct tr_config { +- const char *subsysnqn; +- const char *transport; +- const char *traddr; +- const char *host_traddr; +- const char *host_iface; +- const char *trsvcid; +-}; +- + /* + * Compare two C strings and handle NULL pointers gracefully. + * If either of the two strings is NULL, return 0 +@@ -202,7 +194,7 @@ static nvme_ctrl_t lookup_discovery_ctrl(nvme_root_t r, struct tr_config *trcfg) + return __lookup_ctrl(r, trcfg, disc_ctrl_config_match); + } + +-static nvme_ctrl_t lookup_ctrl(nvme_root_t r, struct tr_config *trcfg) ++nvme_ctrl_t lookup_ctrl(nvme_root_t r, struct tr_config *trcfg) + { + return __lookup_ctrl(r, trcfg, ctrl_config_match); + } +diff --git a/fabrics.h b/fabrics.h +index d1e16fc5..02cebf5d 100644 +--- a/fabrics.h ++++ b/fabrics.h +@@ -2,6 +2,16 @@ + #ifndef _FABRICS_H + #define _FABRICS_H + ++struct tr_config { ++ const char *subsysnqn; ++ const char *transport; ++ const char *traddr; ++ const char *host_traddr; ++ const char *host_iface; ++ const char *trsvcid; ++}; ++ ++extern nvme_ctrl_t lookup_ctrl(nvme_root_t r, struct tr_config *trcfg); + extern int nvmf_discover(const char *desc, int argc, char **argv, bool connect); + extern int nvmf_connect(const char *desc, int argc, char **argv); + extern int nvmf_disconnect(const char *desc, int argc, char **argv); +-- +2.39.1 + diff --git a/0002-nbft-added-NBFT-v1.0-table-support.patch b/0002-nbft-added-NBFT-v1.0-table-support.patch new file mode 100644 index 0000000..8c7dcb0 --- /dev/null +++ b/0002-nbft-added-NBFT-v1.0-table-support.patch @@ -0,0 +1,451 @@ +From a71a08532b5f06ee19ee2ff118a23cec4ecb6719 Mon Sep 17 00:00:00 2001 +From: Stuart Hayes +Date: Wed, 15 Jun 2022 11:42:50 -0500 +Subject: [PATCH] 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.1 + diff --git a/0003-nbft-add-the-nbft-show-plugin.patch b/0003-nbft-add-the-nbft-show-plugin.patch new file mode 100644 index 0000000..8ce3ad6 --- /dev/null +++ b/0003-nbft-add-the-nbft-show-plugin.patch @@ -0,0 +1,640 @@ +From 23daeedc53be9b5225cc6806434888c0a7a8e9bf Mon Sep 17 00:00:00 2001 +From: Stuart Hayes +Date: Fri, 14 Apr 2023 22:01:07 -0400 +Subject: [PATCH] nbft: add the nbft show plugin + +Display contents of the ACPI NBFT files. + +Usage: nvme nbft show [OPTIONS] + +Options: + [ --output-format=, -o ] --- Output format: normal|json + [ --subsystem, -s ] --- show NBFT subsystems + [ --hfi, -H ] --- show NBFT HFIs + [ --discovery, -d ] --- show NBFT discovery controllers + [ --nbft-path= ] --- user-defined path for NBFT tables + +Signed-off-by: Stuart Hayes +Signed-off-by: Martin Wilck +Signed-off-by: John Meneghini +--- + plugins/meson.build | 1 + + plugins/nbft/nbft-plugin.c | 568 +++++++++++++++++++++++++++++++++++++ + plugins/nbft/nbft-plugin.h | 18 ++ + 3 files changed, 587 insertions(+) + create mode 100644 plugins/nbft/nbft-plugin.c + create mode 100644 plugins/nbft/nbft-plugin.h + +diff --git a/plugins/meson.build b/plugins/meson.build +index 2cf2486f..38b7cded 100644 +--- a/plugins/meson.build ++++ b/plugins/meson.build +@@ -12,6 +12,7 @@ if json_c_dep.found() + 'plugins/intel/intel-nvme.c', + 'plugins/memblaze/memblaze-nvme.c', + 'plugins/micron/micron-nvme.c', ++ 'plugins/nbft/nbft-plugin.c', + 'plugins/netapp/netapp-nvme.c', + 'plugins/nvidia/nvidia-nvme.c', + 'plugins/scaleflux/sfx-nvme.c', +diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c +new file mode 100644 +index 00000000..6ae2525a +--- /dev/null ++++ b/plugins/nbft/nbft-plugin.c +@@ -0,0 +1,568 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++ ++#include "nvme-print.h" ++#include "nvme.h" ++#include "nbft.h" ++#include "libnvme.h" ++#include "fabrics.h" ++ ++#define CREATE_CMD ++#include "nbft-plugin.h" ++ ++static const char dash[100] = {[0 ... 98] = '-', [99] = '\0'}; ++ ++#define PCI_SEGMENT(sbdf) ((sbdf & 0xffff0000) >> 16) ++#define PCI_BUS(sbdf) ((sbdf & 0x0000ff00) >> 8) ++#define PCI_DEV(sbdf) ((sbdf & 0x000000f8) >> 3) ++#define PCI_FUNC(sbdf) ((sbdf & 0x00000007) >> 0) ++ ++static const char *pci_sbdf_to_string(__u16 pci_sbdf) ++{ ++ static char pcidev[13]; ++ ++ snprintf(pcidev, sizeof(pcidev), "%x:%x:%x.%x", ++ PCI_SEGMENT(pci_sbdf), ++ PCI_BUS(pci_sbdf), ++ PCI_DEV(pci_sbdf), ++ PCI_FUNC(pci_sbdf)); ++ return pcidev; ++} ++ ++static char *mac_addr_to_string(unsigned char mac_addr[6]) ++{ ++ static char mac_string[18]; ++ ++ snprintf(mac_string, sizeof(mac_string), "%02x:%02x:%02x:%02x:%02x:%02x", ++ mac_addr[0], ++ mac_addr[1], ++ mac_addr[2], ++ mac_addr[3], ++ mac_addr[4], ++ mac_addr[5]); ++ return mac_string; ++} ++ ++static json_object *hfi_to_json(struct nbft_info_hfi *hfi) ++{ ++ struct json_object *hfi_json; ++ ++ hfi_json = json_create_object(); ++ if (!hfi_json) ++ return NULL; ++ ++ if (json_object_add_value_int(hfi_json, "index", hfi->index) ++ || json_object_add_value_string(hfi_json, "transport", hfi->transport)) ++ goto fail; ++ ++ if (strcmp(hfi->transport, "tcp") == 0) { ++ if (json_object_add_value_string(hfi_json, "pcidev", ++ pci_sbdf_to_string(hfi->tcp_info.pci_sbdf)) ++ || json_object_add_value_string(hfi_json, "mac_addr", ++ mac_addr_to_string(hfi->tcp_info.mac_addr)) ++ || json_object_add_value_int(hfi_json, "vlan", ++ hfi->tcp_info.vlan) ++ || json_object_add_value_int(hfi_json, "ip_origin", ++ hfi->tcp_info.ip_origin) ++ || json_object_add_value_string(hfi_json, "ipaddr", ++ hfi->tcp_info.ipaddr) ++ || json_object_add_value_int(hfi_json, "subnet_mask_prefix", ++ hfi->tcp_info.subnet_mask_prefix) ++ || json_object_add_value_string(hfi_json, "gateway_ipaddr", ++ hfi->tcp_info.gateway_ipaddr) ++ || json_object_add_value_int(hfi_json, "route_metric", ++ hfi->tcp_info.route_metric) ++ || json_object_add_value_string(hfi_json, "primary_dns_ipaddr", ++ hfi->tcp_info.primary_dns_ipaddr) ++ || json_object_add_value_string(hfi_json, "secondary_dns_ipaddr", ++ hfi->tcp_info.secondary_dns_ipaddr) ++ || json_object_add_value_string(hfi_json, "dhcp_server_ipaddr", ++ hfi->tcp_info.dhcp_server_ipaddr) ++ || (hfi->tcp_info.host_name ++ && json_object_add_value_string(hfi_json, "host_name", ++ hfi->tcp_info.host_name)) ++ || json_object_add_value_int(hfi_json, "this_hfi_is_default_route", ++ hfi->tcp_info.this_hfi_is_default_route) ++ || json_object_add_value_int(hfi_json, "dhcp_override", ++ hfi->tcp_info.dhcp_override)) ++ goto fail; ++ else ++ return hfi_json; ++ } ++fail: ++ json_free_object(hfi_json); ++ return NULL; ++} ++ ++static json_object *ssns_to_json(struct nbft_info_subsystem_ns *ss) ++{ ++ struct json_object *ss_json; ++ struct json_object *hfi_array_json; ++ char json_str[40]; ++ char *json_str_p; ++ int i; ++ ++ ss_json = json_create_object(); ++ if (!ss_json) ++ return NULL; ++ ++ hfi_array_json = json_create_array(); ++ if (!hfi_array_json) ++ goto fail; ++ ++ for (i = 0; i < ss->num_hfis; i++) ++ if (json_array_add_value_object(hfi_array_json, ++ json_object_new_int(ss->hfis[i]->index))) ++ goto fail; ++ ++ if (json_object_add_value_int(ss_json, "index", ss->index) ++ || json_object_add_value_int(ss_json, "num_hfis", ss->num_hfis) ++ || json_object_object_add(ss_json, "hfis", hfi_array_json) ++ || json_object_add_value_string(ss_json, "transport", ss->transport) ++ || json_object_add_value_string(ss_json, "traddr", ss->traddr) ++ || json_object_add_value_string(ss_json, "trsvcid", ss->trsvcid) ++ || json_object_add_value_int(ss_json, "subsys_port_id", ss->subsys_port_id) ++ || json_object_add_value_int(ss_json, "nsid", ss->nsid)) ++ goto fail; ++ ++ memset(json_str, 0, sizeof(json_str)); ++ json_str_p = json_str; ++ ++ switch (ss->nid_type) { ++ case NBFT_INFO_NID_TYPE_EUI64: ++ if (json_object_add_value_string(ss_json, "nid_type", "eui64")) ++ goto fail; ++ for (i = 0; i < 8; i++) ++ json_str_p += sprintf(json_str_p, "%02x", ss->nid[i]); ++ break; ++ ++ case NBFT_INFO_NID_TYPE_NGUID: ++ if (json_object_add_value_string(ss_json, "nid_type", "nguid")) ++ goto fail; ++ for (i = 0; i < 16; i++) ++ json_str_p += sprintf(json_str_p, "%02x", ss->nid[i]); ++ break; ++ ++ case NBFT_INFO_NID_TYPE_NS_UUID: ++ if (json_object_add_value_string(ss_json, "nid_type", "uuid")) ++ goto fail; ++ nvme_uuid_to_string(ss->nid, json_str); ++ break; ++ ++ default: ++ break; ++ } ++ if (json_object_add_value_string(ss_json, "nid", json_str)) ++ goto fail; ++ ++ if ((ss->subsys_nqn ++ && json_object_add_value_string(ss_json, "subsys_nqn", ss->subsys_nqn)) ++ || json_object_add_value_int(ss_json, "controller_id", ss->controller_id) ++ || json_object_add_value_int(ss_json, "asqsz", ss->asqsz) ++ || (ss->dhcp_root_path_string ++ && json_object_add_value_string(ss_json, "dhcp_root_path_string", ++ ss->dhcp_root_path_string)) ++ || json_object_add_value_int(ss_json, "pdu_header_digest_required", ++ ss->pdu_header_digest_required) ++ || json_object_add_value_int(ss_json, "data_digest_required", ++ ss->data_digest_required)) ++ goto fail; ++ ++ return ss_json; ++fail: ++ json_free_object(ss_json); ++ return NULL; ++} ++ ++static json_object *discovery_to_json(struct nbft_info_discovery *disc) ++{ ++ struct json_object *disc_json; ++ ++ disc_json = json_create_object(); ++ if (!disc_json) ++ return NULL; ++ ++ if (json_object_add_value_int(disc_json, "index", disc->index) ++ || (disc->security ++ && json_object_add_value_int(disc_json, "security", disc->security->index)) ++ || (disc->hfi ++ && json_object_add_value_int(disc_json, "hfi", disc->hfi->index)) ++ || (disc->uri ++ && json_object_add_value_string(disc_json, "uri", disc->uri)) ++ || (disc->nqn ++ && json_object_add_value_string(disc_json, "nqn", disc->nqn))) { ++ json_free_object(disc_json); ++ return NULL; ++ } else ++ return disc_json; ++} ++ ++static const char *primary_admin_host_flag_to_str(unsigned int primary) ++{ ++ static const char * const str[] = { ++ [NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED] = "not indicated", ++ [NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED] = "unselected", ++ [NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED] = "selected", ++ [NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED] = "reserved", ++ }; ++ ++ if (primary > NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_RESERVED) ++ return "INVALID"; ++ return str[primary]; ++} ++ ++static struct json_object *nbft_to_json(struct nbft_info *nbft, bool show_subsys, ++ bool show_hfi, bool show_discovery) ++{ ++ struct json_object *nbft_json, *host_json; ++ ++ nbft_json = json_create_object(); ++ if (!nbft_json) ++ return NULL; ++ ++ if (json_object_add_value_string(nbft_json, "filename", nbft->filename)) ++ goto fail; ++ ++ host_json = json_create_object(); ++ if (!host_json) ++ goto fail; ++ if ((nbft->host.nqn ++ && json_object_add_value_string(host_json, "nqn", nbft->host.nqn)) ++ || (nbft->host.id ++ && json_object_add_value_string(host_json, "id", ++ util_uuid_to_string(nbft->host.id)))) ++ goto fail; ++ json_object_add_value_int(host_json, "host_id_configured", ++ nbft->host.host_id_configured); ++ json_object_add_value_int(host_json, "host_nqn_configured", ++ nbft->host.host_nqn_configured); ++ json_object_add_value_string(host_json, "primary_admin_host_flag", ++ primary_admin_host_flag_to_str(nbft->host.primary)); ++ if (json_object_object_add(nbft_json, "host", host_json)) { ++ json_free_object(host_json); ++ goto fail; ++ } ++ ++ if (show_subsys) { ++ struct json_object *subsys_array_json, *subsys_json; ++ struct nbft_info_subsystem_ns **ss; ++ ++ subsys_array_json = json_create_array(); ++ if (!subsys_array_json) ++ goto fail; ++ for (ss = nbft->subsystem_ns_list; ss && *ss; ss++) { ++ subsys_json = ssns_to_json(*ss); ++ if (!subsys_json) ++ goto fail; ++ if (json_object_array_add(subsys_array_json, subsys_json)) { ++ json_free_object(subsys_json); ++ goto fail; ++ } ++ } ++ if (json_object_object_add(nbft_json, "subsystem", subsys_array_json)) { ++ json_free_object(subsys_array_json); ++ goto fail; ++ } ++ } ++ if (show_hfi) { ++ struct json_object *hfi_array_json, *hfi_json; ++ struct nbft_info_hfi **hfi; ++ ++ hfi_array_json = json_create_array(); ++ if (!hfi_array_json) ++ goto fail; ++ for (hfi = nbft->hfi_list; hfi && *hfi; hfi++) { ++ hfi_json = hfi_to_json(*hfi); ++ if (!hfi_json) ++ goto fail; ++ if (json_object_array_add(hfi_array_json, hfi_json)) { ++ json_free_object(hfi_json); ++ goto fail; ++ } ++ } ++ if (json_object_object_add(nbft_json, "hfi", hfi_array_json)) { ++ json_free_object(hfi_array_json); ++ goto fail; ++ } ++ } ++ if (show_discovery) { ++ struct json_object *discovery_array_json, *discovery_json; ++ struct nbft_info_discovery **disc; ++ ++ discovery_array_json = json_create_array(); ++ if (!discovery_array_json) ++ goto fail; ++ for (disc = nbft->discovery_list; disc && *disc; disc++) { ++ discovery_json = discovery_to_json(*disc); ++ if (!discovery_json) ++ goto fail; ++ if (json_object_array_add(discovery_array_json, discovery_json)) { ++ json_free_object(discovery_json); ++ goto fail; ++ } ++ } ++ if (json_object_object_add(nbft_json, "discovery", discovery_array_json)) { ++ json_free_object(discovery_array_json); ++ goto fail; ++ } ++ } ++ return nbft_json; ++fail: ++ json_free_object(nbft_json); ++ return NULL; ++} ++ ++static int json_show_nbfts(struct list_head *nbft_list, bool show_subsys, ++ bool show_hfi, bool show_discovery) ++{ ++ struct json_object *nbft_json_array, *nbft_json; ++ struct nbft_file_entry *entry; ++ ++ nbft_json_array = json_create_array(); ++ if (!nbft_json_array) ++ return ENOMEM; ++ ++ list_for_each(nbft_list, entry, node) { ++ nbft_json = nbft_to_json(entry->nbft, show_subsys, show_hfi, show_discovery); ++ if (!nbft_json) ++ goto fail; ++ if (json_object_array_add(nbft_json_array, nbft_json)) { ++ json_free_object(nbft_json); ++ goto fail; ++ } ++ } ++ ++ json_print_object(nbft_json_array, NULL); ++ printf("\n"); ++ json_free_object(nbft_json_array); ++ return 0; ++fail: ++ json_free_object(nbft_json_array); ++ return ENOMEM; ++} ++ ++static void print_nbft_hfi_info(struct nbft_info *nbft) ++{ ++ struct nbft_info_hfi **hfi; ++ unsigned int ip_width = 8, gw_width = 8, dns_width = 8; ++ ++ hfi = nbft->hfi_list; ++ if (!hfi || !*hfi) ++ return; ++ ++ for (; *hfi; hfi++) { ++ unsigned int len; ++ ++ len = strlen((*hfi)->tcp_info.ipaddr); ++ if (len > ip_width) ++ ip_width = len; ++ len = strlen((*hfi)->tcp_info.gateway_ipaddr); ++ if (len > gw_width) ++ gw_width = len; ++ len = strlen((*hfi)->tcp_info.primary_dns_ipaddr); ++ if (len > dns_width) ++ dns_width = len; ++ } ++ ++ printf("\nNBFT HFIs:\n\n"); ++ printf("%-3.3s|%-4.4s|%-10.10s|%-17.17s|%-4.4s|%-*.*s|%-4.4s|%-*.*s|%-*.*s\n", ++ "Idx", "Trsp", "PCI Addr", "MAC Addr", "DHCP", ++ ip_width, ip_width, "IP Addr", "Mask", ++ gw_width, gw_width, "Gateway", dns_width, dns_width, "DNS"); ++ printf("%-.3s+%-.4s+%-.10s+%-.17s+%-.4s+%-.*s+%-.4s+%-.*s+%-.*s\n", ++ dash, dash, dash, dash, dash, ip_width, dash, dash, ++ gw_width, dash, dns_width, dash); ++ for (hfi = nbft->hfi_list; *hfi; hfi++) ++ printf("%-3d|%-4.4s|%-10.10s|%-17.17s|%-4.4s|%-*.*s|%-4d|%-*.*s|%-*.*s\n", ++ (*hfi)->index, ++ (*hfi)->transport, ++ pci_sbdf_to_string((*hfi)->tcp_info.pci_sbdf), ++ mac_addr_to_string((*hfi)->tcp_info.mac_addr), ++ (*hfi)->tcp_info.dhcp_override ? "yes" : "no", ++ ip_width, ip_width, (*hfi)->tcp_info.ipaddr, ++ (*hfi)->tcp_info.subnet_mask_prefix, ++ gw_width, gw_width, (*hfi)->tcp_info.gateway_ipaddr, ++ dns_width, dns_width, (*hfi)->tcp_info.primary_dns_ipaddr); ++} ++ ++static void print_nbft_discovery_info(struct nbft_info *nbft) ++{ ++ struct nbft_info_discovery **disc; ++ unsigned int nqn_width = 20, uri_width = 12; ++ ++ disc = nbft->discovery_list; ++ if (!disc || !*disc) ++ return; ++ ++ for (; *disc; disc++) { ++ size_t len; ++ ++ len = strlen((*disc)->uri); ++ if (len > uri_width) ++ uri_width = len; ++ len = strlen((*disc)->nqn); ++ if (len > nqn_width) ++ nqn_width = len; ++ } ++ ++ printf("\nNBFT Discovery Controllers:\n\n"); ++ printf("%-3.3s|%-*.*s|%-*.*s\n", "Idx", uri_width, uri_width, "URI", ++ nqn_width, nqn_width, "NQN"); ++ printf("%-.3s+%-.*s+%-.*s\n", dash, uri_width, dash, nqn_width, dash); ++ for (disc = nbft->discovery_list; *disc; disc++) ++ printf("%-3d|%-*.*s|%-*.*s\n", (*disc)->index, ++ uri_width, uri_width, (*disc)->uri, ++ nqn_width, nqn_width, (*disc)->nqn); ++} ++ ++#define HFIS_LEN 20 ++static size_t print_hfis(const struct nbft_info_subsystem_ns *ss, char buf[HFIS_LEN]) ++{ ++ char hfi_buf[HFIS_LEN]; ++ size_t len, ofs; ++ int i; ++ ++ len = snprintf(hfi_buf, sizeof(hfi_buf), "%d", ss->hfis[0]->index); ++ for (i = 1; i < ss->num_hfis; i++) { ++ ofs = len; ++ len += snprintf(hfi_buf + ofs, sizeof(hfi_buf) - ofs, ",%d", ++ ss->hfis[i]->index); ++ /* ++ * If the list doesn't fit in HFIS_LEN characters, ++ * truncate and end with "..." ++ */ ++ if (len >= sizeof(hfi_buf)) { ++ while (ofs < sizeof(hfi_buf) - 1) ++ hfi_buf[ofs++] = '.'; ++ hfi_buf[ofs] = '\0'; ++ len = sizeof(hfi_buf) - 1; ++ break; ++ } ++ } ++ if (buf) ++ memcpy(buf, hfi_buf, len + 1); ++ return len; ++} ++ ++ ++static void print_nbft_subsys_info(struct nbft_info *nbft) ++{ ++ struct nbft_info_subsystem_ns **ss; ++ unsigned int nqn_width = 20, adr_width = 8, hfi_width = 4; ++ ++ ss = nbft->subsystem_ns_list; ++ if (!ss || !*ss) ++ return; ++ for (; *ss; ss++) { ++ size_t len; ++ ++ len = strlen((*ss)->subsys_nqn); ++ if (len > nqn_width) ++ nqn_width = len; ++ len = strlen((*ss)->traddr); ++ if (len > adr_width) ++ adr_width = len; ++ len = print_hfis(*ss, NULL); ++ if (len > hfi_width) ++ hfi_width = len; ++ } ++ ++ printf("\nNBFT Subsystems:\n\n"); ++ printf("%-3.3s|%-*.*s|%-4.4s|%-*.*s|%-5.5s|%-*.*s\n", ++ "Idx", nqn_width, nqn_width, "NQN", ++ "Trsp", adr_width, adr_width, "Address", "SvcId", hfi_width, hfi_width, "HFIs"); ++ printf("%-.3s+%-.*s+%-.4s+%-.*s+%-.5s+%-.*s\n", ++ dash, nqn_width, dash, dash, adr_width, dash, dash, hfi_width, dash); ++ for (ss = nbft->subsystem_ns_list; *ss; ss++) { ++ char hfi_buf[HFIS_LEN]; ++ ++ print_hfis(*ss, hfi_buf); ++ printf("%-3d|%-*.*s|%-4.4s|%-*.*s|%-5.5s|%-*.*s\n", ++ (*ss)->index, nqn_width, nqn_width, (*ss)->subsys_nqn, ++ (*ss)->transport, adr_width, adr_width, (*ss)->traddr, ++ (*ss)->trsvcid, hfi_width, hfi_width, hfi_buf); ++ } ++} ++ ++static void normal_show_nbft(struct nbft_info *nbft, bool show_subsys, ++ bool show_hfi, bool show_discovery) ++{ ++ printf("%s:\n", nbft->filename); ++ if ((!nbft->hfi_list || !*nbft->hfi_list) && ++ (!nbft->security_list || !*nbft->security_list) && ++ (!nbft->discovery_list || !*nbft->discovery_list) && ++ (!nbft->subsystem_ns_list || !*nbft->subsystem_ns_list)) ++ printf("(empty)\n"); ++ else { ++ if (show_subsys) ++ print_nbft_subsys_info(nbft); ++ if (show_hfi) ++ print_nbft_hfi_info(nbft); ++ if (show_discovery) ++ print_nbft_discovery_info(nbft); ++ } ++} ++ ++static void normal_show_nbfts(struct list_head *nbft_list, bool show_subsys, ++ bool show_hfi, bool show_discovery) ++{ ++ bool not_first = false; ++ struct nbft_file_entry *entry; ++ ++ list_for_each(nbft_list, entry, node) { ++ if (not_first) ++ printf("\n"); ++ normal_show_nbft(entry->nbft, show_subsys, show_hfi, show_discovery); ++ not_first = true; ++ } ++} ++ ++int show_nbft(int argc, char **argv, struct command *cmd, struct plugin *plugin) ++{ ++ const char *desc = "Display contents of the ACPI NBFT files."; ++ struct list_head nbft_list; ++ char *format = "normal"; ++ char *nbft_path = NBFT_SYSFS_PATH; ++ enum nvme_print_flags flags = -1; ++ int ret; ++ bool show_subsys = false, show_hfi = false, show_discovery = false; ++ ++ OPT_ARGS(opts) = { ++ OPT_FMT("output-format", 'o', &format, "Output format: normal|json"), ++ OPT_FLAG("subsystem", 's', &show_subsys, "show NBFT subsystems"), ++ OPT_FLAG("hfi", 'H', &show_hfi, "show NBFT HFIs"), ++ OPT_FLAG("discovery", 'd', &show_discovery, "show NBFT discovery controllers"), ++ OPT_STRING("nbft-path", 0, "STR", &nbft_path, "user-defined path for NBFT tables"), ++ OPT_END() ++ }; ++ ++ ret = argconfig_parse(argc, argv, desc, opts); ++ if (ret) ++ return ret; ++ ++ if (!(show_subsys || show_hfi || show_discovery)) ++ show_subsys = show_hfi = show_discovery = true; ++ ++ if (!strcmp(format, "")) ++ flags = -1; ++ else if (!strcmp(format, "normal")) ++ flags = NORMAL; ++ else if (!strcmp(format, "json")) ++ flags = JSON; ++ else ++ return EINVAL; ++ ++ list_head_init(&nbft_list); ++ ret = read_nbft_files(&nbft_list, nbft_path); ++ if (!ret) { ++ if (flags == NORMAL) ++ normal_show_nbfts(&nbft_list, show_subsys, show_hfi, show_discovery); ++ else if (flags == JSON) ++ ret = json_show_nbfts(&nbft_list, show_subsys, show_hfi, show_discovery); ++ free_nbfts(&nbft_list); ++ } ++ return ret; ++} +diff --git a/plugins/nbft/nbft-plugin.h b/plugins/nbft/nbft-plugin.h +new file mode 100644 +index 00000000..018349d9 +--- /dev/null ++++ b/plugins/nbft/nbft-plugin.h +@@ -0,0 +1,18 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++#undef CMD_INC_FILE ++#define CMD_INC_FILE plugins/nbft/nbft-plugin ++ ++#if !defined(NBFT) || defined(CMD_HEADER_MULTI_READ) ++#define NBFT ++ ++#include "cmd.h" ++ ++PLUGIN(NAME("nbft", "ACPI NBFT table extensions", NVME_VERSION), ++ COMMAND_LIST( ++ ENTRY("show", "Show contents of ACPI NBFT tables", show_nbft) ++ ) ++); ++ ++#endif ++ ++#include "define_cmd.h" +-- +2.39.1 + diff --git a/nvme-cli.spec b/nvme-cli.spec index 3aa62f1..77c8493 100644 --- a/nvme-cli.spec +++ b/nvme-cli.spec @@ -3,13 +3,17 @@ Name: nvme-cli Version: 2.4 -Release: 3%{?dist} +Release: 4%{?dist} Summary: NVMe management command line interface License: GPLv2+ URL: https://github.com/linux-nvme/nvme-cli Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz +Patch0: 0001-nbft-make-lookup_ctrl-function-public.patch +Patch1: 0002-nbft-added-NBFT-v1.0-table-support.patch +Patch2: 0003-nbft-add-the-nbft-show-plugin.patch + BuildRequires: meson >= 0.50.0 BuildRequires: gcc gcc-c++ BuildRequires: libuuid-devel @@ -30,6 +34,10 @@ nvme-cli provides NVM-Express user space tooling for Linux. #%%setup -qn %%{name}-%%{commit0} %setup -q +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 + %build %meson -Dudevrulesdir=%{_udevrulesdir} -Dsystemddir=%{_unitdir} -Ddocs=all -Ddocs-build=true -Dhtmldir=%{_pkgdocdir} %meson_build @@ -88,6 +96,9 @@ fi %changelog +* Tue May 16 2023 Maurizio Lombardi - 2.4-4 +- Add support to NBFT (BZ2188518) + * Fri May 12 2023 Maurizio Lombardi - 2.4-3 - Fix a warning when building the package BZ2195897