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