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