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