diff --git a/fabrics.c b/fabrics.c index 6537610937..d22213bd27 100644 --- a/fabrics.c +++ b/fabrics.c @@ -42,7 +42,6 @@ #include "common.h" #include "nvme.h" -#include "nbft.h" #include "nvme-print.h" #include "fabrics.h" #include "util/cleanup.h" @@ -95,20 +94,37 @@ static const char *nvmf_concat = "enable secure concatenation"; static const char *nvmf_config_file = "Use specified JSON configuration file or 'none' to disable"; static const char *nvmf_context = "execution context identification string"; -#define NVMF_ARGS(n, c, ...) \ +struct fabric_args { + const char *subsysnqn; + const char *transport; + const char *traddr; + const char *host_traddr; + const char *host_iface; + const char *trsvcid; + const char *hostnqn; + const char *hostid; + const char *hostkey; + const char *ctrlkey; + const char *keyring; + const char *tls_key; + const char *tls_key_identity; +}; + +#define NVMF_ARGS(n, f, c, ...) \ struct argconfig_commandline_options n[] = { \ - OPT_STRING("transport", 't', "STR", &transport, nvmf_tport), \ - OPT_STRING("nqn", 'n', "STR", &subsysnqn, nvmf_nqn), \ - OPT_STRING("traddr", 'a', "STR", &traddr, nvmf_traddr), \ - OPT_STRING("trsvcid", 's', "STR", &trsvcid, nvmf_trsvcid), \ - OPT_STRING("host-traddr", 'w', "STR", &c.host_traddr, nvmf_htraddr), \ - OPT_STRING("host-iface", 'f', "STR", &c.host_iface, nvmf_hiface), \ - OPT_STRING("hostnqn", 'q', "STR", &hostnqn, nvmf_hostnqn), \ - OPT_STRING("hostid", 'I', "STR", &hostid, nvmf_hostid), \ - OPT_STRING("dhchap-secret", 'S', "STR", &hostkey, nvmf_hostkey), \ - OPT_STRING("keyring", 0, "STR", &keyring, nvmf_keyring), \ - OPT_STRING("tls-key", 0, "STR", &tls_key, nvmf_tls_key), \ - OPT_STRING("tls-key-identity", 0, "STR", &tls_key_identity, nvmf_tls_key_identity), \ + OPT_STRING("transport", 't', "STR", &f.transport, nvmf_tport), \ + OPT_STRING("nqn", 'n', "STR", &f.subsysnqn, nvmf_nqn), \ + OPT_STRING("traddr", 'a', "STR", &f.traddr, nvmf_traddr), \ + OPT_STRING("trsvcid", 's', "STR", &f.trsvcid, nvmf_trsvcid), \ + OPT_STRING("host-traddr", 'w', "STR", &f.host_traddr, nvmf_htraddr), \ + OPT_STRING("host-iface", 'f', "STR", &f.host_iface, nvmf_hiface), \ + OPT_STRING("hostnqn", 'q', "STR", &f.hostnqn, nvmf_hostnqn), \ + OPT_STRING("hostid", 'I', "STR", &f.hostid, nvmf_hostid), \ + OPT_STRING("dhchap-secret", 'S', "STR", &f.hostkey, nvmf_hostkey), \ + OPT_STRING("dhchap-ctrl-secret", 'C', "STR", &f.ctrlkey, nvmf_ctrlkey), \ + OPT_STRING("keyring", 0, "STR", &f.keyring, nvmf_keyring), \ + OPT_STRING("tls-key", 0, "STR", &f.tls_key, nvmf_tls_key), \ + OPT_STRING("tls-key-identity", 0, "STR", &f.tls_key_identity, nvmf_tls_key_identity), \ OPT_INT("nr-io-queues", 'i', &c.nr_io_queues, nvmf_nr_io_queues), \ OPT_INT("nr-write-queues", 'W', &c.nr_write_queues, nvmf_nr_write_queues), \ OPT_INT("nr-poll-queues", 'P', &c.nr_poll_queues, nvmf_nr_poll_queues), \ @@ -129,155 +145,6 @@ static const char *nvmf_context = "execution context identification string"; OPT_END() \ } -static bool is_persistent_discovery_ctrl(nvme_host_t h, nvme_ctrl_t c) -{ - if (nvme_host_is_pdc_enabled(h, DEFAULT_PDC_ENABLED)) - return nvme_ctrl_is_unique_discovery_ctrl(c); - - return false; -} - -nvme_ctrl_t lookup_ctrl(nvme_host_t h, struct tr_config *trcfg) -{ - nvme_subsystem_t s; - nvme_ctrl_t c; - - nvme_for_each_subsystem(h, s) { - c = nvme_ctrl_find(s, - trcfg->transport, - trcfg->traddr, - trcfg->trsvcid, - trcfg->subsysnqn, - trcfg->host_traddr, - trcfg->host_iface); - if (c) - return c; - } - - return NULL; -} - -static int set_discovery_kato(struct nvme_fabrics_config *cfg) -{ - int tmo = cfg->keep_alive_tmo; - - /* Set kato to NVMF_DEF_DISC_TMO for persistent controllers */ - if (persistent && !cfg->keep_alive_tmo) - cfg->keep_alive_tmo = NVMF_DEF_DISC_TMO; - /* Set kato to zero for non-persistent controllers */ - else if (!persistent && (cfg->keep_alive_tmo > 0)) - cfg->keep_alive_tmo = 0; - - return tmo; -} - -static int nvme_add_ctrl(nvme_host_t h, nvme_ctrl_t c, - struct nvme_fabrics_config *cfg) -{ - int ret; - -retry: - /* - * __create_discover_ctrl and callers depend on errno being set - * in the error case. - */ - ret = nvmf_add_ctrl(h, c, cfg); - if (!ret) - return 0; - - if (ret == -EAGAIN || (ret == -EINTR && !nvme_sigint_received)) { - print_debug("nvmf_add_ctrl returned '%s'\n", strerror(-ret)); - goto retry; - } - - return ret; -} - -static int __create_discover_ctrl(struct nvme_global_ctx *ctx, nvme_host_t h, - struct nvme_fabrics_config *cfg, - struct tr_config *trcfg, - nvme_ctrl_t *ctrl) -{ - nvme_ctrl_t c; - int tmo, ret; - - ret = nvme_create_ctrl(ctx, trcfg->subsysnqn, trcfg->transport, - trcfg->traddr, trcfg->host_traddr, - trcfg->host_iface, trcfg->trsvcid, &c); - if (ret) - return ret; - - nvme_ctrl_set_discovery_ctrl(c, true); - nvme_ctrl_set_unique_discovery_ctrl(c, - strcmp(trcfg->subsysnqn, NVME_DISC_SUBSYS_NAME)); - tmo = set_discovery_kato(cfg); - - ret = nvme_add_ctrl(h, c, cfg); - cfg->keep_alive_tmo = tmo; - if (ret) { - nvme_free_ctrl(c); - return ret; - } - - *ctrl = c; - return 0; -} - -int nvmf_create_discover_ctrl(struct nvme_global_ctx *ctx, nvme_host_t h, - struct nvme_fabrics_config *cfg, - struct tr_config *trcfg, - nvme_ctrl_t *ctrl) -{ - _cleanup_free_ struct nvme_id_ctrl *id = NULL; - nvme_ctrl_t c; - int ret; - - ret = __create_discover_ctrl(ctx, h, cfg, trcfg, &c); - if (ret) - return ret; - - if (nvme_ctrl_is_unique_discovery_ctrl(c)) { - *ctrl = c; - return 0; - } - - id = nvme_alloc(sizeof(*id)); - if (!id) { - nvme_free_ctrl(c); - return -ENOMEM; - } - - /* Find out the name of discovery controller */ - ret = nvme_ctrl_identify(c, id); - if (ret) { - fprintf(stderr, "failed to identify controller, error %s\n", - nvme_strerror(-ret)); - nvme_disconnect_ctrl(c); - nvme_free_ctrl(c); - return ret; - } - - if (!strcmp(id->subnqn, NVME_DISC_SUBSYS_NAME)) { - *ctrl = c; - return 0; - } - - /* - * The subsysnqn is not the well-known name. Prefer the unique - * subsysnqn over the well-known one. - */ - nvme_disconnect_ctrl(c); - nvme_free_ctrl(c); - - trcfg->subsysnqn = id->subnqn; - ret = __create_discover_ctrl(ctx, h, cfg, trcfg, &c); - if (ret) - return ret; - - *ctrl = c; - return 0; -} - static void save_discovery_log(char *raw, struct nvmf_discovery_log *log) { uint64_t numrec = le64_to_cpu(log->numrec); @@ -301,378 +168,251 @@ static void save_discovery_log(char *raw, struct nvmf_discovery_log *log) close(fd); } -static int __discover(nvme_ctrl_t c, struct nvme_fabrics_config *defcfg, - char *raw, bool connect, bool persistent, - nvme_print_flags_t flags) +static int setup_common_context(struct nvmf_context *fctx, + struct fabric_args *fa); + +struct cb_fabrics_data { + struct nvme_fabrics_config *defcfg; + nvme_print_flags_t flags; + char *raw; + char **argv; + FILE *f; +}; + +static bool cb_decide_retry(struct nvmf_context *fctx, int err, + void *user_data) { - struct nvmf_discovery_log *log = NULL; - nvme_subsystem_t s = nvme_ctrl_get_subsystem(c); - nvme_host_t h = nvme_subsystem_get_host(s); - uint64_t numrec; - int err; + if (err == -EAGAIN || (err == -EINTR && !nvme_sigint_received)) { + print_debug("nvmf_add_ctrl returned '%s'\n", strerror(-err)); + return true; + } - struct nvme_get_discovery_args args = { - .c = c, - .args_size = sizeof(args), - .max_retries = MAX_DISC_RETRIES, - .result = 0, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .lsp = 0, - }; + return false; +} - err = nvmf_get_discovery_wargs(&args, &log); - if (err) { - fprintf(stderr, "failed to get discovery log: %s\n", - nvme_strerror(err)); - return err; +static void cb_connected(struct nvmf_context *fctx, + struct nvme_ctrl *c, void *user_data) +{ + struct cb_fabrics_data *cfd = user_data; + + if (cfd->flags == NORMAL) { + printf("device: %s\n", nvme_ctrl_get_name(c)); + return; } - numrec = le64_to_cpu(log->numrec); - if (raw) - save_discovery_log(raw, log); - else if (!connect) { - nvme_show_discovery_log(log, numrec, flags); - } else if (connect) { - int i; - - for (i = 0; i < numrec; i++) { - struct nvmf_disc_log_entry *e = &log->entries[i]; - nvme_ctrl_t cl; - bool discover = false; - bool disconnect; - nvme_ctrl_t child; - int tmo = defcfg->keep_alive_tmo; - - struct tr_config trcfg = { - .subsysnqn = e->subnqn, - .transport = nvmf_trtype_str(e->trtype), - .traddr = e->traddr, - .host_traddr = defcfg->host_traddr, - .host_iface = defcfg->host_iface, - .trsvcid = e->trsvcid, - }; - - /* Already connected ? */ - cl = lookup_ctrl(h, &trcfg); - if (cl && nvme_ctrl_get_name(cl)) - continue; +#ifdef CONFIG_JSONC + if (cfd->flags == JSON) { + struct json_object *root; - /* Skip connect if the transport types don't match */ - if (strcmp(nvme_ctrl_get_transport(c), - nvmf_trtype_str(e->trtype))) - continue; + root = json_create_object(); - if (e->subtype == NVME_NQN_DISC || - e->subtype == NVME_NQN_CURR) { - __u16 eflags = le16_to_cpu(e->eflags); - /* - * Does this discovery controller return the - * same information? - */ - if (eflags & NVMF_DISC_EFLAGS_DUPRETINFO) - continue; + json_object_add_value_string(root, "device", + nvme_ctrl_get_name(c)); - /* Are we supposed to keep the discovery controller around? */ - disconnect = !persistent; - - if (strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME)) { - /* - * Does this discovery controller doesn't - * support explicit persistent connection? - */ - if (!(eflags & NVMF_DISC_EFLAGS_EPCSD)) - disconnect = true; - else - disconnect = false; - } + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } +#endif +} - set_discovery_kato(defcfg); - } else { - /* NVME_NQN_NVME */ - disconnect = false; - } +static void cb_already_connected(struct nvmf_context *fctx, + struct nvme_host *host, const char *subsysnqn, + const char *transport, const char *traddr, + const char *trsvcid, void *user_data) +{ + if (quiet) + return; - err = nvmf_connect_disc_entry(h, e, defcfg, - &discover, &child); + fprintf(stderr, "already connected to hostnqn=%s,nqn=%s,transport=%s,traddr=%s,trsvcid=%s\n", + nvme_host_get_hostnqn(host), subsysnqn, + transport, traddr, trsvcid); +} - defcfg->keep_alive_tmo = tmo; +static void cb_discovery_log(struct nvmf_context *fctx, + bool connect, struct nvmf_discovery_log *log, + uint64_t numrec, void *user_data) +{ + struct cb_fabrics_data *cfd = user_data; - if (!child) { - if (discover) - __discover(child, defcfg, raw, - true, persistent, flags); + if (cfd->raw) + save_discovery_log(cfd->raw, log); + else if (!connect) + nvme_show_discovery_log(log, numrec, cfd->flags); +} - if (disconnect) { - nvme_disconnect_ctrl(child); - nvme_free_ctrl(child); - } - } else if (err == -ENVME_CONNECT_ALREADY && !quiet) { - const char *subnqn = log->entries[i].subnqn; - const char *trtype = nvmf_trtype_str(log->entries[i].trtype); - const char *traddr = log->entries[i].traddr; - const char *trsvcid = log->entries[i].trsvcid; +static int cb_parser_init(struct nvmf_context *dctx, void *user_data) +{ + struct cb_fabrics_data *cfd = user_data; - fprintf(stderr, - "already connected to hostnqn=%s,nqn=%s,transport=%s,traddr=%s,trsvcid=%s\n", - nvme_host_get_hostnqn(h), subnqn, - trtype, traddr, trsvcid); - } - } + cfd->f = fopen(PATH_NVMF_DISC, "r"); + if (cfd->f == NULL) { + fprintf(stderr, "No params given and no %s\n", PATH_NVMF_DISC); + return -ENOENT; } - free(log); + cfd->argv = calloc(MAX_DISC_ARGS, sizeof(char *)); + if (!cfd->argv) + return -1; + + cfd->argv[0] = "discover"; + return 0; } -char * nvmf_get_default_trsvcid(const char *transport, bool discovery_ctrl) +static void cb_parser_cleanup(struct nvmf_context *fctx, void *user_data) { - if (!transport) - return NULL; - if (!strcmp(transport, "tcp")) { - if (discovery_ctrl) - /* Default port for NVMe/TCP discovery controllers */ - return stringify(NVME_DISC_IP_PORT); - /* Default port for NVMe/TCP io controllers */ - return stringify(NVME_RDMA_IP_PORT); - } else if (!strcmp(transport, "rdma")) { - /* Default port for NVMe/RDMA controllers */ - return stringify(NVME_RDMA_IP_PORT); - } + struct cb_fabrics_data *cfd = user_data; - return NULL; + free(cfd->argv); + fclose(cfd->f); } -static int discover_from_conf_file(struct nvme_global_ctx *ctx, nvme_host_t h, - const char *desc, bool connect, - const struct nvme_fabrics_config *defcfg) +static int cb_parser_next_line(struct nvmf_context *fctx, void *user_data) { - char *transport = NULL, *traddr = NULL, *trsvcid = NULL; - char *hostnqn = NULL, *hostid = NULL, *hostkey = NULL; - char *subsysnqn = NULL, *keyring = NULL, *tls_key = NULL; - char *tls_key_identity = NULL; - char *ptr, **argv, *p, line[4096]; - int argc, ret = 0; - unsigned int verbose = 0; - _cleanup_file_ FILE *f = NULL; - nvme_print_flags_t flags; - char *format = "normal"; + struct cb_fabrics_data *cfd = user_data; struct nvme_fabrics_config cfg; + struct fabric_args fa = {}; + char *ptr, *p, line[4096]; + int argc, ret = 0; bool force = false; - NVMF_ARGS(opts, cfg, - OPT_FMT("output-format", 'o', &format, output_format), - OPT_FILE("raw", 'r', &raw, "save raw output to file"), + NVMF_ARGS(opts, fa, cfg, OPT_FLAG("persistent", 'p', &persistent, "persistent discovery connection"), - OPT_FLAG("quiet", 0, &quiet, "suppress already connected errors"), - OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"), OPT_FLAG("force", 0, &force, "Force persistent discovery controller creation")); - nvmf_default_config(&cfg); - - ret = validate_output_format(format, &flags); - if (ret < 0) { - nvme_show_error("Invalid output format"); - return ret; - } - - f = fopen(PATH_NVMF_DISC, "r"); - if (f == NULL) { - fprintf(stderr, "No params given and no %s\n", PATH_NVMF_DISC); - return -ENOENT; - } - - argv = calloc(MAX_DISC_ARGS, sizeof(char *)); - if (!argv) - return -1; + memcpy(&cfg, cfd->defcfg, sizeof(cfg)); +next: + if (fgets(line, sizeof(line), cfd->f) == NULL) + return -EOF; - argv[0] = "discover"; - memset(line, 0, sizeof(line)); - while (fgets(line, sizeof(line), f) != NULL) { - nvme_ctrl_t c; + if (line[0] == '#' || line[0] == '\n') + goto next; - if (line[0] == '#' || line[0] == '\n') - continue; + argc = 1; + p = line; + while ((ptr = strsep(&p, " =\n")) != NULL) + cfd->argv[argc++] = ptr; + cfd->argv[argc] = NULL; - argc = 1; - p = line; - while ((ptr = strsep(&p, " =\n")) != NULL) - argv[argc++] = ptr; - argv[argc] = NULL; - - memcpy(&cfg, defcfg, sizeof(cfg)); - subsysnqn = NVME_DISC_SUBSYS_NAME; - ret = argconfig_parse(argc, argv, desc, opts); - if (ret) - goto next; - if (!transport && !traddr) - goto next; - - if (!trsvcid) - trsvcid = nvmf_get_default_trsvcid(transport, true); - - struct tr_config trcfg = { - .subsysnqn = subsysnqn, - .transport = transport, - .traddr = traddr, - .host_traddr = cfg.host_traddr, - .host_iface = cfg.host_iface, - .trsvcid = trsvcid, - }; - - if (!force) { - c = lookup_ctrl(h, &trcfg); - if (c) { - __discover(c, &cfg, raw, connect, - true, flags); - goto next; - } - } + fa.subsysnqn = NVME_DISC_SUBSYS_NAME; + ret = argconfig_parse(argc, cfd->argv, "config", opts); + if (ret) + goto next; + if (!fa.transport && !fa.traddr) + goto next; - ret = nvmf_create_discover_ctrl(ctx, h, &cfg, &trcfg, &c); - if (ret) - goto next; + if (!fa.trsvcid) + fa.trsvcid = nvmf_get_default_trsvcid(fa.transport, true); - __discover(c, &cfg, raw, connect, persistent, flags); - if (!(persistent || is_persistent_discovery_ctrl(h, c))) - ret = nvme_disconnect_ctrl(c); - nvme_free_ctrl(c); + ret = setup_common_context(fctx, &fa); + if (ret) + return ret; -next: - memset(&cfg, 0, sizeof(cfg)); - } - free(argv); + ret = nvmf_context_set_fabrics_config(fctx, &cfg); + if (ret) + return ret; - return ret; + return 0; } -static int _discover_from_json_config_file(struct nvme_global_ctx *ctx, - nvme_host_t h, nvme_ctrl_t c, - const char *desc, bool connect, - const struct nvme_fabrics_config *defcfg, - nvme_print_flags_t flags, bool force) +static int setup_common_context(struct nvmf_context *fctx, + struct fabric_args *fa) { - const char *transport, *traddr, *host_traddr; - const char *host_iface, *trsvcid, *subsysnqn; - struct nvme_fabrics_config cfg; - nvme_ctrl_t cn; - int ret = 0; - - transport = nvme_ctrl_get_transport(c); - traddr = nvme_ctrl_get_traddr(c); - host_traddr = nvme_ctrl_get_host_traddr(c); - host_iface = nvme_ctrl_get_host_iface(c); - - if (!transport && !traddr) - return 0; - - /* ignore none fabric transports */ - if (strcmp(transport, "tcp") && - strcmp(transport, "rdma") && - strcmp(transport, "fc")) - return 0; - - /* ignore if no host_traddr for fc */ - if (!strcmp(transport, "fc")) { - if (!host_traddr) { - fprintf(stderr, "host_traddr required for fc\n"); - return 0; - } - } + int err; - /* ignore if host_iface set for any transport other than tcp */ - if (!strcmp(transport, "rdma") || !strcmp(transport, "fc")) { - if (host_iface) { - fprintf(stderr, - "host_iface not permitted for rdma or fc\n"); - return 0; - } - } + err = nvmf_context_set_connection(fctx, + fa->subsysnqn, fa->transport, + fa->traddr, fa->trsvcid, + fa->host_traddr, fa->host_iface); + if (err) + return err; - trsvcid = nvme_ctrl_get_trsvcid(c); - if (!trsvcid || !strcmp(trsvcid, "")) - trsvcid = nvmf_get_default_trsvcid(transport, true); + err = nvmf_context_set_hostnqn(fctx, + fa->hostnqn, fa->hostid); + if (err) + return err; - if (force) - subsysnqn = nvme_ctrl_get_subsysnqn(c); - else - subsysnqn = NVME_DISC_SUBSYS_NAME; + err = nvmf_context_set_crypto(fctx, + fa->hostkey, fa->ctrlkey, + fa->keyring, fa->tls_key, + fa->tls_key_identity); + if (err) + return err; - if (nvme_ctrl_is_persistent(c)) - persistent = true; + return 0; +} - memcpy(&cfg, defcfg, sizeof(cfg)); +static int create_common_context(struct nvme_global_ctx *ctx, + bool persistent, struct fabric_args *fa, + struct nvme_fabrics_config *cfg, + void *user_data, struct nvmf_context **fctxp) +{ + struct nvmf_context *fctx; + int err; - struct tr_config trcfg = { - .subsysnqn = subsysnqn, - .transport = transport, - .traddr = traddr, - .host_traddr = host_traddr, - .host_iface = host_iface, - .trsvcid = trsvcid, - }; + err = nvmf_context_create(ctx, cb_decide_retry, cb_connected, + cb_already_connected, user_data, &fctx); + if (err) + return err; - if (!force) { - cn = lookup_ctrl(h, &trcfg); - if (cn) { - __discover(cn, &cfg, raw, connect, true, flags); - return 0; - } - } + err = nvmf_context_set_connection(fctx, fa->subsysnqn, + fa->transport, fa->traddr, fa->trsvcid, + fa->host_traddr, fa->host_iface); + if (err) + goto err; - ret = nvmf_create_discover_ctrl(ctx, h, &cfg, &trcfg, &cn); - if (ret) - return 0; + err = nvmf_context_set_fabrics_config(fctx, cfg); + if (err) + goto err; - __discover(cn, &cfg, raw, connect, persistent, flags); - if (!(persistent || is_persistent_discovery_ctrl(h, cn))) - ret = nvme_disconnect_ctrl(cn); - nvme_free_ctrl(cn); + err = nvmf_context_set_persistent(fctx, persistent); + if (err) + goto err; - return ret; + *fctxp = fctx; + + return 0; + +err: + free(fctx); + return err; } -static int discover_from_json_config_file(struct nvme_global_ctx *ctx, - const char *hostnqn, - const char *hostid, const char *desc, - bool connect, - const struct nvme_fabrics_config *defcfg, - nvme_print_flags_t flags, bool force) +static int create_discovery_context(struct nvme_global_ctx *ctx, + bool persistent, const char *device, + struct fabric_args *fa, + struct nvme_fabrics_config *cfg, + void *user_data, struct nvmf_context **fctxp) { - const char *hnqn, *hid; - nvme_host_t h; - nvme_subsystem_t s; - nvme_ctrl_t c; - int ret = 0, err; + struct nvmf_context *fctx; + int err; - nvme_for_each_host(ctx, h) { - nvme_for_each_subsystem(h, s) { - hnqn = nvme_host_get_hostnqn(h); - if (hostnqn && hnqn && strcmp(hostnqn, hnqn)) - continue; - hid = nvme_host_get_hostid(h); - if (hostid && hid && strcmp(hostid, hid)) - continue; + err = create_common_context(ctx, persistent, fa, cfg, user_data, + &fctx); + if (err) + return err; - nvme_subsystem_for_each_ctrl(s, c) { - err = _discover_from_json_config_file( - ctx, h, c, desc, connect, defcfg, - flags, force); + err = nvmf_context_set_discovery_cbs(fctx, cb_discovery_log, + cb_parser_init, cb_parser_cleanup, cb_parser_next_line); + if (err) + goto err; - if (err) { - fprintf(stderr, - "failed to connect to hostnqn=%s,nqn=%s,%s\n", - nvme_host_get_hostnqn(h), - nvme_subsystem_get_name(s), - nvme_ctrl_get_address(c)); + err = nvmf_context_set_discovery_defaults(fctx, MAX_DISC_RETRIES, + NVMF_DEF_DISC_TMO); + if (err) + goto err; - if (!ret) - ret = err; - } - } - } - } + err = nvmf_context_set_device(fctx, device); + if (err) + goto err; - return ret; + *fctxp = fctx; + return 0; + +err: + free(fctx); + return err; } static int nvme_read_volatile_config(struct nvme_global_ctx *ctx) @@ -718,33 +458,27 @@ static int nvme_read_config_checked(struct nvme_global_ctx *ctx, return nvme_read_config(ctx, filename); } -/* returns negative errno values */ -int nvmf_discover(const char *desc, int argc, char **argv, bool connect) +#define NBFT_SYSFS_PATH "/sys/firmware/acpi/tables" + +int fabrics_discovery(const char *desc, int argc, char **argv, bool connect) { - char *subsysnqn = NVME_DISC_SUBSYS_NAME; - char *hostnqn = NULL, *hostid = NULL, *hostkey = NULL; - char *transport = NULL, *traddr = NULL, *trsvcid = NULL; - char *keyring = NULL, *tls_key = NULL; - char *tls_key_identity = NULL; char *config_file = PATH_NVMF_CONFIG; - _cleanup_free_ char *hnqn = NULL; - _cleanup_free_ char *hid = NULL; char *context = NULL; nvme_print_flags_t flags; _cleanup_nvme_global_ctx_ struct nvme_global_ctx *ctx = NULL; - nvme_host_t h; - nvme_ctrl_t c = NULL; + _cleanup_free_ struct nvmf_context *fctx = NULL; unsigned int verbose = 0; int ret; char *format = "normal"; struct nvme_fabrics_config cfg; + struct fabric_args fa = { .subsysnqn = NVME_DISC_SUBSYS_NAME }; char *device = NULL; bool force = false; bool json_config = false; bool nbft = false, nonbft = false; char *nbft_path = NBFT_SYSFS_PATH; - NVMF_ARGS(opts, cfg, + NVMF_ARGS(opts, fa, cfg, OPT_STRING("device", 'd', "DEV", &device, "use existing discovery controller device"), OPT_FMT("output-format", 'o', &format, output_format), OPT_FILE("raw", 'r', &raw, "save raw output to file"), @@ -798,129 +532,40 @@ int nvmf_discover(const char *desc, int argc, char **argv, bool connect) return ret; } - ret = nvme_host_get_ids(ctx, hostnqn, hostid, &hnqn, &hid); - if (ret < 0) - return ret; - - h = nvme_lookup_host(ctx, hnqn, hid); - if (!h) { - ret = -ENOMEM; - goto out_free; - } - if (device) { if (!strcmp(device, "none")) device = NULL; else if (!strncmp(device, "/dev/", 5)) device += 5; } - if (hostkey) - nvme_host_set_dhchap_key(h, hostkey); - if (!device && !transport && !traddr) { + struct cb_fabrics_data dld = { + .flags = flags, + .raw = raw, + }; + ret = create_discovery_context(ctx, persistent, device, &fa, + &cfg, &dld, &fctx); + if (ret) + return ret; + + if (!device && !fa.transport && !fa.traddr) { if (!nonbft) - ret = discover_from_nbft(ctx, hostnqn, hostid, - hnqn, hid, desc, connect, - &cfg, nbft_path, flags, verbose); + ret = nvmf_discovery_nbft(ctx, fctx, + connect, nbft_path); if (nbft) goto out_free; if (json_config) - ret = discover_from_json_config_file(ctx, hostnqn, hostid, - desc, connect, &cfg, - flags, force); + ret = nvmf_discovery_config_json(ctx, fctx, + connect, force); if (ret || access(PATH_NVMF_DISC, F_OK)) goto out_free; - ret = discover_from_conf_file(ctx, h, desc, connect, &cfg); + ret = nvmf_discovery_config_file(ctx, fctx, connect, force); goto out_free; } - if (!trsvcid) - trsvcid = nvmf_get_default_trsvcid(transport, true); - - struct tr_config trcfg = { - .subsysnqn = subsysnqn, - .transport = transport, - .traddr = traddr, - .host_traddr = cfg.host_traddr, - .host_iface = cfg.host_iface, - .trsvcid = trsvcid, - }; - - if (device && !force) { - ret = nvme_scan_ctrl(ctx, device, &c); - if (!ret) { - /* Check if device matches command-line options */ - if (!nvme_ctrl_config_match(c, transport, traddr, trsvcid, subsysnqn, - cfg.host_traddr, cfg.host_iface)) { - fprintf(stderr, - "ctrl device %s found, ignoring non matching command-line options\n", - device); - } - - if (!nvme_ctrl_is_discovery_ctrl(c)) { - fprintf(stderr, - "ctrl device %s found, ignoring non discovery controller\n", - device); - - nvme_free_ctrl(c); - c = NULL; - persistent = false; - } else { - /* - * If the controller device is found it must - * be persistent, and shouldn't be disconnected - * on exit. - */ - persistent = true; - /* - * When --host-traddr/--host-iface are not specified on the - * command line, use the discovery controller's (c) host- - * traddr/host-iface for the connections to controllers - * returned in the Discovery Log Pages. This is essential - * when invoking "connect-all" with --device to reuse an - * existing persistent discovery controller (as is done - * for the udev rules). This ensures that host-traddr/ - * host-iface are consistent with the discovery controller (c). - */ - if (!cfg.host_traddr) - cfg.host_traddr = (char *)nvme_ctrl_get_host_traddr(c); - if (!cfg.host_iface) - cfg.host_iface = (char *)nvme_ctrl_get_host_iface(c); - } - } else { - /* - * No controller found, fall back to create one. - * But that controller cannot be persistent. - */ - fprintf(stderr, - "ctrl device %s not found%s\n", device, - persistent ? ", ignoring --persistent" : ""); - persistent = false; - } - } - if (!c && !force) { - c = lookup_ctrl(h, &trcfg); - if (c) - persistent = true; - } - if (!c) { - /* No device or non-matching device, create a new controller */ - ret = nvmf_create_discover_ctrl(ctx, h, &cfg, &trcfg, &c); - if (ret) { - if (ret != -ENVME_CONNECT_IGNORED) - fprintf(stderr, - "failed to add controller, error %s\n", - nvme_strerror(-ret)); - goto out_free; - } - } - - ret = __discover(c, &cfg, raw, connect, persistent, flags); - if (!(persistent || is_persistent_discovery_ctrl(h, c))) - nvme_disconnect_ctrl(c); - nvme_free_ctrl(c); + ret = nvmf_discovery(ctx, fctx, connect, force); out_free: if (dump_config) @@ -929,56 +574,6 @@ int nvmf_discover(const char *desc, int argc, char **argv, bool connect) return ret; } -static int nvme_connect_config(struct nvme_global_ctx *ctx, const char *hostnqn, - const char *hostid, - const struct nvme_fabrics_config *cfg) -{ - const char *hnqn, *hid; - const char *transport; - nvme_host_t h; - nvme_subsystem_t s; - nvme_ctrl_t c, _c; - int ret = 0, err; - - nvme_for_each_host(ctx, h) { - nvme_for_each_subsystem(h, s) { - hnqn = nvme_host_get_hostnqn(h); - if (hostnqn && hnqn && strcmp(hostnqn, hnqn)) - continue; - hid = nvme_host_get_hostid(h); - if (hostid && hid && strcmp(hostid, hid)) - continue; - - nvme_subsystem_for_each_ctrl_safe(s, c, _c) { - transport = nvme_ctrl_get_transport(c); - - /* ignore none fabric transports */ - if (strcmp(transport, "tcp") && - strcmp(transport, "rdma") && - strcmp(transport, "fc")) - continue; - - err = nvmf_connect_ctrl(c); - if (err) { - if (err == -ENVME_CONNECT_ALREADY) - continue; - - fprintf(stderr, - "failed to connect to hostnqn=%s,nqn=%s,%s\n", - nvme_host_get_hostnqn(h), - nvme_subsystem_get_name(s), - nvme_ctrl_get_address(c)); - - if (!ret) - ret = err; - } - } - } - } - - return ret; -} - static void nvme_parse_tls_args(const char *keyring, const char *tls_key, const char *tls_key_identity, struct nvme_fabrics_config *cfg, nvme_ctrl_t c) @@ -1007,28 +602,23 @@ static void nvme_parse_tls_args(const char *keyring, const char *tls_key, } } -int nvmf_connect(const char *desc, int argc, char **argv) +int fabrics_connect(const char *desc, int argc, char **argv) { - char *subsysnqn = NULL; - char *transport = NULL, *traddr = NULL; - char *trsvcid = NULL, *hostnqn = NULL, *hostid = NULL; - char *hostkey = NULL, *ctrlkey = NULL, *keyring = NULL; - char *tls_key = NULL, *tls_key_identity = NULL; _cleanup_free_ char *hnqn = NULL; _cleanup_free_ char *hid = NULL; char *config_file = NULL; char *context = NULL; unsigned int verbose = 0; _cleanup_nvme_global_ctx_ struct nvme_global_ctx *ctx = NULL; - nvme_host_t h; + _cleanup_free_ struct nvmf_context *fctx = NULL; _cleanup_nvme_ctrl_ nvme_ctrl_t c = NULL; int ret; nvme_print_flags_t flags; struct nvme_fabrics_config cfg = { 0 }; + struct fabric_args fa = { 0 }; char *format = "normal"; - NVMF_ARGS(opts, cfg, - OPT_STRING("dhchap-ctrl-secret", 'C', "STR", &ctrlkey, nvmf_ctrlkey), + NVMF_ARGS(opts, fa, cfg, OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file), OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"), OPT_FLAG("dump-config", 'O', &dump_config, "Dump JSON configuration to stdout"), @@ -1050,23 +640,23 @@ int nvmf_connect(const char *desc, int argc, char **argv) if (config_file && strcmp(config_file, "none")) goto do_connect; - if (!subsysnqn) { + if (!fa.subsysnqn) { fprintf(stderr, "required argument [--nqn | -n] not specified\n"); return -EINVAL; } - if (!transport) { + if (!fa.transport) { fprintf(stderr, "required argument [--transport | -t] not specified\n"); return -EINVAL; } - if (strcmp(transport, "loop")) { - if (!traddr) { + if (strcmp(fa.transport, "loop")) { + if (!fa.traddr) { fprintf(stderr, "required argument [--traddr | -a] not specified for transport %s\n", - transport); + fa.transport); return -EINVAL; } } @@ -1094,66 +684,25 @@ int nvmf_connect(const char *desc, int argc, char **argv) return ret; } - ret = nvme_host_get_ids(ctx, hostnqn, hostid, &hnqn, &hid); - if (ret < 0) - return ret; - - h = nvme_lookup_host(ctx, hnqn, hid); - if (!h) - return -ENOMEM; - if (hostkey) - nvme_host_set_dhchap_key(h, hostkey); - if (!trsvcid) - trsvcid = nvmf_get_default_trsvcid(transport, false); - - if (config_file) - return nvme_connect_config(ctx, hostnqn, hostid, &cfg); - - struct tr_config trcfg = { - .subsysnqn = subsysnqn, - .transport = transport, - .traddr = traddr, - .host_traddr = cfg.host_traddr, - .host_iface = cfg.host_iface, - .trsvcid = trsvcid, + struct cb_fabrics_data dld = { + .flags = flags, + .raw = raw, }; - - c = lookup_ctrl(h, &trcfg); - if (c && nvme_ctrl_get_name(c) && !cfg.duplicate_connect) { - fprintf(stderr, "already connected\n"); - return -EALREADY; - } - - ret = nvme_create_ctrl(ctx, subsysnqn, transport, traddr, - cfg.host_traddr, cfg.host_iface, trsvcid, &c); + ret = create_common_context(ctx, persistent, &fa, + &cfg, &dld, &fctx); if (ret) return ret; - if (ctrlkey) - nvme_ctrl_set_dhchap_key(c, ctrlkey); - - nvme_parse_tls_args(keyring, tls_key, tls_key_identity, &cfg, c); - - /* - * We are connecting to a discovery controller, so let's treat - * this as a persistent connection and specify a KATO. - */ - if (!strcmp(subsysnqn, NVME_DISC_SUBSYS_NAME)) { - persistent = true; - - set_discovery_kato(&cfg); - } + if (config_file) + return nvmf_connect_config_json(ctx, fctx); - ret = nvme_add_ctrl(h, c, &cfg); + ret = nvmf_connect(ctx, fctx); if (ret) { - fprintf(stderr, "could not add new controller: %s\n", - nvme_strerror(-ret)); + fprintf(stderr, "failed to connected: %s\n", + nvme_strerror(ret)); return ret; } - /* always print connected device */ - nvme_show_connect_msg(c, flags); - if (dump_config) nvme_dump_config(ctx); @@ -1204,7 +753,7 @@ static void nvmf_disconnect_nqn(struct nvme_global_ctx *ctx, char *nqn) printf("NQN:%s disconnected %d controller(s)\n", nqn, i); } -int nvmf_disconnect(const char *desc, int argc, char **argv) +int fabrics_disconnect(const char *desc, int argc, char **argv) { const char *device = "nvme device handle"; _cleanup_nvme_global_ctx_ struct nvme_global_ctx *ctx = NULL; @@ -1293,7 +842,7 @@ int nvmf_disconnect(const char *desc, int argc, char **argv) return 0; } -int nvmf_disconnect_all(const char *desc, int argc, char **argv) +int fabrics_disconnect_all(const char *desc, int argc, char **argv) { _cleanup_nvme_global_ctx_ struct nvme_global_ctx *ctx = NULL; nvme_host_t h; @@ -1363,11 +912,12 @@ int nvmf_disconnect_all(const char *desc, int argc, char **argv) return 0; } -int nvmf_config(const char *desc, int argc, char **argv) +int fabrics_config(const char *desc, int argc, char **argv) { char *subsysnqn = NULL; char *transport = NULL, *traddr = NULL; char *trsvcid = NULL, *hostnqn = NULL, *hostid = NULL; + char *host_traddr = NULL, *host_iface = NULL; _cleanup_free_ char *hnqn = NULL; _cleanup_free_ char *hid = NULL; char *hostkey = NULL, *ctrlkey = NULL; @@ -1377,9 +927,10 @@ int nvmf_config(const char *desc, int argc, char **argv) _cleanup_nvme_global_ctx_ struct nvme_global_ctx *ctx = NULL; int ret; struct nvme_fabrics_config cfg; + struct fabric_args fa = { }; bool scan_tree = false, modify_config = false, update_config = false; - NVMF_ARGS(opts, cfg, + NVMF_ARGS(opts, fa, cfg, OPT_STRING("dhchap-ctrl-secret", 'C', "STR", &ctrlkey, nvmf_ctrlkey), OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file), OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"), @@ -1454,7 +1005,7 @@ int nvmf_config(const char *desc, int argc, char **argv) return -ENODEV; } c = nvme_lookup_ctrl(s, transport, traddr, - cfg.host_traddr, cfg.host_iface, + host_traddr, host_iface, trsvcid, NULL); if (!c) { fprintf(stderr, "Failed to lookup controller\n"); @@ -1501,7 +1052,7 @@ static int dim_operation(nvme_ctrl_t c, enum nvmf_dim_tas tas, const char *name) return nvme_status_to_errno(status, true); } -int nvmf_dim(const char *desc, int argc, char **argv) +int fabrics_dim(const char *desc, int argc, char **argv) { _cleanup_nvme_global_ctx_ struct nvme_global_ctx *ctx = NULL; enum nvmf_dim_tas tas; diff --git a/fabrics.h b/fabrics.h index 50ff00dee4..e66fc4f991 100644 --- a/fabrics.h +++ b/fabrics.h @@ -2,28 +2,12 @@ #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_host_t h, 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); -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 int nvmf_create_discover_ctrl(struct nvme_global_ctx *ctx, nvme_host_t h, - struct nvme_fabrics_config *cfg, - struct tr_config *trcfg, - nvme_ctrl_t *ctrl); -extern char *nvmf_get_default_trsvcid(const char *transport, - bool discovery_ctrl); +int fabrics_discovery(const char *desc, int argc, char **argv, bool connect); +int fabrics_connect(const char *desc, int argc, char **argv); +int fabrics_disconnect(const char *desc, int argc, char **argv); +int fabrics_disconnect_all(const char *desc, int argc, char **argv); +int fabrics_config(const char *desc, int argc, char **argv); +int fabrics_dim(const char *desc, int argc, char **argv); #endif diff --git a/libnvme/libnvme/nvme.i b/libnvme/libnvme/nvme.i index 43312f40cb..2cca7d6565 100644 --- a/libnvme/libnvme/nvme.i +++ b/libnvme/libnvme/nvme.i @@ -110,14 +110,6 @@ PyObject *hostid_from_file(); temp.tos = -1; temp.ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO; while (PyDict_Next($input, &pos, &key, &value)) { - if (!PyUnicode_CompareWithASCIIString(key, "host_traddr")) { - temp.host_traddr = PyBytes_AsString(value); - continue; - } - if (!PyUnicode_CompareWithASCIIString(key, "host_iface")) { - temp.host_iface = PyBytes_AsString(value); - continue; - } if (!PyUnicode_CompareWithASCIIString(key, "nr_io_queues")) { temp.nr_io_queues = PyLong_AsLong(value); continue; diff --git a/libnvme/src/libnvme.map b/libnvme/src/libnvme.map index e0e81af3c4..4054de184f 100644 --- a/libnvme/src/libnvme.map +++ b/libnvme/src/libnvme.map @@ -247,6 +247,7 @@ LIBNVME_2_0 { nvme_skip_namespaces; nvme_status_to_errno; nvme_status_to_string; + nvme_strerror; nvme_submit_admin_passthru; nvme_submit_io_passthru; nvme_subsys_filter; @@ -287,11 +288,52 @@ LIBNVME_2_0 { nvmf_add_ctrl; nvmf_adrfam_str; nvmf_cms_str; + nvmf_connect; + nvmf_connect_config_json; nvmf_connect_ctrl; - nvmf_connect_disc_entry; + nvmf_context_create; + nvmf_context_set_connection; + nvmf_context_set_crypto; + nvmf_context_set_device; + nvmf_context_set_discovery_cbs; + nvmf_context_set_discovery_defaults; + nvmf_context_set_fabrics_config; + nvmf_context_set_hostnqn; + nvmf_context_set_persistent; nvmf_default_config; + nvmf_discovery; + nvmf_discovery_config_file; + nvmf_discovery_config_json; + nvmf_discovery_ctx_already_connected_set; + nvmf_discovery_ctx_connected_set; + nvmf_discovery_ctx_create; + nvmf_discovery_ctx_ctrlkey_set; + nvmf_discovery_ctx_decide_retry_set; + nvmf_discovery_ctx_default_fabrics_config_set; + nvmf_discovery_ctx_device_set; + nvmf_discovery_ctx_discovery_log_set; + nvmf_discovery_ctx_host_iface_set; + nvmf_discovery_ctx_host_traddr_set; + nvmf_discovery_ctx_hostid_set; + nvmf_discovery_ctx_hostkey_set; + nvmf_discovery_ctx_hostnqn_set; + nvmf_discovery_ctx_keep_alive_timeout; + nvmf_discovery_ctx_keyring_set; + nvmf_discovery_ctx_max_retries; + nvmf_discovery_ctx_parser_cleanup_set; + nvmf_discovery_ctx_parser_init_set; + nvmf_discovery_ctx_parser_next_line_set; + nvmf_discovery_ctx_persistent_set; + nvmf_discovery_ctx_subsysnqn_set; + nvmf_discovery_ctx_tls_key_identity_set; + nvmf_discovery_ctx_tls_key_set; + nvmf_discovery_ctx_traddr_set; + nvmf_discovery_ctx_transport_set; + nvmf_discovery_ctx_trsvcid_set; + nvmf_discovery_nbft; nvmf_eflags_str; nvmf_exat_ptr_next; + nvmf_get_default_trsvcid; nvmf_get_discovery_log; nvmf_get_discovery_wargs; nvmf_hostid_from_file; @@ -300,6 +342,8 @@ LIBNVME_2_0 { nvmf_hostnqn_generate; nvmf_hostnqn_generate_from_hostid; nvmf_is_registration_supported; + nvmf_nbft_free; + nvmf_nbft_read_files; nvmf_prtype_str; nvmf_qptype_str; nvmf_register_ctrl; diff --git a/libnvme/src/nvme/cleanup.h b/libnvme/src/nvme/cleanup.h index 4c275398d9..2a1e3db00e 100644 --- a/libnvme/src/nvme/cleanup.h +++ b/libnvme/src/nvme/cleanup.h @@ -10,6 +10,8 @@ #include #include +#include "fabrics.h" + #define __cleanup__(fn) __attribute__((cleanup(fn))) #define DECLARE_CLEANUP_FUNC(name, type) \ @@ -44,4 +46,11 @@ static inline void cleanup_fd(int *fd) static inline DEFINE_CLEANUP_FUNC(cleanup_addrinfo, struct addrinfo *, freeaddrinfo) #define _cleanup_addrinfo_ __cleanup__(cleanup_addrinfo) +static inline void free_uri(struct nvme_fabrics_uri **uri) +{ + if (*uri) + nvme_free_uri(*uri); +} +#define _cleanup_uri_ __cleanup__(free_uri) + #endif diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 7a9c8e4d30..6e8393a529 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,7 @@ #include "fabrics.h" #include "linux.h" #include "ioctl.h" +#include "nbft.h" #include "nvme/tree.h" #include "util.h" #include "log.h" @@ -195,6 +197,121 @@ const char *nvmf_cms_str(__u8 cm) return arg_str(cms, ARRAY_SIZE(cms), cm); } +int nvmf_context_create(struct nvme_global_ctx *ctx, + bool (*decide_retry)(struct nvmf_context *fctx, int err, + void *user_data), + void (*connected)(struct nvmf_context *fctx, + struct nvme_ctrl *c, void *user_data), + void (*already_connected)(struct nvmf_context *fctx, + struct nvme_host *host, const char *subsysnqn, + const char *transport, const char *traddr, + const char *trsvcid, void *user_data), + void *user_data, struct nvmf_context **fctxp) +{ + struct nvmf_context *fctx; + + fctx = calloc(1, sizeof(*fctx)); + if (!fctx) + return -ENOMEM; + + fctx->decide_retry = decide_retry; + fctx->connected = connected; + fctx->already_connected = already_connected; + + fctx->user_data = user_data; + + *fctxp = fctx; + return 0; +} + +int nvmf_context_set_discovery_cbs(struct nvmf_context *fctx, + void (*discovery_log)(struct nvmf_context *fctx, + bool connect, + struct nvmf_discovery_log *log, + uint64_t numrec, void *user_data), + int (*parser_init)(struct nvmf_context *fctx, + void *user_data), + void (*parser_cleanup)(struct nvmf_context *fctx, + void *user_data), + int (*parser_next_line)(struct nvmf_context *fctx, + void *user_data)) +{ + fctx->discovery_log = discovery_log; + fctx->parser_init = parser_init; + fctx->parser_cleanup = parser_cleanup; + fctx->parser_next_line = parser_next_line; + + return 0; +} + +int nvmf_context_set_discovery_defaults(struct nvmf_context *fctx, + int max_discovery_retries, int keep_alive_timeout) +{ + fctx->default_max_discovery_retries = max_discovery_retries; + fctx->default_keep_alive_timeout = keep_alive_timeout; + + return 0; +} + +int nvmf_context_set_fabrics_config(struct nvmf_context *fctx, + struct nvme_fabrics_config *cfg) +{ + fctx->cfg = cfg; + + return 0; +} + +int nvmf_context_set_connection(struct nvmf_context *fctx, + const char *subsysnqn, const char *transport, + const char *traddr, const char *trsvcid, + const char *host_traddr, const char *host_iface) +{ + fctx->subsysnqn = subsysnqn; + fctx->transport = transport; + fctx->traddr = traddr; + fctx->trsvcid = trsvcid; + fctx->host_traddr = host_iface; + + return 0; +} + +int nvmf_context_set_hostnqn(struct nvmf_context *fctx, + const char *hostnqn, const char *hostid) +{ + fctx->hostnqn = hostnqn; + fctx->hostid = hostid; + + return 0; +} + +int nvmf_context_set_crypto(struct nvmf_context *fctx, + const char *hostkey, const char *ctrlkey, + const char *keyring, const char *tls_key, + const char *tls_key_identity) +{ + fctx->hostkey = hostkey; + fctx->ctrlkey = ctrlkey; + fctx->keyring = keyring; + fctx->tls_key = tls_key; + fctx->tls_key_identity = tls_key_identity; + + return 0; +} + +int nvmf_context_set_persistent(struct nvmf_context *fctx, bool persistent) +{ + fctx->persistent = persistent; + + return 0; +} + +int nvmf_context_set_device(struct nvmf_context *fctx, const char *device) +{ + fctx->device = device; + + return 0; +} + /* * Derived from Linux's supported options (the opt_tokens table) * when the mechanism to report supported options was added (f18ee3d988157). @@ -235,15 +352,11 @@ void nvmf_default_config(struct nvme_fabrics_config *cfg) #define MERGE_CFG_OPTION(c, n, o, d) \ if ((c)->o == d) (c)->o = (n)->o -#define MERGE_CFG_OPTION_STR(c, n, o, d) \ - if ((c)->o == d && (n)->o) (c)->o = strdup((n)->o) static struct nvme_fabrics_config *merge_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg) { struct nvme_fabrics_config *ctrl_cfg = nvme_ctrl_get_config(c); - MERGE_CFG_OPTION_STR(ctrl_cfg, cfg, host_traddr, NULL); - MERGE_CFG_OPTION_STR(ctrl_cfg, cfg, host_iface, NULL); MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_io_queues, 0); MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_write_queues, 0); MERGE_CFG_OPTION(ctrl_cfg, cfg, nr_poll_queues, 0); @@ -272,8 +385,6 @@ void nvmf_update_config(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg) { struct nvme_fabrics_config *ctrl_cfg = nvme_ctrl_get_config(c); - UPDATE_CFG_OPTION(ctrl_cfg, cfg, host_traddr, NULL); - UPDATE_CFG_OPTION(ctrl_cfg, cfg, host_iface, NULL); UPDATE_CFG_OPTION(ctrl_cfg, cfg, nr_io_queues, 0); UPDATE_CFG_OPTION(ctrl_cfg, cfg, nr_write_queues, 0); UPDATE_CFG_OPTION(ctrl_cfg, cfg, nr_poll_queues, 0); @@ -637,9 +748,9 @@ static int build_options(nvme_host_t h, nvme_ctrl_t c, char **argstr) add_argument(ctx, argstr, traddr, nvme_ctrl_get_traddr(c)) || add_argument(ctx, argstr, host_traddr, - cfg->host_traddr) || + nvme_ctrl_get_host_traddr(c)) || add_argument(ctx, argstr, host_iface, - cfg->host_iface) || + nvme_ctrl_get_host_iface(c)) || add_argument(ctx, argstr, trsvcid, nvme_ctrl_get_trsvcid(c)) || (hostnqn && add_argument(ctx, argstr, hostnqn, hostnqn)) || @@ -985,14 +1096,14 @@ int nvmf_connect_ctrl(nvme_ctrl_t c) return 0; } -int nvmf_connect_disc_entry(nvme_host_t h, - struct nvmf_disc_log_entry *e, - const struct nvme_fabrics_config *cfg, - bool *discover, - nvme_ctrl_t *cp) +static int nvmf_connect_disc_entry(nvme_host_t h, + struct nvmf_disc_log_entry *e, + const char *host_traddr, const char *host_iface, + const struct nvme_fabrics_config *cfg, + bool *discover, nvme_ctrl_t *cp) { - const char *transport; char *traddr = NULL, *trsvcid = NULL; + const char *transport; nvme_ctrl_t c; int ret; @@ -1040,7 +1151,7 @@ int nvmf_connect_disc_entry(nvme_host_t h, transport, traddr, trsvcid); ret = nvme_create_ctrl(h->ctx, e->subnqn, transport, traddr, - cfg->host_traddr, cfg->host_iface, trsvcid, &c); + host_traddr, host_iface, trsvcid, &c); if (ret) { nvme_msg(h->ctx, LOG_DEBUG, "skipping discovery entry, " "failed to allocate %s controller with traddr %s\n", @@ -1913,3 +2024,1326 @@ void nvme_free_uri(struct nvme_fabrics_uri *uri) free(uri->fragment); free(uri); } + +static nvme_ctrl_t lookup_ctrl(nvme_host_t h, struct fabric_args *trcfg) +{ + nvme_subsystem_t s; + nvme_ctrl_t c; + + nvme_for_each_subsystem(h, s) { + c = nvme_ctrl_find(s, + trcfg->transport, + trcfg->traddr, + trcfg->trsvcid, + trcfg->subsysnqn, + trcfg->host_traddr, + trcfg->host_iface); + if (c) + return c; + } + + return NULL; +} + +static int lookup_host(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, struct nvme_host **host) +{ + _cleanup_free_ char *hnqn = NULL; + _cleanup_free_ char *hid = NULL; + struct nvme_host *h; + int err; + + err = nvme_host_get_ids(ctx, fctx->hostnqn, fctx->hostid, &hnqn, &hid); + if (err < 0) + return err; + + h = nvme_lookup_host(ctx, hnqn, hid); + if (!h) + return -ENOMEM; + + *host = h; + + return 0; +} + +static int setup_connection(struct nvmf_context *fctx, struct nvme_host *h, + bool discovery) +{ + if (fctx->hostkey) + nvme_host_set_dhchap_key(h, fctx->hostkey); + + if (!fctx->trsvcid) + fctx->trsvcid = nvmf_get_default_trsvcid(fctx->transport, + discovery); + + return 0; +} + + +static int set_discovery_kato(struct nvmf_context *fctx, + struct nvme_fabrics_config *cfg) +{ + int tmo = cfg->keep_alive_tmo; + + /* Set kato to NVMF_DEF_DISC_TMO for persistent controllers */ + if (fctx->persistent && !cfg->keep_alive_tmo) + cfg->keep_alive_tmo = fctx->default_keep_alive_timeout; + /* Set kato to zero for non-persistent controllers */ + else if (!fctx->persistent && (cfg->keep_alive_tmo > 0)) + cfg->keep_alive_tmo = 0; + + return tmo; +} + +static int _nvmf_discovery(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, bool connect, + struct nvme_ctrl *c) +{ + _cleanup_free_ struct nvmf_discovery_log *log = NULL; + nvme_subsystem_t s = nvme_ctrl_get_subsystem(c); + nvme_host_t h = nvme_subsystem_get_host(s); + uint64_t numrec; + int err; + + struct nvme_get_discovery_args args = { + .c = c, + .args_size = sizeof(args), + .max_retries = fctx->default_max_discovery_retries, + .result = 0, + .lsp = 0, + }; + + err = nvmf_get_discovery_wargs(&args, &log); + if (err) { + nvme_msg(ctx, LOG_ERR, "failed to get discovery log: %s\n", + nvme_strerror(err)); + return err; + } + + numrec = le64_to_cpu(log->numrec); + if (fctx->discovery_log) + fctx->discovery_log(fctx, connect, log, numrec, + fctx->user_data); + + if (!connect) + return 0; + + for (int i = 0; i < numrec; i++) { + struct nvmf_disc_log_entry *e = &log->entries[i]; + nvme_ctrl_t cl; + bool discover = false; + bool disconnect; + nvme_ctrl_t child; + int tmo = fctx->cfg->keep_alive_tmo; + + struct fabric_args trcfg = { + .subsysnqn = e->subnqn, + .transport = nvmf_trtype_str(e->trtype), + .traddr = e->traddr, + .host_traddr = fctx->host_traddr, + .host_iface = fctx->host_iface, + .trsvcid = e->trsvcid, + }; + + /* 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 || + e->subtype == NVME_NQN_CURR) { + __u16 eflags = le16_to_cpu(e->eflags); + /* + * Does this discovery controller return the + * same information? + */ + if (eflags & NVMF_DISC_EFLAGS_DUPRETINFO) + continue; + + /* + * Are we supposed to keep the discovery + * controller around? + */ + disconnect = !fctx->persistent; + + if (strcmp(e->subnqn, NVME_DISC_SUBSYS_NAME)) { + /* + * Does this discovery controller doesn't + * support explicit persistent connection? + */ + if (!(eflags & NVMF_DISC_EFLAGS_EPCSD)) + disconnect = true; + else + disconnect = false; + } + + set_discovery_kato(fctx, fctx->cfg); + } else { + /* NVME_NQN_NVME */ + disconnect = false; + } + + err = nvmf_connect_disc_entry(h, e, fctx->host_traddr, + fctx->host_iface, fctx->cfg, + &discover, &child); + + fctx->cfg->keep_alive_tmo = tmo; + + if (!child) { + if (discover) + _nvmf_discovery(ctx, fctx, true, child); + + if (disconnect) { + nvme_disconnect_ctrl(child); + nvme_free_ctrl(child); + } + } else if (err == -ENVME_CONNECT_ALREADY) { + struct nvmf_disc_log_entry *e = &log->entries[i]; + + fctx->already_connected(fctx, h, e->subnqn, + nvmf_trtype_str(e->trtype), e->traddr, + e->trsvcid, fctx->user_data); + } + } + + return 0; +} + +const char *nvmf_get_default_trsvcid(const char *transport, + bool discovery_ctrl) +{ + if (!transport) + return NULL; + if (!strcmp(transport, "tcp")) { + if (discovery_ctrl) + /* Default port for NVMe/TCP discovery controllers */ + return stringify(NVME_DISC_IP_PORT); + /* Default port for NVMe/TCP io controllers */ + return stringify(NVME_RDMA_IP_PORT); + } else if (!strcmp(transport, "rdma")) { + /* Default port for NVMe/RDMA controllers */ + return stringify(NVME_RDMA_IP_PORT); + } + + return NULL; +} + +static bool is_persistent_discovery_ctrl(nvme_host_t h, nvme_ctrl_t c) +{ + if (nvme_host_is_pdc_enabled(h, DEFAULT_PDC_ENABLED)) + return nvme_ctrl_is_unique_discovery_ctrl(c); + + return false; +} + +static int nvme_add_ctrl(struct nvmf_context *fctx, + struct nvme_host *h, struct nvme_ctrl *c, + struct nvme_fabrics_config *cfg) +{ + int err; + +retry: + err = nvmf_add_ctrl(h, c, cfg); + if (!err) + return 0; + if (fctx->decide_retry(fctx, err, fctx->user_data)) + goto retry; + + return err; +} + +static int __create_discovery_ctrl(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, nvme_host_t h, + struct nvme_fabrics_config *cfg, struct fabric_args *trcfg, + struct nvme_ctrl **ctrl) +{ + nvme_ctrl_t c; + int tmo, ret; + + ret = nvme_create_ctrl(ctx, trcfg->subsysnqn, trcfg->transport, + trcfg->traddr, trcfg->host_traddr, + trcfg->host_iface, trcfg->trsvcid, &c); + if (ret) + return ret; + + nvme_ctrl_set_discovery_ctrl(c, true); + nvme_ctrl_set_unique_discovery_ctrl(c, + strcmp(trcfg->subsysnqn, NVME_DISC_SUBSYS_NAME)); + tmo = set_discovery_kato(fctx, cfg); + + ret = nvme_add_ctrl(fctx, h, c, cfg); + cfg->keep_alive_tmo = tmo; + if (ret) { + nvme_free_ctrl(c); + return ret; + } + + *ctrl = c; + return 0; +} + +static int nvmf_create_discovery_ctrl(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, nvme_host_t h, + struct nvme_fabrics_config *cfg, + struct fabric_args *trcfg, + struct nvme_ctrl **ctrl) +{ + _cleanup_free_ struct nvme_id_ctrl *id = NULL; + struct nvme_ctrl *c; + int ret; + + ret = __create_discovery_ctrl(ctx, fctx, h, cfg, trcfg, &c); + if (ret) + return ret; + + if (nvme_ctrl_is_unique_discovery_ctrl(c)) { + *ctrl = c; + return 0; + } + + id = __nvme_alloc(sizeof(*id)); + if (!id) { + nvme_free_ctrl(c); + return -ENOMEM; + } + + /* Find out the name of discovery controller */ + ret = nvme_ctrl_identify(c, id); + if (ret) { + fprintf(stderr, "failed to identify controller, error %s\n", + nvme_strerror(-ret)); + nvme_disconnect_ctrl(c); + nvme_free_ctrl(c); + return ret; + } + + if (!strcmp(id->subnqn, NVME_DISC_SUBSYS_NAME)) { + *ctrl = c; + return 0; + } + + /* + * The subsysnqn is not the well-known name. Prefer the unique + * subsysnqn over the well-known one. + */ + nvme_disconnect_ctrl(c); + nvme_free_ctrl(c); + + trcfg->subsysnqn = id->subnqn; + ret = __create_discovery_ctrl(ctx, fctx, h, cfg, trcfg, &c); + if (ret) + return ret; + + *ctrl = c; + return 0; +} + +int _discovery_config_json(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, nvme_host_t h, nvme_ctrl_t c, + bool connect, bool force) +{ + const char *transport, *traddr, *host_traddr; + const char *host_iface, *trsvcid, *subsysnqn; + struct nvme_fabrics_config cfg; + nvme_ctrl_t cn; + int ret = 0; + + transport = nvme_ctrl_get_transport(c); + traddr = nvme_ctrl_get_traddr(c); + host_traddr = nvme_ctrl_get_host_traddr(c); + host_iface = nvme_ctrl_get_host_iface(c); + + if (!transport && !traddr) + return 0; + + /* ignore none fabric transports */ + if (strcmp(transport, "tcp") && + strcmp(transport, "rdma") && + strcmp(transport, "fc")) + return 0; + + /* ignore if no host_traddr for fc */ + if (!strcmp(transport, "fc")) { + if (!host_traddr) { + fprintf(stderr, "host_traddr required for fc\n"); + return 0; + } + } + + /* ignore if host_iface set for any transport other than tcp */ + if (!strcmp(transport, "rdma") || !strcmp(transport, "fc")) { + if (host_iface) { + fprintf(stderr, + "host_iface not permitted for rdma or fc\n"); + return 0; + } + } + + trsvcid = nvme_ctrl_get_trsvcid(c); + if (!trsvcid || !strcmp(trsvcid, "")) + trsvcid = nvmf_get_default_trsvcid(transport, true); + + if (force) + subsysnqn = nvme_ctrl_get_subsysnqn(c); + else + subsysnqn = NVME_DISC_SUBSYS_NAME; + + if (nvme_ctrl_is_persistent(c)) + fctx->persistent = true; + + memcpy(&cfg, fctx->cfg, sizeof(cfg)); + + struct fabric_args trcfg = { + .subsysnqn = subsysnqn, + .transport = transport, + .traddr = traddr, + .host_traddr = host_traddr, + .host_iface = host_iface, + .trsvcid = trsvcid, + }; + + if (!force) { + cn = lookup_ctrl(h, &trcfg); + if (cn) { + fctx->persistent = true; + _nvmf_discovery(ctx, fctx, connect, cn); + return 0; + } + } + + ret = nvmf_create_discovery_ctrl(ctx, fctx, h, &cfg, &trcfg, &cn); + if (ret) + return 0; + + _nvmf_discovery(ctx, fctx, connect, cn); + if (!(fctx->persistent || is_persistent_discovery_ctrl(h, cn))) + ret = nvme_disconnect_ctrl(cn); + nvme_free_ctrl(cn); + + return ret; +} + +int nvmf_discovery_config_json(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, bool connect, bool force) +{ + const char *hnqn, *hid; + struct nvme_subsystem *s; + struct nvme_host *h; + struct nvme_ctrl *c; + int ret = 0, err; + + err = lookup_host(ctx, fctx, &h); + if (err) + return err; + + err = setup_connection(fctx, h, false); + if (err) + return err; + + nvme_for_each_host(ctx, h) { + nvme_for_each_subsystem(h, s) { + hnqn = nvme_host_get_hostnqn(h); + if (fctx->hostnqn && hnqn && + strcmp(fctx->hostnqn, hnqn)) + continue; + hid = nvme_host_get_hostid(h); + if (fctx->hostid && hid && + strcmp(fctx->hostid, hid)) + continue; + + nvme_subsystem_for_each_ctrl(s, c) { + err = _discovery_config_json(ctx, fctx, h, c, + connect, force); + if (err) { + nvme_msg(ctx, LOG_ERR, + "failed to connect to hostnqn=%s,nqn=%s,%s\n", + nvme_host_get_hostnqn(h), + nvme_subsystem_get_name(s), + nvme_ctrl_get_address(c)); + + if (!ret) + ret = err; + } + } + } + } + + return ret; +} + +int nvmf_connect_config_json(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx) +{ + const char *hnqn, *hid; + const char *transport; + nvme_host_t h; + nvme_subsystem_t s; + nvme_ctrl_t c, _c; + int ret = 0, err; + + err = lookup_host(ctx, fctx, &h); + if (err) + return err; + + err = setup_connection(fctx, h, false); + if (err) + return err; + + nvme_for_each_host(ctx, h) { + nvme_for_each_subsystem(h, s) { + hnqn = nvme_host_get_hostnqn(h); + if (fctx->hostnqn && hnqn && + strcmp(fctx->hostnqn, hnqn)) + continue; + hid = nvme_host_get_hostid(h); + if (fctx->hostid && hid && + strcmp(fctx->hostid, hid)) + continue; + + nvme_subsystem_for_each_ctrl_safe(s, c, _c) { + transport = nvme_ctrl_get_transport(c); + + /* ignore none fabric transports */ + if (strcmp(transport, "tcp") && + strcmp(transport, "rdma") && + strcmp(transport, "fc")) + continue; + + err = nvmf_connect_ctrl(c); + if (err) { + if (err == -ENVME_CONNECT_ALREADY) + continue; + + fprintf(stderr, + "failed to connect to hostnqn=%s,nqn=%s,%s\n", + nvme_host_get_hostnqn(h), + nvme_subsystem_get_name(s), + nvme_ctrl_get_address(c)); + + if (!ret) + ret = err; + } + } + } + } + + return ret; +} + +int nvmf_discovery_config_file(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, bool connect, bool force) +{ + struct nvme_host *h; + struct nvme_ctrl *c; + int err; + + err = lookup_host(ctx, fctx, &h); + if (err) + return err; + + err = setup_connection(fctx, h, false); + if (err) + return err; + + err = fctx->parser_init(fctx, fctx->user_data); + if (err) + return err; + + do { + err = fctx->parser_next_line(fctx, fctx->user_data); + if (err) + break; + + struct fabric_args trcfg = { + .transport = fctx->transport, + .traddr = fctx->traddr, + .trsvcid = fctx->trsvcid, + .subsysnqn = fctx->subsysnqn, + .host_traddr = fctx->host_traddr, + .host_iface = fctx->host_iface, + }; + + if (!force) { + c = lookup_ctrl(h, &trcfg); + if (c) { + _nvmf_discovery(ctx, fctx, connect, c); + continue; + } + } + + err = nvmf_create_discovery_ctrl(ctx, fctx, h, fctx->cfg, + &trcfg, &c); + if (err) + continue; + + _nvmf_discovery(ctx, fctx, connect, c); + if (!(fctx->persistent || is_persistent_discovery_ctrl(h, c))) + err = nvme_disconnect_ctrl(c); + nvme_free_ctrl(c); + } while (!err); + + if (err != -EOF) + return err; + + return 0; +} + +#define NBFT_SYSFS_FILENAME "NBFT*" + +static int nbft_filter(const struct dirent *dent) +{ + return !fnmatch(NBFT_SYSFS_FILENAME, dent->d_name, FNM_PATHNAME); +} + +int nvmf_nbft_read_files(char *path, struct nbft_file_entry **head) +{ + struct nbft_file_entry *entry = NULL; + struct nbft_info *nbft; + struct dirent **dent; + char filename[PATH_MAX]; + int i, count, ret; + + count = scandir(path, &dent, nbft_filter, NULL); + if (count < 0) + return -errno; + + 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) { + struct nbft_file_entry *new; + + new = calloc(1, sizeof(*new)); + if (!new) + return -ENOMEM; + new->nbft = nbft; + if (entry) { + entry->next = new; + entry = entry->next; + } else { + entry = new; + *head = entry; + } + } + free(dent[i]); + } + free(dent); + return 0; +} + +void nvmf_nbft_free(struct nbft_file_entry *head) +{ + while (head) { + struct nbft_file_entry *next = head->next; + + nvme_nbft_free(head->nbft); + free(head); + + head = next; + } +} + +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; +} + +static int nbft_connect(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, struct nvme_host *h, + struct nvmf_disc_log_entry *e, + struct nbft_info_subsystem_ns *ss, struct fabric_args *trcfg, + struct nvme_fabrics_config *cfg) +{ + nvme_ctrl_t c; + int saved_log_level; + bool saved_log_tstamp; + bool saved_log_pid; + int ret; + + saved_log_level = nvme_get_logging_level(ctx, &saved_log_tstamp, + &saved_log_pid); + + /* Already connected ? */ + c = lookup_ctrl(h, trcfg); + if (c && nvme_ctrl_get_name(c)) + return 0; + + ret = nvme_create_ctrl(ctx, trcfg->subsysnqn, trcfg->transport, + trcfg->traddr, trcfg->host_traddr, + trcfg->host_iface, trcfg->trsvcid, &c); + if (ret) + return ret; + + /* Pause logging for unavailable SSNSs */ + if (ss && ss->unavailable && saved_log_level < 1) + nvme_init_logging(ctx, -1, false, false); + + if (e) { + if (e->trtype == NVMF_TRTYPE_TCP && + e->tsas.tcp.sectype != NVMF_TCP_SECTYPE_NONE) + cfg->tls = true; + } + + ret = nvmf_add_ctrl(h, c, cfg); + + /* Resume logging */ + if (ss && ss->unavailable && saved_log_level < 1) + nvme_init_logging(ctx, + saved_log_level, + saved_log_pid, + saved_log_tstamp); + + if (ret) { + nvme_free_ctrl(c); + /* + * In case this SSNS was marked as 'unavailable' and + * our connection attempt has failed, ignore it. + */ + if (ss && ss->unavailable) { + nvme_msg(ctx, LOG_INFO, + "SSNS %d reported as unavailable, skipping\n", + ss->index); + return 0; + } + return ret; + } + + if (fctx->connected) + fctx->connected(fctx, c, fctx->user_data); + + return 0; +} + +static int nbft_discovery(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, struct nbft_info_discovery *dd, + struct nvme_host *h, struct nvme_ctrl *c, + struct nvme_fabrics_config *defcfg, struct fabric_args *deftrcfg) +{ + struct nvmf_discovery_log *log = NULL; + int ret; + int i; + + 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, + }; + + ret = nvmf_get_discovery_wargs(&args, &log); + if (ret) { + nvme_msg(ctx, LOG_ERR, + "Discovery Descriptor %d: failed to get discovery log: %s\n", + dd->index, nvme_strerror(ret)); + return ret; + } + + 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 fabric_args trcfg = { + .subsysnqn = e->subnqn, + .transport = nvmf_trtype_str(e->trtype), + .traddr = e->traddr, + .host_traddr = fctx->host_traddr, + .host_iface = fctx->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; + + ret = nvmf_connect_disc_entry(h, e, fctx->host_traddr, + fctx->host_iface, defcfg, NULL, &child); + if (ret) + continue; + nbft_discovery(ctx, fctx, dd, h, child, defcfg, &trcfg); + nvme_disconnect_ctrl(child); + nvme_free_ctrl(child); + } else { + ret = nbft_connect(ctx, fctx, h, e, NULL, + &trcfg, defcfg); + + /* + * 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 = nbft_connect(ctx, fctx, h, e, NULL, + &trcfg, defcfg); + + if (ret == 0) + nvme_msg(ctx, LOG_INFO, + "Discovery Descriptor %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n", + dd->index, + htradr); + } + + if (ret) + nvme_msg(ctx, LOG_ERR, + "Discovery Descriptor %d: no controller found\n", + dd->index); + if (ret == -ENOMEM) + break; + } + + defcfg->keep_alive_tmo = tmo; + } + + free(log); + return 0; +} + +int nvmf_discovery_nbft(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, bool connect, char *nbft_path) +{ + const char *hostnqn = NULL, *hostid = NULL, *host_traddr = NULL; + char uuid[NVME_UUID_LEN_STRING]; + struct nbft_file_entry *entry = NULL; + struct nbft_info_subsystem_ns **ss; + struct nbft_info_hfi *hfi; + struct nbft_info_discovery **dd; + struct nvme_host *h; + int ret, rr, i; + + ret = lookup_host(ctx, fctx, &h); + if (ret) + return ret; + + ret = setup_connection(fctx, h, false); + if (ret) + return ret; + + if (!connect) + /* TODO: print discovery-type info from NBFT tables */ + return 0; + + ret = nvmf_nbft_read_files(nbft_path, &entry); + if (ret) { + if (ret != -ENOENT) + nvme_msg(ctx, LOG_ERR, + "Failed to access ACPI tables directory\n"); + else + ret = 0; /* nothing to connect */ + goto out_free; + } + + for (; entry; entry = entry->next) { + if (fctx->hostnqn) + hostnqn = fctx->hostnqn; + else { + hostnqn = entry->nbft->host.nqn; + if (!hostnqn) + hostnqn = fctx->hostnqn; + } + + if (fctx->hostid) + hostid = fctx->hostid; + else if (*entry->nbft->host.id) { + ret = nvme_uuid_to_string(entry->nbft->host.id, uuid); + if (!ret) + hostid = uuid; + else + hostid = fctx->hostid; + } + + h = nvme_lookup_host(ctx, hostnqn, hostid); + if (!h) { + ret = -ENOENT; + 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]; + + /* Skip discovery NQN records */ + if (strcmp((*ss)->subsys_nqn, + NVME_DISC_SUBSYS_NAME) == 0) { + nvme_msg(ctx, LOG_INFO, + "SSNS %d points to well-known discovery NQN, skipping\n", + (*ss)->index); + continue; + } + + host_traddr = NULL; + if (!fctx->host_traddr && + !strncmp((*ss)->transport, "tcp", 3)) + host_traddr = hfi->tcp_info.ipaddr; + + struct fabric_args trcfg = { + .subsysnqn = (*ss)->subsys_nqn, + .transport = (*ss)->transport, + .traddr = (*ss)->traddr, + .host_traddr = host_traddr, + .host_iface = NULL, + .trsvcid = (*ss)->trsvcid, + }; + + rr = nbft_connect(ctx, fctx, h, NULL, + *ss, &trcfg, fctx->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 (rr == -ENVME_CONNECT_ADDRNOTAVAIL && + !strcmp(trcfg.transport, "tcp") && + strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) { + trcfg.host_traddr = NULL; + + rr = nbft_connect(ctx, fctx, h, NULL, + *ss, &trcfg, fctx->cfg); + + if (rr == 0) + nvme_msg(ctx, LOG_INFO, + "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n", + (*ss)->index, + host_traddr); + } + + if (rr) { + nvme_msg(ctx, LOG_ERR, + "SSNS %d: no controller found\n", + (*ss)->index); + /* report an error */ + ret = rr; + } + + if (rr == -ENOMEM) + goto out_free; + } + + /* Discovery Descriptor List */ + for (dd = entry->nbft->discovery_list; dd && *dd; dd++) { + _cleanup_uri_ struct nvme_fabrics_uri *uri = NULL; + _cleanup_free_ char *trsvcid = NULL; + bool persistent = false; + bool linked = false; + nvme_ctrl_t c; + + /* 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; + ret = nvme_parse_uri((*dd)->uri, &uri); + if (ret) + continue; + if (!validate_uri(*dd, uri)) + continue; + + host_traddr = NULL; + if (!fctx->host_traddr && + !strncmp(uri->protocol, "tcp", 3)) + host_traddr = hfi->tcp_info.ipaddr; + if (uri->port > 0) { + if (asprintf(&trsvcid, "%d", uri->port) < 0) { + ret = -ENOMEM; + goto out_free; + } + } else + trsvcid = + strdup(nvmf_get_default_trsvcid( + uri->protocol, true)); + + struct fabric_args trcfg = { + .subsysnqn = NVME_DISC_SUBSYS_NAME, + .transport = uri->protocol, + .traddr = uri->host, + .host_traddr = host_traddr, + .host_iface = NULL, + .trsvcid = trsvcid, + }; + + /* Lookup existing discovery controller */ + c = lookup_ctrl(h, &trcfg); + if (c && nvme_ctrl_get_name(c)) + persistent = true; + + if (!c) { + ret = nvmf_create_discovery_ctrl(ctx, fctx, h, + fctx->cfg, &trcfg, &c); + if (ret == -ENVME_CONNECT_ADDRNOTAVAIL && + !strcmp(trcfg.transport, "tcp") && + strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) { + trcfg.host_traddr = NULL; + ret = nvmf_create_discovery_ctrl(ctx, + fctx, h, fctx->cfg, &trcfg, &c); + } + } else + ret = 0; + + if (ret) { + nvme_msg(ctx, LOG_ERR, + "Discovery Descriptor %d: failed to add discovery controller: %s\n", + (*dd)->index, nvme_strerror(-ret)); + goto out_free; + } + + rr = nbft_discovery(ctx, fctx, *dd, h, c, fctx->cfg, + &trcfg); + if (!persistent) + nvme_disconnect_ctrl(c); + nvme_free_ctrl(c); + if (rr == -ENOMEM) { + ret = rr; + goto out_free; + } + } + } +out_free: + nvmf_nbft_free(entry); + return ret; +} + +static int __create_discover_ctrl(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, nvme_host_t h, + struct nvme_fabrics_config *cfg, struct fabric_args *trcfg, + struct nvme_ctrl **ctrl) +{ + struct nvme_ctrl *c; + int tmo, ret; + + ret = nvme_create_ctrl(ctx, trcfg->subsysnqn, trcfg->transport, + trcfg->traddr, trcfg->host_traddr, + trcfg->host_iface, trcfg->trsvcid, &c); + if (ret) + return ret; + + nvme_ctrl_set_discovery_ctrl(c, true); + nvme_ctrl_set_unique_discovery_ctrl(c, + strcmp(trcfg->subsysnqn, NVME_DISC_SUBSYS_NAME)); + tmo = set_discovery_kato(fctx, cfg); + + ret = nvme_add_ctrl(fctx, h, c, cfg); + cfg->keep_alive_tmo = tmo; + if (ret) { + nvme_free_ctrl(c); + return ret; + } + + *ctrl = c; + return 0; +} + +static int nvmf_create_discover_ctrl(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, + struct nvme_host *h, struct nvme_fabrics_config *cfg, + struct fabric_args *trcfg, struct nvme_ctrl **ctrl) +{ + _cleanup_free_ struct nvme_id_ctrl *id = NULL; + struct nvme_ctrl *c; + int ret; + + ret = __create_discover_ctrl(ctx, fctx, h, cfg, trcfg, &c); + if (ret) + return ret; + + if (nvme_ctrl_is_unique_discovery_ctrl(c)) { + *ctrl = c; + return 0; + } + + id = __nvme_alloc(sizeof(*id)); + if (!id) { + nvme_free_ctrl(c); + return -ENOMEM; + } + + /* Find out the name of discovery controller */ + ret = nvme_ctrl_identify(c, id); + if (ret) { + fprintf(stderr, "failed to identify controller, error %s\n", + nvme_strerror(-ret)); + nvme_disconnect_ctrl(c); + nvme_free_ctrl(c); + return ret; + } + + if (!strcmp(id->subnqn, NVME_DISC_SUBSYS_NAME)) { + *ctrl = c; + return 0; + } + + /* + * The subsysnqn is not the well-known name. Prefer the unique + * subsysnqn over the well-known one. + */ + nvme_disconnect_ctrl(c); + nvme_free_ctrl(c); + + trcfg->subsysnqn = id->subnqn; + ret = __create_discover_ctrl(ctx, fctx, h, cfg, trcfg, &c); + if (ret) + return ret; + + *ctrl = c; + return 0; +} + +int nvmf_discovery(struct nvme_global_ctx *ctx, struct nvmf_context *fctx, + bool connect, bool force) +{ + struct nvme_ctrl *c = NULL; + struct nvme_host *h; + int ret; + + ret = lookup_host(ctx, fctx, &h); + if (ret) + return ret; + + ret = setup_connection(fctx, h, true); + if (ret) + return ret; + + if (fctx->device && !force) { + ret = nvme_scan_ctrl(ctx, fctx->device, &c); + if (!ret) { + /* Check if device matches command-line options */ + if (!nvme_ctrl_config_match(c, fctx->transport, + fctx->traddr, fctx->trsvcid, + fctx->subsysnqn, fctx->host_traddr, + fctx->host_iface)) { + nvme_msg(ctx, LOG_ERR, + "ctrl device %s found, ignoring non matching command-line options\n", + fctx->device); + } + + if (!nvme_ctrl_is_discovery_ctrl(c)) { + nvme_msg( + ctx, LOG_ERR, + "ctrl device %s found, ignoring non discovery controller\n", + fctx->device); + + nvme_free_ctrl(c); + c = NULL; + fctx->persistent = false; + } else { + /* + * If the controller device is found it must + * be persistent, and shouldn't be disconnected + * on exit. + */ + fctx->persistent = true; + /* + * When --host-traddr/--host-iface are not specified on the + * command line, use the discovery controller's (c) host- + * traddr/host-iface for the connections to controllers + * returned in the Discovery Log Pages. This is essential + * when invoking "connect-all" with --device to reuse an + * existing persistent discovery controller (as is done + * for the udev rules). This ensures that host-traddr/ + * host-iface are consistent with the discovery controller (c). + */ + if (!fctx->host_traddr) + fctx->host_traddr = (char *) + nvme_ctrl_get_host_traddr(c); + if (!fctx->host_iface) + fctx->host_iface = (char *) + nvme_ctrl_get_host_iface(c); + } + } else { + /* + * No controller found, fall back to create one. + * But that controller cannot be persistent. + */ + nvme_msg(ctx, LOG_ERR, + "ctrl device %s not found%s\n", fctx->device, + fctx->persistent ? ", ignoring --persistent" : ""); + fctx->persistent = false; + } + } + + struct fabric_args trcfg = { + .subsysnqn = fctx->subsysnqn, + .transport = fctx->transport, + .traddr = fctx->traddr, + .host_traddr = fctx->host_traddr, + .host_iface = fctx->host_iface, + .trsvcid = fctx->trsvcid, + }; + + if (!c && !force) { + c = lookup_ctrl(h, &trcfg); + if (c) + fctx->persistent = true; + } + if (!c) { + /* No device or non-matching device, create a new controller */ + ret = nvmf_create_discover_ctrl(ctx, fctx, h, fctx->cfg, + &trcfg, &c); + if (ret) { + if (ret != -ENVME_CONNECT_IGNORED) + nvme_msg(ctx, LOG_ERR, + "failed to add controller, error %s\n", + nvme_strerror(-ret)); + return ret; + } + } + + ret = _nvmf_discovery(ctx, fctx, connect, c); + if (!(fctx->persistent || is_persistent_discovery_ctrl(h, c))) + nvme_disconnect_ctrl(c); + nvme_free_ctrl(c); + + return ret; +} + +static void nvme_parse_tls_args(const char *keyring, const char *tls_key, + const char *tls_key_identity, + struct nvme_fabrics_config *cfg, nvme_ctrl_t c) +{ + if (keyring) { + char *endptr; + long id = strtol(keyring, &endptr, 0); + + if (endptr != keyring) + cfg->keyring = id; + else + nvme_ctrl_set_keyring(c, keyring); + } + + if (tls_key_identity) + nvme_ctrl_set_tls_key_identity(c, tls_key_identity); + + if (tls_key) { + char *endptr; + long id = strtol(tls_key, &endptr, 0); + + if (endptr != tls_key) + cfg->tls_key = id; + else + nvme_ctrl_set_tls_key(c, tls_key); + } +} + +int nvmf_connect(struct nvme_global_ctx *ctx, struct nvmf_context *fctx) +{ + struct nvme_host *h; + struct nvme_ctrl *c; + int err; + + err = lookup_host(ctx, fctx, &h); + if (err) + return err; + + err = setup_connection(fctx, h, false); + if (err) + return err; + + struct fabric_args trcfg = { + .subsysnqn = fctx->subsysnqn, + .transport = fctx->transport, + .traddr = fctx->traddr, + .host_traddr = fctx->host_traddr, + .host_iface = fctx->host_iface, + .trsvcid = fctx->trsvcid, + }; + + c = lookup_ctrl(h, &trcfg); + if (c && nvme_ctrl_get_name(c) && !fctx->cfg->duplicate_connect) { + fctx->already_connected(fctx, h, nvme_ctrl_get_subsysnqn(c), + nvme_ctrl_get_transport(c), nvme_ctrl_get_traddr(c), + nvme_ctrl_get_trsvcid(c), fctx->user_data); + return -EALREADY; + } + + err = nvme_create_ctrl(ctx, trcfg.subsysnqn, trcfg.transport, + trcfg.traddr, trcfg.host_traddr, trcfg.host_iface, + trcfg.trsvcid, &c); + if (err) + return err; + + if (fctx->ctrlkey) + nvme_ctrl_set_dhchap_key(c, fctx->ctrlkey); + + nvme_parse_tls_args(fctx->keyring, fctx->tls_key, + fctx->tls_key_identity, fctx->cfg, c); + + /* + * We are connecting to a discovery controller, so let's treat + * this as a persistent connection and specify a KATO. + */ + if (!strcmp(trcfg.subsysnqn, NVME_DISC_SUBSYS_NAME)) { + fctx->persistent = true; + + set_discovery_kato(fctx, fctx->cfg); + } + + err = nvme_add_ctrl(fctx, h, c, fctx->cfg); + if (err) { + nvme_msg(ctx, LOG_ERR, "could not add new controller: %s\n", + nvme_strerror(-err)); + return err; + } + + fctx->connected(fctx, c, fctx->user_data); + + return 0; +} diff --git a/libnvme/src/nvme/fabrics.h b/libnvme/src/nvme/fabrics.h index 01d3db6bda..c4d0ba7fba 100644 --- a/libnvme/src/nvme/fabrics.h +++ b/libnvme/src/nvme/fabrics.h @@ -25,8 +25,6 @@ /** * struct nvme_fabrics_config - Defines all linux nvme fabrics initiator options - * @host_traddr: Host transport address - * @host_iface: Host interface name * @queue_size: Number of IO queue entries * @nr_io_queues: Number of controller IO queues to establish * @reconnect_delay: Time between two consecutive reconnect attempts. @@ -47,8 +45,6 @@ * @concat: Enable secure concatenation (TCP) */ struct nvme_fabrics_config { - char *host_traddr; - char *host_iface; int queue_size; int nr_io_queues; int reconnect_delay; @@ -338,22 +334,6 @@ char *nvmf_hostnqn_from_file(); */ char *nvmf_hostid_from_file(); -/** - * nvmf_connect_disc_entry() - Connect controller based on the discovery log page entry - * @h: Host to which the controller should be connected - * @e: Discovery log page entry - * @defcfg: Default configuration to be used for the new controller - * @discover: Set to 'true' if the new controller is a discovery controller - * @c: crtl object to return - * - * Return: 0 on success, or an error code on failure. - */ -int nvmf_connect_disc_entry(nvme_host_t h, - struct nvmf_disc_log_entry *e, - const struct nvme_fabrics_config *defcfg, - bool *discover, - nvme_ctrl_t *c); - /** * nvmf_is_registration_supported - check whether registration can be performed. * @c: Controller instance @@ -407,4 +387,278 @@ int nvme_parse_uri(const char *str, struct nvme_fabrics_uri **uri); */ void nvme_free_uri(struct nvme_fabrics_uri *uri); +/** + * nvmf_get_default_trsvcid() - Get default transport service ID + * @transport: Transport type string (e.g., "tcp", "rdma") + * @discovery_ctrl: True if for discovery controller, false otherwise + * + * Returns the default trsvcid (port) for the given transport and controller + * type. + * + * Return: Allocated string with default trsvcid, or NULL on failure. + */ +const char *nvmf_get_default_trsvcid(const char *transport, + bool discovery_ctrl); +/* + * struct nvmf_context - Opaque context for fabrics operations + * + * Used to manage state and configuration for fabrics discovery and connect + * operations. + */ +struct nvmf_context; + +/** + * nvmf_context_create() - Create a new fabrics context for discovery/connect + * @ctx: Global context + * @decide_retry: Callback to decide if a retry should be attempted + * @connected: Callback invoked when a connection is established + * @already_connected: Callback invoked if already connected + * @user_data: User data passed to callbacks + * @fctxp: Pointer to store the created context + * + * Allocates and initializes a new fabrics context for discovery/connect + * operations. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_context_create(struct nvme_global_ctx *ctx, + bool (*decide_retry)(struct nvmf_context *fctx, int err, + void *user_data), + void (*connected)(struct nvmf_context *fctx, + struct nvme_ctrl *c, void *user_data), + void (*already_connected)(struct nvmf_context *fctx, + struct nvme_host *host, const char *subsysnqn, + const char *transport, const char *traddr, + const char *trsvcid, void *user_data), + void *user_data, struct nvmf_context **fctxp); + +/** + * nvmf_context_set_discovery_cbs() - Set discovery callbacks for context + * @fctx: Fabrics context + * @discovery_log: Callback for discovery log events + * @parser_init: Callback to initialize parser + * @parser_cleanup: Callback to cleanup parser + * @parser_next_line: Callback to parse next line + * + * Sets the callbacks used during discovery operations for the given context. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_context_set_discovery_cbs(struct nvmf_context *fctx, + void (*discovery_log)(struct nvmf_context *fctx, + bool connect, struct nvmf_discovery_log *log, + uint64_t numrec, void *user_data), + int (*parser_init)(struct nvmf_context *fctx, + void *user_data), + void (*parser_cleanup)(struct nvmf_context *fctx, + void *user_data), + int (*parser_next_line)(struct nvmf_context *fctx, + void *user_data)); + +/** + * nvmf_context_set_discovery_defaults() - Set default discovery parameters + * @fctx: Fabrics context + * @max_discovery_retries: Maximum number of discovery retries + * @keep_alive_timeout: Keep-alive timeout in seconds + * + * Sets default values for discovery retries and keep-alive timeout. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_context_set_discovery_defaults(struct nvmf_context *fctx, + int max_discovery_retries, int keep_alive_timeout); + +/** + * nvmf_context_set_fabrics_config() - Set fabrics configuration for context + * @fctx: Fabrics context + * @cfg: Fabrics configuration to apply + * + * Applies the given fabrics configuration to the context. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_context_set_fabrics_config(struct nvmf_context *fctx, + struct nvme_fabrics_config *cfg); + +/** + * nvmf_context_set_connection() - Set connection parameters for context + * @fctx: Fabrics context + * @subsysnqn: Subsystem NQN + * @transport: Transport type + * @traddr: Transport address + * @trsvcid: Transport service ID + * @host_traddr: Host transport address + * @host_iface: Host interface + * + * Sets the connection parameters for the context. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_context_set_connection(struct nvmf_context *fctx, + const char *subsysnqn, const char *transport, + const char *traddr, const char *trsvcid, + const char *host_traddr, const char *host_iface); + +/** + * nvmf_context_set_hostnqn() - Set host NQN and host ID for context + * @fctx: Fabrics context + * @hostnqn: Host NQN + * @hostid: Host identifier + * + * Sets the host NQN and host ID for the context. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_context_set_hostnqn(struct nvmf_context *fctx, + const char *hostnqn, const char *hostid); + +/** + * nvmf_context_set_crypto() - Set cryptographic parameters for context + * @fctx: Fabrics context + * @hostkey: Host key + * @ctrlkey: Controller key + * @keyring: Keyring identifier + * @tls_key: TLS key + * @tls_key_identity: TLS key identity + * + * Sets cryptographic and TLS parameters for the context. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_context_set_crypto(struct nvmf_context *fctx, + const char *hostkey, const char *ctrlkey, + const char *keyring, const char *tls_key, + const char *tls_key_identity); + +/** + * nvmf_context_set_persistent() - Set persistence for context + * @fctx: Fabrics context + * @persistent: Whether to enable persistent connections + * + * Sets whether the context should use persistent connections. + * + * Return: 0 on success, or a negative error code on failure. + */ + +int nvmf_context_set_persistent(struct nvmf_context *fctx, bool persistent); + +/** + * nvmf_context_set_device() - Set device for context + * @fctx: Fabrics context + * @device: Device path or identifier + * + * Sets the device to be used by the context. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_context_set_device(struct nvmf_context *fctx, const char *device); + +/** + * nvmf_discovery() - Perform fabrics discovery + * @ctx: Global context + * @fctx: Fabrics context + * @connect: Whether to connect discovered subsystems + * @force: Force discovery even if already connected + * + * Performs discovery for fabrics subsystems and optionally connects. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_discovery(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, bool connect, bool force); + +/** + * nvmf_discovery_config_json() - Perform discovery using JSON config + * @ctx: Global context + * @fctx: Fabrics context + * @connect: Whether to connect discovered subsystems + * @force: Force discovery even if already connected + * + * Performs discovery using a JSON configuration. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_discovery_config_json(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, bool connect, bool force); + +/** + * nvmf_discovery_config_file() - Perform discovery using config file + * @ctx: Global context + * @fctx: Fabrics context + * @connect: Whether to connect discovered subsystems + * @force: Force discovery even if already connected + * + * Performs discovery using a configuration file. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_discovery_config_file(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, bool connect, bool force); + +/** + * nvmf_discovery_nbft() - Perform discovery using NBFT + * @ctx: Global context + * @fctx: Fabrics context + * @connect: Whether to connect discovered subsystems + * @nbft_path: Path to NBFT file + * + * Performs discovery using the specified NBFT file. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_discovery_nbft(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx, bool connect, char *nbft_path); + +/** + * nvmf_connect() - Connect to fabrics subsystem + * @ctx: Global context + * @fctx: Fabrics context + * + * Connects to the fabrics subsystem using the provided context. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_connect(struct nvme_global_ctx *ctx, struct nvmf_context *fctx); + +/** + * nvmf_connect_config_json() - Connect using JSON config + * @ctx: Global context + * @fctx: Fabrics context + * + * Connects to the fabrics subsystem using a JSON configuration. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_connect_config_json(struct nvme_global_ctx *ctx, + struct nvmf_context *fctx); + +/** + * struct nbft_file_entry - Linked list entry for NBFT files + * @next: Pointer to next entry + * @nbft: Pointer to NBFT info structure + */ +struct nbft_file_entry { + struct nbft_file_entry *next; + struct nbft_info *nbft; +}; + +/** + * nvmf_nbft_read_files() - Read NBFT files from path + * @path: Path to NBFT files + * @head: Pointer to store linked list of NBFT file entries + * + * Reads NBFT files from the specified path and populates a linked list. + * + * Return: 0 on success, or a negative error code on failure. + */ +int nvmf_nbft_read_files(char *path, struct nbft_file_entry **head); + +/** + * nvmf_nbft_free() - Free NBFT file entry list + * @head: Head of the NBFT file entry list + * + * Frees all memory associated with the NBFT file entry list. + */ +void nvmf_nbft_free(struct nbft_file_entry *head); + #endif /* _LIBNVME_FABRICS_H */ diff --git a/libnvme/src/nvme/private.h b/libnvme/src/nvme/private.h index e7825376b4..6b2a864805 100644 --- a/libnvme/src/nvme/private.h +++ b/libnvme/src/nvme/private.h @@ -188,6 +188,8 @@ struct nvme_ctrl { char *cntlid; char *dctype; char *phy_slot; + char *host_traddr; + char *host_iface; bool discovery_ctrl; bool unique_discovery_ctrl; bool discovered; @@ -272,6 +274,114 @@ struct nvme_global_ctx { struct nvme_fabric_options *options; }; +struct nvmf_discovery_ctx { + /* defaults */ + int default_max_discovery_retries; + int default_keep_alive_timeout; + + void (*discovery_log)(struct nvmf_discovery_ctx *dctx, + bool connect, + struct nvmf_discovery_log *log, + uint64_t numrec, void *user_data); + void (*already_connected)(struct nvme_host *host, + struct nvmf_disc_log_entry *entry, + void *user_data); + bool (*decide_retry)(struct nvmf_discovery_ctx *dctx, int err, + void *user_data); + void (*connected)(struct nvmf_discovery_ctx *dctx, struct nvme_ctrl *c, + void *user_data); + int (*parser_init)(struct nvmf_discovery_ctx *dctx, + void *user_data); + void (*parser_cleanup)(struct nvmf_discovery_ctx *dctx, + void *user_data); + int (*parser_next_line)(struct nvmf_discovery_ctx *dctx, + void *user_data); + + /* connfiguration */ + bool persistent; + const char *device; + const char *subsysnqn; + const char *transport; + const char *traddr; + const char *host_traddr; + const char *host_iface; + const char *trsvcid; + const char *hostnqn; + const char *hostid; + const char *hostkey; + const char *ctrlkey; + const char *keyring; + const char *tls_key; + const char *tls_key_identity; + struct nvme_fabrics_config *cfg; + struct nvme_fabrics_config *defcfg; + + void *user_data; +}; + +struct nvmf_context { + /* common callbacks */ + bool (*decide_retry)(struct nvmf_context *fctx, int err, + void *user_data); + void (*connected)(struct nvmf_context *fctx, struct nvme_ctrl *c, + void *user_data); + void (*already_connected)(struct nvmf_context *fctx, + struct nvme_host *host, const char *subsysnqn, + const char *transport, const char *traddr, + const char *trsvcid, void *user_data); + + /* discovery callbacks */ + void (*discovery_log)(struct nvmf_context *fctx, + bool connect, + struct nvmf_discovery_log *log, + uint64_t numrec, void *user_data); + int (*parser_init)(struct nvmf_context *fctx, + void *user_data); + void (*parser_cleanup)(struct nvmf_context *fctx, + void *user_data); + int (*parser_next_line)(struct nvmf_context *fctx, + void *user_data); + + /* discovery defaults */ + int default_max_discovery_retries; + int default_keep_alive_timeout; + + /* common fabrics configuraiton */ + const char *device; + bool persistent; + struct nvme_fabrics_config *cfg; + + /* connection configuration */ + const char *subsysnqn; + const char *transport; + const char *traddr; + const char *trsvcid; + const char *host_traddr; + const char *host_iface; + + /* host configuration */ + const char *hostnqn; + const char *hostid; + + /* authentication and transport encryption configuration */ + const char *hostkey; + const char *ctrlkey; + const char *keyring; + const char *tls_key; + const char *tls_key_identity; + + void *user_data; +}; + +struct fabric_args { + const char *subsysnqn; + const char *transport; + const char *traddr; + const char *host_traddr; + const char *host_iface; + const char *trsvcid; +}; + int nvme_set_attr(const char *dir, const char *attr, const char *value); int json_read_config(struct nvme_global_ctx *ctx, const char *config_file); diff --git a/libnvme/src/nvme/tree.c b/libnvme/src/nvme/tree.c index c4d5a94b89..2fdde49ee2 100644 --- a/libnvme/src/nvme/tree.c +++ b/libnvme/src/nvme/tree.c @@ -127,7 +127,7 @@ static char *nvme_hostid_from_hostnqn(const char *hostnqn) } int nvme_host_get_ids(struct nvme_global_ctx *ctx, - char *hostnqn_arg, char *hostid_arg, + const char *hostnqn_arg, const char *hostid_arg, char **hostnqn, char **hostid) { _cleanup_free_ char *nqn = NULL; @@ -1155,12 +1155,12 @@ const char *nvme_ctrl_get_trsvcid(nvme_ctrl_t c) const char *nvme_ctrl_get_host_traddr(nvme_ctrl_t c) { - return c->cfg.host_traddr; + return c->host_traddr; } const char *nvme_ctrl_get_host_iface(nvme_ctrl_t c) { - return c->cfg.host_iface; + return c->host_iface; } struct nvme_fabrics_config *nvme_ctrl_get_config(nvme_ctrl_t c) @@ -1379,8 +1379,8 @@ static void __nvme_free_ctrl(nvme_ctrl_t c) FREE_CTRL_ATTR(c->transport); FREE_CTRL_ATTR(c->subsysnqn); FREE_CTRL_ATTR(c->traddr); - FREE_CTRL_ATTR(c->cfg.host_traddr); - FREE_CTRL_ATTR(c->cfg.host_iface); + FREE_CTRL_ATTR(c->host_traddr); + FREE_CTRL_ATTR(c->host_iface); FREE_CTRL_ATTR(c->trsvcid); free(c); } @@ -1445,12 +1445,12 @@ int nvme_create_ctrl(struct nvme_global_ctx *ctx, c->traddr = strdup(traddr); if (host_traddr) { if (traddr_is_hostname(transport, host_traddr)) - hostname2traddr(ctx, host_traddr, &c->cfg.host_traddr); - if (!c->cfg.host_traddr) - c->cfg.host_traddr = strdup(host_traddr); + hostname2traddr(ctx, host_traddr, &c->host_traddr); + if (!c->host_traddr) + c->host_traddr = strdup(host_traddr); } if (host_iface) - c->cfg.host_iface = strdup(host_iface); + c->host_iface = strdup(host_iface); if (trsvcid) c->trsvcid = strdup(trsvcid); @@ -1473,8 +1473,8 @@ int nvme_create_ctrl(struct nvme_global_ctx *ctx, */ static bool _tcp_ctrl_match_host_traddr_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate) { - if (c->cfg.host_traddr) - return candidate->addreq(candidate->host_traddr, c->cfg.host_traddr); + if (c->host_traddr) + return candidate->addreq(candidate->host_traddr, c->host_traddr); /* If c->cfg.host_traddr is NULL, then the controller (c) * uses the interface's primary address as the source @@ -1482,9 +1482,9 @@ static bool _tcp_ctrl_match_host_traddr_no_src_addr(struct nvme_ctrl *c, struct * determine the primary address associated with that * interface and compare that to the candidate->host_traddr. */ - if (c->cfg.host_iface) + if (c->host_iface) return nvme_iface_primary_addr_matches(candidate->iface_list, - c->cfg.host_iface, + c->host_iface, candidate->host_traddr); /* If both c->cfg.host_traddr and c->cfg.host_iface are @@ -1514,16 +1514,16 @@ static bool _tcp_ctrl_match_host_traddr_no_src_addr(struct nvme_ctrl *c, struct */ static bool _tcp_ctrl_match_host_iface_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate) { - if (c->cfg.host_iface) - return streq0(candidate->host_iface, c->cfg.host_iface); + if (c->host_iface) + return streq0(candidate->host_iface, c->host_iface); /* If c->cfg.host_traddr is not NULL we can infer the controller's (c) * interface from it and compare it to the candidate->host_iface. */ - if (c->cfg.host_traddr) { + if (c->host_traddr) { const char *c_host_iface; - c_host_iface = nvme_iface_matching_addr(candidate->iface_list, c->cfg.host_traddr); + c_host_iface = nvme_iface_matching_addr(candidate->iface_list, c->host_traddr); return streq0(candidate->host_iface, c_host_iface); } @@ -1697,12 +1697,12 @@ static bool _match_ctrl(struct nvme_ctrl *c, struct candidate_args *candidate) !candidate->addreq(c->traddr, candidate->traddr)) return false; - if (candidate->host_traddr && c->cfg.host_traddr && - !candidate->addreq(c->cfg.host_traddr, candidate->host_traddr)) + if (candidate->host_traddr && c->host_traddr && + !candidate->addreq(c->host_traddr, candidate->host_traddr)) return false; - if (candidate->host_iface && c->cfg.host_iface && - !streq0(c->cfg.host_iface, candidate->host_iface)) + if (candidate->host_iface && c->host_iface && + !streq0(c->host_iface, candidate->host_iface)) return false; if (candidate->trsvcid && c->trsvcid && diff --git a/libnvme/src/nvme/tree.h b/libnvme/src/nvme/tree.h index c1622d3454..541f1c5601 100644 --- a/libnvme/src/nvme/tree.h +++ b/libnvme/src/nvme/tree.h @@ -191,7 +191,7 @@ int nvme_default_host(struct nvme_global_ctx *ctx, nvme_host_t *h); * which the caller needs to free), or negative error code otherwise. */ int nvme_host_get_ids(struct nvme_global_ctx *ctx, - char *hostnqn_arg, char *hostid_arg, + const char *hostnqn_arg, const char *hostid_arg, char **hostnqn, char **hostid); /** diff --git a/libnvme/src/nvme/util.c b/libnvme/src/nvme/util.c index 18222c4729..3e67020431 100644 --- a/libnvme/src/nvme/util.c +++ b/libnvme/src/nvme/util.c @@ -641,6 +641,13 @@ const char *nvme_errno_to_string(int status) return s; } +const char *nvme_strerror(int errnum) +{ + if (errnum >= ENVME_CONNECT_RESOLVE) + return nvme_errno_to_string(errnum); + return strerror(errnum); +} + #ifdef HAVE_NETDB int hostname2traddr(struct nvme_global_ctx *ctx, const char *traddr, char **hostname) diff --git a/libnvme/src/nvme/util.h b/libnvme/src/nvme/util.h index 0bb7fe0a4d..a32bfc1790 100644 --- a/libnvme/src/nvme/util.h +++ b/libnvme/src/nvme/util.h @@ -95,6 +95,15 @@ const char *nvme_status_to_string(int status, bool fabrics); */ const char *nvme_errno_to_string(int err); +/** + * nvme_strerror() - Returns string describing nvme errors and errno + * @err: Returned error codes from all libnvme functions + * + * Return: String representation of either the nvme connect error codes + * (positive values) or errno string (negative values) + */ +const char *nvme_strerror(int err); + /** * nvme_init_ctrl_list() - Initialize an nvme_ctrl_list structure from an array. * @cntlist: The controller list structure to initialize diff --git a/meson.build b/meson.build index cc8ee695f0..c0c0ca0703 100644 --- a/meson.build +++ b/meson.build @@ -502,7 +502,6 @@ if want_nvme subdir('util') # declares: util_sources sources = [ - 'nbft.c', 'fabrics.c', 'nvme.c', 'nvme-models.c', diff --git a/nbft.c b/nbft.c deleted file mode 100644 index f683f2dfa4..0000000000 --- a/nbft.c +++ /dev/null @@ -1,487 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include - -#include - -#include "common.h" -#include "nvme.h" -#include "nbft.h" -#include "fabrics.h" -#include "nvme-print.h" - -#include "util/types.h" -#include "logging.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) -{ -#ifdef CONFIG_JSONC - 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); -#endif -} - -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) - return -errno; - - 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); - } -} - -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; -} - -static int do_connect(struct nvme_global_ctx *ctx, - nvme_host_t h, - struct nvmf_disc_log_entry *e, - struct nbft_info_subsystem_ns *ss, - struct tr_config *trcfg, - struct nvme_fabrics_config *cfg, - nvme_print_flags_t flags, - unsigned int verbose) -{ - nvme_ctrl_t c; - int saved_log_level = log_level; - bool saved_log_pid = false; - bool saved_log_tstamp = false; - int ret; - - /* Already connected ? */ - c = lookup_ctrl(h, trcfg); - if (c && nvme_ctrl_get_name(c)) - return 0; - - ret = nvme_create_ctrl(ctx, trcfg->subsysnqn, trcfg->transport, - trcfg->traddr, trcfg->host_traddr, - trcfg->host_iface, trcfg->trsvcid, &c); - if (ret) - return ret; - - /* Pause logging for unavailable SSNSs */ - if (ss && ss->unavailable && verbose < 1) { - saved_log_level = nvme_get_logging_level(ctx, - &saved_log_pid, - &saved_log_tstamp); - nvme_init_logging(ctx, -1, false, false); - } - - if (e) { - if (e->trtype == NVMF_TRTYPE_TCP && - e->tsas.tcp.sectype != NVMF_TCP_SECTYPE_NONE) - cfg->tls = true; - } - - ret = nvmf_add_ctrl(h, c, cfg); - - /* Resume logging */ - if (ss && ss->unavailable && verbose < 1) - nvme_init_logging(ctx, - saved_log_level, - saved_log_pid, - saved_log_tstamp); - - if (ret) { - nvme_free_ctrl(c); - /* - * In case this SSNS was marked as 'unavailable' and - * our connection attempt has failed, ignore it. - */ - if (ss && ss->unavailable) { - if (verbose >= 1) - fprintf(stderr, - "SSNS %d reported as unavailable, skipping\n", - ss->index); - return 0; - } - return ret; - } - - if (flags == NORMAL) - print_connect_msg(c); - else if (flags == JSON) - json_connect_msg(c); - - return 0; -} - -static int do_discover(struct nbft_info_discovery *dd, - struct nvme_global_ctx *ctx, - nvme_host_t h, - nvme_ctrl_t c, - struct nvme_fabrics_config *defcfg, - struct tr_config *deftrcfg, - nvme_print_flags_t flags, - unsigned int verbose) -{ - struct nvmf_discovery_log *log = NULL; - int ret; - int i; - - 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, - }; - - ret = nvmf_get_discovery_wargs(&args, &log); - if (ret) { - fprintf(stderr, - "Discovery Descriptor %d: failed to get discovery log: %s\n", - dd->index, nvme_strerror(ret)); - return ret; - } - - 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; - - ret = nvmf_connect_disc_entry(h, e, defcfg, NULL, &child); - if (ret) - continue; - do_discover(dd, ctx, h, child, defcfg, &trcfg, - flags, verbose); - nvme_disconnect_ctrl(child); - nvme_free_ctrl(child); - } else { - ret = do_connect(ctx, 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(ctx, 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(struct nvme_global_ctx *ctx, char *hostnqn_arg, - char *hostid_arg, char *hostnqn_sys, char *hostid_sys, - const char *desc, bool connect, - struct nvme_fabrics_config *cfg, char *nbft_path, - nvme_print_flags_t flags, unsigned int verbose) -{ - char *hostnqn = NULL, *hostid = NULL, *host_traddr = NULL; - nvme_host_t h; - int ret, rr, i; - struct list_head nbft_list; - struct nbft_file_entry *entry = NULL; - struct nbft_info_subsystem_ns **ss; - struct nbft_info_hfi *hfi; - struct nbft_info_discovery **dd; - - if (!connect) - /* TODO: print discovery-type info from NBFT tables */ - return 0; - - list_head_init(&nbft_list); - ret = read_nbft_files(&nbft_list, nbft_path); - if (ret) { - if (ret != -ENOENT) - nvme_show_perror("Failed to access ACPI tables directory"); - else - ret = 0; /* nothing to connect */ - goto out_free; - } - - list_for_each(&nbft_list, entry, node) { - 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(ctx, hostnqn, hostid); - if (!h) { - ret = -ENOENT; - 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]; - - /* 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)) - 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, - }; - - rr = do_connect(ctx, h, NULL, *ss, &trcfg, - cfg, 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 (rr == -ENVME_CONNECT_ADDRNOTAVAIL && - !strcmp(trcfg.transport, "tcp") && - strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) { - trcfg.host_traddr = NULL; - - rr = do_connect(ctx, h, NULL, *ss, &trcfg, - cfg, flags, verbose); - - if (rr == 0 && verbose >= 1) - fprintf(stderr, - "SSNS %d: connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n", - (*ss)->index, - host_traddr); - } - - if (rr) { - fprintf(stderr, "SSNS %d: no controller found\n", - (*ss)->index); - /* report an error */ - ret = rr; - } - - if (rr == -ENOMEM) - goto out_free; - } - - /* Discovery Descriptor List */ - 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; - - /* 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; - ret = nvme_parse_uri((*dd)->uri, &uri); - if (ret) - continue; - 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) { - ret = -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, - }; - - /* Lookup existing discovery controller */ - c = lookup_ctrl(h, &trcfg); - if (c && nvme_ctrl_get_name(c)) - persistent = true; - - if (!c) { - ret = nvmf_create_discover_ctrl(ctx, h, cfg, &trcfg, &c); - if (ret == -ENVME_CONNECT_ADDRNOTAVAIL && - !strcmp(trcfg.transport, "tcp") && - strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) { - trcfg.host_traddr = NULL; - ret = nvmf_create_discover_ctrl(ctx, h, cfg, &trcfg, &c); - } - } else - ret = 0; - - if (ret) { - fprintf(stderr, - "Discovery Descriptor %d: failed to add discovery controller: %s\n", - (*dd)->index, nvme_strerror(-ret)); - goto out_free; - } - - rr = do_discover(*dd, ctx, h, c, cfg, &trcfg, - flags, verbose); - if (!persistent) - nvme_disconnect_ctrl(c); - nvme_free_ctrl(c); - if (rr == -ENOMEM) { - ret = rr; - goto out_free; - } - } - } -out_free: - free_nbfts(&nbft_list); - return ret; -} diff --git a/nbft.h b/nbft.h deleted file mode 100644 index 412ce67f6e..0000000000 --- a/nbft.h +++ /dev/null @@ -1,19 +0,0 @@ -/* 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(struct nvme_global_ctx *ctx, char *hostnqn_arg, - char *hostid_arg, char *hostnqn_sys, - char *hostid_sys, const char *desc, bool connect, - struct nvme_fabrics_config *cfg, char *nbft_path, - nvme_print_flags_t flags, unsigned int verbose); diff --git a/nvme.c b/nvme.c index b453fdf646..58849c5cb8 100644 --- a/nvme.c +++ b/nvme.c @@ -314,13 +314,6 @@ static OPT_VALS(feature_name) = { VAL_END() }; -const char *nvme_strerror(int errnum) -{ - if (errnum >= ENVME_CONNECT_RESOLVE) - return nvme_errno_to_string(errnum); - return strerror(errnum); -} - static ssize_t getrandom_bytes(void *buf, size_t buflen) { ssize_t result; @@ -10166,28 +10159,28 @@ static int discover_cmd(int argc, char **argv, struct command *acmd, struct plug { const char *desc = "Send Get Log Page request to Discovery Controller."; - return nvmf_discover(desc, argc, argv, false); + return fabrics_discovery(desc, argc, argv, false); } static int connect_all_cmd(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Discover NVMeoF subsystems and connect to them"; - return nvmf_discover(desc, argc, argv, true); + return fabrics_discovery(desc, argc, argv, true); } static int connect_cmd(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Connect to NVMeoF subsystem"; - return nvmf_connect(desc, argc, argv); + return fabrics_connect(desc, argc, argv); } static int disconnect_cmd(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Disconnect from NVMeoF subsystem"; - return nvmf_disconnect(desc, argc, argv); + return fabrics_disconnect(desc, argc, argv); } int disconnect_all_cmd(int argc, char **argv, struct command *acmd, @@ -10195,14 +10188,14 @@ int disconnect_all_cmd(int argc, char **argv, struct command *acmd, { const char *desc = "Disconnect from all connected NVMeoF subsystems"; - return nvmf_disconnect_all(desc, argc, argv); + return fabrics_disconnect_all(desc, argc, argv); } static int config_cmd(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Configuration of NVMeoF subsystems"; - return nvmf_config(desc, argc, argv); + return fabrics_config(desc, argc, argv); } static int dim_cmd(int argc, char **argv, struct command *acmd, struct plugin *plugin) @@ -10210,7 +10203,7 @@ static int dim_cmd(int argc, char **argv, struct command *acmd, struct plugin *p const char *desc = "Send Discovery Information Management command to a Discovery Controller (DC)"; - return nvmf_dim(desc, argc, argv); + return fabrics_dim(desc, argc, argv); } static int nvme_mi(int argc, char **argv, __u8 admin_opcode, const char *desc) diff --git a/plugins/nbft/nbft-plugin.c b/plugins/nbft/nbft-plugin.c index f6952348a5..1770bd7607 100644 --- a/plugins/nbft/nbft-plugin.c +++ b/plugins/nbft/nbft-plugin.c @@ -7,7 +7,6 @@ #include #include "nvme-print.h" #include "nvme.h" -#include "nbft.h" #include "fabrics.h" #include "logging.h" @@ -321,24 +320,25 @@ static struct json_object *nbft_to_json(struct nbft_info *nbft, bool show_subsys return NULL; } -static int json_show_nbfts(struct list_head *nbft_list, bool show_subsys, +static int json_show_nbfts(struct nbft_file_entry *head, bool show_subsys, bool show_hfi, bool show_discovery) { struct json_object *nbft_json_array, *nbft_json; - struct nbft_file_entry *entry = NULL; 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); + while (head) { + nbft_json = nbft_to_json(head->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; } + head = head->next; } json_print_object(nbft_json_array, NULL); @@ -515,23 +515,26 @@ static void normal_show_nbft(struct nbft_info *nbft, bool show_subsys, } } -static void normal_show_nbfts(struct list_head *nbft_list, bool show_subsys, +static void normal_show_nbfts(struct nbft_file_entry *head, bool show_subsys, bool show_hfi, bool show_discovery) { bool not_first = false; - struct nbft_file_entry *entry = NULL; - list_for_each(nbft_list, entry, node) { + while (head) { if (not_first) printf("\n"); - normal_show_nbft(entry->nbft, show_subsys, show_hfi, show_discovery); + normal_show_nbft(head->nbft, show_subsys, show_hfi, show_discovery); + head = head->next; not_first = true; } } +#define NBFT_SYSFS_PATH "/sys/firmware/acpi/tables" + int show_nbft(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Display contents of the ACPI NBFT files."; + struct nbft_file_entry *head = NULL; struct list_head nbft_list; char *format = "normal"; char *nbft_path = NBFT_SYSFS_PATH; @@ -565,13 +568,15 @@ int show_nbft(int argc, char **argv, struct command *acmd, struct plugin *plugin show_subsys = show_hfi = show_discovery = true; list_head_init(&nbft_list); - ret = read_nbft_files(&nbft_list, nbft_path); - if (!ret) { + ret = nvmf_nbft_read_files(nbft_path, &head); + if (!ret && head) { if (flags == NORMAL) - normal_show_nbfts(&nbft_list, show_subsys, show_hfi, show_discovery); + normal_show_nbfts(head, 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); + ret = json_show_nbfts(head, show_subsys, + show_hfi, show_discovery); + nvmf_nbft_free(head); } return ret; }