xref: /freebsd/sbin/nvmecontrol/fabrics.c (revision 1662e47d91d137891b62fe64ba1370047e7b506f)
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
init_hostid(void)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
nvmf_parse_address(const char * in_address,const char ** address,const char ** port,char ** tofree)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
nvmf_parse_cntlid(const char * cntlid)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
tcp_qpair_params(struct nvmf_qpair_params * params,int adrfam,const char * address,const char * port)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 	freeaddrinfo(list);
172 	return (false);
173 }
174 
175 static void
tcp_discovery_association_params(struct nvmf_association_params * params)176 tcp_discovery_association_params(struct nvmf_association_params *params)
177 {
178 	params->tcp.pda = 0;
179 	params->tcp.header_digests = false;
180 	params->tcp.data_digests = false;
181 	params->tcp.maxr2t = 1;
182 }
183 
184 struct nvmf_qpair *
connect_discovery_adminq(enum nvmf_trtype trtype,const char * address,const char * port,const char * hostnqn)185 connect_discovery_adminq(enum nvmf_trtype trtype, const char *address,
186     const char *port, const char *hostnqn)
187 {
188 	struct nvmf_association_params aparams;
189 	struct nvmf_qpair_params qparams;
190 	struct nvmf_association *na;
191 	struct nvmf_qpair *qp;
192 	uint64_t cap, cc, csts;
193 	int error, timo;
194 
195 	memset(&aparams, 0, sizeof(aparams));
196 	aparams.sq_flow_control = false;
197 	switch (trtype) {
198 	case NVMF_TRTYPE_TCP:
199 		/* 7.4.9.3 Default port for discovery */
200 		if (port == NULL)
201 			port = "8009";
202 		tcp_discovery_association_params(&aparams);
203 		break;
204 	default:
205 		errx(EX_UNAVAILABLE, "Unsupported transport %s",
206 		    nvmf_transport_type(trtype));
207 	}
208 
209 	if (!init_hostid())
210 		exit(EX_IOERR);
211 	if (hostnqn != NULL) {
212 		if (!nvmf_nqn_valid(hostnqn))
213 			errx(EX_USAGE, "Invalid HostNQN %s", hostnqn);
214 	} else
215 		hostnqn = nqn;
216 
217 	na = nvmf_allocate_association(trtype, false, &aparams);
218 	if (na == NULL)
219 		err(EX_IOERR, "Failed to create discovery association");
220 	memset(&qparams, 0, sizeof(qparams));
221 	qparams.admin = true;
222 	if (!tcp_qpair_params(&qparams, AF_UNSPEC, address, port))
223 		exit(EX_NOHOST);
224 	qp = nvmf_connect(na, &qparams, 0, NVME_MIN_ADMIN_ENTRIES, hostid,
225 	    NVMF_CNTLID_DYNAMIC, NVMF_DISCOVERY_NQN, hostnqn, 0);
226 	if (qp == NULL)
227 		errx(EX_IOERR, "Failed to connect to discovery controller: %s",
228 		    nvmf_association_error(na));
229 	nvmf_free_association(na);
230 
231 	/* Fetch Controller Capabilities Property */
232 	error = nvmf_read_property(qp, NVMF_PROP_CAP, 8, &cap);
233 	if (error != 0)
234 		errc(EX_IOERR, error, "Failed to fetch CAP");
235 
236 	/* Set Controller Configuration Property (CC.EN=1) */
237 	error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc);
238 	if (error != 0)
239 		errc(EX_IOERR, error, "Failed to fetch CC");
240 
241 	/* Clear known fields preserving any reserved fields. */
242 	cc &= ~(NVMEM(NVME_CC_REG_SHN) | NVMEM(NVME_CC_REG_AMS) |
243 	    NVMEM(NVME_CC_REG_MPS) | NVMEM(NVME_CC_REG_CSS));
244 
245 	/* Leave AMS, MPS, and CSS as 0. */
246 
247 	cc |= NVMEF(NVME_CC_REG_EN, 1);
248 
249 	error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc);
250 	if (error != 0)
251 		errc(EX_IOERR, error, "Failed to set CC");
252 
253 	/* Wait for CSTS.RDY in Controller Status */
254 	timo = NVME_CAP_LO_TO(cap);
255 	for (;;) {
256 		error = nvmf_read_property(qp, NVMF_PROP_CSTS, 4, &csts);
257 		if (error != 0)
258 			errc(EX_IOERR, error, "Failed to fetch CSTS");
259 
260 		if (NVMEV(NVME_CSTS_REG_RDY, csts) != 0)
261 			break;
262 
263 		if (timo == 0)
264 			errx(EX_IOERR, "Controller failed to become ready");
265 		timo--;
266 		usleep(500 * 1000);
267 	}
268 
269 	return (qp);
270 }
271 
272 /*
273  * XXX: Should this accept the admin queue size as a parameter rather
274  * than always using NVMF_MIN_ADMIN_MAX_SQ_SIZE?
275  */
276 static int
connect_nvm_adminq(struct nvmf_association * na,const struct nvmf_qpair_params * params,struct nvmf_qpair ** qpp,uint16_t cntlid,const char * subnqn,const char * hostnqn,uint32_t kato,uint16_t * mqes)277 connect_nvm_adminq(struct nvmf_association *na,
278     const struct nvmf_qpair_params *params, struct nvmf_qpair **qpp,
279     uint16_t cntlid, const char *subnqn, const char *hostnqn, uint32_t kato,
280     uint16_t *mqes)
281 {
282 	struct nvmf_qpair *qp;
283 	uint64_t cap, cc, csts;
284 	u_int mps, mpsmin, mpsmax;
285 	int error, timo;
286 
287 	qp = nvmf_connect(na, params, 0, NVMF_MIN_ADMIN_MAX_SQ_SIZE, hostid,
288 	    cntlid, subnqn, hostnqn, kato);
289 	if (qp == NULL) {
290 		warnx("Failed to connect to NVM controller %s: %s", subnqn,
291 		    nvmf_association_error(na));
292 		return (EX_IOERR);
293 	}
294 
295 	/* Fetch Controller Capabilities Property */
296 	error = nvmf_read_property(qp, NVMF_PROP_CAP, 8, &cap);
297 	if (error != 0) {
298 		warnc(error, "Failed to fetch CAP");
299 		nvmf_free_qpair(qp);
300 		return (EX_IOERR);
301 	}
302 
303 	/* Require the NVM command set. */
304 	if (NVME_CAP_HI_CSS_NVM(cap >> 32) == 0) {
305 		warnx("Controller %s does not support the NVM command set",
306 		    subnqn);
307 		nvmf_free_qpair(qp);
308 		return (EX_UNAVAILABLE);
309 	}
310 
311 	*mqes = NVME_CAP_LO_MQES(cap);
312 
313 	/* Prefer native host page size if it fits. */
314 	mpsmin = NVMEV(NVME_CAP_HI_REG_MPSMIN, cap >> 32);
315 	mpsmax = NVMEV(NVME_CAP_HI_REG_MPSMAX, cap >> 32);
316 	mps = ffs(getpagesize()) - 1;
317 	if (mps < mpsmin + NVME_MPS_SHIFT)
318 		mps = mpsmin;
319 	else if (mps > mpsmax + NVME_MPS_SHIFT)
320 		mps = mpsmax;
321 	else
322 		mps -= NVME_MPS_SHIFT;
323 
324 	/* Configure controller. */
325 	error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc);
326 	if (error != 0) {
327 		warnc(error, "Failed to fetch CC");
328 		nvmf_free_qpair(qp);
329 		return (EX_IOERR);
330 	}
331 
332 	/* Clear known fields preserving any reserved fields. */
333 	cc &= ~(NVMEM(NVME_CC_REG_IOCQES) | NVMEM(NVME_CC_REG_IOSQES) |
334 	    NVMEM(NVME_CC_REG_SHN) | NVMEM(NVME_CC_REG_AMS) |
335 	    NVMEM(NVME_CC_REG_MPS) | NVMEM(NVME_CC_REG_CSS));
336 
337 	cc |= NVMEF(NVME_CC_REG_IOCQES, 4);	/* CQE entry size == 16 */
338 	cc |= NVMEF(NVME_CC_REG_IOSQES, 6);	/* SEQ entry size == 64 */
339 	cc |= NVMEF(NVME_CC_REG_AMS, 0);	/* AMS 0 (Round-robin) */
340 	cc |= NVMEF(NVME_CC_REG_MPS, mps);
341 	cc |= NVMEF(NVME_CC_REG_CSS, 0);	/* NVM command set */
342 	cc |= NVMEF(NVME_CC_REG_EN, 1);		/* EN = 1 */
343 
344 	error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc);
345 	if (error != 0) {
346 		warnc(error, "Failed to set CC");
347 		nvmf_free_qpair(qp);
348 		return (EX_IOERR);
349 	}
350 
351 	/* Wait for CSTS.RDY in Controller Status */
352 	timo = NVME_CAP_LO_TO(cap);
353 	for (;;) {
354 		error = nvmf_read_property(qp, NVMF_PROP_CSTS, 4, &csts);
355 		if (error != 0) {
356 			warnc(error, "Failed to fetch CSTS");
357 			nvmf_free_qpair(qp);
358 			return (EX_IOERR);
359 		}
360 
361 		if (NVMEV(NVME_CSTS_REG_RDY, csts) != 0)
362 			break;
363 
364 		if (timo == 0) {
365 			warnx("Controller failed to become ready");
366 			nvmf_free_qpair(qp);
367 			return (EX_IOERR);
368 		}
369 		timo--;
370 		usleep(500 * 1000);
371 	}
372 
373 	*qpp = qp;
374 	return (0);
375 }
376 
377 static void
shutdown_controller(struct nvmf_qpair * qp)378 shutdown_controller(struct nvmf_qpair *qp)
379 {
380 	uint64_t cc;
381 	int error;
382 
383 	error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc);
384 	if (error != 0) {
385 		warnc(error, "Failed to fetch CC");
386 		goto out;
387 	}
388 
389 	cc |= NVMEF(NVME_CC_REG_SHN, NVME_SHN_NORMAL);
390 
391 	error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc);
392 	if (error != 0) {
393 		warnc(error, "Failed to set CC to trigger shutdown");
394 		goto out;
395 	}
396 
397 out:
398 	nvmf_free_qpair(qp);
399 }
400 
401 /* Returns a value from <sysexits.h> */
402 int
connect_nvm_queues(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,struct nvmf_qpair ** admin,struct nvmf_qpair ** io,u_int num_io_queues,u_int queue_size,struct nvme_controller_data * cdata)403 connect_nvm_queues(const struct nvmf_association_params *aparams,
404     enum nvmf_trtype trtype, int adrfam, const char *address,
405     const char *port, uint16_t cntlid, const char *subnqn, const char *hostnqn,
406     uint32_t kato, struct nvmf_qpair **admin, struct nvmf_qpair **io,
407     u_int num_io_queues, u_int queue_size, struct nvme_controller_data *cdata)
408 {
409 	struct nvmf_qpair_params qparams;
410 	struct nvmf_association *na;
411 	u_int queues;
412 	int error;
413 	uint16_t mqes;
414 
415 	switch (trtype) {
416 	case NVMF_TRTYPE_TCP:
417 		break;
418 	default:
419 		warnx("Unsupported transport %s", nvmf_transport_type(trtype));
420 		return (EX_UNAVAILABLE);
421 	}
422 
423 	if (!init_hostid())
424 		return (EX_IOERR);
425 	if (hostnqn != NULL) {
426 		if (!nvmf_nqn_valid(hostnqn)) {
427 			warnx("Invalid HostNQN %s", hostnqn);
428 			return (EX_USAGE);
429 		}
430 	} else
431 		hostnqn = nqn;
432 
433 	/* Association. */
434 	na = nvmf_allocate_association(trtype, false, aparams);
435 	if (na == NULL) {
436 		warn("Failed to create association for %s", subnqn);
437 		return (EX_IOERR);
438 	}
439 
440 	/* Admin queue. */
441 	memset(&qparams, 0, sizeof(qparams));
442 	qparams.admin = true;
443 	if (!tcp_qpair_params(&qparams, adrfam, address, port)) {
444 		nvmf_free_association(na);
445 		return (EX_NOHOST);
446 	}
447 	error = connect_nvm_adminq(na, &qparams, admin, cntlid, subnqn, hostnqn,
448 	    kato, &mqes);
449 	if (error != 0) {
450 		nvmf_free_association(na);
451 		return (error);
452 	}
453 
454 	/* Validate I/O queue size. */
455 	if (queue_size == 0)
456 		queue_size = (u_int)mqes + 1;
457 	else if (queue_size > (u_int)mqes + 1) {
458 		shutdown_controller(*admin);
459 		nvmf_free_association(na);
460 		warn("I/O queue size exceeds controller maximum (%u)",
461 		    mqes + 1);
462 		return (EX_USAGE);
463 	}
464 
465 	/* Fetch controller data. */
466 	error = nvmf_host_identify_controller(*admin, cdata);
467 	if (error != 0) {
468 		shutdown_controller(*admin);
469 		nvmf_free_association(na);
470 		warnc(error, "Failed to fetch controller data for %s", subnqn);
471 		return (EX_IOERR);
472 	}
473 
474 	nvmf_update_assocation(na, cdata);
475 
476 	error = nvmf_host_request_queues(*admin, num_io_queues, &queues);
477 	if (error != 0) {
478 		shutdown_controller(*admin);
479 		nvmf_free_association(na);
480 		warnc(error, "Failed to request I/O queues");
481 		return (EX_IOERR);
482 	}
483 	if (queues < num_io_queues) {
484 		shutdown_controller(*admin);
485 		nvmf_free_association(na);
486 		warnx("Controller enabled fewer I/O queues (%u) than requested (%u)",
487 		    queues, num_io_queues);
488 		return (EX_PROTOCOL);
489 	}
490 
491 	/* I/O queues. */
492 	memset(io, 0, sizeof(*io) * num_io_queues);
493 	for (u_int i = 0; i < num_io_queues; i++) {
494 		memset(&qparams, 0, sizeof(qparams));
495 		qparams.admin = false;
496 		if (!tcp_qpair_params(&qparams, adrfam, address, port)) {
497 			error = EX_NOHOST;
498 			goto out;
499 		}
500 		io[i] = nvmf_connect(na, &qparams, i + 1, queue_size, hostid,
501 		    nvmf_cntlid(*admin), subnqn, hostnqn, 0);
502 		if (io[i] == NULL) {
503 			warnx("Failed to create I/O queue: %s",
504 			    nvmf_association_error(na));
505 			error = EX_IOERR;
506 			goto out;
507 		}
508 	}
509 	nvmf_free_association(na);
510 	return (0);
511 
512 out:
513 	for (u_int i = 0; i < num_io_queues; i++) {
514 		if (io[i] == NULL)
515 			break;
516 		nvmf_free_qpair(io[i]);
517 	}
518 	shutdown_controller(*admin);
519 	nvmf_free_association(na);
520 	return (error);
521 }
522