1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Chelsio Communications, Inc. 5 * Written by: John Baldwin <jhb@FreeBSD.org> 6 */ 7 8#include <sys/socket.h> 9#include <err.h> 10#include <errno.h> 11#include <inttypes.h> 12#include <libnvmf.h> 13#include <netdb.h> 14#include <stdio.h> 15#include <stdlib.h> 16#include <string.h> 17#include <strings.h> 18#include <unistd.h> 19#include <netinet/in.h> 20 21static struct controller_info { 22 uint32_t ioccsz; 23 uint32_t nn; 24 uint16_t mqes; 25 bool disconnect_supported; 26} info; 27 28enum rw { READ, WRITE }; 29 30static bool data_digests, flow_control_disable, header_digests; 31 32static void 33usage(void) 34{ 35 fprintf(stderr, "nvmfdd [-FGg] [-c cntlid] [-t transport] [-o offset] [-l length] [-n nsid]\n" 36 "\tread|write <address:port> <nqn>\n"); 37 exit(1); 38} 39 40static void 41tcp_association_params(struct nvmf_association_params *params) 42{ 43 params->tcp.pda = 0; 44 params->tcp.header_digests = header_digests; 45 params->tcp.data_digests = data_digests; 46 params->tcp.maxr2t = 1; 47} 48 49static void 50tcp_qpair_params(struct nvmf_qpair_params *params, bool admin, 51 const char *address, const char *port) 52{ 53 struct addrinfo hints, *ai, *list; 54 int error, s; 55 56 memset(&hints, 0, sizeof(hints)); 57 hints.ai_family = AF_UNSPEC; 58 hints.ai_protocol = IPPROTO_TCP; 59 error = getaddrinfo(address, port, &hints, &list); 60 if (error != 0) 61 errx(1, "%s", gai_strerror(error)); 62 63 for (ai = list; ai != NULL; ai = ai->ai_next) { 64 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 65 if (s == -1) 66 continue; 67 68 if (connect(s, ai->ai_addr, ai->ai_addrlen) != 0) { 69 close(s); 70 continue; 71 } 72 73 params->admin = admin; 74 params->tcp.fd = s; 75 freeaddrinfo(list); 76 return; 77 } 78 err(1, "Failed to connect to controller"); 79} 80 81static struct nvmf_qpair * 82connect_admin_queue(struct nvmf_association *na, 83 const struct nvmf_qpair_params *params, const uint8_t hostid[16], 84 uint16_t cntlid, const char *hostnqn, const char *subnqn) 85{ 86 struct nvme_controller_data cdata; 87 struct nvmf_qpair *qp; 88 uint64_t cap, cc, csts; 89 u_int mps, mpsmin, mpsmax; 90 int error, timo; 91 92 qp = nvmf_connect(na, params, 0, NVMF_MIN_ADMIN_MAX_SQ_SIZE, hostid, 93 cntlid, subnqn, hostnqn, 0); 94 if (qp == NULL) 95 return (NULL); 96 97 error = nvmf_read_property(qp, NVMF_PROP_CAP, 8, &cap); 98 if (error != 0) 99 errc(1, error, "Failed to fetch CAP"); 100 101 /* Require the NVM command set. */ 102 if (NVME_CAP_HI_CSS_NVM(cap >> 32) == 0) 103 errx(1, "Controller does not support the NVM command set"); 104 105 /* Prefer native host page size if it fits. */ 106 mpsmin = NVMEV(NVME_CAP_HI_REG_MPSMIN, cap >> 32); 107 mpsmax = NVMEV(NVME_CAP_HI_REG_MPSMAX, cap >> 32); 108 mps = ffs(getpagesize()) - 1; 109 if (mps < mpsmin + 12) 110 mps = mpsmin; 111 else if (mps > mpsmax + 12) 112 mps = mpsmax; 113 else 114 mps -= 12; 115 116 /* Configure controller. */ 117 error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc); 118 if (error != 0) 119 errc(1, error, "Failed to fetch CC"); 120 121 /* Clear known fields preserving any reserved fields. */ 122 cc &= ~(NVMEM(NVME_CC_REG_IOCQES) | NVMEM(NVME_CC_REG_IOSQES) | 123 NVMEM(NVME_CC_REG_SHN) | NVMEM(NVME_CC_REG_AMS) | 124 NVMEM(NVME_CC_REG_MPS) | NVMEM(NVME_CC_REG_CSS)); 125 126 cc |= NVMEF(NVME_CC_REG_IOCQES, 4); /* CQE entry size == 16 */ 127 cc |= NVMEF(NVME_CC_REG_IOSQES, 6); /* SQE entry size == 64 */ 128 cc |= NVMEF(NVME_CC_REG_AMS, 0); /* AMS 0 (Round-robin) */ 129 cc |= NVMEF(NVME_CC_REG_MPS, mps); 130 cc |= NVMEF(NVME_CC_REG_CSS, 0); /* NVM command set */ 131 cc |= NVMEF(NVME_CC_REG_EN, 1); /* EN = 1 */ 132 133 error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc); 134 if (error != 0) 135 errc(1, error, "Failed to set CC"); 136 137 /* Wait for CSTS.RDY in Controller Status */ 138 timo = NVME_CAP_LO_TO(cap); 139 for (;;) { 140 error = nvmf_read_property(qp, NVMF_PROP_CSTS, 4, &csts); 141 if (error != 0) 142 errc(1, error, "Failed to fetch CSTS"); 143 144 if (NVMEV(NVME_CSTS_REG_RDY, csts) != 0) 145 break; 146 147 if (timo == 0) 148 errx(1, "Controller failed to become ready"); 149 timo--; 150 usleep(500 * 1000); 151 } 152 153 /* Fetch controller data. */ 154 error = nvmf_host_identify_controller(qp, &cdata); 155 if (error != 0) 156 errc(1, error, "Failed to fetch controller data"); 157 158 nvmf_update_assocation(na, &cdata); 159 160 info.mqes = NVME_CAP_LO_MQES(cap); 161 info.nn = cdata.nn; 162 info.ioccsz = cdata.ioccsz; 163 info.disconnect_supported = (cdata.ofcs & 1) != 0; 164 165 return (qp); 166} 167 168static void 169shutdown_controller(struct nvmf_qpair *qp) 170{ 171 uint64_t cc; 172 int error; 173 174 error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc); 175 if (error != 0) 176 errc(1, error, "Failed to fetch CC"); 177 178 cc |= NVMEF(NVME_CC_REG_SHN, NVME_SHN_NORMAL); 179 180 error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc); 181 if (error != 0) 182 errc(1, error, "Failed to set CC to trigger shutdown"); 183 184 nvmf_free_qpair(qp); 185} 186 187static void 188disconnect_queue(struct nvmf_qpair *qp) 189{ 190 nvmf_free_qpair(qp); 191} 192 193static int 194validate_namespace(struct nvmf_qpair *qp, u_int nsid, u_int *block_size) 195{ 196 struct nvme_namespace_data nsdata; 197 int error; 198 uint8_t lbads, lbaf; 199 200 if (nsid > info.nn) { 201 warnx("Invalid namespace ID %u", nsid); 202 return (ERANGE); 203 } 204 205 error = nvmf_host_identify_namespace(qp, nsid, &nsdata); 206 if (error != 0) { 207 warnc(error, "Failed to identify namespace"); 208 return (error); 209 } 210 211 nvme_namespace_data_swapbytes(&nsdata); 212 213 if (NVMEV(NVME_NS_DATA_DPS_PIT, nsdata.dps) != 0) { 214 warnx("End-to-end data protection is not supported"); 215 return (EINVAL); 216 } 217 218 lbaf = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata.flbas); 219 if (lbaf > nsdata.nlbaf) { 220 warnx("Invalid LBA format index"); 221 return (EINVAL); 222 } 223 224 if (NVMEV(NVME_NS_DATA_LBAF_MS, nsdata.lbaf[lbaf]) != 0) { 225 warnx("Namespaces with metadata are not supported"); 226 return (EINVAL); 227 } 228 229 lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, nsdata.lbaf[lbaf]); 230 if (lbads == 0) { 231 warnx("Invalid LBA format index"); 232 return (EINVAL); 233 } 234 235 *block_size = 1 << lbads; 236 fprintf(stderr, "Detected block size %u\n", *block_size); 237 return (0); 238} 239 240static int 241nvmf_io_command(struct nvmf_qpair *qp, u_int nsid, enum rw command, 242 uint64_t slba, uint16_t nlb, void *buffer, size_t length) 243{ 244 struct nvme_command cmd; 245 const struct nvme_completion *cqe; 246 struct nvmf_capsule *cc, *rc; 247 int error; 248 uint16_t status; 249 250 memset(&cmd, 0, sizeof(cmd)); 251 cmd.opc = command == WRITE ? NVME_OPC_WRITE : NVME_OPC_READ; 252 cmd.nsid = htole32(nsid); 253 cmd.cdw10 = htole32(slba); 254 cmd.cdw11 = htole32(slba >> 32); 255 cmd.cdw12 = htole32(nlb - 1); 256 /* Sequential Request in cdw13? */ 257 258 cc = nvmf_allocate_command(qp, &cmd); 259 if (cc == NULL) 260 return (errno); 261 262 error = nvmf_capsule_append_data(cc, buffer, length, 263 command == WRITE); 264 if (error != 0) { 265 nvmf_free_capsule(cc); 266 return (error); 267 } 268 269 error = nvmf_host_transmit_command(cc); 270 if (error != 0) { 271 nvmf_free_capsule(cc); 272 return (error); 273 } 274 275 error = nvmf_host_wait_for_response(cc, &rc); 276 nvmf_free_capsule(cc); 277 if (error != 0) 278 return (error); 279 280 cqe = nvmf_capsule_cqe(rc); 281 status = le16toh(cqe->status); 282 if (status != 0) { 283 printf("NVMF: %s failed, status %#x\n", command == WRITE ? 284 "WRITE" : "READ", status); 285 nvmf_free_capsule(rc); 286 return (EIO); 287 } 288 289 nvmf_free_capsule(rc); 290 return (0); 291} 292 293static int 294nvmf_io(struct nvmf_qpair *qp, u_int nsid, u_int block_size, enum rw command, 295 off_t offset, off_t length) 296{ 297 char *buf; 298 ssize_t rv; 299 u_int todo; 300 int error; 301 302 if (offset % block_size != 0) { 303 warnx("Misaligned offset"); 304 return (EINVAL); 305 } 306 if (length % block_size != 0 && command == WRITE) 307 warnx("Length is not multiple of block size, will zero pad"); 308 309 buf = malloc(block_size); 310 error = 0; 311 while (length != 0) { 312 todo = length; 313 if (todo > block_size) 314 todo = block_size; 315 316 if (command == WRITE) { 317 rv = read(STDIN_FILENO, buf, todo); 318 if (rv == -1) { 319 error = errno; 320 break; 321 } 322 if (rv != todo) { 323 warn("Short read on input"); 324 error = EIO; 325 break; 326 } 327 328 if (todo < block_size) 329 memset(buf + todo, 0, block_size - todo); 330 } 331 332 error = nvmf_io_command(qp, nsid, command, offset / block_size, 333 1, buf, block_size); 334 if (error != 0) { 335 warnc(error, "Failed I/O request"); 336 break; 337 } 338 339 if (command == READ) 340 (void)write(STDOUT_FILENO, buf, todo); 341 342 offset += block_size; 343 length -= todo; 344 } 345 346 free(buf); 347 return (error); 348} 349 350int 351main(int ac, char **av) 352{ 353 const char *transport; 354 char *address, *port; 355 enum rw command; 356 struct nvmf_association_params aparams; 357 struct nvmf_qpair_params qparams; 358 struct nvmf_association *na; 359 struct nvmf_qpair *admin, *io; 360 char hostnqn[NVMF_NQN_MAX_LEN]; 361 uint8_t hostid[16]; 362 enum nvmf_trtype trtype; 363 off_t offset, length; 364 int ch, error; 365 u_int block_size, cntlid, nsid, queues; 366 367 cntlid = NVMF_CNTLID_DYNAMIC; 368 offset = 0; 369 length = 512; 370 nsid = 1; 371 port = NULL; 372 transport = "tcp"; 373 while ((ch = getopt(ac, av, "FGc:gl:n:o:p:t:")) != -1) { 374 switch (ch) { 375 case 'F': 376 flow_control_disable = true; 377 break; 378 case 'G': 379 data_digests = true; 380 break; 381 case 'c': 382 if (strcasecmp(optarg, "dynamic") == 0) 383 cntlid = NVMF_CNTLID_DYNAMIC; 384 else if (strcasecmp(optarg, "static") == 0) 385 cntlid = NVMF_CNTLID_STATIC_ANY; 386 else 387 cntlid = strtoul(optarg, NULL, 0); 388 break; 389 case 'g': 390 header_digests = true; 391 break; 392 case 'l': 393 length = strtoumax(optarg, NULL, 0); 394 break; 395 case 'n': 396 nsid = strtoul(optarg, NULL, 0); 397 break; 398 case 'o': 399 offset = strtoumax(optarg, NULL, 0); 400 break; 401 case 't': 402 transport = optarg; 403 break; 404 default: 405 usage(); 406 } 407 } 408 409 av += optind; 410 ac -= optind; 411 412 if (ac != 3) 413 usage(); 414 415 if (nsid == 0 || nsid >= 0xffffffff) 416 errx(1, "Invalid namespace ID %u", nsid); 417 418 if (strcasecmp(av[0], "read") == 0) 419 command = READ; 420 else if (strcasecmp(av[0], "write") == 0) 421 command = WRITE; 422 else 423 errx(1, "Invalid command %s", av[0]); 424 425 address = av[1]; 426 port = strrchr(address, ':'); 427 if (port == NULL || port[1] == '\0') 428 errx(1, "Invalid address %s", address); 429 *port = '\0'; 430 port++; 431 432 memset(&aparams, 0, sizeof(aparams)); 433 aparams.sq_flow_control = !flow_control_disable; 434 if (strcasecmp(transport, "tcp") == 0) { 435 trtype = NVMF_TRTYPE_TCP; 436 tcp_association_params(&aparams); 437 } else 438 errx(1, "Invalid transport %s", transport); 439 440 error = nvmf_hostid_from_hostuuid(hostid); 441 if (error != 0) 442 errc(1, error, "Failed to generate hostid"); 443 error = nvmf_nqn_from_hostuuid(hostnqn); 444 if (error != 0) 445 errc(1, error, "Failed to generate host NQN"); 446 447 na = nvmf_allocate_association(trtype, false, &aparams); 448 if (na == NULL) 449 err(1, "Failed to create association"); 450 451 memset(&qparams, 0, sizeof(qparams)); 452 tcp_qpair_params(&qparams, true, address, port); 453 454 admin = connect_admin_queue(na, &qparams, hostid, cntlid, hostnqn, 455 av[2]); 456 if (admin == NULL) 457 errx(1, "Failed to create admin queue: %s", 458 nvmf_association_error(na)); 459 460 error = validate_namespace(admin, nsid, &block_size); 461 if (error != 0) { 462 shutdown_controller(admin); 463 nvmf_free_association(na); 464 return (1); 465 } 466 467 error = nvmf_host_request_queues(admin, 1, &queues); 468 if (error != 0) { 469 shutdown_controller(admin); 470 nvmf_free_association(na); 471 errc(1, error, "Failed to request I/O queues"); 472 } 473 474 memset(&qparams, 0, sizeof(qparams)); 475 tcp_qpair_params(&qparams, false, address, port); 476 477 io = nvmf_connect(na, &qparams, 1, info.mqes + 1, hostid, 478 nvmf_cntlid(admin), av[2], hostnqn, 0); 479 if (io == NULL) { 480 warn("Failed to create I/O queue: %s", 481 nvmf_association_error(na)); 482 shutdown_controller(admin); 483 nvmf_free_association(na); 484 return (1); 485 } 486 nvmf_free_association(na); 487 488 error = nvmf_io(io, nsid, block_size, command, offset, length); 489 490 disconnect_queue(io); 491 shutdown_controller(admin); 492 return (error == 0 ? 0 : 1); 493} 494