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