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