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 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 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 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 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 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 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 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