1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2025 Oxide Computer Company 14 */ 15 16 /* 17 * Common utilities for libnvme tests. 18 */ 19 20 #include <err.h> 21 #include <stdlib.h> 22 #include <time.h> 23 24 #include "libnvme_test_common.h" 25 26 /* 27 * For any test linked against libumem, ensure that umem debugging is enabled by 28 * default. Many tests use umem_setmtbf() and we need to make sure there is no 29 * per-thread cache. 30 */ 31 const char * 32 _umem_debug_init(void) 33 { 34 return ("default,verbose"); 35 } 36 37 const char * 38 _umem_logging_init(void) 39 { 40 return ("fail,contents"); 41 } 42 43 static void 44 libnvme_test_hdl_vwarn(nvme_t *nvme, const char *fmt, va_list ap) 45 { 46 (void) fprintf(stderr, "TEST FAILED: "); 47 (void) vfprintf(stderr, fmt, ap); 48 (void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n", 49 nvme_errmsg(nvme), nvme_errtostr(nvme, nvme_err(nvme)), 50 nvme_err(nvme), nvme_syserr(nvme)); 51 } 52 53 static void 54 libnvme_test_ctrl_vwarn(nvme_ctrl_t *ctrl, const char *fmt, va_list ap) 55 { 56 (void) fprintf(stderr, "TEST FAILED: "); 57 (void) vfprintf(stderr, fmt, ap); 58 (void) fprintf(stderr, ": %s: %s (libnvme: 0x%x, sys: %d)\n", 59 nvme_ctrl_errmsg(ctrl), nvme_ctrl_errtostr(ctrl, 60 nvme_ctrl_err(ctrl)), nvme_ctrl_err(ctrl), nvme_ctrl_syserr(ctrl)); 61 } 62 63 static void 64 libnvme_test_ctrl_info_vwarn(nvme_ctrl_info_t *info, const char *fmt, 65 va_list ap) 66 { 67 (void) fprintf(stderr, "TEST FAILED: "); 68 (void) vfprintf(stderr, fmt, ap); 69 (void) fprintf(stderr, ": %s: %s (libnvme info: 0x%x, sys: %d)\n", 70 nvme_ctrl_info_errmsg(info), nvme_ctrl_info_errtostr(info, 71 nvme_ctrl_info_err(info)), nvme_ctrl_info_err(info), 72 nvme_ctrl_info_syserr(info)); 73 } 74 75 static void 76 libnvme_test_ns_info_vwarn(nvme_ns_info_t *info, const char *fmt, 77 va_list ap) 78 { 79 (void) fprintf(stderr, "TEST FAILED: "); 80 (void) vfprintf(stderr, fmt, ap); 81 (void) fprintf(stderr, ": %s: %s (libnvme info: 0x%x, sys: %d)\n", 82 nvme_ns_info_errmsg(info), nvme_ns_info_errtostr(info, 83 nvme_ns_info_err(info)), nvme_ns_info_err(info), 84 nvme_ns_info_syserr(info)); 85 } 86 87 void 88 libnvme_test_hdl_warn(nvme_t *nvme, const char *fmt, ...) 89 { 90 va_list ap; 91 92 va_start(ap, fmt); 93 libnvme_test_hdl_vwarn(nvme, fmt, ap); 94 va_end(ap); 95 } 96 97 void __NORETURN 98 libnvme_test_hdl_fatal(nvme_t *nvme, const char *fmt, ...) 99 { 100 va_list ap; 101 102 va_start(ap, fmt); 103 libnvme_test_hdl_vwarn(nvme, fmt, ap); 104 va_end(ap); 105 106 exit(EXIT_FAILURE); 107 } 108 109 void 110 libnvme_test_ctrl_warn(nvme_ctrl_t *ctrl, const char *fmt, ...) 111 { 112 va_list ap; 113 114 va_start(ap, fmt); 115 libnvme_test_ctrl_vwarn(ctrl, fmt, ap); 116 va_end(ap); 117 } 118 119 void __NORETURN 120 libnvme_test_ctrl_fatal(nvme_ctrl_t *ctrl, const char *fmt, ...) 121 { 122 va_list ap; 123 124 va_start(ap, fmt); 125 libnvme_test_ctrl_vwarn(ctrl, fmt, ap); 126 va_end(ap); 127 128 exit(EXIT_FAILURE); 129 } 130 131 void 132 libnvme_test_ctrl_info_warn(nvme_ctrl_info_t *info, const char *fmt, ...) 133 { 134 va_list ap; 135 136 va_start(ap, fmt); 137 libnvme_test_ctrl_info_vwarn(info, fmt, ap); 138 va_end(ap); 139 } 140 141 void 142 libnvme_test_ns_info_warn(nvme_ns_info_t *info, const char *fmt, ...) 143 { 144 va_list ap; 145 146 va_start(ap, fmt); 147 libnvme_test_ns_info_vwarn(info, fmt, ap); 148 va_end(ap); 149 } 150 151 void __NORETURN 152 libnvme_test_ctrl_info_fatal(nvme_ctrl_info_t *info, const char *fmt, ...) 153 { 154 va_list ap; 155 156 va_start(ap, fmt); 157 libnvme_test_ctrl_info_vwarn(info, fmt, ap); 158 va_end(ap); 159 160 exit(EXIT_FAILURE); 161 } 162 163 void 164 libnvme_test_init(nvme_t **nvmep, nvme_ctrl_t **ctrlp) 165 { 166 nvme_t *nvme; 167 nvme_ctrl_t *ctrl; 168 const char *dev; 169 170 nvme = nvme_init(); 171 if (nvme == NULL) { 172 err(EXIT_FAILURE, "failed to create libnvme handle"); 173 } 174 175 dev = getenv(NVME_TEST_DEV_ENVVAR); 176 if (dev == NULL) { 177 errx(EXIT_FAILURE, "cannot run test, missing required NVMe " 178 "device, please set the %s environment variable", 179 NVME_TEST_DEV_ENVVAR); 180 } 181 182 if (!nvme_ctrl_ns_init(nvme, dev, &ctrl, NULL)) { 183 libnvme_test_hdl_fatal(nvme, "failed to open %s", dev); 184 } 185 186 *nvmep = nvme; 187 *ctrlp = ctrl; 188 } 189 190 bool 191 libnvme_test_lbaf(nvme_ctrl_info_t *info, uint32_t size, uint32_t *lbap) 192 { 193 uint32_t nfmts, fmt = UINT32_MAX, fmt_rp = UINT32_MAX; 194 195 nfmts = nvme_ctrl_info_nformats(info); 196 if (nfmts == 0) { 197 warnx("no LBA formats found on device"); 198 return (false); 199 } 200 201 for (uint32_t i = 0; i < nfmts; i++) { 202 const nvme_nvm_lba_fmt_t *lba; 203 uint32_t rp; 204 205 if (!nvme_ctrl_info_format(info, i, &lba)) { 206 libnvme_test_ctrl_info_warn(info, "failed to get LBA " 207 "format %u", i); 208 continue; 209 } 210 211 if (nvme_nvm_lba_fmt_meta_size(lba) != 0) 212 continue; 213 214 if (nvme_nvm_lba_fmt_data_size(lba) != size) 215 continue; 216 217 rp = nvme_nvm_lba_fmt_rel_perf(lba); 218 if (rp < fmt_rp) { 219 fmt = i; 220 fmt_rp = rp; 221 } 222 } 223 224 if (fmt != UINT32_MAX) { 225 *lbap = fmt; 226 return (true); 227 } 228 229 return (false); 230 } 231 232 bool 233 libnvme_test_ns_create(nvme_ctrl_t *ctrl, uint64_t size, uint32_t lbaf, 234 uint32_t *nsid, nvme_err_t *err) 235 { 236 nvme_ns_create_req_t *req; 237 bool ret = false; 238 239 if (!nvme_ns_create_req_init_by_csi(ctrl, NVME_CSI_NVM, &req)) { 240 libnvme_test_ctrl_warn(ctrl, "failed to initialize namespace " 241 "create request"); 242 goto done; 243 } 244 245 if (!nvme_ns_create_req_set_flbas(req, lbaf)) { 246 libnvme_test_ctrl_warn(ctrl, "failed to set flbas for " 247 "namespace create request to 0x%x", lbaf); 248 goto done; 249 } 250 251 if (!nvme_ns_create_req_set_nsze(req, size)) { 252 libnvme_test_ctrl_warn(ctrl, "failed to set nsze for " 253 "namespace create request to 0x%" PRIx64, size); 254 goto done; 255 } 256 257 if (!nvme_ns_create_req_set_ncap(req, size)) { 258 libnvme_test_ctrl_warn(ctrl, "failed to set ncap for " 259 "namespace create request to 0x%" PRIx64, size); 260 goto done; 261 } 262 263 if (!nvme_ns_create_req_set_nmic(req, NVME_NS_NMIC_T_NONE)) { 264 libnvme_test_ctrl_warn(ctrl, "failed to set nmic for " 265 "namespace create request"); 266 goto done; 267 } 268 269 ret = nvme_ns_create_req_exec(req); 270 if (err != NULL) { 271 *err = nvme_ctrl_err(ctrl); 272 ret = true; 273 if (*err != NVME_ERR_OK) 274 goto done; 275 } else if (!ret) { 276 libnvme_test_ctrl_warn(ctrl, "failed to execute namespace " 277 "create request"); 278 goto done; 279 } 280 281 if (nsid != NULL) { 282 ret = nvme_ns_create_req_get_nsid(req, nsid); 283 if (!ret) { 284 libnvme_test_ctrl_warn(ctrl, "failed to retrieve " 285 "created namespace id"); 286 } 287 } 288 289 done: 290 nvme_ns_create_req_fini(req); 291 return (ret); 292 } 293 294 bool 295 libnvme_test_ns_delete(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_err_t *err) 296 { 297 bool ret = true; 298 nvme_ns_delete_req_t *req; 299 300 if (!nvme_ns_delete_req_init(ctrl, &req)) { 301 libnvme_test_ctrl_warn(ctrl, "failed to initialize namespace " 302 "delete request for namespace %u", nsid); 303 return (false); 304 } 305 306 if (!nvme_ns_delete_req_set_nsid(req, nsid)) { 307 libnvme_test_ctrl_warn(ctrl, "failed to set namespace for " 308 "ns %u delete request", nsid); 309 ret = false; 310 goto done; 311 } 312 313 ret = nvme_ns_delete_req_exec(req); 314 if (err != NULL) { 315 *err = nvme_ctrl_err(ctrl); 316 ret = true; 317 } else if (!ret) { 318 libnvme_test_ctrl_warn(ctrl, "failed to execute namespace " 319 "delete request for namespace %u", nsid); 320 } 321 322 done: 323 nvme_ns_delete_req_fini(req); 324 return (ret); 325 } 326 327 bool 328 libnvme_test_ctrl_attach(nvme_ctrl_t *ctrl, uint32_t nsid, uint32_t type, 329 nvme_err_t *err) 330 { 331 nvme_ns_attach_req_t *req = NULL; 332 const char *desc; 333 bool ret = true; 334 335 VERIFY(type == NVME_NS_ATTACH_CTRL_DETACH || 336 type == NVME_NS_ATTACH_CTRL_ATTACH); 337 if (type == NVME_NS_ATTACH_CTRL_DETACH) { 338 desc = "detach"; 339 } else { 340 desc = "attach"; 341 } 342 343 if (!nvme_ns_attach_req_init_by_sel(ctrl, type, &req)) { 344 libnvme_test_ctrl_warn(ctrl, "failed to initialize controller " 345 "%s request for ns 0x%x", desc, nsid); 346 ret = false; 347 goto done; 348 } 349 350 if (!nvme_ns_attach_req_set_nsid(req, nsid)) { 351 libnvme_test_ctrl_warn(ctrl, "failed to set namespace for " 352 "ns 0x%x controller %s request", nsid, desc); 353 ret = false; 354 goto done; 355 } 356 357 if (!nvme_ns_attach_req_set_ctrlid_self(req)) { 358 libnvme_test_ctrl_warn(ctrl, "failed to set controller for " 359 "ns 0x%x controller %s request", nsid, desc); 360 ret = false; 361 goto done; 362 363 } 364 365 ret = nvme_ns_attach_req_exec(req); 366 if (err != NULL) { 367 *err = nvme_ctrl_err(ctrl); 368 ret = true; 369 } else if (!ret) { 370 libnvme_test_ctrl_warn(ctrl, "failed to execute controller " 371 "%s request for ns 0x%x", desc, nsid); 372 } 373 374 done: 375 nvme_ns_attach_req_fini(req); 376 return (ret); 377 } 378 379 bool 380 libnvme_test_ns_blkdev(nvme_ctrl_t *ctrl, uint32_t nsid, bool attach, 381 nvme_err_t *err) 382 { 383 nvme_ns_t *ns; 384 bool ret; 385 386 if (!nvme_ns_init(ctrl, nsid, &ns)) { 387 libnvme_test_ctrl_warn(ctrl, "failed to initialize namespace " 388 "%u", nsid); 389 return (false); 390 } 391 392 if (attach) { 393 ret = nvme_ns_bd_attach(ns); 394 } else { 395 /* 396 * Occasionally we've seen a race on blkdev detach during tests 397 * where we have what is most likely a transient reference. If 398 * we get that the kernel failed to detach, try up to 5 times 399 * and wait 10ms between attempts to just smooth it over. 400 */ 401 for (uint32_t i = 0; i < 5; i++) { 402 struct timespec t; 403 404 ret = nvme_ns_bd_detach(ns); 405 if (ret || nvme_ctrl_err(ctrl) != 406 NVME_ERR_DETACH_KERN) { 407 break; 408 } 409 410 t.tv_sec = 0; 411 t.tv_nsec = MSEC2NSEC(10); 412 (void) nanosleep(&t, NULL); 413 } 414 } 415 416 if (err != NULL) { 417 *err = nvme_ctrl_err(ctrl); 418 ret = true; 419 } else if (!ret) { 420 libnvme_test_ctrl_warn(ctrl, "failed to %s namespace %u", 421 attach ? "attach" : "detach", nsid); 422 } 423 nvme_ns_fini(ns); 424 425 return (ret); 426 } 427 428 /* 429 * Non-fatally ensure that the requested NS is in the state that is asked for. 430 * We assume that the caller already has a lock on the device. 431 */ 432 bool 433 libnvme_test_setup_ns(nvme_ctrl_t *ctrl, nvme_ns_disc_level_t level, 434 uint32_t nsid, uint32_t lbaf) 435 { 436 nvme_ns_info_t *info; 437 nvme_ns_disc_level_t cur; 438 uint32_t nsid_out; 439 uint64_t create_size = NVME_TEST_NS_SIZE / NVME_TEST_LBA_SIZE; 440 441 if (!nvme_ctrl_ns_info_snap(ctrl, nsid, &info)) { 442 libnvme_test_ctrl_warn(ctrl, "failed to take snapshot of " 443 "namespace %u", nsid); 444 return (false); 445 } 446 cur = nvme_ns_info_level(info); 447 nvme_ns_info_free(info); 448 449 /* 450 * Whether we end up active or not ignored is basically dependent on 451 * device data. Tests that care ultimately need to check themselves. 452 */ 453 if (level == NVME_NS_DISC_F_NOT_IGNORED) 454 level = NVME_NS_DISC_F_ACTIVE; 455 456 while (cur > level) { 457 switch (cur) { 458 case NVME_NS_DISC_F_BLKDEV: 459 if (!libnvme_test_ns_blkdev(ctrl, nsid, false, NULL)) { 460 return (false); 461 } 462 cur = NVME_NS_DISC_F_NOT_IGNORED; 463 break; 464 case NVME_NS_DISC_F_NOT_IGNORED: 465 case NVME_NS_DISC_F_ACTIVE: 466 if (!libnvme_test_ctrl_attach(ctrl, nsid, 467 NVME_NS_ATTACH_CTRL_DETACH, NULL)) { 468 return (false); 469 } 470 cur = NVME_NS_DISC_F_ALLOCATED; 471 break; 472 case NVME_NS_DISC_F_ALLOCATED: 473 if (!libnvme_test_ns_delete(ctrl, nsid, NULL)) { 474 return (false); 475 } 476 cur = NVME_NS_DISC_F_ALL; 477 break; 478 479 case NVME_NS_DISC_F_ALL: 480 abort(); 481 } 482 } 483 484 while (cur < level) { 485 switch (cur) { 486 case NVME_NS_DISC_F_BLKDEV: 487 abort(); 488 case NVME_NS_DISC_F_NOT_IGNORED: 489 case NVME_NS_DISC_F_ACTIVE: 490 if (!libnvme_test_ns_blkdev(ctrl, nsid, true, NULL)) { 491 return (false); 492 } 493 cur = NVME_NS_DISC_F_BLKDEV; 494 break; 495 case NVME_NS_DISC_F_ALLOCATED: 496 if (!libnvme_test_ctrl_attach(ctrl, nsid, 497 NVME_NS_ATTACH_CTRL_ATTACH, NULL)) { 498 return (false); 499 } 500 cur = NVME_NS_DISC_F_ACTIVE; 501 break; 502 case NVME_NS_DISC_F_ALL: 503 if (!libnvme_test_ns_create(ctrl, create_size, lbaf, 504 &nsid_out, NULL)) { 505 return (false); 506 } 507 508 if (nsid_out != nsid) { 509 warnx("namespace creation resulted in NSID " 510 "%u, but expected %u to be created", nsid, 511 nsid_out); 512 return (false); 513 } 514 515 cur = NVME_NS_DISC_F_ALLOCATED; 516 break; 517 } 518 } 519 520 return (true); 521 } 522 523 bool 524 libnvme_test_ctrl_err(nvme_ctrl_t *ctrl, uint32_t exp_sct, uint32_t exp_sc, 525 const char *desc) 526 { 527 uint32_t sct, sc; 528 nvme_err_t err = nvme_ctrl_err(ctrl); 529 530 if (err != NVME_ERR_CONTROLLER) { 531 warnx("TEST FAILED: %s: got wrong error: found " 532 "%s (0x%x), not NVME_ERR_CONTROLLER (0x%x)", 533 desc, nvme_ctrl_errtostr(ctrl, err), err, 534 NVME_ERR_CONTROLLER); 535 return (false); 536 } 537 538 nvme_ctrl_deverr(ctrl, &sct, &sc); 539 if (sct != exp_sct && sc != exp_sc) { 540 warnx("TEST FAILED: %s: got incorrect controller error: found " 541 "%s/%s (0x%x/0x%x), but expected %s/%s (0x%x/0x%x)", desc, 542 nvme_scttostr(ctrl, sct), 543 nvme_sctostr(ctrl, NVME_CSI_NVM, sct, sc), sct, sc, 544 nvme_scttostr(ctrl, exp_sct), 545 nvme_sctostr(ctrl, NVME_CSI_NVM, exp_sct, exp_sc), exp_sct, 546 exp_sc); 547 return (false); 548 } 549 550 (void) printf("TEST PASSED: %s: got correct controller error\n", desc); 551 return (true); 552 } 553