// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include #include #include #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; }