summaryrefslogtreecommitdiffstats
path: root/nbft.c
diff options
context:
space:
mode:
Diffstat (limited to 'nbft.c')
-rw-r--r--nbft.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/nbft.c b/nbft.c
new file mode 100644
index 0000000..ff36119
--- /dev/null
+++ b/nbft.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <errno.h>
+#include <stdio.h>
+#include <fnmatch.h>
+#include <stdlib.h>
+
+#include <libnvme.h>
+
+#include "nvme.h"
+#include "nbft.h"
+#include "fabrics.h"
+#include "nvme-print.h"
+
+#include "util/types.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);
+ }
+}
+
+int discover_from_nbft(nvme_root_t r, char *hostnqn_arg, char *hostid_arg,
+ char *hostnqn_sys, char *hostid_sys,
+ const char *desc, bool connect,
+ const struct nvme_fabrics_config *cfg, char *nbft_path,
+ enum nvme_print_flags flags, bool verbose)
+{
+ char *hostnqn = NULL, *hostid = NULL, *host_traddr = NULL;
+ nvme_host_t h;
+ nvme_ctrl_t c;
+ int ret, i;
+ struct list_head nbft_list;
+ struct nbft_file_entry *entry;
+ struct nbft_info_subsystem_ns **ss;
+ struct nbft_info_hfi *hfi;
+
+ if (!connect)
+ /* to do: print discovery-type info from NBFT tables */
+ return 0;
+
+ list_head_init(&nbft_list);
+ ret = read_nbft_files(&nbft_list, nbft_path);
+ if (ret) {
+ if (ret != ENOENT)
+ nvme_show_perror("Failed to access ACPI tables directory");
+ goto out_free_2;
+ }
+
+ list_for_each(&nbft_list, entry, node)
+ for (ss = entry->nbft->subsystem_ns_list; ss && *ss; ss++)
+ for (i = 0; i < (*ss)->num_hfis; i++) {
+ nvme_ctrl_t cl;
+
+ hfi = (*ss)->hfis[i];
+ if (hostnqn_arg)
+ hostnqn = hostnqn_arg;
+ else {
+ hostnqn = entry->nbft->host.nqn;
+ if (!hostnqn)
+ hostnqn = hostnqn_sys;
+ }
+
+ if (hostid_arg)
+ hostid = hostid_arg;
+ else if (*entry->nbft->host.id) {
+ hostid = (char *)util_uuid_to_string(entry->nbft->host.id);
+ if (!hostid)
+ hostid = hostid_sys;
+ }
+
+ h = nvme_lookup_host(r, hostnqn, hostid);
+ if (!h) {
+ errno = ENOMEM;
+ goto out_free;
+ }
+
+ if (!cfg->host_traddr) {
+ host_traddr = NULL;
+ if (!strncmp((*ss)->transport, "tcp", 3))
+ host_traddr = hfi->tcp_info.ipaddr;
+ }
+
+ struct tr_config trcfg = {
+ .subsysnqn = (*ss)->subsys_nqn,
+ .transport = (*ss)->transport,
+ .traddr = (*ss)->traddr,
+ .host_traddr = host_traddr,
+ .host_iface = NULL,
+ .trsvcid = (*ss)->trsvcid,
+ };
+
+ /* Already connected ? */
+ cl = lookup_ctrl(h, &trcfg);
+ if (cl && nvme_ctrl_get_name(cl))
+ continue;
+
+ c = nvme_create_ctrl(r, (*ss)->subsys_nqn, (*ss)->transport,
+ (*ss)->traddr, host_traddr, NULL,
+ (*ss)->trsvcid);
+ if (!c) {
+ errno = ENOMEM;
+ goto out_free;
+ }
+
+ errno = 0;
+ ret = nvmf_add_ctrl(h, c, cfg);
+
+ /*
+ * With TCP/DHCP, it can happen that the OS
+ * obtains a different local IP address than the
+ * firmware had. Retry without host_traddr.
+ */
+ if (ret == -1 && errno == ENVME_CONNECT_ADDRNOTAVAIL &&
+ !strcmp((*ss)->transport, "tcp") &&
+ strlen(hfi->tcp_info.dhcp_server_ipaddr) > 0) {
+ nvme_free_ctrl(c);
+
+ trcfg.host_traddr = NULL;
+ cl = lookup_ctrl(h, &trcfg);
+ if (cl && nvme_ctrl_get_name(cl))
+ continue;
+
+ c = nvme_create_ctrl(r, (*ss)->subsys_nqn, (*ss)->transport,
+ (*ss)->traddr,
+ NULL, NULL, (*ss)->trsvcid);
+ if (!c) {
+ errno = ENOMEM;
+ goto out_free;
+ }
+ errno = 0;
+ ret = nvmf_add_ctrl(h, c, cfg);
+ if (ret == 0 && verbose >= 1)
+ fprintf(stderr,
+ "connect with host_traddr=\"%s\" failed, success after omitting host_traddr\n",
+ host_traddr);
+ }
+
+ if (ret)
+ fprintf(stderr, "no controller found\n");
+ else {
+ if (flags == NORMAL)
+ print_connect_msg(c);
+ else if (flags == JSON)
+ json_connect_msg(c);
+ }
+out_free:
+ if (errno == ENOMEM)
+ goto out_free_2;
+ }
+out_free_2:
+ free_nbfts(&nbft_list);
+ return errno;
+}