xref: /illumos-gate/usr/src/test/nvme-tests/tests/libnvme/ns-lifecycle.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  * This test goes through the lifecycle of a namespace and verifies that when we
18  * try to take transitions that it does not support, we can generate the
19  * expected errors.
20  *
21  * This test expects to start from the device-empty profile.
22  */
23 
24 #include <err.h>
25 #include <stdlib.h>
26 #include <sys/sysmacros.h>
27 
28 #include "libnvme_test_common.h"
29 
30 /*
31  * We expect that we should always be issued NSID 1.
32  */
33 #define	NS_LC_NSID	1
34 
35 /*
36  * This is a list of actions we can take in a given state against a namespace.
37  * There is no namespace create in this list as there is no way to specify a
38  * namespace to take action against.
39  */
40 typedef enum {
41 	NS_ACT_NS_DELETE,
42 	NS_ACT_CTRL_ATTACH,
43 	NS_ACT_CTRL_DETACH,
44 	NS_ACT_BD_ATTACH,
45 	NS_ACT_BD_DETACH
46 } ns_act_t;
47 
48 #define	NS_LC_NACTS	(NS_ACT_BD_DETACH + 1)
49 
50 nvme_err_t nvme_unalloc_errs[NS_LC_NACTS] = {
51 	[NS_ACT_NS_DELETE] = NVME_ERR_NS_UNALLOC,
52 	[NS_ACT_CTRL_ATTACH] = NVME_ERR_NS_UNALLOC,
53 	[NS_ACT_CTRL_DETACH] = NVME_ERR_NS_UNALLOC,
54 	[NS_ACT_BD_ATTACH] = NVME_ERR_NS_UNALLOC,
55 	[NS_ACT_BD_DETACH] = NVME_ERR_NS_UNALLOC
56 };
57 
58 nvme_err_t nvme_alloc_errs[NS_LC_NACTS] = {
59 	[NS_ACT_NS_DELETE] = NVME_ERR_OK,
60 	[NS_ACT_CTRL_ATTACH] = NVME_ERR_OK,
61 	[NS_ACT_CTRL_DETACH] = NVME_ERR_NS_CTRL_NOT_ATTACHED,
62 	[NS_ACT_BD_ATTACH] = NVME_ERR_NS_CTRL_NOT_ATTACHED,
63 	[NS_ACT_BD_DETACH] = NVME_ERR_NS_CTRL_NOT_ATTACHED
64 };
65 
66 nvme_err_t nvme_attach_errs[NS_LC_NACTS] = {
67 	[NS_ACT_NS_DELETE] = NVME_ERR_NS_CTRL_ATTACHED,
68 	[NS_ACT_CTRL_ATTACH] = NVME_ERR_NS_CTRL_ATTACHED,
69 	[NS_ACT_CTRL_DETACH] = NVME_ERR_OK,
70 	[NS_ACT_BD_ATTACH] = NVME_ERR_OK,
71 	[NS_ACT_BD_DETACH] = NVME_ERR_NS_CTRL_ATTACHED
72 };
73 
74 nvme_err_t nvme_blkdev_errs[NS_LC_NACTS] = {
75 	[NS_ACT_NS_DELETE] = NVME_ERR_NS_BLKDEV_ATTACH,
76 	[NS_ACT_CTRL_ATTACH] = NVME_ERR_NS_BLKDEV_ATTACH,
77 	[NS_ACT_CTRL_DETACH] = NVME_ERR_NS_BLKDEV_ATTACH,
78 	[NS_ACT_BD_ATTACH] = NVME_ERR_NS_BLKDEV_ATTACH,
79 	[NS_ACT_BD_DETACH] = NVME_ERR_OK
80 };
81 
82 static bool
ns_life_err_comp(nvme_ctrl_t * ctrl,nvme_err_t exp,nvme_err_t act,const char * desc,const char * state)83 ns_life_err_comp(nvme_ctrl_t *ctrl, nvme_err_t exp, nvme_err_t act,
84     const char *desc, const char *state)
85 {
86 	if (act != exp) {
87 		warnx("TEST FAILED: %s in state %s returned %s (0x%x), but "
88 		    "expected %s (0x%x)", desc, state,
89 		    nvme_ctrl_errtostr(ctrl, act), act,
90 		    nvme_ctrl_errtostr(ctrl, exp), exp);
91 	} else {
92 		(void) printf("TEST PASSED: %s in state %s correctly returned "
93 		    "%s\n", desc, state, nvme_ctrl_errtostr(ctrl, act));
94 	}
95 
96 	return (act == exp);
97 }
98 
99 static bool
ns_life_ns_delete(nvme_ctrl_t * ctrl,uint32_t nsid,nvme_err_t exp,const char * state)100 ns_life_ns_delete(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_err_t exp,
101     const char *state)
102 {
103 	nvme_err_t act;
104 
105 	if (!libnvme_test_ns_delete(ctrl, nsid, &act)) {
106 		return (false);
107 	}
108 
109 	return (ns_life_err_comp(ctrl, exp, act, "namespace delete", state));
110 }
111 
112 static bool
ns_life_ctrl_attach(nvme_ctrl_t * ctrl,uint32_t nsid,nvme_err_t exp,const char * state)113 ns_life_ctrl_attach(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_err_t exp,
114     const char *state)
115 {
116 	nvme_err_t act;
117 
118 	if (!libnvme_test_ctrl_attach(ctrl, nsid, NVME_NS_ATTACH_CTRL_ATTACH,
119 	    &act)) {
120 		return (false);
121 	}
122 
123 	return (ns_life_err_comp(ctrl, exp, act, "controller attach", state));
124 }
125 
126 static bool
ns_life_ctrl_detach(nvme_ctrl_t * ctrl,uint32_t nsid,nvme_err_t exp,const char * state)127 ns_life_ctrl_detach(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_err_t exp,
128     const char *state)
129 {
130 	nvme_err_t act;
131 
132 	if (!libnvme_test_ctrl_attach(ctrl, nsid, NVME_NS_ATTACH_CTRL_DETACH,
133 	    &act)) {
134 		return (false);
135 	}
136 
137 	return (ns_life_err_comp(ctrl, exp, act, "controller detach", state));
138 }
139 
140 static bool
ns_life_blkdev_attach(nvme_ctrl_t * ctrl,uint32_t nsid,nvme_err_t exp,const char * state)141 ns_life_blkdev_attach(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_err_t exp,
142     const char *state)
143 {
144 	nvme_err_t act;
145 
146 	if (!libnvme_test_ns_blkdev(ctrl, nsid, true, &act)) {
147 		return (false);
148 	}
149 
150 	return (ns_life_err_comp(ctrl, exp, act, "blkdev attach", state));
151 }
152 
153 static bool
ns_life_blkdev_detach(nvme_ctrl_t * ctrl,uint32_t nsid,nvme_err_t exp,const char * state)154 ns_life_blkdev_detach(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_err_t exp,
155     const char *state)
156 {
157 	nvme_err_t act;
158 
159 	if (!libnvme_test_ns_blkdev(ctrl, nsid, false, &act)) {
160 		return (false);
161 	}
162 
163 	return (ns_life_err_comp(ctrl, exp, act, "blkdev detach", state));
164 }
165 
166 typedef bool (*ns_life_f)(nvme_ctrl_t *, uint32_t, nvme_err_t,
167     const char *);
168 
169 static const ns_life_f ns_lf_funcs[NS_LC_NACTS] = {
170 	[NS_ACT_NS_DELETE] = ns_life_ns_delete,
171 	[NS_ACT_CTRL_ATTACH] = ns_life_ctrl_attach,
172 	[NS_ACT_CTRL_DETACH] = ns_life_ctrl_detach,
173 	[NS_ACT_BD_ATTACH] = ns_life_blkdev_attach,
174 	[NS_ACT_BD_DETACH] = ns_life_blkdev_detach
175 };
176 
177 typedef struct ns_life_test {
178 	nvme_ns_disc_level_t nlt_disc;
179 	const char *nlt_desc;
180 	nvme_err_t *nlt_errs;
181 	size_t nlt_nerrs;
182 } ns_life_test_t;
183 
184 static const ns_life_test_t ns_life_tests[] = { {
185 	.nlt_disc = NVME_NS_DISC_F_ALL,
186 	.nlt_desc = "unallocated",
187 	.nlt_errs = nvme_unalloc_errs,
188 	.nlt_nerrs = ARRAY_SIZE(nvme_unalloc_errs)
189 }, {
190 	.nlt_disc = NVME_NS_DISC_F_ALLOCATED,
191 	.nlt_desc = "allocated",
192 	.nlt_errs = nvme_alloc_errs,
193 	.nlt_nerrs = ARRAY_SIZE(nvme_alloc_errs)
194 }, {
195 	.nlt_disc = NVME_NS_DISC_F_NOT_IGNORED,
196 	.nlt_desc = "active",
197 	.nlt_errs = nvme_attach_errs,
198 	.nlt_nerrs = ARRAY_SIZE(nvme_attach_errs)
199 }, {
200 	.nlt_disc = NVME_NS_DISC_F_BLKDEV,
201 	.nlt_desc = "blkdev",
202 	.nlt_errs = nvme_blkdev_errs,
203 	.nlt_nerrs = ARRAY_SIZE(nvme_blkdev_errs)
204 } };
205 
206 static bool
ns_life_run_one(nvme_ctrl_t * ctrl,uint32_t lbaf,const ns_life_test_t * test)207 ns_life_run_one(nvme_ctrl_t *ctrl, uint32_t lbaf, const ns_life_test_t *test)
208 {
209 	bool ret = true;
210 
211 	for (size_t i = 0; i < test->nlt_nerrs; i++) {
212 		if (!libnvme_test_setup_ns(ctrl, test->nlt_disc, NS_LC_NSID,
213 		    lbaf)) {
214 			warnx("TEST FAILED: failed to transition ns to %s",
215 			    test->nlt_desc);
216 			ret = false;
217 		}
218 
219 		if (!ns_lf_funcs[i](ctrl, NS_LC_NSID, test->nlt_errs[i],
220 		    test->nlt_desc)) {
221 			ret = false;
222 		}
223 	}
224 
225 	return (ret);
226 }
227 
228 int
main(void)229 main(void)
230 {
231 	int ret = EXIT_SUCCESS;
232 	nvme_t *nvme;
233 	nvme_ctrl_t *ctrl;
234 	nvme_ctrl_info_t *info;
235 	uint32_t lbaf;
236 
237 	libnvme_test_init(&nvme, &ctrl);
238 	if (!nvme_ctrl_lock(ctrl, NVME_LOCK_L_WRITE, NVME_LOCK_F_DONT_BLOCK)) {
239 		libnvme_test_ctrl_fatal(ctrl, "failed to obtain write lock");
240 	}
241 
242 	if (!nvme_ctrl_info_snap(ctrl, &info)) {
243 		libnvme_test_ctrl_fatal(ctrl, "failed to get info snapshot");
244 	}
245 
246 	if (!libnvme_test_lbaf(info, NVME_TEST_LBA_SIZE, &lbaf)) {
247 		errx(EXIT_FAILURE, "failed to find 4K LBA format, cannot "
248 		    "continue");
249 	}
250 
251 	for (size_t i = 0; i < ARRAY_SIZE(ns_life_tests); i++) {
252 		if (!ns_life_run_one(ctrl, lbaf, &ns_life_tests[i]))
253 			ret = EXIT_FAILURE;
254 	}
255 
256 	nvme_ctrl_info_free(info);
257 	nvme_ctrl_unlock(ctrl);
258 	nvme_ctrl_fini(ctrl);
259 	nvme_fini(nvme);
260 
261 	if (ret == EXIT_SUCCESS) {
262 		(void) printf("All tests passed successfully\n");
263 	}
264 
265 	return (ret);
266 }
267