Skip to content

Commit

Permalink
nbft: Perform actual discovery
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
tbzatek committed May 17, 2024
1 parent 4def0b6 commit 130078d
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 11 deletions.
232 changes: 222 additions & 10 deletions nbft.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <libnvme.h>

#include "common.h"
#include "nvme.h"
#include "nbft.h"
#include "fabrics.h"
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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 */
Expand Down Expand Up @@ -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,
Expand All @@ -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);

/*
Expand All @@ -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)
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion nbft.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

0 comments on commit 130078d

Please sign in to comment.