HardenedBSD/sbin/nvmecontrol/discover.c
John Baldwin 1058c12197 nvmecontrol: New commands to support Fabrics hosts
- discover: Connects to a remote Discovery controller, fetches its
  Discovery Log Page, and enumerates the remote controllers described
  in the log page.

  The -v option can be used to display the Identify Controller data
  structure for the Discovery controller.  This is only really useful
  for debugging.

- connect: Connects to a remote I/O controller and establishes an
  association of an admin queue and a single I/O queue.  The
  association is handed off to the in-kernel host to create a new
  nvmeX device.

- connect-all: Connects to a Discovery controller and attempts to
  create an association with each I/O controller enumerated in the
  Discovery controller's Discovery Log Page.

- reconnect: Establishes a new association with a remote I/O
  controller for an existing nvmeX device.  This can be used to
  restore access to a remote I/O controller after the loss of a prior
  association due to a transport error, controller reboot, etc.

- disconnect: Deletes one or more nvmeX devices after detaching its
  namespaces and terminating any active associations.  The devices to
  delete can be identified by either a nvmeX device name or the NQN of
  the remote controller.

- disconnect-all: Deletes all active associations with remote
  controllers.

Reviewed by:	imp
Sponsored by:	Chelsio Communications
Differential Revision:	https://reviews.freebsd.org/D44715
2024-05-02 16:30:10 -07:00

301 lines
6.7 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2023-2024 Chelsio Communications, Inc.
* Written by: John Baldwin <jhb@FreeBSD.org>
*/
#include <err.h>
#include <libnvmf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include "comnd.h"
#include "fabrics.h"
#include "nvmecontrol_ext.h"
static struct options {
const char *transport;
const char *address;
const char *hostnqn;
bool verbose;
} opt = {
.transport = "tcp",
.address = NULL,
.hostnqn = NULL,
.verbose = false,
};
static void
identify_controller(struct nvmf_qpair *qp)
{
struct nvme_controller_data cdata;
int error;
error = nvmf_host_identify_controller(qp, &cdata);
if (error != 0)
errc(EX_IOERR, error, "Failed to fetch controller data");
nvme_print_controller(&cdata);
}
static const char *
nvmf_address_family(uint8_t adrfam)
{
static char buf[8];
switch (adrfam) {
case NVMF_ADRFAM_IPV4:
return ("AF_INET");
case NVMF_ADRFAM_IPV6:
return ("AF_INET6");
case NVMF_ADRFAM_IB:
return ("InfiniBand");
case NVMF_ADRFAM_FC:
return ("Fibre Channel");
case NVMF_ADRFAM_INTRA_HOST:
return ("Intra-host");
default:
snprintf(buf, sizeof(buf), "0x%02x\n", adrfam);
return (buf);
}
}
static const char *
nvmf_subsystem_type(uint8_t subtype)
{
static char buf[8];
switch (subtype) {
case NVMF_SUBTYPE_DISCOVERY:
return ("Discovery");
case NVMF_SUBTYPE_NVME:
return ("NVMe");
default:
snprintf(buf, sizeof(buf), "0x%02x\n", subtype);
return (buf);
}
}
static const char *
nvmf_secure_channel(uint8_t treq)
{
switch (treq & 0x03) {
case NVMF_TREQ_SECURE_CHANNEL_NOT_SPECIFIED:
return ("Not specified");
case NVMF_TREQ_SECURE_CHANNEL_REQUIRED:
return ("Required");
case NVMF_TREQ_SECURE_CHANNEL_NOT_REQUIRED:
return ("Not required");
default:
return ("0x03");
}
}
static const char *
nvmf_controller_id(uint16_t cntlid)
{
static char buf[8];
switch (cntlid) {
case NVMF_CNTLID_DYNAMIC:
return ("Dynamic");
case NVMF_CNTLID_STATIC_ANY:
return ("Static");
default:
snprintf(buf, sizeof(buf), "%u", cntlid);
return (buf);
}
}
static const char *
nvmf_rdma_service_type(uint8_t qptype)
{
static char buf[8];
switch (qptype) {
case NVMF_RDMA_QPTYPE_RELIABLE_CONNECTED:
return ("Reliable connected");
case NVMF_RDMA_QPTYPE_RELIABLE_DATAGRAM:
return ("Reliable datagram");
default:
snprintf(buf, sizeof(buf), "0x%02x\n", qptype);
return (buf);
}
}
static const char *
nvmf_rdma_provider_type(uint8_t prtype)
{
static char buf[8];
switch (prtype) {
case NVMF_RDMA_PRTYPE_NONE:
return ("None");
case NVMF_RDMA_PRTYPE_IB:
return ("InfiniBand");
case NVMF_RDMA_PRTYPE_ROCE:
return ("RoCE (v1)");
case NVMF_RDMA_PRTYPE_ROCE2:
return ("RoCE (v2)");
case NVMF_RDMA_PRTYPE_IWARP:
return ("iWARP");
default:
snprintf(buf, sizeof(buf), "0x%02x\n", prtype);
return (buf);
}
}
static const char *
nvmf_rdma_cms(uint8_t cms)
{
static char buf[8];
switch (cms) {
case NVMF_RDMA_CMS_RDMA_CM:
return ("RDMA_IP_CM");
default:
snprintf(buf, sizeof(buf), "0x%02x\n", cms);
return (buf);
}
}
static const char *
nvmf_tcp_security_type(uint8_t sectype)
{
static char buf[8];
switch (sectype) {
case NVME_TCP_SECURITY_NONE:
return ("None");
case NVME_TCP_SECURITY_TLS_1_2:
return ("TLS 1.2");
case NVME_TCP_SECURITY_TLS_1_3:
return ("TLS 1.3");
default:
snprintf(buf, sizeof(buf), "0x%02x\n", sectype);
return (buf);
}
}
static void
print_discovery_entry(u_int i, struct nvme_discovery_log_entry *entry)
{
printf("Entry %02d\n", i + 1);
printf("========\n");
printf(" Transport type: %s\n",
nvmf_transport_type(entry->trtype));
printf(" Address family: %s\n",
nvmf_address_family(entry->adrfam));
printf(" Subsystem type: %s\n",
nvmf_subsystem_type(entry->subtype));
printf(" SQ flow control: %s\n",
(entry->treq & (1 << 2)) == 0 ? "required" : "optional");
printf(" Secure Channel: %s\n", nvmf_secure_channel(entry->treq));
printf(" Port ID: %u\n", entry->portid);
printf(" Controller ID: %s\n",
nvmf_controller_id(entry->cntlid));
printf(" Max Admin SQ Size: %u\n", entry->aqsz);
printf(" Sub NQN: %s\n", entry->subnqn);
printf(" Transport address: %s\n", entry->traddr);
printf(" Service identifier: %s\n", entry->trsvcid);
switch (entry->trtype) {
case NVMF_TRTYPE_RDMA:
printf(" RDMA Service Type: %s\n",
nvmf_rdma_service_type(entry->tsas.rdma.rdma_qptype));
printf(" RDMA Provider Type: %s\n",
nvmf_rdma_provider_type(entry->tsas.rdma.rdma_prtype));
printf(" RDMA CMS: %s\n",
nvmf_rdma_cms(entry->tsas.rdma.rdma_cms));
printf(" Partition key: %u\n",
entry->tsas.rdma.rdma_pkey);
break;
case NVMF_TRTYPE_TCP:
printf(" Security Type: %s\n",
nvmf_tcp_security_type(entry->tsas.tcp.sectype));
break;
}
}
static void
dump_discovery_log_page(struct nvmf_qpair *qp)
{
struct nvme_discovery_log *log;
int error;
error = nvmf_host_fetch_discovery_log_page(qp, &log);
if (error != 0)
errc(EX_IOERR, error, "Failed to fetch discovery log page");
printf("Discovery\n");
printf("=========\n");
if (log->numrec == 0) {
printf("No entries found\n");
} else {
for (u_int i = 0; i < log->numrec; i++)
print_discovery_entry(i, &log->entries[i]);
}
free(log);
}
static void
discover(const struct cmd *f, int argc, char *argv[])
{
enum nvmf_trtype trtype;
struct nvmf_qpair *qp;
const char *address, *port;
char *tofree;
if (arg_parse(argc, argv, f))
return;
if (strcasecmp(opt.transport, "tcp") == 0) {
trtype = NVMF_TRTYPE_TCP;
} else
errx(EX_USAGE, "Unsupported or invalid transport");
nvmf_parse_address(opt.address, &address, &port, &tofree);
qp = connect_discovery_adminq(trtype, address, port, opt.hostnqn);
free(tofree);
/* Use Identify to fetch controller data */
if (opt.verbose) {
identify_controller(qp);
printf("\n");
}
/* Fetch Log pages */
dump_discovery_log_page(qp);
nvmf_free_qpair(qp);
}
static const struct opts discover_opts[] = {
#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
OPT("transport", 't', arg_string, opt, transport,
"Transport type"),
OPT("hostnqn", 'q', arg_string, opt, hostnqn,
"Host NQN"),
OPT("verbose", 'v', arg_none, opt, verbose,
"Display the discovery controller's controller data"),
{ NULL, 0, arg_none, NULL, NULL }
};
#undef OPT
static const struct args discover_args[] = {
{ arg_string, &opt.address, "address" },
{ arg_none, NULL, NULL },
};
static struct cmd discover_cmd = {
.name = "discover",
.fn = discover,
.descr = "List discovery log pages from a fabrics controller",
.ctx_size = sizeof(opt),
.opts = discover_opts,
.args = discover_args,
};
CMD_COMMAND(discover_cmd);