2025-02-16 12:16:06 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
2025-02-16 11:09:01 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2016 Intel Corporation. All rights reserved.
|
|
|
|
* Copyright (c) 2016 HGST, a Western Digital Company.
|
|
|
|
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License version
|
|
|
|
* 2 as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
*
|
|
|
|
* This file implements the discovery controller feature of NVMe over
|
|
|
|
* Fabrics specification standard.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <stddef.h>
|
2025-02-16 11:30:13 +01:00
|
|
|
#include <syslog.h>
|
|
|
|
#include <time.h>
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
#include <sys/types.h>
|
2025-02-16 12:15:30 +01:00
|
|
|
#include <linux/types.h>
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
#include "common.h"
|
2025-02-16 11:09:01 +01:00
|
|
|
#include "nvme.h"
|
2025-02-16 12:15:30 +01:00
|
|
|
#include "libnvme.h"
|
|
|
|
#include "nvme-print.h"
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
#define PATH_NVMF_DISC SYSCONFDIR "/nvme/discovery.conf"
|
|
|
|
#define PATH_NVMF_CONFIG SYSCONFDIR "/nvme/config.json"
|
|
|
|
#define MAX_DISC_ARGS 32
|
|
|
|
#define MAX_DISC_RETRIES 10
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
#define NVMF_DEF_DISC_TMO 30
|
|
|
|
|
|
|
|
/* Name of file to output log pages in their raw format */
|
|
|
|
static char *raw;
|
|
|
|
static bool persistent;
|
|
|
|
static bool quiet;
|
|
|
|
static bool dump_config;
|
|
|
|
|
|
|
|
static const char *nvmf_tport = "transport type";
|
|
|
|
static const char *nvmf_traddr = "transport address";
|
|
|
|
static const char *nvmf_nqn = "subsystem nqn";
|
|
|
|
static const char *nvmf_trsvcid = "transport service id (e.g. IP port)";
|
|
|
|
static const char *nvmf_htraddr = "host traddr (e.g. FC WWN's)";
|
|
|
|
static const char *nvmf_hiface = "host interface (for tcp transport)";
|
|
|
|
static const char *nvmf_hostnqn = "user-defined hostnqn";
|
|
|
|
static const char *nvmf_hostid = "user-defined hostid (if default not used)";
|
|
|
|
static const char *nvmf_hostkey = "user-defined dhchap key (if default not used)";
|
|
|
|
static const char *nvmf_ctrlkey = "user-defined dhchap controller key (for bi-directional authentication)";
|
|
|
|
static const char *nvmf_nr_io_queues = "number of io queues to use (default is core count)";
|
|
|
|
static const char *nvmf_nr_write_queues = "number of write queues to use (default 0)";
|
|
|
|
static const char *nvmf_nr_poll_queues = "number of poll queues to use (default 0)";
|
|
|
|
static const char *nvmf_queue_size = "number of io queue elements to use (default 128)";
|
|
|
|
static const char *nvmf_keep_alive_tmo = "keep alive timeout period in seconds";
|
|
|
|
static const char *nvmf_reconnect_delay = "reconnect timeout period in seconds";
|
|
|
|
static const char *nvmf_ctrl_loss_tmo = "controller loss timeout period in seconds";
|
|
|
|
static const char *nvmf_tos = "type of service";
|
|
|
|
static const char *nvmf_dup_connect = "allow duplicate connections between same transport host and subsystem port";
|
|
|
|
static const char *nvmf_disable_sqflow = "disable controller sq flow control (default false)";
|
|
|
|
static const char *nvmf_hdr_digest = "enable transport protocol header digest (TCP transport)";
|
|
|
|
static const char *nvmf_data_digest = "enable transport protocol data digest (TCP transport)";
|
|
|
|
static const char *nvmf_config_file = "Use specified JSON configuration file or 'none' to disable";
|
|
|
|
|
|
|
|
#define NVMF_OPTS(c) \
|
|
|
|
OPT_STRING("transport", 't', "STR", &transport, nvmf_tport), \
|
|
|
|
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("nqn", 'n', "STR", &subsysnqn, nvmf_nqn), \
|
|
|
|
OPT_STRING("dhchap-secret", 'S', "STR", &hostkey, nvmf_hostkey), \
|
|
|
|
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), \
|
|
|
|
OPT_INT("queue-size", 'Q', &c.queue_size, nvmf_queue_size), \
|
|
|
|
OPT_INT("keep-alive-tmo", 'k', &c.keep_alive_tmo, nvmf_keep_alive_tmo), \
|
|
|
|
OPT_INT("reconnect-delay", 'c', &c.reconnect_delay, nvmf_reconnect_delay),\
|
|
|
|
OPT_INT("ctrl-loss-tmo", 'l', &c.ctrl_loss_tmo, nvmf_ctrl_loss_tmo), \
|
|
|
|
OPT_INT("tos", 'T', &c.tos, nvmf_tos), \
|
|
|
|
OPT_FLAG("duplicate-connect", 'D', &c.duplicate_connect, nvmf_dup_connect), \
|
|
|
|
OPT_FLAG("disable-sqflow", 'd', &c.disable_sqflow, nvmf_disable_sqflow), \
|
|
|
|
OPT_FLAG("hdr-digest", 'g', &c.hdr_digest, nvmf_hdr_digest), \
|
|
|
|
OPT_FLAG("data-digest", 'G', &c.data_digest, nvmf_data_digest) \
|
|
|
|
|
|
|
|
struct tr_config {
|
2025-02-16 12:17:56 +01:00
|
|
|
const char *subsysnqn;
|
|
|
|
const char *transport;
|
|
|
|
const char *traddr;
|
|
|
|
const char *host_traddr;
|
|
|
|
const char *host_iface;
|
|
|
|
const char *trsvcid;
|
2025-02-16 11:09:01 +01:00
|
|
|
};
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static void space_strip_len(int max, char *str)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
int i;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
for (i = max - 1; i >= 0; i--) {
|
|
|
|
if (str[i] != '\0' && str[i] != ' ')
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
str[i] = '\0';
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static int set_discovery_kato(struct nvme_fabrics_config *cfg)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
int tmo = cfg->keep_alive_tmo;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
/* 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;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
return tmo;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static nvme_ctrl_t __create_discover_ctrl(nvme_root_t r, nvme_host_t h,
|
|
|
|
struct nvme_fabrics_config *cfg,
|
|
|
|
struct tr_config *trcfg)
|
2025-02-16 11:30:13 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_ctrl_t c;
|
|
|
|
int tmo, ret;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
c = nvme_create_ctrl(r, trcfg->subsysnqn, trcfg->transport,
|
|
|
|
trcfg->traddr, trcfg->host_traddr,
|
|
|
|
trcfg->host_iface, trcfg->trsvcid);
|
|
|
|
if (!c)
|
2025-02-16 11:30:13 +01:00
|
|
|
return NULL;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_ctrl_set_discovery_ctrl(c, true);
|
|
|
|
tmo = set_discovery_kato(cfg);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
errno = 0;
|
|
|
|
ret = nvmf_add_ctrl(h, c, cfg);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
cfg->keep_alive_tmo = tmo;
|
|
|
|
if (ret) {
|
|
|
|
errno = ret;
|
|
|
|
nvme_free_ctrl(c);
|
|
|
|
return NULL;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
return c;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static nvme_ctrl_t create_discover_ctrl(nvme_root_t r, nvme_host_t h,
|
|
|
|
struct nvme_fabrics_config *cfg,
|
|
|
|
struct tr_config *trcfg)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_ctrl_t c;
|
|
|
|
|
|
|
|
c = __create_discover_ctrl(r, h, cfg, trcfg);
|
2025-02-16 12:16:06 +01:00
|
|
|
if (!c)
|
|
|
|
return NULL;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!persistent)
|
|
|
|
return c;
|
|
|
|
|
|
|
|
/* Find out the name of discovery controller */
|
|
|
|
struct nvme_id_ctrl id = { 0 };
|
|
|
|
if (nvme_ctrl_identify(c, &id)) {
|
|
|
|
fprintf(stderr, "failed to identify controller, error %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
nvme_disconnect_ctrl(c);
|
|
|
|
nvme_free_ctrl(c);
|
|
|
|
return NULL;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!strcmp(id.subnqn, NVME_DISC_SUBSYS_NAME))
|
|
|
|
return c;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
/*
|
2025-02-16 12:15:30 +01:00
|
|
|
* The subsysnqn is not the well-known name. Prefer the unique
|
|
|
|
* subsysnqn over the well-known one.
|
2025-02-16 11:09:01 +01:00
|
|
|
*/
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_disconnect_ctrl(c);
|
|
|
|
nvme_free_ctrl(c);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
trcfg->subsysnqn = id.subnqn;
|
|
|
|
return __create_discover_ctrl(r, h, cfg, trcfg);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static void print_discovery_log(struct nvmf_discovery_log *log, int numrec)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
printf("\nDiscovery Log Number of Records %d, "
|
2025-02-16 11:09:01 +01:00
|
|
|
"Generation counter %"PRIu64"\n",
|
|
|
|
numrec, le64_to_cpu(log->genctr));
|
|
|
|
|
|
|
|
for (i = 0; i < numrec; i++) {
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvmf_disc_log_entry *e = &log->entries[i];
|
|
|
|
|
|
|
|
space_strip_len(NVMF_TRSVCID_SIZE, e->trsvcid);
|
|
|
|
space_strip_len(NVMF_TRADDR_SIZE, e->traddr);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
printf("=====Discovery Log Entry %d======\n", i);
|
2025-02-16 12:15:30 +01:00
|
|
|
printf("trtype: %s\n", nvmf_trtype_str(e->trtype));
|
|
|
|
printf("adrfam: %s\n",
|
2025-02-16 12:16:06 +01:00
|
|
|
strlen(e->traddr) ?
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_adrfam_str(e->adrfam): "");
|
|
|
|
printf("subtype: %s\n", nvmf_subtype_str(e->subtype));
|
|
|
|
printf("treq: %s\n", nvmf_treq_str(e->treq));
|
2025-02-16 11:09:01 +01:00
|
|
|
printf("portid: %d\n", e->portid);
|
2025-02-16 12:15:30 +01:00
|
|
|
printf("trsvcid: %s\n", e->trsvcid);
|
2025-02-16 11:09:01 +01:00
|
|
|
printf("subnqn: %s\n", e->subnqn);
|
2025-02-16 12:15:30 +01:00
|
|
|
printf("traddr: %s\n", e->traddr);
|
|
|
|
printf("eflags: %s\n", nvmf_eflags_str(e->eflags));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
switch (e->trtype) {
|
|
|
|
case NVMF_TRTYPE_RDMA:
|
|
|
|
printf("rdma_prtype: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_prtype_str(e->tsas.rdma.prtype));
|
2025-02-16 11:09:01 +01:00
|
|
|
printf("rdma_qptype: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_qptype_str(e->tsas.rdma.qptype));
|
2025-02-16 11:09:01 +01:00
|
|
|
printf("rdma_cms: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_cms_str(e->tsas.rdma.cms));
|
2025-02-16 11:09:01 +01:00
|
|
|
printf("rdma_pkey: 0x%04x\n",
|
2025-02-16 12:10:50 +01:00
|
|
|
le16_to_cpu(e->tsas.rdma.pkey));
|
2025-02-16 11:09:01 +01:00
|
|
|
break;
|
|
|
|
case NVMF_TRTYPE_TCP:
|
|
|
|
printf("sectype: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_sectype_str(e->tsas.tcp.sectype));
|
2025-02-16 11:09:01 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static void json_discovery_log(struct nvmf_discovery_log *log, int numrec)
|
2025-02-16 11:30:13 +01:00
|
|
|
{
|
|
|
|
struct json_object *root;
|
|
|
|
struct json_object *entries;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
root = json_create_object();
|
|
|
|
entries = json_create_array();
|
2025-02-16 12:15:30 +01:00
|
|
|
json_object_add_value_uint64(root, "genctr", le64_to_cpu(log->genctr));
|
2025-02-16 11:30:13 +01:00
|
|
|
json_object_add_value_array(root, "records", entries);
|
|
|
|
|
|
|
|
for (i = 0; i < numrec; i++) {
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvmf_disc_log_entry *e = &log->entries[i];
|
2025-02-16 11:30:13 +01:00
|
|
|
struct json_object *entry = json_create_object();
|
|
|
|
|
2025-02-16 12:10:50 +01:00
|
|
|
nvme_strip_spaces(e->trsvcid, NVMF_TRSVCID_SIZE);
|
|
|
|
nvme_strip_spaces(e->subnqn, NVMF_NQN_SIZE);
|
|
|
|
nvme_strip_spaces(e->traddr, NVMF_TRADDR_SIZE);
|
|
|
|
|
2025-02-16 11:30:13 +01:00
|
|
|
json_object_add_value_string(entry, "trtype",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_trtype_str(e->trtype));
|
2025-02-16 11:30:13 +01:00
|
|
|
json_object_add_value_string(entry, "adrfam",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_adrfam_str(e->adrfam));
|
2025-02-16 11:30:13 +01:00
|
|
|
json_object_add_value_string(entry, "subtype",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_subtype_str(e->subtype));
|
2025-02-16 11:30:13 +01:00
|
|
|
json_object_add_value_string(entry,"treq",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_treq_str(e->treq));
|
|
|
|
json_object_add_value_uint(entry, "portid",
|
|
|
|
le16_to_cpu(e->portid));
|
2025-02-16 12:10:50 +01:00
|
|
|
json_object_add_value_string(entry, "trsvcid", e->trsvcid);
|
2025-02-16 12:15:30 +01:00
|
|
|
json_object_add_value_string(entry, "subnqn", e->subnqn);
|
|
|
|
json_object_add_value_string(entry, "traddr", e->traddr);
|
|
|
|
json_object_add_value_uint(entry, "eflags", e->eflags);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
switch (e->trtype) {
|
|
|
|
case NVMF_TRTYPE_RDMA:
|
|
|
|
json_object_add_value_string(entry, "rdma_prtype",
|
|
|
|
nvmf_prtype_str(e->tsas.rdma.prtype));
|
|
|
|
json_object_add_value_string(entry, "rdma_qptype",
|
|
|
|
nvmf_qptype_str(e->tsas.rdma.qptype));
|
|
|
|
json_object_add_value_string(entry, "rdma_cms",
|
|
|
|
nvmf_cms_str(e->tsas.rdma.cms));
|
|
|
|
json_object_add_value_uint(entry, "rdma_pkey",
|
|
|
|
le16_to_cpu(e->tsas.rdma.pkey));
|
2025-02-16 11:09:01 +01:00
|
|
|
break;
|
2025-02-16 12:15:30 +01:00
|
|
|
case NVMF_TRTYPE_TCP:
|
|
|
|
json_object_add_value_string(entry, "sectype",
|
|
|
|
nvmf_sectype_str(e->tsas.tcp.sectype));
|
2025-02-16 11:09:01 +01:00
|
|
|
break;
|
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
json_array_add_value_object(entries, entry);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
json_print_object(root, NULL);
|
|
|
|
printf("\n");
|
|
|
|
json_free_object(root);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static void save_discovery_log(char *raw, struct nvmf_discovery_log *log)
|
2025-02-16 11:30:13 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
uint64_t numrec = le64_to_cpu(log->numrec);
|
|
|
|
int fd, len, ret;
|
|
|
|
|
|
|
|
fd = open(raw, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "failed to open %s: %s\n",
|
|
|
|
raw, strerror(errno));
|
|
|
|
return;
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
len = sizeof(struct nvmf_discovery_log) +
|
|
|
|
numrec * sizeof(struct nvmf_disc_log_entry);
|
|
|
|
ret = write(fd, log, len);
|
|
|
|
if (ret < 0)
|
|
|
|
fprintf(stderr, "failed to write to %s: %s\n",
|
|
|
|
raw, strerror(errno));
|
|
|
|
else
|
|
|
|
printf("Discovery log is saved to %s\n", raw);
|
|
|
|
|
|
|
|
close(fd);
|
2025-02-16 11:30:13 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static void print_connect_msg(nvme_ctrl_t c)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
printf("device: %s\n", nvme_ctrl_get_name(c));
|
|
|
|
}
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static void json_connect_msg(nvme_ctrl_t c)
|
|
|
|
{
|
|
|
|
struct json_object *root;
|
2025-02-16 12:10:50 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
root = json_create_object();
|
|
|
|
json_object_add_value_string(root, "device", nvme_ctrl_get_name(c));
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
json_print_object(root, NULL);
|
|
|
|
printf("\n");
|
|
|
|
json_free_object(root);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static int __discover(nvme_ctrl_t c, struct nvme_fabrics_config *defcfg,
|
|
|
|
char *raw, bool connect, bool persistent,
|
|
|
|
enum nvme_print_flags flags)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
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;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
err = nvmf_get_discovery_log(c, &log, MAX_DISC_RETRIES);
|
|
|
|
if (err) {
|
|
|
|
if (err > 0)
|
|
|
|
nvme_show_status(err);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "failed to get discovery log: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
return err;
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
numrec = le64_to_cpu(log->numrec);
|
|
|
|
if (raw)
|
|
|
|
save_discovery_log(raw, log);
|
|
|
|
else if (!connect) {
|
|
|
|
if (flags == JSON)
|
|
|
|
json_discovery_log(log, numrec);
|
|
|
|
else
|
|
|
|
print_discovery_log(log, numrec);
|
|
|
|
} else if (connect) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < numrec; i++) {
|
|
|
|
struct nvmf_disc_log_entry *e = &log->entries[i];
|
|
|
|
bool discover = false;
|
|
|
|
nvme_ctrl_t child;
|
|
|
|
int tmo = defcfg->keep_alive_tmo;
|
|
|
|
|
2025-02-16 12:16:06 +01:00
|
|
|
/* Skip connect if the transport types don't match */
|
|
|
|
if (strcmp(nvme_ctrl_get_transport(c), nvmf_trtype_str(e->trtype)))
|
|
|
|
continue;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (e->subtype == NVME_NQN_DISC)
|
|
|
|
set_discovery_kato(defcfg);
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
child = nvmf_connect_disc_entry(h, e, defcfg,
|
|
|
|
&discover);
|
|
|
|
|
|
|
|
defcfg->keep_alive_tmo = tmo;
|
|
|
|
|
|
|
|
if (child) {
|
|
|
|
if (discover)
|
|
|
|
__discover(child, defcfg, raw,
|
|
|
|
true, persistent, flags);
|
|
|
|
if (e->subtype != NVME_NQN_NVME &&
|
|
|
|
!persistent) {
|
|
|
|
nvme_disconnect_ctrl(child);
|
|
|
|
nvme_free_ctrl(child);
|
|
|
|
}
|
2025-02-16 12:16:06 +01:00
|
|
|
} else if (errno == ENVME_CONNECT_ALREADY && !quiet) {
|
2025-02-16 12:15:30 +01:00
|
|
|
char *traddr = log->entries[i].traddr;
|
|
|
|
|
|
|
|
space_strip_len(NVMF_TRADDR_SIZE, traddr);
|
|
|
|
fprintf(stderr,
|
|
|
|
"traddr=%s is already connected\n",
|
2025-02-16 11:09:01 +01:00
|
|
|
traddr);
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
free(log);
|
|
|
|
return 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
/*
|
|
|
|
* Compare two C strings and handle NULL pointers gracefully.
|
|
|
|
* If either of the two strings is NULL, return 0
|
|
|
|
* to let caller ignore the compare.
|
|
|
|
*/
|
|
|
|
static inline int strcmp0(const char *s1, const char *s2)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!s1 || !s2)
|
|
|
|
return 0;
|
|
|
|
return strcmp(s1, s2);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
/*
|
|
|
|
* Compare two C strings and handle NULL pointers gracefully.
|
|
|
|
* If either of the two strings is NULL, return 0
|
|
|
|
* to let caller ignore the compare.
|
|
|
|
*/
|
|
|
|
static inline int strcasecmp0(const char *s1, const char *s2)
|
|
|
|
{
|
|
|
|
if (!s1 || !s2)
|
|
|
|
return 0;
|
|
|
|
return strcasecmp(s1, s2);
|
|
|
|
}
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static bool ctrl_config_match(nvme_ctrl_t c, struct tr_config *trcfg)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!strcmp0(nvme_ctrl_get_transport(c), trcfg->transport) &&
|
|
|
|
!strcasecmp0(nvme_ctrl_get_traddr(c), trcfg->traddr) &&
|
|
|
|
!strcmp0(nvme_ctrl_get_trsvcid(c), trcfg->trsvcid) &&
|
|
|
|
!strcmp0(nvme_ctrl_get_host_traddr(c), trcfg->host_traddr) &&
|
|
|
|
!strcmp0(nvme_ctrl_get_host_iface(c), trcfg->host_iface))
|
|
|
|
return true;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static nvme_ctrl_t lookup_discover_ctrl(nvme_root_t r, struct tr_config *trcfg)
|
|
|
|
{
|
|
|
|
nvme_host_t h;
|
|
|
|
nvme_subsystem_t s;
|
|
|
|
nvme_ctrl_t c;
|
|
|
|
|
|
|
|
nvme_for_each_host(r, h) {
|
|
|
|
nvme_for_each_subsystem(h, s) {
|
|
|
|
nvme_subsystem_for_each_ctrl(s, c) {
|
|
|
|
if (!nvme_ctrl_is_discovery_ctrl(c))
|
|
|
|
continue;
|
|
|
|
if (ctrl_config_match(c, trcfg))
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *get_default_trsvcid(const char *transport,
|
|
|
|
bool discovery_ctrl)
|
|
|
|
{
|
2025-02-16 12:16:06 +01:00
|
|
|
if (!transport)
|
|
|
|
return NULL;
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!strcmp(transport, "tcp")) {
|
|
|
|
if (discovery_ctrl) {
|
|
|
|
/* Default port for NVMe/TCP discovery controllers */
|
|
|
|
return stringify(NVME_DISC_IP_PORT);
|
|
|
|
} else {
|
|
|
|
/* 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);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
return NULL;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
static int discover_from_conf_file(nvme_root_t r, nvme_host_t h,
|
|
|
|
const char *desc, bool connect,
|
|
|
|
const struct nvme_fabrics_config *defcfg)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
char *transport = NULL, *traddr = NULL, *trsvcid = NULL;
|
2025-02-16 12:16:44 +01:00
|
|
|
char *hostnqn = NULL, *hostid = NULL, *hostkey = NULL;
|
2025-02-16 12:15:30 +01:00
|
|
|
char *subsysnqn = NULL;
|
|
|
|
char *ptr, **argv, *p, line[4096];
|
|
|
|
int argc, ret = 0;
|
|
|
|
unsigned int verbose = 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
FILE *f;
|
2025-02-16 12:15:30 +01:00
|
|
|
enum nvme_print_flags flags;
|
|
|
|
char *format = "normal";
|
|
|
|
struct nvme_fabrics_config cfg;
|
|
|
|
bool force = false;
|
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
NVMF_OPTS(cfg),
|
|
|
|
OPT_FMT("output-format", 'o', &format, output_format),
|
|
|
|
OPT_FILE("raw", 'r', &raw, "save raw output to file"),
|
|
|
|
OPT_FLAG("persistent", 'p', &persistent, "persistent discovery connection"),
|
|
|
|
OPT_FLAG("quiet", 'S', &quiet, "suppress already connected errors"),
|
|
|
|
OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"),
|
|
|
|
OPT_FLAG("force", 0, &force, "Force persistent discovery controller creation"),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
nvmf_default_config(&cfg);
|
|
|
|
|
|
|
|
ret = flags = validate_output_format(format);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
f = fopen(PATH_NVMF_DISC, "r");
|
|
|
|
if (f == NULL) {
|
2025-02-16 12:16:06 +01:00
|
|
|
fprintf(stderr, "No params given and no %s\n", PATH_NVMF_DISC);
|
2025-02-16 12:15:30 +01:00
|
|
|
errno = ENOENT;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
argv = calloc(MAX_DISC_ARGS, sizeof(char *));
|
|
|
|
if (!argv) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
argv[0] = "discover";
|
|
|
|
memset(line, 0, sizeof(line));
|
2025-02-16 11:09:01 +01:00
|
|
|
while (fgets(line, sizeof(line), f) != NULL) {
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_ctrl_t c;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
if (line[0] == '#' || line[0] == '\n')
|
|
|
|
continue;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
argc = 1;
|
|
|
|
p = line;
|
|
|
|
while ((ptr = strsep(&p, " =\n")) != NULL)
|
2025-02-16 11:09:01 +01:00
|
|
|
argv[argc++] = ptr;
|
2025-02-16 12:15:30 +01:00
|
|
|
argv[argc] = NULL;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
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 = 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_discover_ctrl(r, &trcfg);
|
|
|
|
if (c) {
|
|
|
|
__discover(c, &cfg, raw, connect,
|
|
|
|
true, flags);
|
|
|
|
goto next;
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
c = create_discover_ctrl(r, h, &cfg, &trcfg);
|
|
|
|
if (!c)
|
|
|
|
goto next;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
__discover(c, &cfg, raw, connect, persistent, flags);
|
|
|
|
if (!persistent)
|
|
|
|
ret = nvme_disconnect_ctrl(c);
|
|
|
|
nvme_free_ctrl(c);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
next:
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
free(argv);
|
2025-02-16 11:09:01 +01:00
|
|
|
out:
|
|
|
|
fclose(f);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
static int discover_from_json_config_file(nvme_root_t r, nvme_host_t h,
|
|
|
|
const char *desc, bool connect,
|
|
|
|
const struct nvme_fabrics_config *defcfg,
|
|
|
|
enum nvme_print_flags flags,
|
|
|
|
bool force)
|
|
|
|
{
|
|
|
|
const char *transport, *traddr, *trsvcid, *subsysnqn;
|
|
|
|
nvme_subsystem_t s;
|
|
|
|
nvme_ctrl_t c, cn;
|
|
|
|
struct nvme_fabrics_config cfg;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
nvme_for_each_subsystem(h, s) {
|
|
|
|
nvme_subsystem_for_each_ctrl(s, c) {
|
|
|
|
transport = nvme_ctrl_get_transport(c);
|
|
|
|
traddr = nvme_ctrl_get_traddr(c);
|
|
|
|
|
|
|
|
if (!transport && !traddr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* ignore none fabric transports */
|
|
|
|
if (strcmp(transport, "tcp") &&
|
|
|
|
strcmp(transport, "rdma") &&
|
|
|
|
strcmp(transport, "fc"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
trsvcid = nvme_ctrl_get_trsvcid(c);
|
|
|
|
if (!trsvcid || !strcmp(trsvcid, ""))
|
|
|
|
trsvcid = 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))
|
|
|
|
persistent = true;
|
|
|
|
|
|
|
|
memcpy(&cfg, defcfg, sizeof(cfg));
|
|
|
|
|
|
|
|
struct tr_config trcfg = {
|
|
|
|
.subsysnqn = subsysnqn,
|
|
|
|
.transport = transport,
|
|
|
|
.traddr = traddr,
|
|
|
|
.host_traddr = cfg.host_traddr,
|
|
|
|
.host_iface = cfg.host_iface,
|
|
|
|
.trsvcid = trsvcid,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!force) {
|
|
|
|
cn = lookup_discover_ctrl(r, &trcfg);
|
|
|
|
if (cn) {
|
|
|
|
__discover(c, &cfg, raw, connect,
|
|
|
|
true, flags);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cn = create_discover_ctrl(r, h, &cfg, &trcfg);
|
|
|
|
if (!cn)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
__discover(cn, &cfg, raw, connect, persistent, flags);
|
|
|
|
if (!persistent)
|
|
|
|
ret = nvme_disconnect_ctrl(cn);
|
|
|
|
nvme_free_ctrl(cn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
int nvmf_discover(const char *desc, int argc, char **argv, bool connect)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
char *subsysnqn = NVME_DISC_SUBSYS_NAME;
|
2025-02-16 12:16:44 +01:00
|
|
|
char *hostnqn = NULL, *hostid = NULL, *hostkey = NULL;
|
2025-02-16 12:15:30 +01:00
|
|
|
char *transport = NULL, *traddr = NULL, *trsvcid = NULL;
|
|
|
|
char *config_file = PATH_NVMF_CONFIG;
|
|
|
|
char *hnqn = NULL, *hid = NULL;
|
2025-02-16 11:30:13 +01:00
|
|
|
enum nvme_print_flags flags;
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_root_t r;
|
|
|
|
nvme_host_t h;
|
|
|
|
nvme_ctrl_t c = NULL;
|
|
|
|
unsigned int verbose = 0;
|
|
|
|
int ret;
|
|
|
|
char *format = "normal";
|
|
|
|
struct nvme_fabrics_config cfg;
|
|
|
|
char *device = NULL;
|
|
|
|
bool force = false;
|
2025-02-16 12:17:56 +01:00
|
|
|
bool json_config = false;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_STRING("device", 'd', "DEV", &device, "use existing discovery controller device"),
|
|
|
|
NVMF_OPTS(cfg),
|
|
|
|
OPT_FMT("output-format", 'o', &format, output_format),
|
|
|
|
OPT_FILE("raw", 'r', &raw, "save raw output to file"),
|
|
|
|
OPT_FLAG("persistent", 'p', &persistent, "persistent discovery connection"),
|
|
|
|
OPT_FLAG("quiet", 'S', &quiet, "suppress already connected errors"),
|
|
|
|
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 configuration file to stdout"),
|
|
|
|
OPT_FLAG("force", 0, &force, "Force persistent discovery controller creation"),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_default_config(&cfg);
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
ret = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (ret)
|
2025-02-16 12:15:30 +01:00
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
ret = flags = validate_output_format(format);
|
2025-02-16 11:30:13 +01:00
|
|
|
if (ret < 0)
|
2025-02-16 12:15:30 +01:00
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!strcmp(config_file, "none"))
|
|
|
|
config_file = NULL;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
r = nvme_create_root(stderr, map_log_level(verbose, quiet));
|
|
|
|
if (!r) {
|
|
|
|
fprintf(stderr, "Failed to create topology root: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
ret = nvme_scan_topology(r, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
2025-02-16 12:16:06 +01:00
|
|
|
fprintf(stderr, "Failed to scan topology: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_strerror(errno));
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return ret;
|
|
|
|
}
|
2025-02-16 12:17:56 +01:00
|
|
|
if (!nvme_read_config(r, config_file))
|
|
|
|
json_config = true;
|
2025-02-16 11:30:13 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!hostnqn)
|
|
|
|
hostnqn = hnqn = nvmf_hostnqn_from_file();
|
|
|
|
if (!hostnqn)
|
|
|
|
hostnqn = hnqn = nvmf_hostnqn_generate();
|
|
|
|
if (!hostid)
|
|
|
|
hostid = hid = nvmf_hostid_from_file();
|
|
|
|
h = nvme_lookup_host(r, hostnqn, hostid);
|
|
|
|
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) {
|
2025-02-16 12:17:56 +01:00
|
|
|
if (json_config)
|
|
|
|
ret = discover_from_json_config_file(r, h, desc,
|
|
|
|
connect, &cfg,
|
|
|
|
flags, force);
|
|
|
|
if (ret || access(PATH_NVMF_DISC, F_OK))
|
|
|
|
goto out_free;
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
ret = discover_from_conf_file(r, h, desc, connect, &cfg);
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!trsvcid)
|
|
|
|
trsvcid = 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,
|
|
|
|
};
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (device && !force) {
|
|
|
|
c = nvme_scan_ctrl(r, device);
|
|
|
|
if (c) {
|
|
|
|
/* Check if device matches command-line options */
|
|
|
|
if (!ctrl_config_match(c, &trcfg)) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
} 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;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
}
|
|
|
|
if (!c && !force) {
|
|
|
|
c = lookup_discover_ctrl(r, &trcfg);
|
|
|
|
if (c)
|
|
|
|
persistent = true;
|
|
|
|
}
|
|
|
|
if (!c) {
|
|
|
|
/* No device or non-matching device, create a new controller */
|
|
|
|
c = create_discover_ctrl(r, h, &cfg, &trcfg);
|
|
|
|
if (!c) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"failed to add controller, error %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
ret = errno;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
ret = __discover(c, &cfg, raw, connect,
|
|
|
|
persistent, flags);
|
|
|
|
if (!persistent)
|
|
|
|
nvme_disconnect_ctrl(c);
|
|
|
|
nvme_free_ctrl(c);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
out_free:
|
|
|
|
free(hnqn);
|
|
|
|
free(hid);
|
|
|
|
if (dump_config)
|
|
|
|
nvme_dump_config(r);
|
|
|
|
nvme_free_tree(r);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
int nvmf_connect(const char *desc, int argc, char **argv)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
char *subsysnqn = NULL;
|
|
|
|
char *transport = NULL, *traddr = NULL;
|
|
|
|
char *trsvcid = NULL, *hostnqn = NULL, *hostid = NULL;
|
|
|
|
char *hostkey = NULL, *ctrlkey = NULL;
|
|
|
|
char *hnqn = NULL, *hid = NULL;
|
|
|
|
char *config_file = PATH_NVMF_CONFIG;
|
|
|
|
unsigned int verbose = 0;
|
|
|
|
nvme_root_t r;
|
|
|
|
nvme_host_t h;
|
|
|
|
nvme_ctrl_t c;
|
|
|
|
int ret;
|
|
|
|
struct nvme_fabrics_config cfg;
|
2025-02-16 12:10:50 +01:00
|
|
|
enum nvme_print_flags flags = -1;
|
2025-02-16 12:15:30 +01:00
|
|
|
char *format = "";
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
NVMF_OPTS(cfg),
|
2025-02-16 12:16:44 +01:00
|
|
|
OPT_STRING("dhchap-ctrl-secret", 'C', "STR", &ctrlkey, nvmf_ctrlkey),
|
2025-02-16 12:15:30 +01:00
|
|
|
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"),
|
|
|
|
OPT_FMT("output-format", 'o', &format, "Output format: normal|json"),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_default_config(&cfg);
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
ret = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (ret)
|
2025-02-16 12:15:30 +01:00
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!strcmp(format, ""))
|
2025-02-16 12:10:50 +01:00
|
|
|
flags = -1;
|
2025-02-16 12:15:30 +01:00
|
|
|
else if (!strcmp(format, "normal"))
|
2025-02-16 12:10:50 +01:00
|
|
|
flags = NORMAL;
|
2025-02-16 12:15:30 +01:00
|
|
|
else if (!strcmp(format, "json"))
|
2025-02-16 12:10:50 +01:00
|
|
|
flags = JSON;
|
2025-02-16 12:15:30 +01:00
|
|
|
else
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
if (!subsysnqn) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"required argument [--nqn | -n] not specified\n");
|
|
|
|
return EINVAL;
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!transport) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"required argument [--transport | -t] not specified\n");
|
|
|
|
return EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (strcmp(transport, "loop")) {
|
|
|
|
if (!traddr) {
|
|
|
|
fprintf(stderr,
|
2025-02-16 12:16:06 +01:00
|
|
|
"required argument [--traddr | -a] not specified for transport %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
transport);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2025-02-16 12:10:50 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!strcmp(config_file, "none"))
|
|
|
|
config_file = NULL;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
r = nvme_create_root(stderr, map_log_level(verbose, quiet));
|
|
|
|
if (!r) {
|
|
|
|
fprintf(stderr, "Failed to create topology root: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
return -errno;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
ret = nvme_scan_topology(r, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
2025-02-16 12:16:06 +01:00
|
|
|
fprintf(stderr, "Failed to scan topology: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_strerror(errno));
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
nvme_read_config(r, config_file);
|
|
|
|
|
|
|
|
if (!hostnqn)
|
|
|
|
hostnqn = hnqn = nvmf_hostnqn_from_file();
|
|
|
|
if (!hostnqn)
|
|
|
|
hostnqn = hnqn = nvmf_hostnqn_generate();
|
|
|
|
if (!hostid)
|
|
|
|
hostid = hid = nvmf_hostid_from_file();
|
|
|
|
h = nvme_lookup_host(r, hostnqn, hostid);
|
|
|
|
if (!h) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
if (hostkey)
|
|
|
|
nvme_host_set_dhchap_key(h, hostkey);
|
|
|
|
if (!trsvcid)
|
|
|
|
trsvcid = get_default_trsvcid(transport, false);
|
|
|
|
c = nvme_create_ctrl(r, subsysnqn, transport, traddr,
|
|
|
|
cfg.host_traddr, cfg.host_iface, trsvcid);
|
|
|
|
if (!c) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
if (ctrlkey)
|
|
|
|
nvme_ctrl_set_dhchap_key(c, ctrlkey);
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
ret = nvmf_add_ctrl(h, c, &cfg);
|
|
|
|
if (ret)
|
|
|
|
fprintf(stderr, "no controller found: %s\n",
|
|
|
|
nvme_strerror(errno));
|
2025-02-16 12:10:50 +01:00
|
|
|
else {
|
2025-02-16 12:15:30 +01:00
|
|
|
errno = 0;
|
2025-02-16 12:10:50 +01:00
|
|
|
if (flags == NORMAL)
|
2025-02-16 12:15:30 +01:00
|
|
|
print_connect_msg(c);
|
2025-02-16 12:10:50 +01:00
|
|
|
else if (flags == JSON)
|
2025-02-16 12:15:30 +01:00
|
|
|
json_connect_msg(c);
|
2025-02-16 12:10:50 +01:00
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
out_free:
|
|
|
|
free(hnqn);
|
|
|
|
free(hid);
|
|
|
|
if (dump_config)
|
|
|
|
nvme_dump_config(r);
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return errno;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:16:44 +01:00
|
|
|
static nvme_ctrl_t lookup_nvme_ctrl(nvme_root_t r, const char *name)
|
|
|
|
{
|
|
|
|
nvme_host_t h;
|
|
|
|
nvme_subsystem_t s;
|
|
|
|
nvme_ctrl_t c;
|
|
|
|
|
|
|
|
nvme_for_each_host(r, h) {
|
|
|
|
nvme_for_each_subsystem(h, s) {
|
|
|
|
nvme_subsystem_for_each_ctrl(s, c) {
|
|
|
|
if (!strcmp(nvme_ctrl_get_name(c), name))
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
int nvmf_disconnect(const char *desc, int argc, char **argv)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
const char *device = "nvme device handle";
|
|
|
|
nvme_root_t r;
|
|
|
|
nvme_host_t h;
|
|
|
|
nvme_subsystem_t s;
|
|
|
|
nvme_ctrl_t c;
|
|
|
|
char *p;
|
|
|
|
int ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct config {
|
|
|
|
char *nqn;
|
|
|
|
char *device;
|
|
|
|
unsigned int verbose;
|
|
|
|
};
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct config cfg = { 0 };
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_STRING("nqn", 'n', "NAME", &cfg.nqn, nvmf_nqn),
|
|
|
|
OPT_STRING("device", 'd', "DEV", &cfg.device, device),
|
|
|
|
OPT_INCR("verbose", 'v', &cfg.verbose, "Increase logging verbosity"),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
ret = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (!cfg.nqn && !cfg.device) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n");
|
|
|
|
return EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
|
|
|
|
if (!r) {
|
|
|
|
fprintf(stderr, "Failed to create topology root: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
ret = nvme_scan_topology(r, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
2025-02-16 12:16:06 +01:00
|
|
|
fprintf(stderr, "Failed to scan topology: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_strerror(errno));
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return ret;
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.nqn) {
|
|
|
|
int i = 0;
|
|
|
|
char *n = cfg.nqn;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
while ((p = strsep(&n, ",")) != NULL) {
|
|
|
|
if (!strlen(p))
|
|
|
|
continue;
|
|
|
|
nvme_for_each_host(r, h) {
|
|
|
|
nvme_for_each_subsystem(h, s) {
|
|
|
|
if (strcmp(nvme_subsystem_get_nqn(s), p))
|
|
|
|
continue;
|
|
|
|
nvme_subsystem_for_each_ctrl(s, c) {
|
|
|
|
if (!nvme_disconnect_ctrl(c))
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("NQN:%s disconnected %d controller(s)\n", cfg.nqn, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg.device) {
|
|
|
|
char *d;
|
|
|
|
|
|
|
|
d = cfg.device;
|
|
|
|
while ((p = strsep(&d, ",")) != NULL) {
|
|
|
|
if (!strncmp(p, "/dev/", 5))
|
|
|
|
p += 5;
|
2025-02-16 12:16:44 +01:00
|
|
|
c = lookup_nvme_ctrl(r, p);
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!c) {
|
|
|
|
fprintf(stderr,
|
2025-02-16 12:16:44 +01:00
|
|
|
"Did not find device %s\n", p);
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_free_tree(r);
|
|
|
|
return errno;
|
|
|
|
}
|
|
|
|
ret = nvme_disconnect_ctrl(c);
|
|
|
|
if (ret)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Failed to disconnect %s: %s\n",
|
|
|
|
p, nvme_strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nvme_free_tree(r);
|
|
|
|
|
|
|
|
return 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
int nvmf_disconnect_all(const char *desc, int argc, char **argv)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_host_t h;
|
|
|
|
nvme_subsystem_t s;
|
|
|
|
nvme_root_t r;
|
|
|
|
nvme_ctrl_t c;
|
|
|
|
int ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct config {
|
|
|
|
char *transport;
|
|
|
|
unsigned int verbose;
|
|
|
|
};
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
struct config cfg = { 0 };
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_ARGS(opts) = {
|
|
|
|
OPT_STRING("transport", 'r', "STR", (char *)&cfg.transport, nvmf_tport),
|
|
|
|
OPT_INCR("verbose", 'v', &cfg.verbose, "Increase logging verbosity"),
|
|
|
|
OPT_END()
|
|
|
|
};
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
ret = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
|
|
|
|
if (!r) {
|
|
|
|
fprintf(stderr, "Failed to create topology root: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
ret = nvme_scan_topology(r, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
2025-02-16 12:16:06 +01:00
|
|
|
fprintf(stderr, "Failed to scan topology: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_strerror(errno));
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return ret;
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_for_each_host(r, h) {
|
|
|
|
nvme_for_each_subsystem(h, s) {
|
|
|
|
nvme_subsystem_for_each_ctrl(s, c) {
|
|
|
|
if (cfg.transport &&
|
|
|
|
strcmp(cfg.transport,
|
|
|
|
nvme_ctrl_get_transport(c)))
|
|
|
|
continue;
|
|
|
|
else if (!strcmp(nvme_ctrl_get_transport(c),
|
|
|
|
"pcie"))
|
|
|
|
continue;
|
|
|
|
if (nvme_disconnect_ctrl(c))
|
|
|
|
fprintf(stderr,
|
|
|
|
"failed to disconnect %s\n",
|
|
|
|
nvme_ctrl_get_name(c));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nvme_free_tree(r);
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
return 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
int nvmf_config(const char *desc, int argc, char **argv)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
char *subsysnqn = NULL;
|
|
|
|
char *transport = NULL, *traddr = NULL;
|
|
|
|
char *trsvcid = NULL, *hostnqn = NULL, *hostid = NULL;
|
|
|
|
char *hnqn = NULL, *hid = NULL;
|
|
|
|
char *hostkey = NULL, *ctrlkey = NULL;
|
|
|
|
char *config_file = PATH_NVMF_CONFIG;
|
|
|
|
unsigned int verbose = 0;
|
|
|
|
nvme_root_t r;
|
2025-02-16 11:09:01 +01:00
|
|
|
int ret;
|
2025-02-16 12:15:30 +01:00
|
|
|
struct nvme_fabrics_config cfg;
|
|
|
|
bool scan_tree = false, modify_config = false, update_config = false;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
NVMF_OPTS(cfg),
|
2025-02-16 12:16:44 +01:00
|
|
|
OPT_STRING("dhchap-ctrl-secret", 'C', "STR", &ctrlkey, nvmf_ctrlkey),
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_STRING("config", 'J', "FILE", &config_file, nvmf_config_file),
|
|
|
|
OPT_INCR("verbose", 'v', &verbose, "Increase logging verbosity"),
|
|
|
|
OPT_FLAG("scan", 'R', &scan_tree, "Scan current NVMeoF topology"),
|
|
|
|
OPT_FLAG("modify", 'M', &modify_config, "Modify JSON configuration file"),
|
|
|
|
OPT_FLAG("dump", 'O', &dump_config, "Dump JSON configuration to stdout"),
|
|
|
|
OPT_FLAG("update", 'U', &update_config, "Update JSON configuration file"),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
nvmf_default_config(&cfg);
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
ret = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (ret)
|
2025-02-16 12:15:30 +01:00
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!strcmp(config_file, "none"))
|
|
|
|
config_file = NULL;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
r = nvme_create_root(stderr, map_log_level(verbose, quiet));
|
|
|
|
if (!r) {
|
|
|
|
fprintf(stderr, "Failed to create topology root: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
if (scan_tree) {
|
|
|
|
ret = nvme_scan_topology(r, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
2025-02-16 12:16:06 +01:00
|
|
|
fprintf(stderr, "Failed to scan topology: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_strerror(errno));
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_read_config(r, config_file);
|
|
|
|
|
|
|
|
if (modify_config) {
|
|
|
|
nvme_host_t h;
|
|
|
|
nvme_subsystem_t s;
|
|
|
|
nvme_ctrl_t c;
|
|
|
|
|
2025-02-16 12:17:56 +01:00
|
|
|
if (!subsysnqn) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"required argument [--nqn | -n] needed with --modify\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!transport) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"required argument [--transport | -t] needed with --modify\n");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!hostnqn)
|
|
|
|
hostnqn = hnqn = nvmf_hostnqn_from_file();
|
|
|
|
if (!hostid && hnqn)
|
|
|
|
hostid = hid = nvmf_hostid_from_file();
|
|
|
|
h = nvme_lookup_host(r, hostnqn, hostid);
|
|
|
|
if (!h) {
|
|
|
|
fprintf(stderr, "Failed to lookup host '%s': %s\n",
|
|
|
|
hostnqn, nvme_strerror(errno));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (hostkey)
|
|
|
|
nvme_host_set_dhchap_key(h, hostkey);
|
|
|
|
s = nvme_lookup_subsystem(h, NULL, subsysnqn);
|
|
|
|
if (!s) {
|
|
|
|
fprintf(stderr, "Failed to lookup subsystem '%s': %s\n",
|
|
|
|
subsysnqn, nvme_strerror(errno));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
c = nvme_lookup_ctrl(s, transport, traddr,
|
|
|
|
cfg.host_traddr, cfg.host_iface,
|
|
|
|
trsvcid, NULL);
|
|
|
|
if (!c) {
|
|
|
|
fprintf(stderr, "Failed to lookup controller: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
nvmf_update_config(c, &cfg);
|
|
|
|
if (ctrlkey)
|
|
|
|
nvme_ctrl_set_dhchap_key(c, ctrlkey);
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (update_config)
|
|
|
|
nvme_update_config(r);
|
|
|
|
|
|
|
|
if (dump_config)
|
|
|
|
nvme_dump_config(r);
|
|
|
|
|
2025-02-16 11:09:01 +01:00
|
|
|
out:
|
2025-02-16 12:15:30 +01:00
|
|
|
if (hid)
|
|
|
|
free(hid);
|
|
|
|
if (hnqn)
|
|
|
|
free(hnqn);
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dim_operation(nvme_ctrl_t c, enum nvmf_dim_tas tas, const char * name)
|
|
|
|
{
|
|
|
|
static const char * const task[] = {
|
|
|
|
[NVMF_DIM_TAS_REGISTER] = "register",
|
|
|
|
[NVMF_DIM_TAS_DEREGISTER] = "deregister",
|
|
|
|
};
|
|
|
|
const char * t;
|
|
|
|
int status;
|
|
|
|
__u32 result;
|
|
|
|
|
|
|
|
t = (tas > NVMF_DIM_TAS_DEREGISTER || !task[tas]) ? "reserved" : task[tas];
|
|
|
|
status = nvmf_register_ctrl(c, tas, &result);
|
|
|
|
if (status == NVME_SC_SUCCESS) {
|
|
|
|
printf("%s DIM %s command success\n", name, t);
|
|
|
|
} else if (status < NVME_SC_SUCCESS) {
|
|
|
|
fprintf(stderr, "%s DIM %s command error. Status:0x%04x - %s\n",
|
|
|
|
name, t, status, nvme_status_to_string(status, false));
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "%s DIM %s command error. Result:0x%04x, Status:0x%04x - %s\n",
|
|
|
|
name, t, result, status, nvme_status_to_string(status, false));
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
int nvmf_dim(const char *desc, int argc, char **argv)
|
2025-02-16 11:09:01 +01:00
|
|
|
{
|
2025-02-16 12:15:30 +01:00
|
|
|
enum nvmf_dim_tas tas;
|
|
|
|
nvme_root_t r;
|
|
|
|
nvme_ctrl_t c;
|
|
|
|
char *p;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
char *nqn;
|
|
|
|
char *device;
|
|
|
|
char *tas;
|
|
|
|
unsigned int verbose;
|
|
|
|
} cfg = { 0 };
|
2025-02-16 11:09:01 +01:00
|
|
|
|
|
|
|
OPT_ARGS(opts) = {
|
2025-02-16 12:15:30 +01:00
|
|
|
OPT_STRING("nqn", 'n', "NAME", &cfg.nqn, "Comma-separated list of DC nqn"),
|
|
|
|
OPT_STRING("device", 'd', "DEV", &cfg.device, "Comma-separated list of DC nvme device handle."),
|
|
|
|
OPT_STRING("task", 't', "TASK", &cfg.tas, "[register|deregister]"),
|
|
|
|
OPT_INCR("verbose", 'v', &cfg.verbose, "Increase logging verbosity"),
|
2025-02-16 11:09:01 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
ret = argconfig_parse(argc, argv, desc, opts);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!cfg.nqn && !cfg.device) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Neither device name [--device | -d] nor NQN [--nqn | -n] provided\n");
|
|
|
|
return EINVAL;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (!cfg.tas) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Task [--task | -t] must be specified\n");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allow partial name (e.g. "reg" for "register" */
|
|
|
|
if (strstarts("register", cfg.tas)) {
|
|
|
|
tas = NVMF_DIM_TAS_REGISTER;
|
|
|
|
} else if (strstarts("deregister", cfg.tas)) {
|
|
|
|
tas = NVMF_DIM_TAS_DEREGISTER;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Invalid --task: %s\n", cfg.tas);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = nvme_create_root(stderr, map_log_level(cfg.verbose, false));
|
|
|
|
if (!r) {
|
|
|
|
fprintf(stderr, "Failed to create topology root: %s\n",
|
|
|
|
nvme_strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
ret = nvme_scan_topology(r, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
2025-02-16 12:16:06 +01:00
|
|
|
fprintf(stderr, "Failed to scan topology: %s\n",
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_strerror(errno));
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return ret;
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
if (cfg.nqn) {
|
|
|
|
nvme_host_t h;
|
|
|
|
nvme_subsystem_t s;
|
|
|
|
char *n = cfg.nqn;
|
2025-02-16 11:09:01 +01:00
|
|
|
|
2025-02-16 12:15:30 +01:00
|
|
|
while ((p = strsep(&n, ",")) != NULL) {
|
|
|
|
if (!strlen(p))
|
2025-02-16 11:09:01 +01:00
|
|
|
continue;
|
2025-02-16 12:15:30 +01:00
|
|
|
nvme_for_each_host(r, h) {
|
|
|
|
nvme_for_each_subsystem(h, s) {
|
|
|
|
if (strcmp(nvme_subsystem_get_nqn(s), p))
|
|
|
|
continue;
|
|
|
|
nvme_subsystem_for_each_ctrl(s, c) {
|
|
|
|
dim_operation(c, tas, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|
|
|
|
}
|
2025-02-16 12:15:30 +01:00
|
|
|
|
|
|
|
if (cfg.device) {
|
|
|
|
char *d = cfg.device;
|
|
|
|
|
|
|
|
while ((p = strsep(&d, ",")) != NULL) {
|
|
|
|
if (!strncmp(p, "/dev/", 5))
|
|
|
|
p += 5;
|
|
|
|
c = nvme_scan_ctrl(r, p);
|
|
|
|
if (!c) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Did not find device %s: %s\n",
|
|
|
|
p, nvme_strerror(errno));
|
|
|
|
nvme_free_tree(r);
|
|
|
|
return errno;
|
|
|
|
}
|
|
|
|
dim_operation(c, tas, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nvme_free_tree(r);
|
|
|
|
|
|
|
|
return 0;
|
2025-02-16 11:09:01 +01:00
|
|
|
}
|