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 2024 Oxide Computer Company 14 */ 15 16 /* 17 * Test namespace information snapshots. Because devices are all different, we 18 * mostly try to get a device's identify namespace and then compare that to the 19 * fields we have here. 20 */ 21 22 #include <stdlib.h> 23 #include <stdbool.h> 24 #include <err.h> 25 #include <umem.h> 26 27 #include "libnvme_test_common.h" 28 29 static bool 30 ns_info_test_inactive(nvme_ns_info_t *info, uint32_t nsid) 31 { 32 uint8_t guid[16]; 33 bool ret = true; 34 uint64_t val; 35 const nvme_nvm_lba_fmt_t *fmt; 36 const char *bd; 37 38 if (nvme_ns_info_nguid(info, guid)) { 39 warnx("TEST FAILED: ns %u returned a namespace guid in error", 40 nsid); 41 ret = false; 42 } else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) { 43 warnx("TEST FAILED: ns %u nvme_ns_info_nguid() returned " 44 "wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE " 45 "(0x%x)", nsid, nvme_ns_info_errtostr(info, 46 nvme_ns_info_err(info)), nvme_ns_info_err(info), 47 NVME_INFO_ERR_NS_INACTIVE); 48 ret = false; 49 } else { 50 (void) printf("TEST PASSED: ns %u: nvme_ns_info_nguid() " 51 "returned NVME_INFO_ERR_NS_INACTIVE\n", nsid); 52 } 53 54 if (nvme_ns_info_eui64(info, guid)) { 55 warnx("TEST FAILED: ns %u returned a namespace eui64 in error", 56 nsid); 57 ret = false; 58 } else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) { 59 warnx("TEST FAILED: ns %u nvme_ns_info_eui64() returned " 60 "wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE " 61 "(0x%x)", nsid, nvme_ns_info_errtostr(info, 62 nvme_ns_info_err(info)), nvme_ns_info_err(info), 63 NVME_INFO_ERR_NS_INACTIVE); 64 ret = false; 65 } else { 66 (void) printf("TEST PASSED: ns %u: nvme_ns_info_eui64() " 67 "returned NVME_INFO_ERR_NS_INACTIVE\n", nsid); 68 } 69 70 if (nvme_ns_info_size(info, &val)) { 71 warnx("TEST FAILED: ns %u returned a namespace size in error", 72 nsid); 73 ret = false; 74 } else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) { 75 warnx("TEST FAILED: ns %u nvme_ns_info_size() returned " 76 "wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE " 77 "(0x%x)", nsid, nvme_ns_info_errtostr(info, 78 nvme_ns_info_err(info)), nvme_ns_info_err(info), 79 NVME_INFO_ERR_NS_INACTIVE); 80 ret = false; 81 } else { 82 (void) printf("TEST PASSED: ns %u: nvme_ns_info_size() " 83 "returned NVME_INFO_ERR_NS_INACTIVE\n", nsid); 84 } 85 86 if (nvme_ns_info_cap(info, &val)) { 87 warnx("TEST FAILED: ns %u returned a namespace cap in error", 88 nsid); 89 ret = false; 90 } else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) { 91 warnx("TEST FAILED: ns %u nvme_ns_info_cap() returned " 92 "wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE " 93 "(0x%x)", nsid, nvme_ns_info_errtostr(info, 94 nvme_ns_info_err(info)), nvme_ns_info_err(info), 95 NVME_INFO_ERR_NS_INACTIVE); 96 ret = false; 97 } else { 98 (void) printf("TEST PASSED: ns %u: nvme_ns_info_cap() " 99 "returned NVME_INFO_ERR_NS_INACTIVE\n", nsid); 100 } 101 102 if (nvme_ns_info_use(info, &val)) { 103 warnx("TEST FAILED: ns %u returned a namespace use in error", 104 nsid); 105 ret = false; 106 } else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) { 107 warnx("TEST FAILED: ns %u nvme_ns_info_use() returned " 108 "wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE " 109 "(0x%x)", nsid, nvme_ns_info_errtostr(info, 110 nvme_ns_info_err(info)), nvme_ns_info_err(info), 111 NVME_INFO_ERR_NS_INACTIVE); 112 ret = false; 113 } else { 114 (void) printf("TEST PASSED: ns %u: nvme_ns_info_use() " 115 "returned NVME_INFO_ERR_NS_INACTIVE\n", nsid); 116 } 117 118 if (nvme_ns_info_curformat(info, &fmt)) { 119 warnx("TEST FAILED: ns %u returned a current format in error", 120 nsid); 121 ret = false; 122 } else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) { 123 warnx("TEST FAILED: ns %u nvme_ns_info_curformat() returned " 124 "wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE " 125 "(0x%x)", nsid, nvme_ns_info_errtostr(info, 126 nvme_ns_info_err(info)), nvme_ns_info_err(info), 127 NVME_INFO_ERR_NS_INACTIVE); 128 ret = false; 129 } else { 130 (void) printf("TEST PASSED: ns %u: nvme_ns_info_curformat() " 131 "returned NVME_INFO_ERR_NS_INACTIVE\n", nsid); 132 } 133 134 if (nvme_ns_info_format(info, 0, &fmt)) { 135 warnx("TEST FAILED: ns %u returned format 0 in error", 136 nsid); 137 ret = false; 138 } else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_INACTIVE) { 139 warnx("TEST FAILED: ns %u nvme_ns_info_format() returned " 140 "wrong error %s (0x%x), not NVME_INFO_ERR_NS_INACTIVE " 141 "(0x%x)", nsid, nvme_ns_info_errtostr(info, 142 nvme_ns_info_err(info)), nvme_ns_info_err(info), 143 NVME_INFO_ERR_NS_INACTIVE); 144 ret = false; 145 } else { 146 (void) printf("TEST PASSED: ns %u: nvme_ns_info_curformat() " 147 "returned NVME_INFO_ERR_NS_INACTIVE\n", nsid); 148 } 149 150 if (nvme_ns_info_bd_addr(info, &bd)) { 151 warnx("TEST FAILED: ns %u returned a blkdev address in error", 152 nsid); 153 ret = false; 154 } else if (nvme_ns_info_err(info) != NVME_INFO_ERR_NS_NO_BLKDEV) { 155 warnx("TEST FAILED: ns %u nvme_ns_info_bd_addr() returned " 156 "wrong error %s (0x%x), not NVME_INFO_ERR_NS_NO_BLKDEV " 157 "(0x%x)", nsid, nvme_ns_info_errtostr(info, 158 nvme_ns_info_err(info)), nvme_ns_info_err(info), 159 NVME_INFO_ERR_NS_NO_BLKDEV); 160 ret = false; 161 } else { 162 (void) printf("TEST PASSED: ns %u: nvme_ns_info_curformat() " 163 "returned NVME_INFO_ERR_NS_NO_BLKDEV\n", nsid); 164 } 165 166 return (ret); 167 } 168 169 static bool 170 ns_info_test_size(nvme_ns_info_t *info, 171 bool (*func)(nvme_ns_info_t *, uint64_t *), uint64_t exp_size, 172 const char *name, uint32_t nsid) 173 { 174 uint64_t val; 175 176 if (!func(info, &val)) { 177 libnvme_test_ns_info_warn(info, "ns %u nvme_ns_info_%s() " 178 "unexpected failed", nsid, name); 179 return (false); 180 } else if (val != exp_size) { 181 warnx("TEST FAILED: ns %u: nvme_ns_info_%s() value was 0x%" 182 PRIx64 ", but expected 0x%" PRIx64, nsid, name, val, 183 exp_size); 184 return (false); 185 } else { 186 (void) printf("TEST PASSED: ns %u: nvme_ns_info_%s() returned " 187 "correct value\n", nsid, name); 188 return (true); 189 } 190 } 191 192 static bool 193 ns_info_test(nvme_ctrl_t *ctrl, const nvme_version_t *vers, uint32_t nsid) 194 { 195 bool ret = true; 196 nvme_ns_t *ns = NULL; 197 nvme_ns_info_t *info = NULL; 198 nvme_ns_disc_level_t level; 199 const nvme_identify_nsid_t *idns; 200 const nvme_nvm_lba_fmt_t *fmt; 201 uint32_t nfmt; 202 203 /* 204 * We do this to test both ways of taking a snapshot. 205 */ 206 if ((nsid % 2) == 0) { 207 if (!nvme_ns_init(ctrl, nsid, &ns)) { 208 libnvme_test_ctrl_warn(ctrl, "failed to init ns %u", 209 nsid); 210 ret = false; 211 goto done; 212 } 213 214 if (!nvme_ns_info_snap(ns, &info)) { 215 libnvme_test_ctrl_warn(ctrl, "failed to take snapshot " 216 "of ns %u", nsid); 217 ret = false; 218 goto done; 219 } 220 } else { 221 if (!nvme_ctrl_ns_info_snap(ctrl, nsid, &info)) { 222 libnvme_test_ctrl_warn(ctrl, "failed to take snapshot " 223 "of ns %u", nsid); 224 ret = false; 225 goto done; 226 } 227 } 228 229 (void) printf("TEST PASSED: ns %u: successfully got info snapshot\n", 230 nsid); 231 if (nvme_ns_info_nsid(info) != nsid) { 232 warnx("TEST FAILED: nsid %u info snapshot returned wrong " 233 "nsid: %u", nsid, nvme_ns_info_nsid(info)); 234 ret = false; 235 } else { 236 (void) printf("TEST PASSED: ns %u: info snapshot had correct " 237 "nsid\n", nsid); 238 } 239 240 /* 241 * The rest of the information snapshot APIs depend on if the namespace 242 * is active in the controller. If it is not, then we expect each 243 * function to fail. Even if we have an active namespace, this may fail 244 * if a controller isn't of a sufficient version. 245 */ 246 level = nvme_ns_info_level(info); 247 if (level < NVME_NS_DISC_F_ACTIVE) { 248 if (!ns_info_test_inactive(info, nsid)) { 249 ret = false; 250 } 251 goto done; 252 } 253 254 idns = nvme_ns_info_identify(info); 255 256 /* 257 * We don't explicitly test the GUID logic or blkdev address here. That 258 * is done in the ns-disc.c test. 259 */ 260 261 if (!ns_info_test_size(info, nvme_ns_info_size, idns->id_nsize, "size", 262 nsid)) { 263 ret = false; 264 } 265 266 if (!ns_info_test_size(info, nvme_ns_info_cap, idns->id_ncap, "cap", 267 nsid)) { 268 ret = false; 269 } 270 271 if (!ns_info_test_size(info, nvme_ns_info_use, idns->id_nuse, "use", 272 nsid)) { 273 ret = false; 274 } 275 276 if (!nvme_ns_info_curformat(info, &fmt)) { 277 libnvme_test_ns_info_warn(info, "ns %u failed to get current " 278 "format", nsid); 279 ret = false; 280 } else if (nvme_nvm_lba_fmt_id(fmt) != idns->id_flbas.lba_format) { 281 warnx("TEST FAILED: current LBA format 0x%x does not match " 282 "identify namespace 0x%x", nvme_nvm_lba_fmt_id(fmt), 283 idns->id_flbas.lba_format); 284 ret = false; 285 } else { 286 (void) printf("TEST PASSED: ns %u: nvme_ns_info_curformat() " 287 "returned correct format\n", nsid); 288 } 289 290 if (!nvme_ns_info_nformats(info, &nfmt)) { 291 libnvme_test_ns_info_warn(info, "ns %u failed to get number " 292 "of formats", nsid); 293 ret = false; 294 } else if (nfmt != idns->id_nlbaf + 1) { 295 warnx("TEST FAILED: number of LBA formats 0x%x does not match " 296 "identify namespace 0x%x", nvme_nvm_lba_fmt_id(fmt), 297 idns->id_nlbaf + 1); 298 ret = false; 299 } else { 300 (void) printf("TEST PASSED: ns %u: nvme_ns_info_nformats() " 301 "returned correct number of formats\n", nsid); 302 } 303 304 if (nvme_ns_info_format(info, 0x7777, &fmt)) { 305 warnx("TEST FAILED: ns %u erroneously returned info for format " 306 "0x7777", nsid); 307 ret = false; 308 } else if (nvme_ns_info_err(info) != NVME_INFO_ERR_BAD_FMT) { 309 warnx("TEST FAILED: ns %u nvme_ns_info_format() returned " 310 "wrong error %s (0x%x), not NVME_INFO_ERR_BAD_FMT " 311 "(0x%x)", nsid, nvme_ns_info_errtostr(info, 312 nvme_ns_info_err(info)), nvme_ns_info_err(info), 313 NVME_INFO_ERR_BAD_FMT); 314 ret = false; 315 } else { 316 (void) printf("TEST PASSED: ns %u: invalid format id 0x7777 " 317 "correctly rejected\n", nsid); 318 } 319 320 done: 321 nvme_ns_info_free(info); 322 nvme_ns_fini(ns); 323 return (ret); 324 } 325 326 static bool 327 ns_info_bad_snap(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_ns_info_t **infop, 328 nvme_err_t exp_err, const char *desc) 329 { 330 if (nvme_ctrl_ns_info_snap(ctrl, nsid, infop)) { 331 warnx("TEST FAILED: nvme_ctrl_ns_info_snap() erroneously " 332 "passed despite %s", desc); 333 return (false); 334 } else if (nvme_ctrl_err(ctrl) != exp_err) { 335 warnx("TEST FAILED: nvme_ctrl_ns_info_snap() returned " 336 "wrong error %s (0x%x), not %s (0x%x)", 337 nvme_ctrl_errtostr(ctrl, nvme_ctrl_err(ctrl)), 338 nvme_ctrl_err(ctrl), nvme_ctrl_errtostr(ctrl, 339 exp_err), exp_err); 340 341 return (false); 342 } else { 343 (void) printf("TEST PASSED: nvme_ctrl_ns_info_snap() failed " 344 "correctly for %s\n", desc); 345 return (true); 346 } 347 } 348 349 int 350 main(void) 351 { 352 int ret = EXIT_SUCCESS; 353 nvme_t *nvme; 354 nvme_ctrl_t *ctrl; 355 nvme_ctrl_info_t *info; 356 nvme_ns_info_t *ns_info; 357 uint32_t nns; 358 const nvme_version_t *vers; 359 360 libnvme_test_init(&nvme, &ctrl); 361 362 if (!nvme_ctrl_info_snap(ctrl, &info)) { 363 libnvme_test_ctrl_fatal(ctrl, "failed to take information " 364 "snapshot"); 365 } 366 367 nns = nvme_ctrl_info_nns(info); 368 if (nns == 0) { 369 errx(EXIT_FAILURE, "TEST FAILED: somehow discovered 0 " 370 "namespaces"); 371 } 372 373 vers = nvme_ctrl_info_version(info); 374 for (uint32_t i = 1; i <= nns; i++) { 375 if (!ns_info_test(ctrl, vers, i)) { 376 ret = EXIT_FAILURE; 377 } 378 } 379 380 /* 381 * Explicitly verify a few failures of namespace information snapshots. 382 */ 383 if (!ns_info_bad_snap(ctrl, NVME_NSID_BCAST, &ns_info, 384 NVME_ERR_NS_RANGE, "invalid nsid")) { 385 ret = EXIT_FAILURE; 386 } 387 388 if (!ns_info_bad_snap(ctrl, 1, NULL, NVME_ERR_BAD_PTR, 389 "invalid output pointer")) { 390 ret = EXIT_FAILURE; 391 } 392 393 umem_setmtbf(1); 394 if (!ns_info_bad_snap(ctrl, 1, &ns_info, NVME_ERR_NO_MEM, 395 "no memory")) { 396 ret = EXIT_FAILURE; 397 } 398 umem_setmtbf(0); 399 400 if (ret == EXIT_SUCCESS) { 401 (void) printf("All tests passed successfully\n"); 402 } 403 404 nvme_ctrl_info_free(info); 405 nvme_ctrl_fini(ctrl); 406 nvme_fini(nvme); 407 return (ret); 408 } 409