xref: /freebsd/sbin/nvmecontrol/reconnect.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 
8*8bba2c0fSJohn Baldwin #include <sys/dnv.h>
9365b89e8SJohn Baldwin #include <sys/nv.h>
101058c121SJohn Baldwin #include <sys/socket.h>
111058c121SJohn Baldwin #include <err.h>
121058c121SJohn Baldwin #include <libnvmf.h>
131058c121SJohn Baldwin #include <stdlib.h>
141058c121SJohn Baldwin #include <string.h>
151058c121SJohn Baldwin #include <sysexits.h>
161058c121SJohn Baldwin #include <unistd.h>
171058c121SJohn Baldwin 
181058c121SJohn Baldwin #include "nvmecontrol.h"
191058c121SJohn Baldwin #include "fabrics.h"
201058c121SJohn Baldwin 
211058c121SJohn Baldwin /*
221058c121SJohn Baldwin  * See comment about other possible settings in connect.c.
231058c121SJohn Baldwin  */
241058c121SJohn Baldwin 
251058c121SJohn Baldwin static struct options {
261058c121SJohn Baldwin 	const char	*dev;
271058c121SJohn Baldwin 	const char	*transport;
281058c121SJohn Baldwin 	const char	*hostnqn;
291058c121SJohn Baldwin 	uint32_t	kato;
301058c121SJohn Baldwin 	uint16_t	num_io_queues;
311058c121SJohn Baldwin 	uint16_t	queue_size;
321058c121SJohn Baldwin 	bool		data_digests;
331058c121SJohn Baldwin 	bool		flow_control;
341058c121SJohn Baldwin 	bool		header_digests;
351058c121SJohn Baldwin } opt = {
361058c121SJohn Baldwin 	.dev = NULL,
371058c121SJohn Baldwin 	.transport = "tcp",
381058c121SJohn Baldwin 	.hostnqn = NULL,
391058c121SJohn Baldwin 	.kato = NVMF_KATO_DEFAULT / 1000,
401058c121SJohn Baldwin 	.num_io_queues = 1,
411058c121SJohn Baldwin 	.queue_size = 0,
421058c121SJohn Baldwin 	.data_digests = false,
431058c121SJohn Baldwin 	.flow_control = false,
441058c121SJohn Baldwin 	.header_digests = false,
451058c121SJohn Baldwin };
461058c121SJohn Baldwin 
471058c121SJohn Baldwin static void
tcp_association_params(struct nvmf_association_params * params,bool header_digests,bool data_digests)48*8bba2c0fSJohn Baldwin tcp_association_params(struct nvmf_association_params *params,
49*8bba2c0fSJohn Baldwin     bool header_digests, bool data_digests)
501058c121SJohn Baldwin {
511058c121SJohn Baldwin 	params->tcp.pda = 0;
52*8bba2c0fSJohn Baldwin 	params->tcp.header_digests = header_digests;
53*8bba2c0fSJohn Baldwin 	params->tcp.data_digests = data_digests;
541058c121SJohn Baldwin 	/* XXX */
551058c121SJohn Baldwin 	params->tcp.maxr2t = 1;
561058c121SJohn Baldwin }
571058c121SJohn Baldwin 
581058c121SJohn Baldwin static int
reconnect_nvm_controller(int fd,const struct nvmf_association_params * aparams,enum nvmf_trtype trtype,int adrfam,const char * address,const char * port,uint16_t cntlid,const char * subnqn,const char * hostnqn,uint32_t kato,u_int num_io_queues,u_int queue_size,const struct nvme_discovery_log_entry * dle)59*8bba2c0fSJohn Baldwin reconnect_nvm_controller(int fd, const struct nvmf_association_params *aparams,
60*8bba2c0fSJohn Baldwin     enum nvmf_trtype trtype, int adrfam, const char *address, const char *port,
61*8bba2c0fSJohn Baldwin     uint16_t cntlid, const char *subnqn, const char *hostnqn, uint32_t kato,
62*8bba2c0fSJohn Baldwin     u_int num_io_queues, u_int queue_size,
63*8bba2c0fSJohn Baldwin     const struct nvme_discovery_log_entry *dle)
641058c121SJohn Baldwin {
651058c121SJohn Baldwin 	struct nvme_controller_data cdata;
66*8bba2c0fSJohn Baldwin 	struct nvme_discovery_log_entry dle_thunk;
671058c121SJohn Baldwin 	struct nvmf_qpair *admin, **io;
681058c121SJohn Baldwin 	int error;
691058c121SJohn Baldwin 
70*8bba2c0fSJohn Baldwin 	io = calloc(num_io_queues, sizeof(*io));
71*8bba2c0fSJohn Baldwin 	error = connect_nvm_queues(aparams, trtype, adrfam, address, port,
72*8bba2c0fSJohn Baldwin 	    cntlid, subnqn, hostnqn, kato, &admin, io, num_io_queues,
73*8bba2c0fSJohn Baldwin 	    queue_size, &cdata);
740ac468c7SJohn Baldwin 	if (error != 0) {
750ac468c7SJohn Baldwin 		free(io);
761058c121SJohn Baldwin 		return (error);
770ac468c7SJohn Baldwin 	}
781058c121SJohn Baldwin 
79*8bba2c0fSJohn Baldwin 	if (dle == NULL) {
80*8bba2c0fSJohn Baldwin 		error = nvmf_init_dle_from_admin_qp(admin, &cdata, &dle_thunk);
81*8bba2c0fSJohn Baldwin 		if (error != 0) {
82*8bba2c0fSJohn Baldwin 			warnc(error, "Failed to generate handoff parameters");
83*8bba2c0fSJohn Baldwin 			disconnect_nvm_queues(admin, io, num_io_queues);
84*8bba2c0fSJohn Baldwin 			free(io);
85*8bba2c0fSJohn Baldwin 			return (EX_IOERR);
86*8bba2c0fSJohn Baldwin 		}
87*8bba2c0fSJohn Baldwin 		dle = &dle_thunk;
88*8bba2c0fSJohn Baldwin 	}
89*8bba2c0fSJohn Baldwin 
90*8bba2c0fSJohn Baldwin 	error = nvmf_reconnect_host(fd, dle, hostnqn, admin, num_io_queues, io,
91*8bba2c0fSJohn Baldwin 	    &cdata);
921058c121SJohn Baldwin 	if (error != 0) {
931058c121SJohn Baldwin 		warnc(error, "Failed to handoff queues to kernel");
940ac468c7SJohn Baldwin 		free(io);
951058c121SJohn Baldwin 		return (EX_IOERR);
961058c121SJohn Baldwin 	}
971058c121SJohn Baldwin 	free(io);
981058c121SJohn Baldwin 	return (0);
991058c121SJohn Baldwin }
1001058c121SJohn Baldwin 
101*8bba2c0fSJohn Baldwin static int
reconnect_by_address(int fd,const nvlist_t * rparams,const char * addr)102*8bba2c0fSJohn Baldwin reconnect_by_address(int fd, const nvlist_t *rparams, const char *addr)
103*8bba2c0fSJohn Baldwin {
104*8bba2c0fSJohn Baldwin 	const struct nvme_discovery_log_entry *dle;
105*8bba2c0fSJohn Baldwin 	struct nvmf_association_params aparams;
106*8bba2c0fSJohn Baldwin 	enum nvmf_trtype trtype;
107*8bba2c0fSJohn Baldwin 	const char *address, *hostnqn, *port;
108*8bba2c0fSJohn Baldwin 	char *subnqn, *tofree;
109*8bba2c0fSJohn Baldwin 	int error;
110*8bba2c0fSJohn Baldwin 
111*8bba2c0fSJohn Baldwin 	memset(&aparams, 0, sizeof(aparams));
112*8bba2c0fSJohn Baldwin 	aparams.sq_flow_control = opt.flow_control;
113*8bba2c0fSJohn Baldwin 	if (strcasecmp(opt.transport, "tcp") == 0) {
114*8bba2c0fSJohn Baldwin 		trtype = NVMF_TRTYPE_TCP;
115*8bba2c0fSJohn Baldwin 		tcp_association_params(&aparams, opt.header_digests,
116*8bba2c0fSJohn Baldwin 		    opt.data_digests);
117*8bba2c0fSJohn Baldwin 	} else {
118*8bba2c0fSJohn Baldwin 		warnx("Unsupported or invalid transport");
119*8bba2c0fSJohn Baldwin 		return (EX_USAGE);
120*8bba2c0fSJohn Baldwin 	}
121*8bba2c0fSJohn Baldwin 
122*8bba2c0fSJohn Baldwin 	nvmf_parse_address(addr, &address, &port, &tofree);
123*8bba2c0fSJohn Baldwin 	if (port == NULL) {
124*8bba2c0fSJohn Baldwin 		free(tofree);
125*8bba2c0fSJohn Baldwin 		warnx("Explicit port required");
126*8bba2c0fSJohn Baldwin 		return (EX_USAGE);
127*8bba2c0fSJohn Baldwin 	}
128*8bba2c0fSJohn Baldwin 
129*8bba2c0fSJohn Baldwin 	dle = nvlist_get_binary(rparams, "dle", NULL);
130*8bba2c0fSJohn Baldwin 
131*8bba2c0fSJohn Baldwin 	hostnqn = opt.hostnqn;
132*8bba2c0fSJohn Baldwin 	if (hostnqn == NULL)
133*8bba2c0fSJohn Baldwin 		hostnqn = nvmf_default_hostnqn();
134*8bba2c0fSJohn Baldwin 
135*8bba2c0fSJohn Baldwin 	/* Ensure subnqn is a terminated C string. */
136*8bba2c0fSJohn Baldwin 	subnqn = strndup(dle->subnqn, sizeof(dle->subnqn));
137*8bba2c0fSJohn Baldwin 
138*8bba2c0fSJohn Baldwin 	error = reconnect_nvm_controller(fd, &aparams, trtype, AF_UNSPEC,
139*8bba2c0fSJohn Baldwin 	    address, port, le16toh(dle->cntlid), subnqn, hostnqn,
140*8bba2c0fSJohn Baldwin 	    opt.kato * 1000, opt.num_io_queues, opt.queue_size, NULL);
141*8bba2c0fSJohn Baldwin 	free(subnqn);
142*8bba2c0fSJohn Baldwin 	free(tofree);
143*8bba2c0fSJohn Baldwin 	return (error);
144*8bba2c0fSJohn Baldwin }
145*8bba2c0fSJohn Baldwin 
146*8bba2c0fSJohn Baldwin static int
reconnect_by_params(int fd,const nvlist_t * rparams)147*8bba2c0fSJohn Baldwin reconnect_by_params(int fd, const nvlist_t *rparams)
148*8bba2c0fSJohn Baldwin {
149*8bba2c0fSJohn Baldwin 	struct nvmf_association_params aparams;
150*8bba2c0fSJohn Baldwin 	const struct nvme_discovery_log_entry *dle;
151*8bba2c0fSJohn Baldwin 	char *address, *port, *subnqn;
152*8bba2c0fSJohn Baldwin 	int adrfam, error;
153*8bba2c0fSJohn Baldwin 
154*8bba2c0fSJohn Baldwin 	dle = nvlist_get_binary(rparams, "dle", NULL);
155*8bba2c0fSJohn Baldwin 
156*8bba2c0fSJohn Baldwin 	memset(&aparams, 0, sizeof(aparams));
157*8bba2c0fSJohn Baldwin 	aparams.sq_flow_control = nvlist_get_bool(rparams, "sq_flow_control");
158*8bba2c0fSJohn Baldwin 	switch (dle->trtype) {
159*8bba2c0fSJohn Baldwin 	case NVMF_TRTYPE_TCP:
160*8bba2c0fSJohn Baldwin 		switch (dle->adrfam) {
161*8bba2c0fSJohn Baldwin 		case NVMF_ADRFAM_IPV4:
162*8bba2c0fSJohn Baldwin 			adrfam = AF_INET;
163*8bba2c0fSJohn Baldwin 			break;
164*8bba2c0fSJohn Baldwin 		case NVMF_ADRFAM_IPV6:
165*8bba2c0fSJohn Baldwin 			adrfam = AF_INET6;
166*8bba2c0fSJohn Baldwin 			break;
167*8bba2c0fSJohn Baldwin 		default:
168*8bba2c0fSJohn Baldwin 			warnx("Unsupported address family");
169*8bba2c0fSJohn Baldwin 			return (EX_UNAVAILABLE);
170*8bba2c0fSJohn Baldwin 		}
171*8bba2c0fSJohn Baldwin 		switch (dle->tsas.tcp.sectype) {
172*8bba2c0fSJohn Baldwin 		case NVME_TCP_SECURITY_NONE:
173*8bba2c0fSJohn Baldwin 			break;
174*8bba2c0fSJohn Baldwin 		default:
175*8bba2c0fSJohn Baldwin 			warnx("Unsupported TCP security type");
176*8bba2c0fSJohn Baldwin 			return (EX_UNAVAILABLE);
177*8bba2c0fSJohn Baldwin 		}
178*8bba2c0fSJohn Baldwin 		break;
179*8bba2c0fSJohn Baldwin 
180*8bba2c0fSJohn Baldwin 		tcp_association_params(&aparams,
181*8bba2c0fSJohn Baldwin 		    nvlist_get_bool(rparams, "header_digests"),
182*8bba2c0fSJohn Baldwin 		    nvlist_get_bool(rparams, "data_digests"));
183*8bba2c0fSJohn Baldwin 		break;
184*8bba2c0fSJohn Baldwin 	default:
185*8bba2c0fSJohn Baldwin 		warnx("Unsupported transport %s",
186*8bba2c0fSJohn Baldwin 		    nvmf_transport_type(dle->trtype));
187*8bba2c0fSJohn Baldwin 		return (EX_UNAVAILABLE);
188*8bba2c0fSJohn Baldwin 	}
189*8bba2c0fSJohn Baldwin 
190*8bba2c0fSJohn Baldwin 	/* Ensure address, port, and subnqn is a terminated C string. */
191*8bba2c0fSJohn Baldwin 	address = strndup(dle->traddr, sizeof(dle->traddr));
192*8bba2c0fSJohn Baldwin 	port = strndup(dle->trsvcid, sizeof(dle->trsvcid));
193*8bba2c0fSJohn Baldwin 	subnqn = strndup(dle->subnqn, sizeof(dle->subnqn));
194*8bba2c0fSJohn Baldwin 
195*8bba2c0fSJohn Baldwin 	error = reconnect_nvm_controller(fd, &aparams, dle->trtype, adrfam,
196*8bba2c0fSJohn Baldwin 	    address, port, le16toh(dle->cntlid), dle->subnqn,
197*8bba2c0fSJohn Baldwin 	    nvlist_get_string(rparams, "hostnqn"),
198*8bba2c0fSJohn Baldwin 	    dnvlist_get_number(rparams, "kato", 0),
199*8bba2c0fSJohn Baldwin 	    nvlist_get_number(rparams, "num_io_queues"),
200*8bba2c0fSJohn Baldwin 	    nvlist_get_number(rparams, "io_qsize"), dle);
201*8bba2c0fSJohn Baldwin 	free(subnqn);
202*8bba2c0fSJohn Baldwin 	free(port);
203*8bba2c0fSJohn Baldwin 	free(address);
204*8bba2c0fSJohn Baldwin 	return (error);
205*8bba2c0fSJohn Baldwin }
206*8bba2c0fSJohn Baldwin 
207*8bba2c0fSJohn Baldwin static int
fetch_and_validate_rparams(int fd,nvlist_t ** rparamsp)208*8bba2c0fSJohn Baldwin fetch_and_validate_rparams(int fd, nvlist_t **rparamsp)
209*8bba2c0fSJohn Baldwin {
210*8bba2c0fSJohn Baldwin 	const struct nvme_discovery_log_entry *dle;
211*8bba2c0fSJohn Baldwin 	nvlist_t *rparams;
212*8bba2c0fSJohn Baldwin 	size_t len;
213*8bba2c0fSJohn Baldwin 	int error;
214*8bba2c0fSJohn Baldwin 
215*8bba2c0fSJohn Baldwin 	error = nvmf_reconnect_params(fd, &rparams);
216*8bba2c0fSJohn Baldwin 	if (error != 0) {
217*8bba2c0fSJohn Baldwin 		warnc(error, "Failed to fetch reconnect parameters");
218*8bba2c0fSJohn Baldwin 		return (EX_IOERR);
219*8bba2c0fSJohn Baldwin 	}
220*8bba2c0fSJohn Baldwin 
221*8bba2c0fSJohn Baldwin 	if (!nvlist_exists_binary(rparams, "dle") ||
222*8bba2c0fSJohn Baldwin 	    !nvlist_exists_string(rparams, "hostnqn") ||
223*8bba2c0fSJohn Baldwin 	    !nvlist_exists_number(rparams, "num_io_queues") ||
224*8bba2c0fSJohn Baldwin 	    !nvlist_exists_number(rparams, "io_qsize") ||
225*8bba2c0fSJohn Baldwin 	    !nvlist_exists_bool(rparams, "sq_flow_control")) {
226*8bba2c0fSJohn Baldwin 		nvlist_destroy(rparams);
227*8bba2c0fSJohn Baldwin 		warnx("Missing required reconnect parameters");
228*8bba2c0fSJohn Baldwin 		return (EX_IOERR);
229*8bba2c0fSJohn Baldwin 	}
230*8bba2c0fSJohn Baldwin 
231*8bba2c0fSJohn Baldwin 	dle = nvlist_get_binary(rparams, "dle", &len);
232*8bba2c0fSJohn Baldwin 	if (len != sizeof(*dle)) {
233*8bba2c0fSJohn Baldwin 		nvlist_destroy(rparams);
234*8bba2c0fSJohn Baldwin 		warnx("Discovery Log entry reconnect parameter is wrong size");
235*8bba2c0fSJohn Baldwin 		return (EX_IOERR);
236*8bba2c0fSJohn Baldwin 	}
237*8bba2c0fSJohn Baldwin 
238*8bba2c0fSJohn Baldwin 	switch (dle->trtype) {
239*8bba2c0fSJohn Baldwin 	case NVMF_TRTYPE_TCP:
240*8bba2c0fSJohn Baldwin 		if (!nvlist_exists_bool(rparams, "header_digests") ||
241*8bba2c0fSJohn Baldwin 		    !nvlist_exists_bool(rparams, "data_digests")) {
242*8bba2c0fSJohn Baldwin 			nvlist_destroy(rparams);
243*8bba2c0fSJohn Baldwin 			warnx("Missing required reconnect parameters");
244*8bba2c0fSJohn Baldwin 			return (EX_IOERR);
245*8bba2c0fSJohn Baldwin 		}
246*8bba2c0fSJohn Baldwin 		break;
247*8bba2c0fSJohn Baldwin 	default:
248*8bba2c0fSJohn Baldwin 		nvlist_destroy(rparams);
249*8bba2c0fSJohn Baldwin 		warnx("Unsupported transport %s",
250*8bba2c0fSJohn Baldwin 		    nvmf_transport_type(dle->trtype));
251*8bba2c0fSJohn Baldwin 		return (EX_UNAVAILABLE);
252*8bba2c0fSJohn Baldwin 	}
253*8bba2c0fSJohn Baldwin 
254*8bba2c0fSJohn Baldwin 	*rparamsp = rparams;
255*8bba2c0fSJohn Baldwin 	return (0);
256*8bba2c0fSJohn Baldwin }
257*8bba2c0fSJohn Baldwin 
2581058c121SJohn Baldwin static void
reconnect_fn(const struct cmd * f,int argc,char * argv[])2591058c121SJohn Baldwin reconnect_fn(const struct cmd *f, int argc, char *argv[])
2601058c121SJohn Baldwin {
261*8bba2c0fSJohn Baldwin 	nvlist_t *rparams;
2621058c121SJohn Baldwin 	int error, fd;
2631058c121SJohn Baldwin 
2641058c121SJohn Baldwin 	if (arg_parse(argc, argv, f))
2651058c121SJohn Baldwin 		return;
2661058c121SJohn Baldwin 
2671058c121SJohn Baldwin 	open_dev(opt.dev, &fd, 1, 1);
268*8bba2c0fSJohn Baldwin 	error = fetch_and_validate_rparams(fd, &rparams);
2691058c121SJohn Baldwin 	if (error != 0)
2701058c121SJohn Baldwin 		exit(error);
2711058c121SJohn Baldwin 
272*8bba2c0fSJohn Baldwin 	/* Check for optional address. */
273*8bba2c0fSJohn Baldwin 	if (optind < argc)
274*8bba2c0fSJohn Baldwin 		error = reconnect_by_address(fd, rparams, argv[optind]);
275*8bba2c0fSJohn Baldwin 	else
276*8bba2c0fSJohn Baldwin 		error = reconnect_by_params(fd, rparams);
277*8bba2c0fSJohn Baldwin 	if (error != 0)
278*8bba2c0fSJohn Baldwin 		exit(error);
279*8bba2c0fSJohn Baldwin 
280*8bba2c0fSJohn Baldwin 	nvlist_destroy(rparams);
2811058c121SJohn Baldwin 	close(fd);
2821058c121SJohn Baldwin }
2831058c121SJohn Baldwin 
2841058c121SJohn Baldwin static const struct opts reconnect_opts[] = {
2851058c121SJohn Baldwin #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
2861058c121SJohn Baldwin 	OPT("transport", 't', arg_string, opt, transport,
2871058c121SJohn Baldwin 	    "Transport type"),
2881058c121SJohn Baldwin 	OPT("nr-io-queues", 'i', arg_uint16, opt, num_io_queues,
2891058c121SJohn Baldwin 	    "Number of I/O queues"),
2901058c121SJohn Baldwin 	OPT("queue-size", 'Q', arg_uint16, opt, queue_size,
2911058c121SJohn Baldwin 	    "Number of entries in each I/O queue"),
2921058c121SJohn Baldwin 	OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato,
2931058c121SJohn Baldwin 	    "Keep Alive timeout (in seconds)"),
2941058c121SJohn Baldwin 	OPT("hostnqn", 'q', arg_string, opt, hostnqn,
2951058c121SJohn Baldwin 	    "Host NQN"),
2961058c121SJohn Baldwin 	OPT("flow_control", 'F', arg_none, opt, flow_control,
2971058c121SJohn Baldwin 	    "Request SQ flow control"),
2981058c121SJohn Baldwin 	OPT("hdr_digests", 'g', arg_none, opt, header_digests,
2991058c121SJohn Baldwin 	    "Enable TCP PDU header digests"),
3001058c121SJohn Baldwin 	OPT("data_digests", 'G', arg_none, opt, data_digests,
3011058c121SJohn Baldwin 	    "Enable TCP PDU data digests"),
3021058c121SJohn Baldwin 	{ NULL, 0, arg_none, NULL, NULL }
3031058c121SJohn Baldwin };
3041058c121SJohn Baldwin #undef OPT
3051058c121SJohn Baldwin 
3061058c121SJohn Baldwin static const struct args reconnect_args[] = {
3071058c121SJohn Baldwin 	{ arg_string, &opt.dev, "controller-id" },
3081058c121SJohn Baldwin 	{ arg_none, NULL, NULL },
3091058c121SJohn Baldwin };
3101058c121SJohn Baldwin 
3111058c121SJohn Baldwin static struct cmd reconnect_cmd = {
3121058c121SJohn Baldwin 	.name = "reconnect",
3131058c121SJohn Baldwin 	.fn = reconnect_fn,
3141058c121SJohn Baldwin 	.descr = "Reconnect to a fabrics controller",
3151058c121SJohn Baldwin 	.ctx_size = sizeof(opt),
3161058c121SJohn Baldwin 	.opts = reconnect_opts,
3171058c121SJohn Baldwin 	.args = reconnect_args,
3181058c121SJohn Baldwin };
3191058c121SJohn Baldwin 
3201058c121SJohn Baldwin CMD_COMMAND(reconnect_cmd);
321