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 freeaddrinfo(list); 172 return (false); 173 } 174 175 static void 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 * 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 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 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 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