xref: /freebsd/sbin/nvmecontrol/fabrics.c (revision 84943d6f38e936ac3b7a3947ca26eeb27a39f938)
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 <netinet/in.h>
10 #include <err.h>
11 #include <libnvmf.h>
12 #include <netdb.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sysexits.h>
17 #include <unistd.h>
18 
19 #include "fabrics.h"
20 
21 /*
22  * Subroutines shared by several Fabrics commands.
23  */
24 static char nqn[NVMF_NQN_MAX_LEN];
25 static uint8_t hostid[16];
26 static bool hostid_initted = false;
27 
28 static bool
29 init_hostid(void)
30 {
31 	int error;
32 
33 	if (hostid_initted)
34 		return (true);
35 
36 	error = nvmf_hostid_from_hostuuid(hostid);
37 	if (error != 0) {
38 		warnc(error, "Failed to generate hostid");
39 		return (false);
40 	}
41 	error = nvmf_nqn_from_hostuuid(nqn);
42 	if (error != 0) {
43 		warnc(error, "Failed to generate host NQN");
44 		return (false);
45 	}
46 
47 	hostid_initted = true;
48 	return (true);
49 }
50 
51 void
52 nvmf_parse_address(const char *in_address, const char **address,
53     const char **port, char **tofree)
54 {
55 	char *cp;
56 
57 	/*
58 	 * Accepts the following address formats:
59 	 *
60 	 * [IPv6 address]:port
61 	 * IPv4 address:port
62 	 * hostname:port
63 	 * [IPv6 address]
64 	 * IPv6 address
65 	 * IPv4 address
66 	 * hostname
67 	 */
68 	if (in_address[0] == '[') {
69 		/* IPv6 address in square brackets. */
70 		cp = strchr(in_address + 1, ']');
71 		if (cp == NULL || cp == in_address + 1)
72 			errx(EX_USAGE, "Invalid address %s", in_address);
73 		*tofree = strndup(in_address + 1, cp - (in_address + 1));
74 		*address = *tofree;
75 
76 		/* Skip over ']' */
77 		cp++;
78 		switch (*cp) {
79 		case '\0':
80 			*port = NULL;
81 			return;
82 		case ':':
83 			if (cp[1] != '\0') {
84 				*port = cp + 1;
85 				return;
86 			}
87 			/* FALLTHROUGH */
88 		default:
89 			errx(EX_USAGE, "Invalid address %s", in_address);
90 		}
91 	}
92 
93 	/* Look for the first colon. */
94 	cp = strchr(in_address, ':');
95 	if (cp == NULL) {
96 		*address = in_address;
97 		*port = NULL;
98 		*tofree = NULL;
99 		return;
100 	}
101 
102 	/* If there is another colon, assume this is an IPv6 address. */
103 	if (strchr(cp + 1, ':') != NULL) {
104 		*address = in_address;
105 		*port = NULL;
106 		*tofree = NULL;
107 		return;
108 	}
109 
110 	/* Both strings on either side of the colon must be non-empty. */
111 	if (cp == in_address || cp[1] == '\0')
112 		errx(EX_USAGE, "Invalid address %s", in_address);
113 
114 	*tofree = strndup(in_address, cp - in_address);
115 	*address = *tofree;
116 
117 	/* Skip over ':' */
118 	*port = cp + 1;
119 }
120 
121 uint16_t
122 nvmf_parse_cntlid(const char *cntlid)
123 {
124 	u_long value;
125 
126 	if (strcasecmp(cntlid, "dynamic") == 0)
127 		return (NVMF_CNTLID_DYNAMIC);
128 	else if (strcasecmp(cntlid, "static") == 0)
129 		return (NVMF_CNTLID_STATIC_ANY);
130 	else {
131 		value = strtoul(cntlid, NULL, 0);
132 
133 		if (value > NVMF_CNTLID_STATIC_MAX)
134 			errx(EX_USAGE, "Invalid controller ID");
135 
136 		return (value);
137 	}
138 }
139 
140 bool
141 tcp_qpair_params(struct nvmf_qpair_params *params, int adrfam,
142     const char *address, const char *port)
143 {
144 	struct addrinfo hints, *ai, *list;
145 	int error, s;
146 
147 	memset(&hints, 0, sizeof(hints));
148 	hints.ai_family = adrfam;
149 	hints.ai_protocol = IPPROTO_TCP;
150 	error = getaddrinfo(address, port, &hints, &list);
151 	if (error != 0) {
152 		warnx("%s", gai_strerror(error));
153 		return (false);
154 	}
155 
156 	for (ai = list; ai != NULL; ai = ai->ai_next) {
157 		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
158 		if (s == -1)
159 			continue;
160 
161 		if (connect(s, ai->ai_addr, ai->ai_addrlen) != 0) {
162 			close(s);
163 			continue;
164 		}
165 
166 		params->tcp.fd = s;
167 		freeaddrinfo(list);
168 		return (true);
169 	}
170 	warn("Failed to connect to controller at %s:%s", address, port);
171 	return (false);
172 }
173 
174 static void
175 tcp_discovery_association_params(struct nvmf_association_params *params)
176 {
177 	params->tcp.pda = 0;
178 	params->tcp.header_digests = false;
179 	params->tcp.data_digests = false;
180 	params->tcp.maxr2t = 1;
181 }
182 
183 struct nvmf_qpair *
184 connect_discovery_adminq(enum nvmf_trtype trtype, const char *address,
185     const char *port, const char *hostnqn)
186 {
187 	struct nvmf_association_params aparams;
188 	struct nvmf_qpair_params qparams;
189 	struct nvmf_association *na;
190 	struct nvmf_qpair *qp;
191 	uint64_t cap, cc, csts;
192 	int error, timo;
193 
194 	memset(&aparams, 0, sizeof(aparams));
195 	aparams.sq_flow_control = false;
196 	switch (trtype) {
197 	case NVMF_TRTYPE_TCP:
198 		/* 7.4.9.3 Default port for discovery */
199 		if (port == NULL)
200 			port = "8009";
201 		tcp_discovery_association_params(&aparams);
202 		break;
203 	default:
204 		errx(EX_UNAVAILABLE, "Unsupported transport %s",
205 		    nvmf_transport_type(trtype));
206 	}
207 
208 	if (!init_hostid())
209 		exit(EX_IOERR);
210 	if (hostnqn != NULL) {
211 		if (!nvmf_nqn_valid(hostnqn))
212 			errx(EX_USAGE, "Invalid HostNQN %s", hostnqn);
213 	} else
214 		hostnqn = nqn;
215 
216 	na = nvmf_allocate_association(trtype, false, &aparams);
217 	if (na == NULL)
218 		err(EX_IOERR, "Failed to create discovery association");
219 	memset(&qparams, 0, sizeof(qparams));
220 	qparams.admin = true;
221 	if (!tcp_qpair_params(&qparams, AF_UNSPEC, address, port))
222 		exit(EX_NOHOST);
223 	qp = nvmf_connect(na, &qparams, 0, NVME_MIN_ADMIN_ENTRIES, hostid,
224 	    NVMF_CNTLID_DYNAMIC, NVMF_DISCOVERY_NQN, hostnqn, 0);
225 	if (qp == NULL)
226 		errx(EX_IOERR, "Failed to connect to discovery controller: %s",
227 		    nvmf_association_error(na));
228 	nvmf_free_association(na);
229 
230 	/* Fetch Controller Capabilities Property */
231 	error = nvmf_read_property(qp, NVMF_PROP_CAP, 8, &cap);
232 	if (error != 0)
233 		errc(EX_IOERR, error, "Failed to fetch CAP");
234 
235 	/* Set Controller Configuration Property (CC.EN=1) */
236 	error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc);
237 	if (error != 0)
238 		errc(EX_IOERR, error, "Failed to fetch CC");
239 
240 	/* Clear known fields preserving any reserved fields. */
241 	cc &= ~(NVMEM(NVME_CC_REG_SHN) | NVMEM(NVME_CC_REG_AMS) |
242 	    NVMEM(NVME_CC_REG_MPS) | NVMEM(NVME_CC_REG_CSS));
243 
244 	/* Leave AMS, MPS, and CSS as 0. */
245 
246 	cc |= NVMEF(NVME_CC_REG_EN, 1);
247 
248 	error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc);
249 	if (error != 0)
250 		errc(EX_IOERR, error, "Failed to set CC");
251 
252 	/* Wait for CSTS.RDY in Controller Status */
253 	timo = NVME_CAP_LO_TO(cap);
254 	for (;;) {
255 		error = nvmf_read_property(qp, NVMF_PROP_CSTS, 4, &csts);
256 		if (error != 0)
257 			errc(EX_IOERR, error, "Failed to fetch CSTS");
258 
259 		if (NVMEV(NVME_CSTS_REG_RDY, csts) != 0)
260 			break;
261 
262 		if (timo == 0)
263 			errx(EX_IOERR, "Controller failed to become ready");
264 		timo--;
265 		usleep(500 * 1000);
266 	}
267 
268 	return (qp);
269 }
270 
271 /*
272  * XXX: Should this accept the admin queue size as a parameter rather
273  * than always using NVMF_MIN_ADMIN_MAX_SQ_SIZE?
274  */
275 static int
276 connect_nvm_adminq(struct nvmf_association *na,
277     const struct nvmf_qpair_params *params, struct nvmf_qpair **qpp,
278     uint16_t cntlid, const char *subnqn, const char *hostnqn, uint32_t kato,
279     uint16_t *mqes)
280 {
281 	struct nvmf_qpair *qp;
282 	uint64_t cap, cc, csts;
283 	u_int mps, mpsmin, mpsmax;
284 	int error, timo;
285 
286 	qp = nvmf_connect(na, params, 0, NVMF_MIN_ADMIN_MAX_SQ_SIZE, hostid,
287 	    cntlid, subnqn, hostnqn, kato);
288 	if (qp == NULL) {
289 		warnx("Failed to connect to NVM controller %s: %s", subnqn,
290 		    nvmf_association_error(na));
291 		return (EX_IOERR);
292 	}
293 
294 	/* Fetch Controller Capabilities Property */
295 	error = nvmf_read_property(qp, NVMF_PROP_CAP, 8, &cap);
296 	if (error != 0) {
297 		warnc(error, "Failed to fetch CAP");
298 		nvmf_free_qpair(qp);
299 		return (EX_IOERR);
300 	}
301 
302 	/* Require the NVM command set. */
303 	if (NVME_CAP_HI_CSS_NVM(cap >> 32) == 0) {
304 		warnx("Controller %s does not support the NVM command set",
305 		    subnqn);
306 		nvmf_free_qpair(qp);
307 		return (EX_UNAVAILABLE);
308 	}
309 
310 	*mqes = NVME_CAP_LO_MQES(cap);
311 
312 	/* Prefer native host page size if it fits. */
313 	mpsmin = NVMEV(NVME_CAP_HI_REG_MPSMIN, cap >> 32);
314 	mpsmax = NVMEV(NVME_CAP_HI_REG_MPSMAX, cap >> 32);
315 	mps = ffs(getpagesize()) - 1;
316 	if (mps < mpsmin + NVME_MPS_SHIFT)
317 		mps = mpsmin;
318 	else if (mps > mpsmax + NVME_MPS_SHIFT)
319 		mps = mpsmax;
320 	else
321 		mps -= NVME_MPS_SHIFT;
322 
323 	/* Configure controller. */
324 	error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc);
325 	if (error != 0) {
326 		warnc(error, "Failed to fetch CC");
327 		nvmf_free_qpair(qp);
328 		return (EX_IOERR);
329 	}
330 
331 	/* Clear known fields preserving any reserved fields. */
332 	cc &= ~(NVMEM(NVME_CC_REG_IOCQES) | NVMEM(NVME_CC_REG_IOSQES) |
333 	    NVMEM(NVME_CC_REG_SHN) | NVMEM(NVME_CC_REG_AMS) |
334 	    NVMEM(NVME_CC_REG_MPS) | NVMEM(NVME_CC_REG_CSS));
335 
336 	cc |= NVMEF(NVME_CC_REG_IOCQES, 4);	/* CQE entry size == 16 */
337 	cc |= NVMEF(NVME_CC_REG_IOSQES, 6);	/* SEQ entry size == 64 */
338 	cc |= NVMEF(NVME_CC_REG_AMS, 0);	/* AMS 0 (Round-robin) */
339 	cc |= NVMEF(NVME_CC_REG_MPS, mps);
340 	cc |= NVMEF(NVME_CC_REG_CSS, 0);	/* NVM command set */
341 	cc |= NVMEF(NVME_CC_REG_EN, 1);		/* EN = 1 */
342 
343 	error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc);
344 	if (error != 0) {
345 		warnc(error, "Failed to set CC");
346 		nvmf_free_qpair(qp);
347 		return (EX_IOERR);
348 	}
349 
350 	/* Wait for CSTS.RDY in Controller Status */
351 	timo = NVME_CAP_LO_TO(cap);
352 	for (;;) {
353 		error = nvmf_read_property(qp, NVMF_PROP_CSTS, 4, &csts);
354 		if (error != 0) {
355 			warnc(error, "Failed to fetch CSTS");
356 			nvmf_free_qpair(qp);
357 			return (EX_IOERR);
358 		}
359 
360 		if (NVMEV(NVME_CSTS_REG_RDY, csts) != 0)
361 			break;
362 
363 		if (timo == 0) {
364 			warnx("Controller failed to become ready");
365 			nvmf_free_qpair(qp);
366 			return (EX_IOERR);
367 		}
368 		timo--;
369 		usleep(500 * 1000);
370 	}
371 
372 	*qpp = qp;
373 	return (0);
374 }
375 
376 static void
377 shutdown_controller(struct nvmf_qpair *qp)
378 {
379 	uint64_t cc;
380 	int error;
381 
382 	error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc);
383 	if (error != 0) {
384 		warnc(error, "Failed to fetch CC");
385 		goto out;
386 	}
387 
388 	cc |= NVMEF(NVME_CC_REG_SHN, NVME_SHN_NORMAL);
389 
390 	error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc);
391 	if (error != 0) {
392 		warnc(error, "Failed to set CC to trigger shutdown");
393 		goto out;
394 	}
395 
396 out:
397 	nvmf_free_qpair(qp);
398 }
399 
400 /* Returns a value from <sysexits.h> */
401 int
402 connect_nvm_queues(const struct nvmf_association_params *aparams,
403     enum nvmf_trtype trtype, int adrfam, const char *address,
404     const char *port, uint16_t cntlid, const char *subnqn, const char *hostnqn,
405     uint32_t kato, struct nvmf_qpair **admin, struct nvmf_qpair **io,
406     u_int num_io_queues, u_int queue_size, struct nvme_controller_data *cdata)
407 {
408 	struct nvmf_qpair_params qparams;
409 	struct nvmf_association *na;
410 	u_int queues;
411 	int error;
412 	uint16_t mqes;
413 
414 	switch (trtype) {
415 	case NVMF_TRTYPE_TCP:
416 		break;
417 	default:
418 		warnx("Unsupported transport %s", nvmf_transport_type(trtype));
419 		return (EX_UNAVAILABLE);
420 	}
421 
422 	if (!init_hostid())
423 		return (EX_IOERR);
424 	if (hostnqn != NULL) {
425 		if (!nvmf_nqn_valid(hostnqn)) {
426 			warnx("Invalid HostNQN %s", hostnqn);
427 			return (EX_USAGE);
428 		}
429 	} else
430 		hostnqn = nqn;
431 
432 	/* Association. */
433 	na = nvmf_allocate_association(trtype, false, aparams);
434 	if (na == NULL) {
435 		warn("Failed to create association for %s", subnqn);
436 		return (EX_IOERR);
437 	}
438 
439 	/* Admin queue. */
440 	memset(&qparams, 0, sizeof(qparams));
441 	qparams.admin = true;
442 	if (!tcp_qpair_params(&qparams, adrfam, address, port)) {
443 		nvmf_free_association(na);
444 		return (EX_NOHOST);
445 	}
446 	error = connect_nvm_adminq(na, &qparams, admin, cntlid, subnqn, hostnqn,
447 	    kato, &mqes);
448 	if (error != 0) {
449 		nvmf_free_association(na);
450 		return (error);
451 	}
452 
453 	/* Validate I/O queue size. */
454 	if (queue_size == 0)
455 		queue_size = mqes + 1;
456 	else if (queue_size > mqes + 1) {
457 		shutdown_controller(*admin);
458 		nvmf_free_association(na);
459 		warn("I/O queue size exceeds controller maximum (%u)",
460 		    mqes + 1);
461 		return (EX_USAGE);
462 	}
463 
464 	/* Fetch controller data. */
465 	error = nvmf_host_identify_controller(*admin, cdata);
466 	if (error != 0) {
467 		shutdown_controller(*admin);
468 		nvmf_free_association(na);
469 		warnc(error, "Failed to fetch controller data for %s", subnqn);
470 		return (EX_IOERR);
471 	}
472 
473 	nvmf_update_assocation(na, cdata);
474 
475 	error = nvmf_host_request_queues(*admin, num_io_queues, &queues);
476 	if (error != 0) {
477 		shutdown_controller(*admin);
478 		nvmf_free_association(na);
479 		warnc(error, "Failed to request I/O queues");
480 		return (EX_IOERR);
481 	}
482 	if (queues < num_io_queues) {
483 		shutdown_controller(*admin);
484 		nvmf_free_association(na);
485 		warnx("Controller enabled fewer I/O queues (%u) than requested (%u)",
486 		    queues, num_io_queues);
487 		return (EX_PROTOCOL);
488 	}
489 
490 	/* I/O queues. */
491 	memset(io, 0, sizeof(io) * num_io_queues);
492 	for (u_int i = 0; i < num_io_queues; i++) {
493 		memset(&qparams, 0, sizeof(qparams));
494 		qparams.admin = false;
495 		if (!tcp_qpair_params(&qparams, adrfam, address, port)) {
496 			error = EX_NOHOST;
497 			goto out;
498 		}
499 		io[i] = nvmf_connect(na, &qparams, i + 1, queue_size, hostid,
500 		    nvmf_cntlid(*admin), subnqn, hostnqn, 0);
501 		if (io[i] == NULL) {
502 			warnx("Failed to create I/O queue: %s",
503 			    nvmf_association_error(na));
504 			error = EX_IOERR;
505 			goto out;
506 		}
507 	}
508 	nvmf_free_association(na);
509 	return (0);
510 
511 out:
512 	for (u_int i = 0; i < num_io_queues; i++) {
513 		if (io[i] == NULL)
514 			break;
515 		nvmf_free_qpair(io[i]);
516 	}
517 	shutdown_controller(*admin);
518 	nvmf_free_association(na);
519 	return (error);
520 }
521