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