xref: /illumos-gate/usr/src/test/nvme-tests/tests/libnvme/ns-max.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 is a basic test that goes through and creates as many namespaces as
18  * possible, each sized at 1 GiB. It ensures that we can create them all, attach
19  * them to the controller, and with blkdev. We verify the basic properties of
20  * the namespaces that we get back.
21  *
22  * This test expects to start from the device-empty profile.
23  */
24 
25 #include <err.h>
26 #include <stdlib.h>
27 #include "libnvme_test_common.h"
28 
29 typedef struct {
30 	uint32_t nc_lbaf;
31 	uint32_t nc_nns;
32 	bool nc_pass;
33 } ns_cb_t;
34 
35 static int
nsid_comp(const void * left,const void * right)36 nsid_comp(const void *left, const void *right)
37 {
38 	uint32_t l = *(const uint32_t *)left;
39 	uint32_t r = *(const uint32_t *)right;
40 
41 	if (l > r)
42 		return (1);
43 	if (l < r)
44 		return (-1);
45 	return (0);
46 }
47 
48 static bool
ns_max_fail_ns(nvme_ctrl_t * ctrl,uint32_t lbaf)49 ns_max_fail_ns(nvme_ctrl_t *ctrl, uint32_t lbaf)
50 {
51 	nvme_err_t err;
52 	const uint64_t size = NVME_TEST_NS_SIZE / NVME_TEST_LBA_SIZE;
53 
54 	if (!libnvme_test_ns_create(ctrl, size, lbaf, NULL, &err)) {
55 		warnx("TEST FAILED: failed to initialize namespace create "
56 		    "request when out of namesapces");
57 		return (false);
58 	} else if (!libnvme_test_ctrl_err(ctrl, NVME_CQE_SCT_SPECIFIC,
59 	    NVME_CQE_SC_SPC_NS_NO_ID, "running out of namespace IDs")) {
60 		return (false);
61 	}
62 
63 	return (true);
64 }
65 
66 static bool
ns_max_alloc_cb(nvme_ctrl_t * ctrl,const nvme_ns_disc_t * disc,void * arg)67 ns_max_alloc_cb(nvme_ctrl_t *ctrl, const nvme_ns_disc_t *disc,
68     void *arg)
69 {
70 	ns_cb_t *cb = arg;
71 	const uint32_t nsid = nvme_ns_disc_nsid(disc);
72 
73 	cb->nc_nns++;
74 	if (!libnvme_test_setup_ns(ctrl, NVME_NS_DISC_F_ACTIVE, nsid,
75 	    cb->nc_lbaf)) {
76 		cb->nc_pass = false;
77 	}
78 	return (true);
79 }
80 
81 static bool
ns_max_active_cb(nvme_ctrl_t * ctrl,const nvme_ns_disc_t * disc,void * arg)82 ns_max_active_cb(nvme_ctrl_t *ctrl, const nvme_ns_disc_t *disc,
83     void *arg)
84 {
85 	ns_cb_t *cb = arg;
86 	const uint32_t nsid = nvme_ns_disc_nsid(disc);
87 
88 	cb->nc_nns++;
89 	if (nvme_ns_disc_level(disc) != NVME_NS_DISC_F_NOT_IGNORED) {
90 		warnx("TEST FAILED: encountered unusable namespace %u", nsid);
91 		cb->nc_pass = false;
92 		return (true);
93 	}
94 
95 	if (!libnvme_test_setup_ns(ctrl, NVME_NS_DISC_F_BLKDEV, nsid,
96 	    cb->nc_lbaf)) {
97 		cb->nc_pass = false;
98 	}
99 
100 	return (true);
101 }
102 
103 static bool
ns_max_blkdev_cb(nvme_ctrl_t * ctrl,const nvme_ns_disc_t * disc,void * arg)104 ns_max_blkdev_cb(nvme_ctrl_t *ctrl, const nvme_ns_disc_t *disc,
105     void *arg)
106 {
107 	ns_cb_t *cb = arg;
108 	const uint32_t nsid = nvme_ns_disc_nsid(disc);
109 
110 	cb->nc_nns++;
111 	if (!libnvme_test_setup_ns(ctrl, NVME_NS_DISC_F_ALL, nsid,
112 	    cb->nc_lbaf)) {
113 		cb->nc_pass = false;
114 	}
115 
116 	return (true);
117 }
118 
119 static bool
ns_max_cb_check(const ns_cb_t * cb,const char * desc,uint32_t nns)120 ns_max_cb_check(const ns_cb_t *cb, const char *desc, uint32_t nns)
121 {
122 	bool ret = true;
123 
124 	if (cb->nc_nns != nns) {
125 		ret = false;
126 		warnx("TEST FAILED: only iterated over %u/%u namespaces "
127 		    "during %s pass", cb->nc_nns, nns, desc);
128 	}
129 
130 	if (!cb->nc_pass) {
131 		ret = false;
132 		warnx("TEST FAILED: %s iteration did not work on all "
133 		    "devices", desc);
134 	}
135 
136 	if (ret) {
137 		(void) printf("TEST PASSED: successfully processed all %u %s "
138 		    "namespaces\n", nns, desc);
139 	}
140 
141 	return (ret);
142 }
143 
144 int
main(void)145 main(void)
146 {
147 	int ret = EXIT_SUCCESS;
148 	nvme_t *nvme;
149 	nvme_ctrl_t *ctrl;
150 	nvme_ctrl_info_t *info;
151 	uint32_t nns, lbaf, *nsids;
152 	ns_cb_t cb;
153 
154 	libnvme_test_init(&nvme, &ctrl);
155 	if (!nvme_ctrl_lock(ctrl, NVME_LOCK_L_WRITE, NVME_LOCK_F_DONT_BLOCK)) {
156 		libnvme_test_ctrl_fatal(ctrl, "failed to obtain write lock");
157 	}
158 
159 	if (!nvme_ctrl_info_snap(ctrl, &info)) {
160 		libnvme_test_ctrl_fatal(ctrl, "failed to get info snapshot");
161 	}
162 
163 	if (!libnvme_test_lbaf(info, NVME_TEST_LBA_SIZE, &lbaf)) {
164 		errx(EXIT_FAILURE, "failed to find 4K LBA format, cannot "
165 		    "continue");
166 	}
167 
168 	nns = nvme_ctrl_info_nns(info);
169 	nsids = calloc(nns, sizeof (uint32_t));
170 	if (nsids == NULL) {
171 		errx(EXIT_FAILURE, "failed to allocate space to store %u NSIDs",
172 		    nns);
173 	}
174 
175 	for (uint32_t i = 0; i < nns; i++) {
176 		const uint64_t size = NVME_TEST_NS_SIZE /
177 		    NVME_TEST_LBA_SIZE;
178 		if (!libnvme_test_ns_create(ctrl, size, lbaf,
179 		    &nsids[i], NULL)) {
180 			errx(EXIT_FAILURE, "failed to create namespace %u", i);
181 		}
182 		if (((i + 1) % 10) == 0) {
183 			(void) printf("Created %u/%u namespaces\n", i + 1, nns);
184 		}
185 	}
186 	(void) printf("TEST PASSED: successfully created all namespaces (%u)\n",
187 	    nns);
188 
189 	/*
190 	 * Sort all of the IDs. They should be in the range [1, nns]. They
191 	 * should be all off by one from the array index.
192 	 */
193 	bool valid = true;
194 	qsort(nsids, nns, sizeof (uint32_t), nsid_comp);
195 	for (uint32_t i = 0; i < nns; i++) {
196 		if (nsids[i] != i + 1) {
197 			warnx("TEST FAILED: returned namespace ID %u is not %u",
198 			    nsids[i], i + 1);
199 			valid = false;
200 		}
201 	}
202 	if (valid) {
203 		(void) printf("TEST PASSED: all namespaces have unique IDs\n");
204 	}
205 
206 	/*
207 	 * At this point creating one more namespace should fail with an error.
208 	 */
209 	if (!ns_max_fail_ns(ctrl, lbaf))
210 		ret = EXIT_FAILURE;
211 
212 	/*
213 	 * Now go through and attach everything to the point that it exists with
214 	 * blkdev. We do this in a few passes to make sure that properly refer
215 	 * to it all.
216 	 */
217 	cb.nc_lbaf = lbaf;
218 	cb.nc_nns = 0;
219 	cb.nc_pass = true;
220 	if (!nvme_ns_discover(ctrl, NVME_NS_DISC_F_ALL, ns_max_alloc_cb, &cb)) {
221 		libnvme_test_ctrl_warn(ctrl, "failed to iterate allocated "
222 		    "namespaces");
223 		ret = EXIT_FAILURE;
224 	}
225 
226 	if (!ns_max_cb_check(&cb, "allocated", nns))
227 		ret = EXIT_FAILURE;
228 
229 	cb.nc_nns = 0;
230 	cb.nc_pass = true;
231 	if (!nvme_ns_discover(ctrl, NVME_NS_DISC_F_ALL, ns_max_active_cb,
232 	    &cb)) {
233 		libnvme_test_ctrl_warn(ctrl, "failed to iterate active "
234 		    "namespaces");
235 		ret = EXIT_FAILURE;
236 	}
237 
238 	if (!ns_max_cb_check(&cb, "active", nns))
239 		ret = EXIT_FAILURE;
240 
241 	cb.nc_nns = 0;
242 	cb.nc_pass = true;
243 	if (!nvme_ns_discover(ctrl, NVME_NS_DISC_F_ALL, ns_max_blkdev_cb,
244 	    &cb)) {
245 		libnvme_test_ctrl_warn(ctrl, "failed to iterate blkdev "
246 		    "namespaces");
247 		ret = EXIT_FAILURE;
248 	}
249 
250 	if (!ns_max_cb_check(&cb, "blkdev", nns))
251 		ret = EXIT_FAILURE;
252 
253 	free(nsids);
254 	nvme_ctrl_info_free(info);
255 	nvme_ctrl_unlock(ctrl);
256 	nvme_ctrl_fini(ctrl);
257 	nvme_fini(nvme);
258 
259 	if (ret == EXIT_SUCCESS) {
260 		(void) printf("All tests passed successfully\n");
261 	}
262 
263 	return (ret);
264 }
265