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 controller discovery. Because we've been given a controller, we expect
18  * to be able to find a controller with the same devi while performing
19  * controller discovery. Its minor should point to the same devi that the normal
20  * discovery and controller do.
21  */
22 
23 #include <err.h>
24 #include <string.h>
25 #include <umem.h>
26 #include <sys/stat.h>
27 
28 #include "libnvme_test_common.h"
29 
30 static bool
ctrl_disc_count_cb(nvme_t * nvme,const nvme_ctrl_disc_t * disc,void * arg)31 ctrl_disc_count_cb(nvme_t *nvme, const nvme_ctrl_disc_t *disc, void *arg)
32 {
33 	uint32_t *valp = arg;
34 	*valp = *valp + 1;
35 	return (true);
36 }
37 
38 static bool
ctrl_check_disc(const nvme_ctrl_disc_t * disc)39 ctrl_check_disc(const nvme_ctrl_disc_t *disc)
40 {
41 	bool ret = true;
42 	di_node_t ctrl_devi, minor_devi;
43 	di_minor_t minor;
44 	const char *mname;
45 
46 	ctrl_devi = nvme_ctrl_disc_devi(disc);
47 	minor = nvme_ctrl_disc_minor(disc);
48 	minor_devi = di_minor_devinfo(minor);
49 	mname = di_minor_name(minor);
50 
51 	if (di_minor_spectype(minor) != S_IFCHR) {
52 		warnx("TEST FAILED: %s minor is not a character device, found "
53 		    "0x%x\n", mname, di_minor_spectype(minor));
54 		ret = false;
55 	} else {
56 		(void) printf("TEST PASSED: %s: minor is a character device\n",
57 		    mname);
58 	}
59 
60 	if (strcmp(di_minor_nodetype(minor), DDI_NT_NVME_NEXUS) != 0) {
61 		warnx("TEST FAILED: %s minor has wrong node type %s, expected "
62 		    "%s", mname, di_minor_nodetype(minor), DDI_NT_NVME_NEXUS);
63 	} else {
64 		(void) printf("TEST PASSED: %s minor has correct node types\n",
65 		    mname);
66 	}
67 
68 	if (minor_devi != ctrl_devi) {
69 		warnx("TEST FAILED: %s minor devi does not match the "
70 		    "controller devi", mname);
71 		ret = false;
72 	} else {
73 		(void) printf("TEST PASSED: %s minor devi matches its "
74 		    "controller\n", mname);
75 	}
76 
77 	return (ret);
78 }
79 
80 static bool
ctrl_match(nvme_t * nvme,nvme_ctrl_t * targ)81 ctrl_match(nvme_t *nvme, nvme_ctrl_t *targ)
82 {
83 	bool ret = true, match = false;
84 	nvme_ctrl_iter_t *iter = NULL;
85 	const nvme_ctrl_disc_t *disc;
86 	nvme_iter_t iret;
87 	di_node_t targ_di;
88 
89 	if (!nvme_ctrl_devi(targ, &targ_di)) {
90 		libnvme_test_ctrl_warn(targ, "failed to obtain di_node_t from "
91 		    "controller");
92 		return (false);
93 	}
94 
95 	if (!nvme_ctrl_discover_init(nvme, &iter)) {
96 		libnvme_test_hdl_warn(nvme, "failed to initialize controller "
97 		    "discovery");
98 		return (false);
99 	}
100 
101 	while ((iret = nvme_ctrl_discover_step(iter, &disc)) ==
102 	    NVME_ITER_VALID) {
103 		if (!ctrl_check_disc(disc)) {
104 			ret = false;
105 		}
106 
107 		if (nvme_ctrl_disc_devi(disc) == targ_di) {
108 			match = true;
109 		}
110 	}
111 
112 	if (iret != NVME_ITER_DONE) {
113 		libnvme_test_hdl_warn(nvme, "failed to iterate controllers");
114 		ret = false;
115 	}
116 
117 	if (!match) {
118 		warnx("TEST FAILED: failed to find matching controller");
119 		ret = false;
120 	} else {
121 		(void) printf("TEST PASSED: found matching controller in "
122 		    "discovery for device %s\n", getenv(NVME_TEST_DEV_ENVVAR));
123 	}
124 
125 	nvme_ctrl_discover_fini(iter);
126 	return (ret);
127 }
128 
129 static bool
ctrl_disc_nop_cb(nvme_t * nvme,const nvme_ctrl_disc_t * disc,void * arg)130 ctrl_disc_nop_cb(nvme_t *nvme, const nvme_ctrl_disc_t *disc, void *arg)
131 {
132 	return (true);
133 }
134 
135 static bool
ctrl_disc_bad_disc_init(nvme_t * nvme,nvme_ctrl_iter_t ** iterp,nvme_err_t exp_err,const char * desc)136 ctrl_disc_bad_disc_init(nvme_t *nvme, nvme_ctrl_iter_t **iterp,
137     nvme_err_t exp_err, const char *desc)
138 {
139 	if (nvme_ctrl_discover_init(nvme, iterp)) {
140 		warnx("TEST FAILED: nvme_ctrl_discover_init() erroneously "
141 		    "passed despite %s\n", desc);
142 		return (false);
143 	} else if (nvme_err(nvme) != exp_err) {
144 		warnx("TEST FAILED: nvme_ctrl_discover_init() returned "
145 		    "wrong error %s (0x%x), not %s (0x%x)",
146 		    nvme_errtostr(nvme, nvme_err(nvme)), nvme_err(nvme),
147 		    nvme_errtostr(nvme, exp_err), exp_err);
148 		return (false);
149 	} else {
150 		(void) printf("TEST PASSED: nvme_ctrl_discover_init() failed "
151 		    "correctly for %s\n", desc);
152 		return (true);
153 	}
154 }
155 
156 static bool
ctrl_disc_bad_disc(nvme_t * nvme,nvme_ctrl_disc_f func,nvme_err_t exp_err,const char * desc)157 ctrl_disc_bad_disc(nvme_t *nvme, nvme_ctrl_disc_f func, nvme_err_t exp_err,
158     const char *desc)
159 {
160 	if (nvme_ctrl_discover(nvme, func, NULL)) {
161 		warnx("TEST FAILED: nvme_ctrl_discover() erroneously "
162 		    "passed despite %s\n", desc);
163 		return (false);
164 	} else if (nvme_err(nvme) != exp_err) {
165 		warnx("TEST FAILED: nvme_ctrl_discover() returned "
166 		    "wrong error %s (0x%x), not %s (0x%x)",
167 		    nvme_errtostr(nvme, nvme_err(nvme)), nvme_err(nvme),
168 		    nvme_errtostr(nvme, exp_err), exp_err);
169 		return (false);
170 	} else {
171 		(void) printf("TEST PASSED: nvme_ctrl_discover() failed "
172 		    "correctly for %s\n", desc);
173 		return (true);
174 	}
175 }
176 
177 int
main(void)178 main(void)
179 {
180 	int ret = EXIT_SUCCESS;
181 	nvme_t *nvme;
182 	nvme_ctrl_t *ctrl;
183 	uint32_t nctrl = 0;
184 	nvme_ctrl_iter_t *iter;
185 
186 	libnvme_test_init(&nvme, &ctrl);
187 
188 	if (!nvme_ctrl_discover(nvme, ctrl_disc_count_cb, &nctrl)) {
189 		libnvme_test_hdl_warn(nvme, "failed to discover controllers");
190 		ret = EXIT_FAILURE;
191 	} else if (nctrl == 0) {
192 		warnx("TEST FAILED: discovered zero controllers somehow!");
193 		ret = EXIT_FAILURE;
194 	} else {
195 		(void) printf("TEST PASSED: discovered some number of "
196 		    "controllers");
197 	}
198 
199 	if (!ctrl_match(nvme, ctrl)) {
200 		ret = EXIT_FAILURE;
201 	}
202 
203 	if (!ctrl_disc_bad_disc_init(nvme, NULL, NVME_ERR_BAD_PTR,
204 	    "invalid iter pointer")) {
205 		ret = EXIT_FAILURE;
206 	}
207 
208 	if (!ctrl_disc_bad_disc(nvme, NULL, NVME_ERR_BAD_PTR,
209 	    "invalid function pointer")) {
210 		ret = EXIT_FAILURE;
211 	}
212 
213 	umem_setmtbf(1);
214 	if (!ctrl_disc_bad_disc_init(nvme, &iter, NVME_ERR_NO_MEM,
215 	    "no memory")) {
216 		ret = EXIT_FAILURE;
217 	}
218 
219 	if (!ctrl_disc_bad_disc(nvme, ctrl_disc_nop_cb, NVME_ERR_NO_MEM,
220 	    "no memory")) {
221 		ret = EXIT_FAILURE;
222 	}
223 	umem_setmtbf(0);
224 
225 	if (ret == EXIT_SUCCESS) {
226 		(void) printf("All tests passed successfully\n");
227 	}
228 
229 	nvme_ctrl_fini(ctrl);
230 	nvme_fini(nvme);
231 	return (ret);
232 }
233