xref: /freebsd-src/sbin/nvmecontrol/connect.c (revision 8bba2c0f8958443790b1f3abc0675719da987e87)
11058c121SJohn Baldwin /*-
21058c121SJohn Baldwin  * SPDX-License-Identifier: BSD-2-Clause
31058c121SJohn Baldwin  *
41058c121SJohn Baldwin  * Copyright (c) 2023-2024 Chelsio Communications, Inc.
51058c121SJohn Baldwin  * Written by: John Baldwin <jhb@FreeBSD.org>
61058c121SJohn Baldwin  */
71058c121SJohn Baldwin 
81058c121SJohn Baldwin #include <sys/socket.h>
91058c121SJohn Baldwin #include <err.h>
101058c121SJohn Baldwin #include <libnvmf.h>
111058c121SJohn Baldwin #include <stdlib.h>
121058c121SJohn Baldwin #include <string.h>
131058c121SJohn Baldwin #include <sysexits.h>
141058c121SJohn Baldwin #include <unistd.h>
151058c121SJohn Baldwin 
161058c121SJohn Baldwin #include "comnd.h"
171058c121SJohn Baldwin #include "fabrics.h"
181058c121SJohn Baldwin 
191058c121SJohn Baldwin /*
201058c121SJohn Baldwin  * Settings that are currently hardcoded but could be exposed to the
211058c121SJohn Baldwin  * user via additional command line options:
221058c121SJohn Baldwin  *
231058c121SJohn Baldwin  * - ADMIN queue entries
241058c121SJohn Baldwin  * - MaxR2T
251058c121SJohn Baldwin  */
261058c121SJohn Baldwin 
271058c121SJohn Baldwin static struct options {
281058c121SJohn Baldwin 	const char	*transport;
291058c121SJohn Baldwin 	const char	*address;
301058c121SJohn Baldwin 	const char	*cntlid;
311058c121SJohn Baldwin 	const char	*subnqn;
321058c121SJohn Baldwin 	const char	*hostnqn;
331058c121SJohn Baldwin 	uint32_t	kato;
341058c121SJohn Baldwin 	uint16_t	num_io_queues;
351058c121SJohn Baldwin 	uint16_t	queue_size;
361058c121SJohn Baldwin 	bool		data_digests;
371058c121SJohn Baldwin 	bool		flow_control;
381058c121SJohn Baldwin 	bool		header_digests;
391058c121SJohn Baldwin } opt = {
401058c121SJohn Baldwin 	.transport = "tcp",
411058c121SJohn Baldwin 	.address = NULL,
421058c121SJohn Baldwin 	.cntlid = "dynamic",
431058c121SJohn Baldwin 	.subnqn = NULL,
441058c121SJohn Baldwin 	.hostnqn = NULL,
451058c121SJohn Baldwin 	.kato = NVMF_KATO_DEFAULT / 1000,
461058c121SJohn Baldwin 	.num_io_queues = 1,
471058c121SJohn Baldwin 	.queue_size = 0,
481058c121SJohn Baldwin 	.data_digests = false,
491058c121SJohn Baldwin 	.flow_control = false,
501058c121SJohn Baldwin 	.header_digests = false,
511058c121SJohn Baldwin };
521058c121SJohn Baldwin 
531058c121SJohn Baldwin static void
541058c121SJohn Baldwin tcp_association_params(struct nvmf_association_params *params)
551058c121SJohn Baldwin {
561058c121SJohn Baldwin 	params->tcp.pda = 0;
571058c121SJohn Baldwin 	params->tcp.header_digests = opt.header_digests;
581058c121SJohn Baldwin 	params->tcp.data_digests = opt.data_digests;
591058c121SJohn Baldwin 	/* XXX */
601058c121SJohn Baldwin 	params->tcp.maxr2t = 1;
611058c121SJohn Baldwin }
621058c121SJohn Baldwin 
631058c121SJohn Baldwin static int
641058c121SJohn Baldwin connect_nvm_controller(enum nvmf_trtype trtype, int adrfam, const char *address,
65*8bba2c0fSJohn Baldwin     const char *port, uint16_t cntlid, const char *subnqn,
66*8bba2c0fSJohn Baldwin     const struct nvme_discovery_log_entry *dle)
671058c121SJohn Baldwin {
681058c121SJohn Baldwin 	struct nvme_controller_data cdata;
69*8bba2c0fSJohn Baldwin 	struct nvme_discovery_log_entry dle_thunk;
701058c121SJohn Baldwin 	struct nvmf_association_params aparams;
711058c121SJohn Baldwin 	struct nvmf_qpair *admin, **io;
72*8bba2c0fSJohn Baldwin 	const char *hostnqn;
731058c121SJohn Baldwin 	int error;
741058c121SJohn Baldwin 
751058c121SJohn Baldwin 	memset(&aparams, 0, sizeof(aparams));
761058c121SJohn Baldwin 	aparams.sq_flow_control = opt.flow_control;
771058c121SJohn Baldwin 	switch (trtype) {
781058c121SJohn Baldwin 	case NVMF_TRTYPE_TCP:
791058c121SJohn Baldwin 		tcp_association_params(&aparams);
801058c121SJohn Baldwin 		break;
811058c121SJohn Baldwin 	default:
821058c121SJohn Baldwin 		warnx("Unsupported transport %s", nvmf_transport_type(trtype));
831058c121SJohn Baldwin 		return (EX_UNAVAILABLE);
841058c121SJohn Baldwin 	}
851058c121SJohn Baldwin 
86*8bba2c0fSJohn Baldwin 	hostnqn = opt.hostnqn;
87*8bba2c0fSJohn Baldwin 	if (hostnqn == NULL)
88*8bba2c0fSJohn Baldwin 		hostnqn = nvmf_default_hostnqn();
891058c121SJohn Baldwin 	io = calloc(opt.num_io_queues, sizeof(*io));
901058c121SJohn Baldwin 	error = connect_nvm_queues(&aparams, trtype, adrfam, address, port,
91*8bba2c0fSJohn Baldwin 	    cntlid, subnqn, hostnqn, opt.kato * 1000, &admin, io,
921058c121SJohn Baldwin 	    opt.num_io_queues, opt.queue_size, &cdata);
930ac468c7SJohn Baldwin 	if (error != 0) {
940ac468c7SJohn Baldwin 		free(io);
951058c121SJohn Baldwin 		return (error);
960ac468c7SJohn Baldwin 	}
971058c121SJohn Baldwin 
98*8bba2c0fSJohn Baldwin 	if (dle == NULL) {
99*8bba2c0fSJohn Baldwin 		error = nvmf_init_dle_from_admin_qp(admin, &cdata, &dle_thunk);
100*8bba2c0fSJohn Baldwin 		if (error != 0) {
101*8bba2c0fSJohn Baldwin 			warnc(error, "Failed to generate handoff parameters");
102*8bba2c0fSJohn Baldwin 			disconnect_nvm_queues(admin, io, opt.num_io_queues);
103*8bba2c0fSJohn Baldwin 			free(io);
104*8bba2c0fSJohn Baldwin 			return (EX_IOERR);
105*8bba2c0fSJohn Baldwin 		}
106*8bba2c0fSJohn Baldwin 		dle = &dle_thunk;
107*8bba2c0fSJohn Baldwin 	}
108*8bba2c0fSJohn Baldwin 
109*8bba2c0fSJohn Baldwin 	error = nvmf_handoff_host(dle, hostnqn, admin, opt.num_io_queues, io,
110*8bba2c0fSJohn Baldwin 	    &cdata);
1111058c121SJohn Baldwin 	if (error != 0) {
1121058c121SJohn Baldwin 		warnc(error, "Failed to handoff queues to kernel");
1130ac468c7SJohn Baldwin 		free(io);
1141058c121SJohn Baldwin 		return (EX_IOERR);
1151058c121SJohn Baldwin 	}
1161058c121SJohn Baldwin 	free(io);
1171058c121SJohn Baldwin 	return (0);
1181058c121SJohn Baldwin }
1191058c121SJohn Baldwin 
1201058c121SJohn Baldwin static void
1211058c121SJohn Baldwin connect_discovery_entry(struct nvme_discovery_log_entry *entry)
1221058c121SJohn Baldwin {
1231058c121SJohn Baldwin 	int adrfam;
1241058c121SJohn Baldwin 
1251058c121SJohn Baldwin 	switch (entry->trtype) {
1261058c121SJohn Baldwin 	case NVMF_TRTYPE_TCP:
1271058c121SJohn Baldwin 		switch (entry->adrfam) {
1281058c121SJohn Baldwin 		case NVMF_ADRFAM_IPV4:
1291058c121SJohn Baldwin 			adrfam = AF_INET;
1301058c121SJohn Baldwin 			break;
1311058c121SJohn Baldwin 		case NVMF_ADRFAM_IPV6:
1321058c121SJohn Baldwin 			adrfam = AF_INET6;
1331058c121SJohn Baldwin 			break;
1341058c121SJohn Baldwin 		default:
1351058c121SJohn Baldwin 			warnx("Skipping unsupported address family for %s",
1361058c121SJohn Baldwin 			    entry->subnqn);
1371058c121SJohn Baldwin 			return;
1381058c121SJohn Baldwin 		}
1391058c121SJohn Baldwin 		switch (entry->tsas.tcp.sectype) {
1401058c121SJohn Baldwin 		case NVME_TCP_SECURITY_NONE:
1411058c121SJohn Baldwin 			break;
1421058c121SJohn Baldwin 		default:
1431058c121SJohn Baldwin 			warnx("Skipping unsupported TCP security type for %s",
1441058c121SJohn Baldwin 			    entry->subnqn);
1451058c121SJohn Baldwin 			return;
1461058c121SJohn Baldwin 		}
1471058c121SJohn Baldwin 		break;
1481058c121SJohn Baldwin 	default:
1491058c121SJohn Baldwin 		warnx("Skipping unsupported transport %s for %s",
1501058c121SJohn Baldwin 		    nvmf_transport_type(entry->trtype), entry->subnqn);
1511058c121SJohn Baldwin 		return;
1521058c121SJohn Baldwin 	}
1531058c121SJohn Baldwin 
1541058c121SJohn Baldwin 	/*
1551058c121SJohn Baldwin 	 * XXX: Track portids and avoid duplicate connections for a
1561058c121SJohn Baldwin 	 * given (subnqn,portid)?
1571058c121SJohn Baldwin 	 */
1581058c121SJohn Baldwin 
1591058c121SJohn Baldwin 	/* XXX: Should this make use of entry->aqsz in some way? */
1601058c121SJohn Baldwin 	connect_nvm_controller(entry->trtype, adrfam, entry->traddr,
161*8bba2c0fSJohn Baldwin 	    entry->trsvcid, entry->cntlid, entry->subnqn, entry);
1621058c121SJohn Baldwin }
1631058c121SJohn Baldwin 
1641058c121SJohn Baldwin static void
1651058c121SJohn Baldwin connect_discovery_log_page(struct nvmf_qpair *qp)
1661058c121SJohn Baldwin {
1671058c121SJohn Baldwin 	struct nvme_discovery_log *log;
1681058c121SJohn Baldwin 	int error;
1691058c121SJohn Baldwin 
1701058c121SJohn Baldwin 	error = nvmf_host_fetch_discovery_log_page(qp, &log);
1711058c121SJohn Baldwin 	if (error != 0)
1721058c121SJohn Baldwin 		errc(EX_IOERR, error, "Failed to fetch discovery log page");
1731058c121SJohn Baldwin 
1741058c121SJohn Baldwin 	for (u_int i = 0; i < log->numrec; i++)
1751058c121SJohn Baldwin 		connect_discovery_entry(&log->entries[i]);
1761058c121SJohn Baldwin 	free(log);
1771058c121SJohn Baldwin }
1781058c121SJohn Baldwin 
1791058c121SJohn Baldwin static void
1801058c121SJohn Baldwin discover_controllers(enum nvmf_trtype trtype, const char *address,
1811058c121SJohn Baldwin     const char *port)
1821058c121SJohn Baldwin {
1831058c121SJohn Baldwin 	struct nvmf_qpair *qp;
1841058c121SJohn Baldwin 
1851058c121SJohn Baldwin 	qp = connect_discovery_adminq(trtype, address, port, opt.hostnqn);
1861058c121SJohn Baldwin 
1871058c121SJohn Baldwin 	connect_discovery_log_page(qp);
1881058c121SJohn Baldwin 
1891058c121SJohn Baldwin 	nvmf_free_qpair(qp);
1901058c121SJohn Baldwin }
1911058c121SJohn Baldwin 
1921058c121SJohn Baldwin static void
1931058c121SJohn Baldwin connect_fn(const struct cmd *f, int argc, char *argv[])
1941058c121SJohn Baldwin {
1951058c121SJohn Baldwin 	enum nvmf_trtype trtype;
1961058c121SJohn Baldwin 	const char *address, *port;
1971058c121SJohn Baldwin 	char *tofree;
1981058c121SJohn Baldwin 	u_long cntlid;
1991058c121SJohn Baldwin 	int error;
2001058c121SJohn Baldwin 
2011058c121SJohn Baldwin 	if (arg_parse(argc, argv, f))
2021058c121SJohn Baldwin 		return;
2031058c121SJohn Baldwin 
2041058c121SJohn Baldwin 	if (opt.num_io_queues <= 0)
2051058c121SJohn Baldwin 		errx(EX_USAGE, "Invalid number of I/O queues");
2061058c121SJohn Baldwin 
2071058c121SJohn Baldwin 	if (strcasecmp(opt.transport, "tcp") == 0) {
2081058c121SJohn Baldwin 		trtype = NVMF_TRTYPE_TCP;
2091058c121SJohn Baldwin 	} else
2101058c121SJohn Baldwin 		errx(EX_USAGE, "Unsupported or invalid transport");
2111058c121SJohn Baldwin 
2121058c121SJohn Baldwin 	nvmf_parse_address(opt.address, &address, &port, &tofree);
2131058c121SJohn Baldwin 	if (port == NULL)
2141058c121SJohn Baldwin 		errx(EX_USAGE, "Explicit port required");
2151058c121SJohn Baldwin 
2161058c121SJohn Baldwin 	cntlid = nvmf_parse_cntlid(opt.cntlid);
2171058c121SJohn Baldwin 
2181058c121SJohn Baldwin 	error = connect_nvm_controller(trtype, AF_UNSPEC, address, port, cntlid,
219*8bba2c0fSJohn Baldwin 	    opt.subnqn, NULL);
2201058c121SJohn Baldwin 	if (error != 0)
2211058c121SJohn Baldwin 		exit(error);
2221058c121SJohn Baldwin 
2231058c121SJohn Baldwin 	free(tofree);
2241058c121SJohn Baldwin }
2251058c121SJohn Baldwin 
2261058c121SJohn Baldwin static void
2271058c121SJohn Baldwin connect_all_fn(const struct cmd *f, int argc, char *argv[])
2281058c121SJohn Baldwin {
2291058c121SJohn Baldwin 	enum nvmf_trtype trtype;
2301058c121SJohn Baldwin 	const char *address, *port;
2311058c121SJohn Baldwin 	char *tofree;
2321058c121SJohn Baldwin 
2331058c121SJohn Baldwin 	if (arg_parse(argc, argv, f))
2341058c121SJohn Baldwin 		return;
2351058c121SJohn Baldwin 
2361058c121SJohn Baldwin 	if (opt.num_io_queues <= 0)
2371058c121SJohn Baldwin 		errx(EX_USAGE, "Invalid number of I/O queues");
2381058c121SJohn Baldwin 
2391058c121SJohn Baldwin 	if (strcasecmp(opt.transport, "tcp") == 0) {
2401058c121SJohn Baldwin 		trtype = NVMF_TRTYPE_TCP;
2411058c121SJohn Baldwin 	} else
2421058c121SJohn Baldwin 		errx(EX_USAGE, "Unsupported or invalid transport");
2431058c121SJohn Baldwin 
2441058c121SJohn Baldwin 	nvmf_parse_address(opt.address, &address, &port, &tofree);
2451058c121SJohn Baldwin 	discover_controllers(trtype, address, port);
2461058c121SJohn Baldwin 
2471058c121SJohn Baldwin 	free(tofree);
2481058c121SJohn Baldwin }
2491058c121SJohn Baldwin 
2501058c121SJohn Baldwin static const struct opts connect_opts[] = {
2511058c121SJohn Baldwin #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
2521058c121SJohn Baldwin 	OPT("transport", 't', arg_string, opt, transport,
2531058c121SJohn Baldwin 	    "Transport type"),
2541058c121SJohn Baldwin 	OPT("cntlid", 'c', arg_string, opt, cntlid,
2551058c121SJohn Baldwin 	    "Controller ID"),
2561058c121SJohn Baldwin 	OPT("nr-io-queues", 'i', arg_uint16, opt, num_io_queues,
2571058c121SJohn Baldwin 	    "Number of I/O queues"),
2581058c121SJohn Baldwin 	OPT("queue-size", 'Q', arg_uint16, opt, queue_size,
2591058c121SJohn Baldwin 	    "Number of entries in each I/O queue"),
2601058c121SJohn Baldwin 	OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato,
2611058c121SJohn Baldwin 	    "Keep Alive timeout (in seconds)"),
2621058c121SJohn Baldwin 	OPT("hostnqn", 'q', arg_string, opt, hostnqn,
2631058c121SJohn Baldwin 	    "Host NQN"),
2641058c121SJohn Baldwin 	OPT("flow_control", 'F', arg_none, opt, flow_control,
2651058c121SJohn Baldwin 	    "Request SQ flow control"),
2661058c121SJohn Baldwin 	OPT("hdr_digests", 'g', arg_none, opt, header_digests,
2671058c121SJohn Baldwin 	    "Enable TCP PDU header digests"),
2681058c121SJohn Baldwin 	OPT("data_digests", 'G', arg_none, opt, data_digests,
2691058c121SJohn Baldwin 	    "Enable TCP PDU data digests"),
2701058c121SJohn Baldwin 	{ NULL, 0, arg_none, NULL, NULL }
2711058c121SJohn Baldwin };
2721058c121SJohn Baldwin #undef OPT
2731058c121SJohn Baldwin 
2741058c121SJohn Baldwin static const struct args connect_args[] = {
2751058c121SJohn Baldwin 	{ arg_string, &opt.address, "address" },
2761058c121SJohn Baldwin 	{ arg_string, &opt.subnqn, "SubNQN" },
2771058c121SJohn Baldwin 	{ arg_none, NULL, NULL },
2781058c121SJohn Baldwin };
2791058c121SJohn Baldwin 
2801058c121SJohn Baldwin static const struct args connect_all_args[] = {
2811058c121SJohn Baldwin 	{ arg_string, &opt.address, "address" },
2821058c121SJohn Baldwin 	{ arg_none, NULL, NULL },
2831058c121SJohn Baldwin };
2841058c121SJohn Baldwin 
2851058c121SJohn Baldwin static struct cmd connect_cmd = {
2861058c121SJohn Baldwin 	.name = "connect",
2871058c121SJohn Baldwin 	.fn = connect_fn,
2881058c121SJohn Baldwin 	.descr = "Connect to a fabrics controller",
2891058c121SJohn Baldwin 	.ctx_size = sizeof(opt),
2901058c121SJohn Baldwin 	.opts = connect_opts,
2911058c121SJohn Baldwin 	.args = connect_args,
2921058c121SJohn Baldwin };
2931058c121SJohn Baldwin 
2941058c121SJohn Baldwin static struct cmd connect_all_cmd = {
2951058c121SJohn Baldwin 	.name = "connect-all",
2961058c121SJohn Baldwin 	.fn = connect_all_fn,
2971058c121SJohn Baldwin 	.descr = "Discover and connect to fabrics controllers",
2981058c121SJohn Baldwin 	.ctx_size = sizeof(opt),
2991058c121SJohn Baldwin 	.opts = connect_opts,
3001058c121SJohn Baldwin 	.args = connect_all_args,
3011058c121SJohn Baldwin };
3021058c121SJohn Baldwin 
3031058c121SJohn Baldwin CMD_COMMAND(connect_cmd);
3041058c121SJohn Baldwin CMD_COMMAND(connect_all_cmd);
305