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 2024 Oxide Computer Company 14 */ 15 16 /* 17 * Namespace information. 18 */ 19 20 #include <string.h> 21 #include <stdlib.h> 22 #include <stdarg.h> 23 24 #include "libnvme_impl.h" 25 26 bool 27 nvme_ns_info_error(nvme_ns_info_t *info, nvme_info_err_t err, int32_t sys, 28 const char *fmt, ...) 29 { 30 int ret; 31 va_list ap; 32 33 info->nni_err = err; 34 info->nni_syserr = sys; 35 va_start(ap, fmt); 36 ret = vsnprintf(info->nni_errmsg, sizeof (info->nni_errmsg), fmt, ap); 37 va_end(ap); 38 if (ret >= sizeof (info->nni_errmsg)) { 39 info->nni_errlen = sizeof (info->nni_errmsg) - 1; 40 } else if (ret <= 0) { 41 info->nni_errlen = 0; 42 info->nni_errmsg[0] = '\0'; 43 } else { 44 info->nni_errlen = (size_t)ret; 45 } 46 47 return (false); 48 } 49 50 bool 51 nvme_ns_info_success(nvme_ns_info_t *info) 52 { 53 info->nni_err = NVME_INFO_ERR_OK; 54 info->nni_syserr = 0; 55 info->nni_errmsg[0] = '\0'; 56 info->nni_errlen = 0; 57 58 return (true); 59 } 60 61 nvme_info_err_t 62 nvme_ns_info_err(nvme_ns_info_t *info) 63 { 64 return (info->nni_err); 65 } 66 67 int32_t 68 nvme_ns_info_syserr(nvme_ns_info_t *info) 69 { 70 return (info->nni_syserr); 71 } 72 73 const char * 74 nvme_ns_info_errmsg(nvme_ns_info_t *info) 75 { 76 return (info->nni_errmsg); 77 } 78 79 size_t 80 nvme_ns_info_errlen(nvme_ns_info_t *info) 81 { 82 return (info->nni_errlen); 83 } 84 85 const char * 86 nvme_ns_info_errtostr(nvme_ns_info_t *info, nvme_info_err_t err) 87 { 88 return (nvme_ctrl_info_errtostr(NULL, err)); 89 } 90 91 void 92 nvme_ns_info_free(nvme_ns_info_t *info) 93 { 94 free(info); 95 } 96 97 bool 98 nvme_ns_info_snap(nvme_ns_t *ns, nvme_ns_info_t **infop) 99 { 100 nvme_ctrl_t *ctrl = ns->nn_ctrl; 101 nvme_ns_info_t *info; 102 103 if (infop == NULL) { 104 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 105 "encountered invalid nvme_ns_info_t output pointer: %p", 106 infop)); 107 } 108 109 info = calloc(1, sizeof (nvme_ns_info_t)); 110 if (info == NULL) { 111 int e = errno; 112 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 113 "allocate memory for a new nvme_ns_info_t: %s", 114 strerror(e))); 115 } 116 117 info->nni_nsid = ns->nn_nsid; 118 if (!nvme_ioc_ns_info(ns->nn_ctrl, ns->nn_nsid, &info->nni_info)) { 119 nvme_ns_info_free(info); 120 return (false); 121 } 122 info->nni_vers = ns->nn_ctrl->nc_vers; 123 info->nni_level = nvme_ns_state_to_disc_level(info->nni_info.nni_state); 124 125 *infop = info; 126 return (nvme_ctrl_success(ctrl)); 127 } 128 129 bool 130 nvme_ctrl_ns_info_snap(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_ns_info_t **infop) 131 { 132 nvme_ns_info_t *info; 133 134 if (infop == NULL) { 135 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 136 "encountered invalid nvme_ns_info_t output pointer: %p", 137 infop)); 138 } 139 140 if (nsid < NVME_NSID_MIN || nsid > ctrl->nc_info.id_nn) { 141 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "requested " 142 "namespace %u is invalid, valid namespaces are [0x%x, " 143 "0x%x]", nsid, NVME_NSID_MIN, ctrl->nc_info.id_nn)); 144 } 145 146 info = calloc(1, sizeof (nvme_ns_info_t)); 147 if (info == NULL) { 148 int e = errno; 149 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 150 "allocate memory for a new nvme_ns_info_t: %s", 151 strerror(e))); 152 } 153 154 info->nni_nsid = nsid; 155 if (!nvme_ioc_ns_info(ctrl, nsid, &info->nni_info)) { 156 nvme_ns_info_free(info); 157 return (false); 158 } 159 info->nni_vers = ctrl->nc_vers; 160 info->nni_level = nvme_ns_state_to_disc_level(info->nni_info.nni_state); 161 162 *infop = info; 163 return (nvme_ctrl_success(ctrl)); 164 } 165 166 uint32_t 167 nvme_ns_info_nsid(nvme_ns_info_t *info) 168 { 169 return (info->nni_nsid); 170 } 171 172 const nvme_identify_nsid_t * 173 nvme_ns_info_identify(nvme_ns_info_t *info) 174 { 175 return (&info->nni_info.nni_id); 176 } 177 178 nvme_ns_disc_level_t 179 nvme_ns_info_level(nvme_ns_info_t *info) 180 { 181 return (info->nni_level); 182 } 183 184 static bool 185 nvme_ns_info_req_active(nvme_ns_info_t *info, const nvme_version_t *vers) 186 { 187 if (info->nni_level < NVME_NS_DISC_F_ACTIVE) { 188 return (nvme_ns_info_error(info, NVME_INFO_ERR_NS_INACTIVE, 0, 189 "information cannot be provided for inactive namespaces: " 190 "namespace is %s (0x%x)", 191 nvme_nsleveltostr(info->nni_level), info->nni_level)); 192 } 193 194 if (!nvme_vers_ns_info_atleast(info, vers)) { 195 return (nvme_ns_info_error(info, NVME_INFO_ERR_VERSION, 0, 196 "cannot provide information, device must be at least " 197 "version %u.%u, but is %u.%u", vers->v_major, vers->v_minor, 198 info->nni_vers.v_major, info->nni_vers.v_minor)); 199 } 200 201 return (true); 202 } 203 204 bool 205 nvme_ns_info_nguid(nvme_ns_info_t *info, uint8_t nguid[16]) 206 { 207 const uint8_t zero_guid[16] = { 0 }; 208 209 if (!nvme_ns_info_req_active(info, &nvme_vers_1v2)) { 210 return (false); 211 } 212 213 if (memcmp(zero_guid, info->nni_info.nni_id.id_nguid, 214 sizeof (zero_guid)) == 0) { 215 return (nvme_ns_info_error(info, NVME_INFO_ERR_MISSING_CAP, 0, 216 "Namespace GUID invalid: found all 0s")); 217 } 218 219 (void) memcpy(nguid, info->nni_info.nni_id.id_nguid, 220 sizeof (info->nni_info.nni_id.id_nguid)); 221 222 return (nvme_ns_info_success(info)); 223 } 224 225 bool 226 nvme_ns_info_eui64(nvme_ns_info_t *info, uint8_t eui64[8]) 227 { 228 const uint8_t zero_eui64[8] = { 0 }; 229 230 if (!nvme_ns_info_req_active(info, &nvme_vers_1v1)) { 231 return (false); 232 } 233 234 if (memcmp(zero_eui64, info->nni_info.nni_id.id_eui64, 235 sizeof (zero_eui64)) == 0) { 236 return (nvme_ns_info_error(info, NVME_INFO_ERR_MISSING_CAP, 0, 237 "Namespace EUI64 invalid: found all 0s")); 238 } 239 240 (void) memcpy(eui64, info->nni_info.nni_id.id_eui64, 241 sizeof (info->nni_info.nni_id.id_eui64)); 242 243 return (nvme_ns_info_success(info)); 244 } 245 246 bool 247 nvme_ns_info_size(nvme_ns_info_t *info, uint64_t *sizep) 248 { 249 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) { 250 return (false); 251 } 252 253 *sizep = info->nni_info.nni_id.id_nsize; 254 return (nvme_ns_info_success(info)); 255 } 256 257 bool 258 nvme_ns_info_cap(nvme_ns_info_t *info, uint64_t *capp) 259 { 260 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) { 261 return (false); 262 } 263 264 *capp = info->nni_info.nni_id.id_ncap; 265 return (nvme_ns_info_success(info)); 266 } 267 268 bool 269 nvme_ns_info_use(nvme_ns_info_t *info, uint64_t *usep) 270 { 271 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) { 272 return (false); 273 } 274 275 *usep = info->nni_info.nni_id.id_nuse; 276 return (nvme_ns_info_success(info)); 277 } 278 279 bool 280 nvme_ns_info_nformats(nvme_ns_info_t *info, uint32_t *nfmtp) 281 { 282 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) { 283 return (false); 284 } 285 286 *nfmtp = info->nni_info.nni_id.id_nlbaf + 1; 287 return (nvme_ns_info_success(info)); 288 } 289 290 bool 291 nvme_ns_info_format(nvme_ns_info_t *info, uint32_t idx, 292 const nvme_nvm_lba_fmt_t **fmtp) 293 { 294 uint32_t max; 295 const nvme_identify_nsid_t *nsid = &info->nni_info.nni_id; 296 297 if (!nvme_ns_info_nformats(info, &max)) { 298 return (false); 299 } 300 301 if (idx >= max) { 302 return (nvme_ns_info_error(info, NVME_INFO_ERR_BAD_FMT, 0, 303 "requested index %u is invalid: valid range is [0, %u]", 304 idx, max - 1)); 305 } 306 307 if (!info->nni_lbaf_valid[idx]) { 308 uint8_t lbads = nsid->id_lbaf[idx].lbaf_lbads; 309 310 if (lbads == 0) { 311 return (nvme_ns_info_error(info, NVME_INFO_ERR_BAD_FMT, 312 0, "format %u is not actually valid due to 0 LBA " 313 "data size even though it is considered a valid " 314 "LBA format by NLBAF", lbads)); 315 } 316 317 if (lbads < 9) { 318 return (nvme_ns_info_error(info, 319 NVME_INFO_ERR_BAD_FMT_DATA, 0, "NVMe devices are " 320 "not allowed to have a LBA data size of less than " 321 "512 bytes, found raw shift value of %u for " 322 "format %u", lbads, idx)); 323 } 324 325 if (lbads >= 64) { 326 return (nvme_ns_info_error(info, 327 NVME_INFO_ERR_BAD_FMT_DATA, 0, "LBA format %u has " 328 "LBA data size greater " "than 64 (%u), cannot be " 329 "represented as a byte size", idx, lbads)); 330 } 331 332 info->nni_lbaf[idx].nnlf_id = idx; 333 info->nni_lbaf[idx].nnlf_ms = nsid->id_lbaf[idx].lbaf_ms; 334 info->nni_lbaf[idx].nnlf_lbasz = 1ULL << lbads; 335 info->nni_lbaf[idx].nnlf_rel = nsid->id_lbaf[idx].lbaf_rp; 336 info->nni_lbaf_valid[idx] = true; 337 } 338 339 *fmtp = &info->nni_lbaf[idx]; 340 return (nvme_ns_info_success(info)); 341 } 342 343 344 bool 345 nvme_ns_info_curformat(nvme_ns_info_t *info, const nvme_nvm_lba_fmt_t **fmtp) 346 { 347 uint32_t idx; 348 349 if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) { 350 return (false); 351 } 352 353 idx = info->nni_info.nni_id.id_flbas.lba_format; 354 return (nvme_ns_info_format(info, idx, fmtp)); 355 } 356 357 bool 358 nvme_ns_info_bd_addr(nvme_ns_info_t *info, const char **addrp) 359 { 360 if (info->nni_level < NVME_NS_DISC_F_BLKDEV) { 361 return (nvme_ns_info_error(info, NVME_INFO_ERR_NS_NO_BLKDEV, 0, 362 "the blkdev address cannot be provided for namespaces " 363 "without blkdev attached: namespace is %s (0x%x)", 364 nvme_nsleveltostr(info->nni_level), info->nni_level)); 365 } 366 367 *addrp = info->nni_info.nni_addr; 368 return (nvme_ns_info_success(info)); 369 } 370