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