nvme-cli/nvme-cli-2.10-nbft-discovery.patch
Tomas Bzatek 885a96f17b Backport NBFT discovery support
Resolves: RHEL-37601

Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
2024-07-24 17:19:10 +02:00

597 lines
18 KiB
Diff

From ca58ecc64852fc413a04f41d953914720d05459f Mon Sep 17 00:00:00 2001
From: Tomas Bzatek <tbzatek@redhat.com>
Date: Wed, 24 Apr 2024 17:46:19 +0200
Subject: [PATCH 1/5] fabrics: Make some symbols public
Needed for nbft, useful to have public.
Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
---
fabrics.c | 24 ++++++++++++------------
fabrics.h | 6 ++++++
2 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/fabrics.c b/fabrics.c
index 0b70d290a9..a82a56c7b0 100644
--- a/fabrics.c
+++ b/fabrics.c
@@ -195,9 +195,9 @@ static nvme_ctrl_t __create_discover_ctrl(nvme_root_t r, nvme_host_t h,
return c;
}
-static nvme_ctrl_t create_discover_ctrl(nvme_root_t r, nvme_host_t h,
- struct nvme_fabrics_config *cfg,
- struct tr_config *trcfg)
+nvme_ctrl_t nvmf_create_discover_ctrl(nvme_root_t r, nvme_host_t h,
+ struct nvme_fabrics_config *cfg,
+ struct tr_config *trcfg)
{
_cleanup_free_ struct nvme_id_ctrl *id = NULL;
nvme_ctrl_t c;
@@ -378,8 +378,7 @@ static int __discover(nvme_ctrl_t c, struct nvme_fabrics_config *defcfg,
return 0;
}
-static char *get_default_trsvcid(const char *transport,
- bool discovery_ctrl)
+char *nvmf_get_default_trsvcid(const char *transport, bool discovery_ctrl)
{
if (!transport)
return NULL;
@@ -465,7 +464,7 @@ static int discover_from_conf_file(nvme_root_t r, nvme_host_t h,
goto next;
if (!trsvcid)
- trsvcid = get_default_trsvcid(transport, true);
+ trsvcid = nvmf_get_default_trsvcid(transport, true);
struct tr_config trcfg = {
.subsysnqn = subsysnqn,
@@ -485,7 +484,7 @@ static int discover_from_conf_file(nvme_root_t r, nvme_host_t h,
}
}
- c = create_discover_ctrl(r, h, &cfg, &trcfg);
+ c = nvmf_create_discover_ctrl(r, h, &cfg, &trcfg);
if (!c)
goto next;
@@ -549,7 +548,8 @@ static int discover_from_json_config_file(nvme_root_t r, nvme_host_t h,
trsvcid = nvme_ctrl_get_trsvcid(c);
if (!trsvcid || !strcmp(trsvcid, ""))
- trsvcid = get_default_trsvcid(transport, true);
+ trsvcid = nvmf_get_default_trsvcid(transport,
+ true);
if (force)
subsysnqn = nvme_ctrl_get_subsysnqn(c);
@@ -579,7 +579,7 @@ static int discover_from_json_config_file(nvme_root_t r, nvme_host_t h,
}
}
- cn = create_discover_ctrl(r, h, &cfg, &trcfg);
+ cn = nvmf_create_discover_ctrl(r, h, &cfg, &trcfg);
if (!cn)
continue;
@@ -806,7 +806,7 @@ int nvmf_discover(const char *desc, int argc, char **argv, bool connect)
}
if (!trsvcid)
- trsvcid = get_default_trsvcid(transport, true);
+ trsvcid = nvmf_get_default_trsvcid(transport, true);
struct tr_config trcfg = {
.subsysnqn = subsysnqn,
@@ -876,7 +876,7 @@ int nvmf_discover(const char *desc, int argc, char **argv, bool connect)
}
if (!c) {
/* No device or non-matching device, create a new controller */
- c = create_discover_ctrl(r, h, &cfg, &trcfg);
+ c = nvmf_create_discover_ctrl(r, h, &cfg, &trcfg);
if (!c) {
if (errno != ENVME_CONNECT_IGNORED)
fprintf(stderr,
@@ -1005,7 +1005,7 @@ int nvmf_connect(const char *desc, int argc, char **argv)
if (hostkey)
nvme_host_set_dhchap_key(h, hostkey);
if (!trsvcid)
- trsvcid = get_default_trsvcid(transport, false);
+ trsvcid = nvmf_get_default_trsvcid(transport, false);
struct tr_config trcfg = {
.subsysnqn = subsysnqn,
diff --git a/fabrics.h b/fabrics.h
index c16df60472..aec305dc31 100644
--- a/fabrics.h
+++ b/fabrics.h
@@ -18,5 +18,11 @@ extern int nvmf_disconnect(const char *desc, int argc, char **argv);
extern int nvmf_disconnect_all(const char *desc, int argc, char **argv);
extern int nvmf_config(const char *desc, int argc, char **argv);
extern int nvmf_dim(const char *desc, int argc, char **argv);
+extern nvme_ctrl_t nvmf_create_discover_ctrl(nvme_root_t r, nvme_host_t h,
+ struct nvme_fabrics_config *cfg,
+ struct tr_config *trcfg);
+extern char *nvmf_get_default_trsvcid(const char *transport,
+ bool discovery_ctrl);
+
#endif
From 996fd5554157949ed77154eecd0f8236590cc716 Mon Sep 17 00:00:00 2001
From: Tomas Bzatek <tbzatek@redhat.com>
Date: Wed, 24 Apr 2024 17:47:02 +0200
Subject: [PATCH 2/5] util/cleanup: Add cleanup for struct nvme_fabrics_uri
libnvme cleanup definitions are not part of public API.
Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
---
util/cleanup.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff -up nvme-cli-2.9.1/util/cleanup.h.bak nvme-cli-2.9.1/util/cleanup.h
--- nvme-cli-2.9.1/util/cleanup.h.bak 2024-06-21 15:53:35.839852234 +0200
+++ nvme-cli-2.9.1/util/cleanup.h 2024-05-03 16:03:42.000000000 +0200
@@ -34,4 +34,12 @@ static inline void close_file(int *f)
}
#define _cleanup_file_ __cleanup__(close_file)
+static inline void free_uri(struct nvme_fabrics_uri **uri)
+{
+ if (*uri)
+ nvme_free_uri(*uri);
+}
+
+#define _cleanup_uri_ __cleanup__(free_uri)
+
#endif
From 2f79015b83faff4947d29fb172d6f92d81f078e2 Mon Sep 17 00:00:00 2001
From: Tomas Bzatek <tbzatek@redhat.com>
Date: Wed, 24 Apr 2024 17:52:46 +0200
Subject: [PATCH 3/5] nbft: Perform actual discovery
This adds actual discovery support for Discovery Descriptor records.
SSNS records are connected first. Discovery Descriptor records
are checked for any existing (back-)reference from SSNS records
and are skipped if so. It is assumed in such case that the pre-OS
driver has succeeded in discovery and filled SSNS records
accordingly.
In case no SSNS record references the particular Discovery
record, an actual discovery is performed.
Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
---
nbft.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
nbft.h | 2 +-
2 files changed, 223 insertions(+), 11 deletions(-)
diff --git a/nbft.c b/nbft.c
index 7ff8765a80..995b31ca54 100644
--- a/nbft.c
+++ b/nbft.c
@@ -7,6 +7,7 @@
#include <libnvme.h>
+#include "common.h"
#include "nvme.h"
#include "nbft.h"
#include "fabrics.h"
@@ -77,12 +78,38 @@ void free_nbfts(struct list_head *nbft_list)
}
}
+static bool validate_uri(struct nbft_info_discovery *dd,
+ struct nvme_fabrics_uri *uri)
+{
+ if (!uri) {
+ fprintf(stderr,
+ "Discovery Descriptor %d: failed to parse URI %s\n",
+ dd->index, dd->uri);
+ return false;
+ }
+ if (strcmp(uri->scheme, "nvme") != 0) {
+ fprintf(stderr,
+ "Discovery Descriptor %d: unsupported scheme '%s'\n",
+ dd->index, uri->scheme);
+ return false;
+ }
+ if (!uri->protocol || strcmp(uri->protocol, "tcp") != 0) {
+ fprintf(stderr,
+ "Discovery Descriptor %d: unsupported transport '%s'\n",
+ dd->index, uri->protocol);
+ return false;
+ }
+
+ return true;
+}
+
/* returns 0 for success or negative errno otherwise */
static int do_connect(nvme_root_t r,
nvme_host_t h,
+ struct nvmf_disc_log_entry *e,
struct nbft_info_subsystem_ns *ss,
struct tr_config *trcfg,
- const struct nvme_fabrics_config *cfg,
+ struct nvme_fabrics_config *cfg,
enum nvme_print_flags flags,
unsigned int verbose)
{
@@ -111,6 +138,12 @@ static int do_connect(nvme_root_t r,
nvme_init_logging(r, -1, false, false);
}
+ if (e) {
+ if (e->trtype == NVMF_TRTYPE_TCP &&
+ e->tsas.tcp.sectype != NVMF_TCP_SECTYPE_NONE)
+ cfg->tls = true;
+ }
+
errno = 0;
ret = nvmf_add_ctrl(h, c, cfg);
@@ -145,10 +178,114 @@ static int do_connect(nvme_root_t r,
return 0;
}
+static int do_discover(struct nbft_info_discovery *dd,
+ nvme_root_t r,
+ nvme_host_t h,
+ nvme_ctrl_t c,
+ struct nvme_fabrics_config *defcfg,
+ struct tr_config *deftrcfg,
+ enum nvme_print_flags flags,
+ unsigned int verbose)
+{
+ struct nvmf_discovery_log *log = NULL;
+ int i;
+ int ret;
+
+ struct nvme_get_discovery_args args = {
+ .c = c,
+ .args_size = sizeof(args),
+ .max_retries = 10 /* MAX_DISC_RETRIES */,
+ .result = 0,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lsp = 0,
+ };
+
+ log = nvmf_get_discovery_wargs(&args);
+ if (!log) {
+ fprintf(stderr,
+ "Discovery Descriptor %d: failed to get discovery log: %s\n",
+ dd->index, nvme_strerror(errno));
+ return -errno;
+ }
+
+ for (i = 0; i < le64_to_cpu(log->numrec); i++) {
+ struct nvmf_disc_log_entry *e = &log->entries[i];
+ nvme_ctrl_t cl;
+ int tmo = defcfg->keep_alive_tmo;
+
+ struct tr_config trcfg = {
+ .subsysnqn = e->subnqn,
+ .transport = nvmf_trtype_str(e->trtype),
+ .traddr = e->traddr,
+ .host_traddr = deftrcfg->host_traddr,
+ .host_iface = deftrcfg->host_iface,
+ .trsvcid = e->trsvcid,
+ };
+
+ if (e->subtype == NVME_NQN_CURR)
+ continue;
+
+ /* Already connected ? */
+ cl = lookup_ctrl(h, &trcfg);
+ if (cl && nvme_ctrl_get_name(cl))
+ continue;
+
+ /* Skip connect if the transport types don't match */
+ if (strcmp(nvme_ctrl_get_transport(c),
+ nvmf_trtype_str(e->trtype)))
+ continue;
+
+ if (e->subtype == NVME_NQN_DISC) {
+ nvme_ctrl_t child;
+
+ child = nvmf_connect_disc_entry(h, e, defcfg, NULL);
+ do_discover(dd, r, h, child, defcfg, &trcfg,
+ flags, verbose);
+ nvme_disconnect_ctrl(child);
+ nvme_free_ctrl(child);
+ } else {
+ ret = do_connect(r, h, e, NULL, &trcfg,
+ defcfg, flags, verbose);
+
+ /*
+ * 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 == -ENVME_CONNECT_ADDRNOTAVAIL &&
+ !strcmp(trcfg.transport, "tcp") &&
+ strlen(dd->hfi->tcp_info.dhcp_server_ipaddr) > 0) {
+ const char *htradr = trcfg.host_traddr;
+
+ trcfg.host_traddr = NULL;
+ ret = do_connect(r, h, e, NULL, &trcfg,
+ defcfg, flags, verbose);
+
+ if (ret == 0 && verbose >= 1)
+ fprintf(stderr,
+ "Discovery Descriptor %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n",
+ dd->index,
+ htradr);
+ }
+
+ if (ret)
+ fprintf(stderr, "Discovery Descriptor %d: no controller found\n",
+ dd->index);
+ if (ret == -ENOMEM)
+ break;
+ }
+
+ defcfg->keep_alive_tmo = tmo;
+ }
+
+ free(log);
+ return 0;
+}
+
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,
+ struct nvme_fabrics_config *cfg, char *nbft_path,
enum nvme_print_flags flags, unsigned int verbose)
{
char *hostnqn = NULL, *hostid = NULL, *host_traddr = NULL;
@@ -158,6 +295,7 @@ int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
struct nbft_file_entry *entry = NULL;
struct nbft_info_subsystem_ns **ss;
struct nbft_info_hfi *hfi;
+ struct nbft_info_discovery **dd;
if (!connect)
/* to do: print discovery-type info from NBFT tables */
@@ -192,15 +330,15 @@ int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
if (!h)
goto out_free;
+ /* Subsystem Namespace Descriptor List */
for (ss = entry->nbft->subsystem_ns_list; ss && *ss; ss++)
for (i = 0; i < (*ss)->num_hfis; i++) {
hfi = (*ss)->hfis[i];
- if (!cfg->host_traddr) {
- host_traddr = NULL;
- if (!strncmp((*ss)->transport, "tcp", 3))
- host_traddr = hfi->tcp_info.ipaddr;
- }
+ host_traddr = NULL;
+ if (!cfg->host_traddr &&
+ !strncmp((*ss)->transport, "tcp", 3))
+ host_traddr = hfi->tcp_info.ipaddr;
struct tr_config trcfg = {
.subsysnqn = (*ss)->subsys_nqn,
@@ -211,7 +349,7 @@ int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
.trsvcid = (*ss)->trsvcid,
};
- ret = do_connect(r, h, *ss, &trcfg,
+ ret = do_connect(r, h, NULL, *ss, &trcfg,
cfg, flags, verbose);
/*
@@ -220,11 +358,11 @@ int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
* firmware had. Retry without host_traddr.
*/
if (ret == -ENVME_CONNECT_ADDRNOTAVAIL &&
- !strcmp((*ss)->transport, "tcp") &&
+ !strcmp(trcfg.transport, "tcp") &&
strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) {
trcfg.host_traddr = NULL;
- ret = do_connect(r, h, *ss, &trcfg,
+ ret = do_connect(r, h, NULL, *ss, &trcfg,
cfg, flags, verbose);
if (ret == 0 && verbose >= 1)
@@ -241,6 +379,80 @@ int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
if (ret == -ENOMEM)
goto out_free;
}
+
+ /* Discovery Descriptor List */
+ for (dd = entry->nbft->discovery_list; dd && *dd; dd++) {
+ nvme_ctrl_t c;
+ bool linked = false;
+ _cleanup_uri_ struct nvme_fabrics_uri *uri = NULL;
+ _cleanup_free_ char *trsvcid = NULL;
+
+ /* only perform discovery when no SSNS record references it */
+ for (ss = entry->nbft->subsystem_ns_list; ss && *ss; ss++)
+ if ((*ss)->discovery &&
+ (*ss)->discovery->index == (*dd)->index &&
+ /* unavailable boot attempts are not discovered
+ * and may get transferred along with a well-known
+ * discovery NQN into an SSNS record.
+ */
+ strcmp((*ss)->subsys_nqn, NVME_DISC_SUBSYS_NAME) != 0) {
+ linked = true;
+ break;
+ }
+ if (linked)
+ continue;
+
+ hfi = (*dd)->hfi;
+ uri = nvme_parse_uri((*dd)->uri);
+ if (!validate_uri(*dd, uri))
+ continue;
+
+ host_traddr = NULL;
+ if (!cfg->host_traddr &&
+ !strncmp(uri->protocol, "tcp", 3))
+ host_traddr = hfi->tcp_info.ipaddr;
+ if (uri->port > 0) {
+ if (asprintf(&trsvcid, "%d", uri->port) < 0) {
+ errno = ENOMEM;
+ goto out_free;
+ }
+ } else
+ trsvcid = strdup(nvmf_get_default_trsvcid(uri->protocol, true));
+
+ struct tr_config trcfg = {
+ .subsysnqn = NVME_DISC_SUBSYS_NAME,
+ .transport = uri->protocol,
+ .traddr = uri->host,
+ .host_traddr = host_traddr,
+ .host_iface = NULL,
+ .trsvcid = trsvcid,
+ };
+
+ c = nvmf_create_discover_ctrl(r, h, cfg, &trcfg);
+ if (!c && errno == ENVME_CONNECT_ADDRNOTAVAIL &&
+ !strcmp(trcfg.transport, "tcp") &&
+ strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) {
+ trcfg.host_traddr = NULL;
+ c = nvmf_create_discover_ctrl(r, h, cfg, &trcfg);
+ }
+
+ if (!c) {
+ fprintf(stderr,
+ "Discovery Descriptor %d: failed to add discovery controller: %s\n",
+ (*dd)->index,
+ nvme_strerror(errno));
+ if (errno == ENOMEM)
+ goto out_free;
+ continue;
+ }
+
+ ret = do_discover(*dd, r, h, c, cfg, &trcfg,
+ flags, verbose);
+ nvme_disconnect_ctrl(c);
+ nvme_free_ctrl(c);
+ if (ret == -ENOMEM)
+ goto out_free;
+ }
}
out_free:
free_nbfts(&nbft_list);
diff --git a/nbft.h b/nbft.h
index 0f7e33cee5..5dfb8704fd 100644
--- a/nbft.h
+++ b/nbft.h
@@ -15,5 +15,5 @@ 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,
+ struct nvme_fabrics_config *cfg, char *nbft_path,
enum nvme_print_flags flags, unsigned int verbose);
From 1074da1dbbbef229bf7e42a3cb644ab5b9745267 Mon Sep 17 00:00:00 2001
From: Tomas Bzatek <tbzatek@redhat.com>
Date: Thu, 25 Apr 2024 15:54:53 +0200
Subject: [PATCH 4/5] nbft: Skip SSNS records pointing to well-known discovery
NQN
Depending on a pre-OS implementation, boot attempts pointing
to the well-known discovery NQN may get transformed in
an SSNS record (and marked as 'unavailable') in case
the discovery cannot be performed. Otherwise the NBFT
table should be populated by discovered records instead.
Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
---
nbft.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/nbft.c b/nbft.c
index 995b31ca54..9d1834d0db 100644
--- a/nbft.c
+++ b/nbft.c
@@ -335,6 +335,15 @@ int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
for (i = 0; i < (*ss)->num_hfis; i++) {
hfi = (*ss)->hfis[i];
+ /* Skip discovery NQN records */
+ if (strcmp((*ss)->subsys_nqn, NVME_DISC_SUBSYS_NAME) == 0) {
+ if (verbose >= 1)
+ fprintf(stderr,
+ "SSNS %d points to well-known discovery NQN, skipping\n",
+ (*ss)->index);
+ continue;
+ }
+
host_traddr = NULL;
if (!cfg->host_traddr &&
!strncmp((*ss)->transport, "tcp", 3))
From 8b4c30014ed17fa226a34a3dd92167f122ccec42 Mon Sep 17 00:00:00 2001
From: Tomas Bzatek <tbzatek@redhat.com>
Date: Mon, 20 May 2024 17:43:01 +0200
Subject: [PATCH 5/5] nbft: Reuse existing discovery controller
Attempt to look up and use existing (persistent) discovery
controller.
Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
---
nbft.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/nbft.c b/nbft.c
index 9d1834d0db..8c03a1f53b 100644
--- a/nbft.c
+++ b/nbft.c
@@ -393,6 +393,7 @@ int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
for (dd = entry->nbft->discovery_list; dd && *dd; dd++) {
nvme_ctrl_t c;
bool linked = false;
+ bool persistent = false;
_cleanup_uri_ struct nvme_fabrics_uri *uri = NULL;
_cleanup_free_ char *trsvcid = NULL;
@@ -437,12 +438,19 @@ int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
.trsvcid = trsvcid,
};
- c = nvmf_create_discover_ctrl(r, h, cfg, &trcfg);
- if (!c && errno == ENVME_CONNECT_ADDRNOTAVAIL &&
- !strcmp(trcfg.transport, "tcp") &&
- strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) {
- trcfg.host_traddr = NULL;
+ /* Lookup existing discovery controller */
+ c = lookup_ctrl(h, &trcfg);
+ if (c && nvme_ctrl_get_name(c))
+ persistent = true;
+
+ if (!c) {
c = nvmf_create_discover_ctrl(r, h, cfg, &trcfg);
+ if (!c && errno == ENVME_CONNECT_ADDRNOTAVAIL &&
+ !strcmp(trcfg.transport, "tcp") &&
+ strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) {
+ trcfg.host_traddr = NULL;
+ c = nvmf_create_discover_ctrl(r, h, cfg, &trcfg);
+ }
}
if (!c) {
@@ -457,7 +465,8 @@ int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
ret = do_discover(*dd, r, h, c, cfg, &trcfg,
flags, verbose);
- nvme_disconnect_ctrl(c);
+ if (!persistent)
+ nvme_disconnect_ctrl(c);
nvme_free_ctrl(c);
if (ret == -ENOMEM)
goto out_free;