xref: /freebsd/sbin/nvmecontrol/reconnect.c (revision b2d2a78ad80ec68d4a17f5aef97d21686cb1e29b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023-2024 Chelsio Communications, Inc.
5  * Written by: John Baldwin <jhb@FreeBSD.org>
6  */
7 
8 #include <sys/nv.h>
9 #include <sys/socket.h>
10 #include <err.h>
11 #include <libnvmf.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sysexits.h>
15 #include <unistd.h>
16 
17 #include "nvmecontrol.h"
18 #include "fabrics.h"
19 
20 /*
21  * See comment about other possible settings in connect.c.
22  */
23 
24 static struct options {
25 	const char	*dev;
26 	const char	*transport;
27 	const char	*address;
28 	const char	*hostnqn;
29 	uint32_t	kato;
30 	uint16_t	num_io_queues;
31 	uint16_t	queue_size;
32 	bool		data_digests;
33 	bool		flow_control;
34 	bool		header_digests;
35 } opt = {
36 	.dev = NULL,
37 	.transport = "tcp",
38 	.address = NULL,
39 	.hostnqn = NULL,
40 	.kato = NVMF_KATO_DEFAULT / 1000,
41 	.num_io_queues = 1,
42 	.queue_size = 0,
43 	.data_digests = false,
44 	.flow_control = false,
45 	.header_digests = false,
46 };
47 
48 static void
49 tcp_association_params(struct nvmf_association_params *params)
50 {
51 	params->tcp.pda = 0;
52 	params->tcp.header_digests = opt.header_digests;
53 	params->tcp.data_digests = opt.data_digests;
54 	/* XXX */
55 	params->tcp.maxr2t = 1;
56 }
57 
58 static int
59 reconnect_nvm_controller(int fd, enum nvmf_trtype trtype, int adrfam,
60     const char *address, const char *port)
61 {
62 	struct nvme_controller_data cdata;
63 	struct nvmf_association_params aparams;
64 	nvlist_t *rparams;
65 	struct nvmf_qpair *admin, **io;
66 	int error;
67 
68 	error = nvmf_reconnect_params(fd, &rparams);
69 	if (error != 0) {
70 		warnc(error, "Failed to fetch reconnect parameters");
71 		return (EX_IOERR);
72 	}
73 
74 	if (!nvlist_exists_number(rparams, "cntlid") ||
75 	    !nvlist_exists_string(rparams, "subnqn")) {
76 		nvlist_destroy(rparams);
77 		warnx("Missing required reconnect parameters");
78 		return (EX_IOERR);
79 	}
80 
81 	memset(&aparams, 0, sizeof(aparams));
82 	aparams.sq_flow_control = opt.flow_control;
83 	switch (trtype) {
84 	case NVMF_TRTYPE_TCP:
85 		tcp_association_params(&aparams);
86 		break;
87 	default:
88 		nvlist_destroy(rparams);
89 		warnx("Unsupported transport %s", nvmf_transport_type(trtype));
90 		return (EX_UNAVAILABLE);
91 	}
92 
93 	io = calloc(opt.num_io_queues, sizeof(*io));
94 	error = connect_nvm_queues(&aparams, trtype, adrfam, address, port,
95 	    nvlist_get_number(rparams, "cntlid"),
96 	    nvlist_get_string(rparams, "subnqn"), opt.hostnqn, opt.kato,
97 	    &admin, io, opt.num_io_queues, opt.queue_size, &cdata);
98 	if (error != 0) {
99 		free(io);
100 		nvlist_destroy(rparams);
101 		return (error);
102 	}
103 	nvlist_destroy(rparams);
104 
105 	error = nvmf_reconnect_host(fd, admin, opt.num_io_queues, io, &cdata);
106 	if (error != 0) {
107 		warnc(error, "Failed to handoff queues to kernel");
108 		free(io);
109 		return (EX_IOERR);
110 	}
111 	free(io);
112 	return (0);
113 }
114 
115 static void
116 reconnect_fn(const struct cmd *f, int argc, char *argv[])
117 {
118 	enum nvmf_trtype trtype;
119 	const char *address, *port;
120 	char *tofree;
121 	int error, fd;
122 
123 	if (arg_parse(argc, argv, f))
124 		return;
125 
126 	if (strcasecmp(opt.transport, "tcp") == 0) {
127 		trtype = NVMF_TRTYPE_TCP;
128 	} else
129 		errx(EX_USAGE, "Unsupported or invalid transport");
130 
131 	nvmf_parse_address(opt.address, &address, &port, &tofree);
132 
133 	open_dev(opt.dev, &fd, 1, 1);
134 	if (port == NULL)
135 		errx(EX_USAGE, "Explicit port required");
136 
137 	error = reconnect_nvm_controller(fd, trtype, AF_UNSPEC, address, port);
138 	if (error != 0)
139 		exit(error);
140 
141 	close(fd);
142 	free(tofree);
143 }
144 
145 static const struct opts reconnect_opts[] = {
146 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
147 	OPT("transport", 't', arg_string, opt, transport,
148 	    "Transport type"),
149 	OPT("nr-io-queues", 'i', arg_uint16, opt, num_io_queues,
150 	    "Number of I/O queues"),
151 	OPT("queue-size", 'Q', arg_uint16, opt, queue_size,
152 	    "Number of entries in each I/O queue"),
153 	OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato,
154 	    "Keep Alive timeout (in seconds)"),
155 	OPT("hostnqn", 'q', arg_string, opt, hostnqn,
156 	    "Host NQN"),
157 	OPT("flow_control", 'F', arg_none, opt, flow_control,
158 	    "Request SQ flow control"),
159 	OPT("hdr_digests", 'g', arg_none, opt, header_digests,
160 	    "Enable TCP PDU header digests"),
161 	OPT("data_digests", 'G', arg_none, opt, data_digests,
162 	    "Enable TCP PDU data digests"),
163 	{ NULL, 0, arg_none, NULL, NULL }
164 };
165 #undef OPT
166 
167 static const struct args reconnect_args[] = {
168 	{ arg_string, &opt.dev, "controller-id" },
169 	{ arg_string, &opt.address, "address" },
170 	{ arg_none, NULL, NULL },
171 };
172 
173 static struct cmd reconnect_cmd = {
174 	.name = "reconnect",
175 	.fn = reconnect_fn,
176 	.descr = "Reconnect to a fabrics controller",
177 	.ctx_size = sizeof(opt),
178 	.opts = reconnect_opts,
179 	.args = reconnect_args,
180 };
181 
182 CMD_COMMAND(reconnect_cmd);
183