xref: /illumos-gate/usr/src/test/nvme-tests/tests/libnvme/libnvme_test_common.c (revision f5f0964ce91892f7482efc86903b0ec7c7b6ba66)
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 *
_umem_debug_init(void)32 _umem_debug_init(void)
33 {
34 	return ("default,verbose");
35 }
36 
37 const char *
_umem_logging_init(void)38 _umem_logging_init(void)
39 {
40 	return ("fail,contents");
41 }
42 
43 static void
libnvme_test_hdl_vwarn(nvme_t * nvme,const char * fmt,va_list ap)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
libnvme_test_ctrl_vwarn(nvme_ctrl_t * ctrl,const char * fmt,va_list ap)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
libnvme_test_ctrl_info_vwarn(nvme_ctrl_info_t * info,const char * fmt,va_list ap)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
libnvme_test_ns_info_vwarn(nvme_ns_info_t * info,const char * fmt,va_list ap)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
libnvme_test_hdl_warn(nvme_t * nvme,const char * fmt,...)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
libnvme_test_hdl_fatal(nvme_t * nvme,const char * fmt,...)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
libnvme_test_ctrl_warn(nvme_ctrl_t * ctrl,const char * fmt,...)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
libnvme_test_ctrl_fatal(nvme_ctrl_t * ctrl,const char * fmt,...)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
libnvme_test_ctrl_info_warn(nvme_ctrl_info_t * info,const char * fmt,...)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
libnvme_test_ns_info_warn(nvme_ns_info_t * info,const char * fmt,...)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
libnvme_test_ctrl_info_fatal(nvme_ctrl_info_t * info,const char * fmt,...)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
libnvme_test_init(nvme_t ** nvmep,nvme_ctrl_t ** ctrlp)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
libnvme_test_lbaf(nvme_ctrl_info_t * info,uint32_t size,uint32_t * lbap)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
libnvme_test_ns_create(nvme_ctrl_t * ctrl,uint64_t size,uint32_t lbaf,uint32_t * nsid,nvme_err_t * err)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
libnvme_test_ns_delete(nvme_ctrl_t * ctrl,uint32_t nsid,nvme_err_t * err)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
libnvme_test_ctrl_attach(nvme_ctrl_t * ctrl,uint32_t nsid,uint32_t type,nvme_err_t * err)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
libnvme_test_ns_blkdev(nvme_ctrl_t * ctrl,uint32_t nsid,bool attach,nvme_err_t * err)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
libnvme_test_setup_ns(nvme_ctrl_t * ctrl,nvme_ns_disc_level_t level,uint32_t nsid,uint32_t lbaf)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
libnvme_test_ctrl_err(nvme_ctrl_t * ctrl,uint32_t exp_sct,uint32_t exp_sc,const char * desc)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