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