1*533affcbSRobert Mustacchi /*
2*533affcbSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*533affcbSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*533affcbSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*533affcbSRobert Mustacchi  * 1.0 of the CDDL.
6*533affcbSRobert Mustacchi  *
7*533affcbSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*533affcbSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*533affcbSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*533affcbSRobert Mustacchi  */
11*533affcbSRobert Mustacchi 
12*533affcbSRobert Mustacchi /*
13*533affcbSRobert Mustacchi  * Copyright 2024 Oxide Computer Company
14*533affcbSRobert Mustacchi  */
15*533affcbSRobert Mustacchi 
16*533affcbSRobert Mustacchi /*
17*533affcbSRobert Mustacchi  * Iterate over the namespaces and ensure that the information we get in the
18*533affcbSRobert Mustacchi  * discovery is the same as when we take a snapshot. To ensure that nothing
19*533affcbSRobert Mustacchi  * changes out from under us, we take a controller write lock which will ensure
20*533affcbSRobert Mustacchi  * that no modifications can occur.
21*533affcbSRobert Mustacchi  *
22*533affcbSRobert Mustacchi  * In addition, we want to test that discovery filters work so we first go
23*533affcbSRobert Mustacchi  * through and count the different levels.
24*533affcbSRobert Mustacchi  */
25*533affcbSRobert Mustacchi 
26*533affcbSRobert Mustacchi #include <err.h>
27*533affcbSRobert Mustacchi #include <string.h>
28*533affcbSRobert Mustacchi #include <umem.h>
29*533affcbSRobert Mustacchi 
30*533affcbSRobert Mustacchi #include "libnvme_test_common.h"
31*533affcbSRobert Mustacchi 
32*533affcbSRobert Mustacchi static bool
ns_disc_count_cb(nvme_ctrl_t * ctrl,const nvme_ns_disc_t * disc,void * arg)33*533affcbSRobert Mustacchi ns_disc_count_cb(nvme_ctrl_t *ctrl, const nvme_ns_disc_t *disc, void *arg)
34*533affcbSRobert Mustacchi {
35*533affcbSRobert Mustacchi 	uint32_t *valp = arg;
36*533affcbSRobert Mustacchi 	*valp = *valp + 1;
37*533affcbSRobert Mustacchi 	return (true);
38*533affcbSRobert Mustacchi }
39*533affcbSRobert Mustacchi 
40*533affcbSRobert Mustacchi static bool
ns_disc_count(nvme_ctrl_t * ctrl,nvme_ns_disc_level_t level,uint32_t exp)41*533affcbSRobert Mustacchi ns_disc_count(nvme_ctrl_t *ctrl, nvme_ns_disc_level_t level, uint32_t exp)
42*533affcbSRobert Mustacchi {
43*533affcbSRobert Mustacchi 	uint32_t count = 0;
44*533affcbSRobert Mustacchi 
45*533affcbSRobert Mustacchi 	if (!nvme_ns_discover(ctrl, level, ns_disc_count_cb, &count)) {
46*533affcbSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to discover at level %u",
47*533affcbSRobert Mustacchi 		    level);
48*533affcbSRobert Mustacchi 		return (false);
49*533affcbSRobert Mustacchi 	} else if (count != exp) {
50*533affcbSRobert Mustacchi 		warnx("TEST FAILED: ns discovery level %u found 0x%x "
51*533affcbSRobert Mustacchi 		    "namespaces, but expected 0x%x", level, count, exp);
52*533affcbSRobert Mustacchi 		return (false);
53*533affcbSRobert Mustacchi 	} else {
54*533affcbSRobert Mustacchi 		(void) printf("TEST PASSED: ns discovery level %u had correct "
55*533affcbSRobert Mustacchi 		    "count (0x%x)\n", level, exp);
56*533affcbSRobert Mustacchi 		return (true);
57*533affcbSRobert Mustacchi 	}
58*533affcbSRobert Mustacchi }
59*533affcbSRobert Mustacchi 
60*533affcbSRobert Mustacchi static bool
ns_disc_blkdev_cb(nvme_ctrl_t * ctrl,const nvme_ns_disc_t * disc,void * arg)61*533affcbSRobert Mustacchi ns_disc_blkdev_cb(nvme_ctrl_t *ctrl, const nvme_ns_disc_t *disc, void *arg)
62*533affcbSRobert Mustacchi {
63*533affcbSRobert Mustacchi 	int *ret = arg;
64*533affcbSRobert Mustacchi 	nvme_ns_info_t *info;
65*533affcbSRobert Mustacchi 	const uint32_t nsid = nvme_ns_disc_nsid(disc);
66*533affcbSRobert Mustacchi 	const char *addr;
67*533affcbSRobert Mustacchi 
68*533affcbSRobert Mustacchi 	if (nvme_ns_disc_level(disc) < NVME_NS_DISC_F_BLKDEV) {
69*533affcbSRobert Mustacchi 		warnx("TEST FAILED: ns %u has level %u, but filtering on "
70*533affcbSRobert Mustacchi 		    "blkdev (%u)", nsid, nvme_ns_disc_level(disc),
71*533affcbSRobert Mustacchi 		    NVME_NS_DISC_F_BLKDEV);
72*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
73*533affcbSRobert Mustacchi 		return (true);
74*533affcbSRobert Mustacchi 	}
75*533affcbSRobert Mustacchi 
76*533affcbSRobert Mustacchi 	if (!nvme_ctrl_ns_info_snap(ctrl, nsid, &info)) {
77*533affcbSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to get info snapshot for "
78*533affcbSRobert Mustacchi 		    "nsid %u", nsid);
79*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
80*533affcbSRobert Mustacchi 		return (true);
81*533affcbSRobert Mustacchi 	}
82*533affcbSRobert Mustacchi 
83*533affcbSRobert Mustacchi 	if (!nvme_ns_info_bd_addr(info, &addr)) {
84*533affcbSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to get bd addr for nsid "
85*533affcbSRobert Mustacchi 		    "%u", nsid);
86*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
87*533affcbSRobert Mustacchi 	} else if (addr[0] == '\0') {
88*533affcbSRobert Mustacchi 		warnx("TEST FAILED: nsid %u has invalid bd addr", nsid);
89*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
90*533affcbSRobert Mustacchi 	} else {
91*533affcbSRobert Mustacchi 		(void) printf("TEST PASSED: nsid %u bd addr valid\n", nsid);
92*533affcbSRobert Mustacchi 	}
93*533affcbSRobert Mustacchi 
94*533affcbSRobert Mustacchi 	nvme_ns_info_free(info);
95*533affcbSRobert Mustacchi 	return (true);
96*533affcbSRobert Mustacchi }
97*533affcbSRobert Mustacchi 
98*533affcbSRobert Mustacchi static bool
ns_disc_guids_cb(nvme_ctrl_t * ctrl,const nvme_ns_disc_t * disc,void * arg)99*533affcbSRobert Mustacchi ns_disc_guids_cb(nvme_ctrl_t *ctrl, const nvme_ns_disc_t *disc, void *arg)
100*533affcbSRobert Mustacchi {
101*533affcbSRobert Mustacchi 	int *ret = arg;
102*533affcbSRobert Mustacchi 	nvme_ns_info_t *info;
103*533affcbSRobert Mustacchi 	const uint32_t nsid = nvme_ns_disc_nsid(disc);
104*533affcbSRobert Mustacchi 	const nvme_ns_disc_flags_t flags = nvme_ns_disc_flags(disc);
105*533affcbSRobert Mustacchi 	uint8_t id[16];
106*533affcbSRobert Mustacchi 	bool bret;
107*533affcbSRobert Mustacchi 
108*533affcbSRobert Mustacchi 	if (nvme_ns_disc_level(disc) < NVME_NS_DISC_F_ACTIVE) {
109*533affcbSRobert Mustacchi 		warnx("TEST FAILED: ns %u has level %u, but filtering on "
110*533affcbSRobert Mustacchi 		    "active (%u)", nsid, nvme_ns_disc_level(disc),
111*533affcbSRobert Mustacchi 		    NVME_NS_DISC_F_ACTIVE);
112*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
113*533affcbSRobert Mustacchi 		return (true);
114*533affcbSRobert Mustacchi 	}
115*533affcbSRobert Mustacchi 
116*533affcbSRobert Mustacchi 	if (!nvme_ctrl_ns_info_snap(ctrl, nsid, &info)) {
117*533affcbSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to get info snapshot for "
118*533affcbSRobert Mustacchi 		    "nsid %u", nsid);
119*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
120*533affcbSRobert Mustacchi 		return (true);
121*533affcbSRobert Mustacchi 	}
122*533affcbSRobert Mustacchi 
123*533affcbSRobert Mustacchi 	bret = nvme_ns_info_eui64(info, id);
124*533affcbSRobert Mustacchi 	if (bret != ((flags & NVME_NS_DISC_F_EUI64_VALID) != 0)) {
125*533affcbSRobert Mustacchi 		warnx("TEST FAILED: nvme_ns_info_eui64() returned %s, but "
126*533affcbSRobert Mustacchi 		    "expected %s from discovery information for nsid %u",
127*533affcbSRobert Mustacchi 		    bret ? "true" : "false",
128*533affcbSRobert Mustacchi 		    (flags & NVME_NS_DISC_F_EUI64_VALID) != 0 ? "true" :
129*533affcbSRobert Mustacchi 		    "false", nsid);
130*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
131*533affcbSRobert Mustacchi 	} else {
132*533affcbSRobert Mustacchi 		(void) printf("TEST PASSED: namespace snapshot and discovery "
133*533affcbSRobert Mustacchi 		    "agreed on EUI64 presence for nsid %u\n", nsid);
134*533affcbSRobert Mustacchi 	}
135*533affcbSRobert Mustacchi 
136*533affcbSRobert Mustacchi 	if (bret) {
137*533affcbSRobert Mustacchi 		const uint8_t *eui64 = nvme_ns_disc_eui64(disc);
138*533affcbSRobert Mustacchi 		const uint8_t zero[8] = { 0 };
139*533affcbSRobert Mustacchi 
140*533affcbSRobert Mustacchi 		if (memcmp(eui64, id, sizeof (zero)) != 0) {
141*533affcbSRobert Mustacchi 			warnx("TEST FAILED: EUI64 differs between "
142*533affcbSRobert Mustacchi 			    "discovery and info snapshot for nsid %u", nsid);
143*533affcbSRobert Mustacchi 			*ret = EXIT_FAILURE;
144*533affcbSRobert Mustacchi 		} else {
145*533affcbSRobert Mustacchi 			(void) printf("TEST PASSED: EUI64 equal between "
146*533affcbSRobert Mustacchi 			    "discovery and info snapshot for nsid %u\n", nsid);
147*533affcbSRobert Mustacchi 		}
148*533affcbSRobert Mustacchi 
149*533affcbSRobert Mustacchi 		if (memcmp(id, zero, sizeof (zero)) == 0) {
150*533affcbSRobert Mustacchi 			warnx("TEST FAILED: Found invalid zero EUI64 for nsid "
151*533affcbSRobert Mustacchi 			    "%u", nsid);
152*533affcbSRobert Mustacchi 			*ret = EXIT_FAILURE;
153*533affcbSRobert Mustacchi 		} else {
154*533affcbSRobert Mustacchi 			(void) printf("TEST PASSED: EUI64 is non-zero for "
155*533affcbSRobert Mustacchi 			    "nsid %u\n", nsid);
156*533affcbSRobert Mustacchi 		}
157*533affcbSRobert Mustacchi 	} else {
158*533affcbSRobert Mustacchi 		if (nvme_ns_disc_eui64(disc) != NULL) {
159*533affcbSRobert Mustacchi 			warnx("TEST FAILED: discovery EUI64 was valid, but "
160*533affcbSRobert Mustacchi 			    "should be NULL for nsid %u", nsid);
161*533affcbSRobert Mustacchi 			*ret = EXIT_FAILURE;
162*533affcbSRobert Mustacchi 		} else {
163*533affcbSRobert Mustacchi 			(void) printf("TEST PASSED: discovery EUI64 correctly "
164*533affcbSRobert Mustacchi 			    "returned NULL for nsid %u\n", nsid);
165*533affcbSRobert Mustacchi 		}
166*533affcbSRobert Mustacchi 
167*533affcbSRobert Mustacchi 		switch (nvme_ns_info_err(info)) {
168*533affcbSRobert Mustacchi 		case NVME_INFO_ERR_VERSION:
169*533affcbSRobert Mustacchi 		case NVME_INFO_ERR_MISSING_CAP:
170*533affcbSRobert Mustacchi 			(void) printf("TEST PASSED: nvme_ns_info_eui64() "
171*533affcbSRobert Mustacchi 			    "returned a valid error for nsid %u\n", nsid);
172*533affcbSRobert Mustacchi 			break;
173*533affcbSRobert Mustacchi 		default:
174*533affcbSRobert Mustacchi 			warnx("TEST FAILED: nvme_ns_info_eui64() returned an "
175*533affcbSRobert Mustacchi 			    "invalid error for nsid %u: %s (%u)", nsid,
176*533affcbSRobert Mustacchi 			    nvme_ns_info_errtostr(info, nvme_ns_info_err(info)),
177*533affcbSRobert Mustacchi 			    nvme_ns_info_err(info));
178*533affcbSRobert Mustacchi 			*ret = EXIT_FAILURE;
179*533affcbSRobert Mustacchi 			break;
180*533affcbSRobert Mustacchi 		}
181*533affcbSRobert Mustacchi 	}
182*533affcbSRobert Mustacchi 
183*533affcbSRobert Mustacchi 	bret = nvme_ns_info_nguid(info, id);
184*533affcbSRobert Mustacchi 	if (bret != ((flags & NVME_NS_DISC_F_NGUID_VALID) != 0)) {
185*533affcbSRobert Mustacchi 		warnx("TEST FAILED: nvme_ns_info_nguid() returned %s, but "
186*533affcbSRobert Mustacchi 		    "expected %s from discovery information for nsid %u",
187*533affcbSRobert Mustacchi 		    bret ? "true" : "false",
188*533affcbSRobert Mustacchi 		    (flags & NVME_NS_DISC_F_NGUID_VALID) != 0 ? "true" :
189*533affcbSRobert Mustacchi 		    "false", nsid);
190*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
191*533affcbSRobert Mustacchi 	} else {
192*533affcbSRobert Mustacchi 		(void) printf("TEST PASSED: namespace snapshot and discovery "
193*533affcbSRobert Mustacchi 		    "agreed on NGUID presence for nsid %u\n", nsid);
194*533affcbSRobert Mustacchi 	}
195*533affcbSRobert Mustacchi 
196*533affcbSRobert Mustacchi 	if (bret) {
197*533affcbSRobert Mustacchi 		const uint8_t *nguid = nvme_ns_disc_nguid(disc);
198*533affcbSRobert Mustacchi 		const uint8_t zero[16] = { 0 };
199*533affcbSRobert Mustacchi 
200*533affcbSRobert Mustacchi 		if (memcmp(nguid, id, sizeof (zero)) != 0) {
201*533affcbSRobert Mustacchi 			warnx("TEST FAILED: NGUID differs between "
202*533affcbSRobert Mustacchi 			    "discovery and info snapshot for nsid %u", nsid);
203*533affcbSRobert Mustacchi 			*ret = EXIT_FAILURE;
204*533affcbSRobert Mustacchi 		} else {
205*533affcbSRobert Mustacchi 			(void) printf("TEST PASSED: NGUID equal between "
206*533affcbSRobert Mustacchi 			    "discovery and info snapshot for nsid %u\n", nsid);
207*533affcbSRobert Mustacchi 		}
208*533affcbSRobert Mustacchi 
209*533affcbSRobert Mustacchi 		if (memcmp(id, zero, sizeof (zero)) == 0) {
210*533affcbSRobert Mustacchi 			warnx("TEST FAILED: Found invalid zero NGUID for nsid "
211*533affcbSRobert Mustacchi 			    "%u", nsid);
212*533affcbSRobert Mustacchi 			*ret = EXIT_FAILURE;
213*533affcbSRobert Mustacchi 		} else {
214*533affcbSRobert Mustacchi 			(void) printf("TEST PASSED: NGUID is non-zero for "
215*533affcbSRobert Mustacchi 			    "nsid %u\n", nsid);
216*533affcbSRobert Mustacchi 		}
217*533affcbSRobert Mustacchi 	} else {
218*533affcbSRobert Mustacchi 		if (nvme_ns_disc_nguid(disc) != NULL) {
219*533affcbSRobert Mustacchi 			warnx("TEST FAILED: discovery NGUID was valid, but "
220*533affcbSRobert Mustacchi 			    "should be NULL for nsid %u", nsid);
221*533affcbSRobert Mustacchi 			*ret = EXIT_FAILURE;
222*533affcbSRobert Mustacchi 		} else {
223*533affcbSRobert Mustacchi 			(void) printf("TEST PASSED: discovery NGUID correctly "
224*533affcbSRobert Mustacchi 			    "returned NULL for nsid %u\n", nsid);
225*533affcbSRobert Mustacchi 		}
226*533affcbSRobert Mustacchi 
227*533affcbSRobert Mustacchi 		switch (nvme_ns_info_err(info)) {
228*533affcbSRobert Mustacchi 		case NVME_INFO_ERR_VERSION:
229*533affcbSRobert Mustacchi 		case NVME_INFO_ERR_MISSING_CAP:
230*533affcbSRobert Mustacchi 			(void) printf("TEST PASSED: nvme_ns_info_nguid() "
231*533affcbSRobert Mustacchi 			    "returned a valid error for nsid %u\n", nsid);
232*533affcbSRobert Mustacchi 			break;
233*533affcbSRobert Mustacchi 		default:
234*533affcbSRobert Mustacchi 			warnx("TEST FAILED: nvme_ns_info_nguid() returned an "
235*533affcbSRobert Mustacchi 			    "invalid error for nsid %u: %s (%u)", nsid,
236*533affcbSRobert Mustacchi 			    nvme_ns_info_errtostr(info, nvme_ns_info_err(info)),
237*533affcbSRobert Mustacchi 			    nvme_ns_info_err(info));
238*533affcbSRobert Mustacchi 			*ret = EXIT_FAILURE;
239*533affcbSRobert Mustacchi 			break;
240*533affcbSRobert Mustacchi 		}
241*533affcbSRobert Mustacchi 	}
242*533affcbSRobert Mustacchi 	nvme_ns_info_free(info);
243*533affcbSRobert Mustacchi 	return (true);
244*533affcbSRobert Mustacchi }
245*533affcbSRobert Mustacchi 
246*533affcbSRobert Mustacchi static bool
ns_disc_level_cb(nvme_ctrl_t * ctrl,const nvme_ns_disc_t * disc,void * arg)247*533affcbSRobert Mustacchi ns_disc_level_cb(nvme_ctrl_t *ctrl, const nvme_ns_disc_t *disc, void *arg)
248*533affcbSRobert Mustacchi {
249*533affcbSRobert Mustacchi 	int *ret = arg;
250*533affcbSRobert Mustacchi 	nvme_ns_info_t *info;
251*533affcbSRobert Mustacchi 	const uint32_t nsid = nvme_ns_disc_nsid(disc);
252*533affcbSRobert Mustacchi 
253*533affcbSRobert Mustacchi 	if (!nvme_ctrl_ns_info_snap(ctrl, nsid, &info)) {
254*533affcbSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to get info snapshot for "
255*533affcbSRobert Mustacchi 		    "nsid %u", nsid);
256*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
257*533affcbSRobert Mustacchi 		return (true);
258*533affcbSRobert Mustacchi 	}
259*533affcbSRobert Mustacchi 
260*533affcbSRobert Mustacchi 	if (nvme_ns_disc_level(disc) != nvme_ns_info_level(info)) {
261*533affcbSRobert Mustacchi 		warnx("TEST FAILED: discovery and ns info snapshot disagree "
262*533affcbSRobert Mustacchi 		    "on discovery level: disc has %u, info has %u",
263*533affcbSRobert Mustacchi 		    nvme_ns_disc_level(disc), nvme_ns_info_level(info));
264*533affcbSRobert Mustacchi 		*ret = EXIT_FAILURE;
265*533affcbSRobert Mustacchi 	} else {
266*533affcbSRobert Mustacchi 		(void) printf("TEST PASSED: discovery and ns info snapshot "
267*533affcbSRobert Mustacchi 		    "agree for nsid %u\n", nsid);
268*533affcbSRobert Mustacchi 	}
269*533affcbSRobert Mustacchi 
270*533affcbSRobert Mustacchi 	nvme_ns_info_free(info);
271*533affcbSRobert Mustacchi 	return (true);
272*533affcbSRobert Mustacchi }
273*533affcbSRobert Mustacchi 
274*533affcbSRobert Mustacchi static bool
ns_disc_bad_disc_init(nvme_ctrl_t * ctrl,nvme_ns_disc_level_t level,nvme_ns_iter_t ** iterp,nvme_err_t exp_err,const char * desc)275*533affcbSRobert Mustacchi ns_disc_bad_disc_init(nvme_ctrl_t *ctrl, nvme_ns_disc_level_t level,
276*533affcbSRobert Mustacchi     nvme_ns_iter_t **iterp, nvme_err_t exp_err, const char *desc)
277*533affcbSRobert Mustacchi {
278*533affcbSRobert Mustacchi 	if (nvme_ns_discover_init(ctrl, level, iterp)) {
279*533affcbSRobert Mustacchi 		warnx("TEST FAILED: nvme_ns_discover_init() erroneously "
280*533affcbSRobert Mustacchi 		    "passed despite %s", desc);
281*533affcbSRobert Mustacchi 		nvme_ns_discover_fini(*iterp);
282*533affcbSRobert Mustacchi 		return (false);
283*533affcbSRobert Mustacchi 	} else if (nvme_ctrl_err(ctrl) != exp_err) {
284*533affcbSRobert Mustacchi 		warnx("TEST FAILED: nvme_ns_discover_init() returned "
285*533affcbSRobert Mustacchi 		    "wrong error %s (0x%x), not %s (0x%x)",
286*533affcbSRobert Mustacchi 		    nvme_ctrl_errtostr(ctrl, nvme_ctrl_err(ctrl)),
287*533affcbSRobert Mustacchi 		    nvme_ctrl_err(ctrl), nvme_ctrl_errtostr(ctrl,
288*533affcbSRobert Mustacchi 		    exp_err), exp_err);
289*533affcbSRobert Mustacchi 		return (false);
290*533affcbSRobert Mustacchi 	} else {
291*533affcbSRobert Mustacchi 		(void) printf("TEST PASSED: nvme_ns_discover_init() failed "
292*533affcbSRobert Mustacchi 		    "correctly for %s\n", desc);
293*533affcbSRobert Mustacchi 		return (true);
294*533affcbSRobert Mustacchi 	}
295*533affcbSRobert Mustacchi }
296*533affcbSRobert Mustacchi 
297*533affcbSRobert Mustacchi static bool
ns_disc_bad_disc(nvme_ctrl_t * ctrl,nvme_ns_disc_level_t level,nvme_ns_disc_f func,nvme_err_t exp_err,const char * desc)298*533affcbSRobert Mustacchi ns_disc_bad_disc(nvme_ctrl_t *ctrl, nvme_ns_disc_level_t level,
299*533affcbSRobert Mustacchi     nvme_ns_disc_f func, nvme_err_t exp_err, const char *desc)
300*533affcbSRobert Mustacchi {
301*533affcbSRobert Mustacchi 	if (nvme_ns_discover(ctrl, level, func, NULL)) {
302*533affcbSRobert Mustacchi 		warnx("TEST FAILED: nvme_ns_discover() erroneously "
303*533affcbSRobert Mustacchi 		    "passed despite %s", desc);
304*533affcbSRobert Mustacchi 		return (false);
305*533affcbSRobert Mustacchi 	} else if (nvme_ctrl_err(ctrl) != exp_err) {
306*533affcbSRobert Mustacchi 		warnx("TEST FAILED: nvme_ns_discover() returned "
307*533affcbSRobert Mustacchi 		    "wrong error %s (0x%x), not %s (0x%x)",
308*533affcbSRobert Mustacchi 		    nvme_ctrl_errtostr(ctrl, nvme_ctrl_err(ctrl)),
309*533affcbSRobert Mustacchi 		    nvme_ctrl_err(ctrl), nvme_ctrl_errtostr(ctrl,
310*533affcbSRobert Mustacchi 		    exp_err), exp_err);
311*533affcbSRobert Mustacchi 		return (false);
312*533affcbSRobert Mustacchi 	} else {
313*533affcbSRobert Mustacchi 		(void) printf("TEST PASSED: nvme_ns_discover() failed "
314*533affcbSRobert Mustacchi 		    "correctly for %s\n", desc);
315*533affcbSRobert Mustacchi 		return (true);
316*533affcbSRobert Mustacchi 	}
317*533affcbSRobert Mustacchi }
318*533affcbSRobert Mustacchi 
319*533affcbSRobert Mustacchi static bool
ns_disc_nop_cb(nvme_ctrl_t * ctrl,const nvme_ns_disc_t * disc,void * arg)320*533affcbSRobert Mustacchi ns_disc_nop_cb(nvme_ctrl_t *ctrl, const nvme_ns_disc_t *disc, void *arg)
321*533affcbSRobert Mustacchi {
322*533affcbSRobert Mustacchi 	return (true);
323*533affcbSRobert Mustacchi }
324*533affcbSRobert Mustacchi 
325*533affcbSRobert Mustacchi int
main(void)326*533affcbSRobert Mustacchi main(void)
327*533affcbSRobert Mustacchi {
328*533affcbSRobert Mustacchi 	int ret = EXIT_SUCCESS;
329*533affcbSRobert Mustacchi 	nvme_t *nvme;
330*533affcbSRobert Mustacchi 	nvme_ctrl_t *ctrl;
331*533affcbSRobert Mustacchi 	nvme_ctrl_info_t *info;
332*533affcbSRobert Mustacchi 	nvme_iter_t iret;
333*533affcbSRobert Mustacchi 	nvme_ns_iter_t *iter;
334*533affcbSRobert Mustacchi 	const nvme_ns_disc_t *disc;
335*533affcbSRobert Mustacchi 	uint32_t nbd = 0, nni = 0, nact = 0, nalloc = 0, nns = 0;
336*533affcbSRobert Mustacchi 
337*533affcbSRobert Mustacchi 	libnvme_test_init(&nvme, &ctrl);
338*533affcbSRobert Mustacchi 	if (!nvme_ctrl_lock(ctrl, NVME_LOCK_L_WRITE, NVME_LOCK_F_DONT_BLOCK)) {
339*533affcbSRobert Mustacchi 		libnvme_test_ctrl_fatal(ctrl, "failed to obtain write lock");
340*533affcbSRobert Mustacchi 	}
341*533affcbSRobert Mustacchi 
342*533affcbSRobert Mustacchi 	if (!nvme_ns_discover_init(ctrl, NVME_NS_DISC_F_ALL, &iter)) {
343*533affcbSRobert Mustacchi 		libnvme_test_ctrl_fatal(ctrl, "failed to initialize initial "
344*533affcbSRobert Mustacchi 		    "ns discovery");
345*533affcbSRobert Mustacchi 	}
346*533affcbSRobert Mustacchi 
347*533affcbSRobert Mustacchi 	while ((iret = nvme_ns_discover_step(iter, &disc)) == NVME_ITER_VALID) {
348*533affcbSRobert Mustacchi 		switch (nvme_ns_disc_level(disc)) {
349*533affcbSRobert Mustacchi 		case NVME_NS_DISC_F_BLKDEV:
350*533affcbSRobert Mustacchi 			nbd++;
351*533affcbSRobert Mustacchi 			/* FALLTHROUGH */
352*533affcbSRobert Mustacchi 		case NVME_NS_DISC_F_NOT_IGNORED:
353*533affcbSRobert Mustacchi 			nni++;
354*533affcbSRobert Mustacchi 			/* FALLTHROUGH */
355*533affcbSRobert Mustacchi 		case NVME_NS_DISC_F_ACTIVE:
356*533affcbSRobert Mustacchi 			nact++;
357*533affcbSRobert Mustacchi 			/* FALLTHROUGH */
358*533affcbSRobert Mustacchi 		case NVME_NS_DISC_F_ALLOCATED:
359*533affcbSRobert Mustacchi 			nalloc++;
360*533affcbSRobert Mustacchi 			/* FALLTHROUGH */
361*533affcbSRobert Mustacchi 		case NVME_NS_DISC_F_ALL:
362*533affcbSRobert Mustacchi 			nns++;
363*533affcbSRobert Mustacchi 			break;
364*533affcbSRobert Mustacchi 		}
365*533affcbSRobert Mustacchi 	}
366*533affcbSRobert Mustacchi 
367*533affcbSRobert Mustacchi 	nvme_ns_discover_fini(iter);
368*533affcbSRobert Mustacchi 	if (iret != NVME_ITER_DONE) {
369*533affcbSRobert Mustacchi 		libnvme_test_ctrl_fatal(ctrl, "initial ns discovery failed");
370*533affcbSRobert Mustacchi 	}
371*533affcbSRobert Mustacchi 
372*533affcbSRobert Mustacchi 	if (!nvme_ctrl_info_snap(ctrl, &info)) {
373*533affcbSRobert Mustacchi 		libnvme_test_ctrl_fatal(ctrl, "failed to get info snapshot");
374*533affcbSRobert Mustacchi 	}
375*533affcbSRobert Mustacchi 
376*533affcbSRobert Mustacchi 	if (nns != nvme_ctrl_info_nns(info)) {
377*533affcbSRobert Mustacchi 		warnx("TEST FAILED: discovery found %u namespaces, but the "
378*533affcbSRobert Mustacchi 		    "identify controller suggests there are %u", nns,
379*533affcbSRobert Mustacchi 		    nvme_ctrl_info_nns(info));
380*533affcbSRobert Mustacchi 	}
381*533affcbSRobert Mustacchi 
382*533affcbSRobert Mustacchi 	if (!ns_disc_count(ctrl, NVME_NS_DISC_F_BLKDEV, nbd)) {
383*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
384*533affcbSRobert Mustacchi 	}
385*533affcbSRobert Mustacchi 
386*533affcbSRobert Mustacchi 	if (!ns_disc_count(ctrl, NVME_NS_DISC_F_NOT_IGNORED, nni)) {
387*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
388*533affcbSRobert Mustacchi 	}
389*533affcbSRobert Mustacchi 
390*533affcbSRobert Mustacchi 	if (!ns_disc_count(ctrl, NVME_NS_DISC_F_ACTIVE, nact)) {
391*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
392*533affcbSRobert Mustacchi 	}
393*533affcbSRobert Mustacchi 
394*533affcbSRobert Mustacchi 	if (!ns_disc_count(ctrl, NVME_NS_DISC_F_ALLOCATED, nalloc)) {
395*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
396*533affcbSRobert Mustacchi 	}
397*533affcbSRobert Mustacchi 
398*533affcbSRobert Mustacchi 	if (!ns_disc_count(ctrl, NVME_NS_DISC_F_ALL, nns)) {
399*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
400*533affcbSRobert Mustacchi 	}
401*533affcbSRobert Mustacchi 
402*533affcbSRobert Mustacchi 	/*
403*533affcbSRobert Mustacchi 	 * For anything that has a blkdev address, ensure that our info snapshot
404*533affcbSRobert Mustacchi 	 * has a valid blkdev address.
405*533affcbSRobert Mustacchi 	 */
406*533affcbSRobert Mustacchi 	if (!nvme_ns_discover(ctrl, NVME_NS_DISC_F_BLKDEV, ns_disc_blkdev_cb,
407*533affcbSRobert Mustacchi 	    &ret)) {
408*533affcbSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "discovery failed for blkdev "
409*533affcbSRobert Mustacchi 		    "test");
410*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
411*533affcbSRobert Mustacchi 	}
412*533affcbSRobert Mustacchi 
413*533affcbSRobert Mustacchi 	/*
414*533affcbSRobert Mustacchi 	 * For anything active, check if there are guids and that the
415*533affcbSRobert Mustacchi 	 * information snapshot matches the same logic.
416*533affcbSRobert Mustacchi 	 */
417*533affcbSRobert Mustacchi 	if (!nvme_ns_discover(ctrl, NVME_NS_DISC_F_ACTIVE, ns_disc_guids_cb,
418*533affcbSRobert Mustacchi 	    &ret)) {
419*533affcbSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "discovery failed for guids "
420*533affcbSRobert Mustacchi 		    "test");
421*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
422*533affcbSRobert Mustacchi 	}
423*533affcbSRobert Mustacchi 
424*533affcbSRobert Mustacchi 	/*
425*533affcbSRobert Mustacchi 	 * For everything, make sure the levels match.
426*533affcbSRobert Mustacchi 	 */
427*533affcbSRobert Mustacchi 	if (!nvme_ns_discover(ctrl, NVME_NS_DISC_F_ALL, ns_disc_level_cb,
428*533affcbSRobert Mustacchi 	    &ret)) {
429*533affcbSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "discovery failed for levels "
430*533affcbSRobert Mustacchi 		    "test");
431*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
432*533affcbSRobert Mustacchi 	}
433*533affcbSRobert Mustacchi 
434*533affcbSRobert Mustacchi 	nvme_ctrl_unlock(ctrl);
435*533affcbSRobert Mustacchi 
436*533affcbSRobert Mustacchi 	if (!ns_disc_bad_disc_init(ctrl, INT32_MAX, &iter, NVME_ERR_BAD_FLAG,
437*533affcbSRobert Mustacchi 	    "invalid level")) {
438*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
439*533affcbSRobert Mustacchi 	}
440*533affcbSRobert Mustacchi 
441*533affcbSRobert Mustacchi 	if (!ns_disc_bad_disc_init(ctrl, NVME_NS_DISC_F_ALL, NULL,
442*533affcbSRobert Mustacchi 	    NVME_ERR_BAD_PTR, "invalid iter pointer")) {
443*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
444*533affcbSRobert Mustacchi 	}
445*533affcbSRobert Mustacchi 
446*533affcbSRobert Mustacchi 	if (!ns_disc_bad_disc(ctrl, UINT32_MAX, ns_disc_nop_cb,
447*533affcbSRobert Mustacchi 	    NVME_ERR_BAD_FLAG, "invalid level")) {
448*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
449*533affcbSRobert Mustacchi 	}
450*533affcbSRobert Mustacchi 
451*533affcbSRobert Mustacchi 	if (!ns_disc_bad_disc(ctrl, NVME_NS_DISC_F_ALL, NULL,
452*533affcbSRobert Mustacchi 	    NVME_ERR_BAD_PTR, "invalid function pointer")) {
453*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
454*533affcbSRobert Mustacchi 	}
455*533affcbSRobert Mustacchi 
456*533affcbSRobert Mustacchi 	umem_setmtbf(1);
457*533affcbSRobert Mustacchi 	if (!ns_disc_bad_disc_init(ctrl, NVME_NS_DISC_F_ALL, &iter,
458*533affcbSRobert Mustacchi 	    NVME_ERR_NO_MEM, "no memory")) {
459*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
460*533affcbSRobert Mustacchi 	}
461*533affcbSRobert Mustacchi 
462*533affcbSRobert Mustacchi 	if (!ns_disc_bad_disc(ctrl, NVME_NS_DISC_F_ALL, ns_disc_nop_cb,
463*533affcbSRobert Mustacchi 	    NVME_ERR_NO_MEM, "no memory")) {
464*533affcbSRobert Mustacchi 		ret = EXIT_FAILURE;
465*533affcbSRobert Mustacchi 	}
466*533affcbSRobert Mustacchi 	umem_setmtbf(0);
467*533affcbSRobert Mustacchi 
468*533affcbSRobert Mustacchi 	if (ret == EXIT_SUCCESS) {
469*533affcbSRobert Mustacchi 		(void) printf("All tests passed successfully\n");
470*533affcbSRobert Mustacchi 	}
471*533affcbSRobert Mustacchi 
472*533affcbSRobert Mustacchi 	nvme_ctrl_info_free(info);
473*533affcbSRobert Mustacchi 	nvme_ctrl_fini(ctrl);
474*533affcbSRobert Mustacchi 	nvme_fini(nvme);
475*533affcbSRobert Mustacchi 	return (ret);
476*533affcbSRobert Mustacchi }
477