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