/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2024 Oxide Computer Company */ /* * This file implements the logic behind the NVMe controller information * interface. * * The idea behind controller information is to gather all of the information * related to controller information in one structure that can then be * interrogated. This data should have its own lifetime and represents a point * in time snapshot. This then allows this information to be saved and restored * across systems. */ #include #include #include #include #include "libnvme_impl.h" bool nvme_info_error(nvme_ctrl_info_t *ci, nvme_info_err_t err, int32_t sys, const char *fmt, ...) { int ret; va_list ap; ci->nci_err = err; ci->nci_syserr = sys; va_start(ap, fmt); ret = vsnprintf(ci->nci_errmsg, sizeof (ci->nci_errmsg), fmt, ap); va_end(ap); if (ret >= sizeof (ci->nci_errmsg)) { ci->nci_errlen = sizeof (ci->nci_errmsg) - 1; } else if (ret <= 0) { ci->nci_errlen = 0; ci->nci_errmsg[0] = '\0'; } else { ci->nci_errlen = (size_t)ret; } return (false); } bool nvme_info_success(nvme_ctrl_info_t *ci) { ci->nci_err = NVME_INFO_ERR_OK; ci->nci_syserr = 0; ci->nci_errmsg[0] = '\0'; ci->nci_errlen = 0; return (true); } nvme_info_err_t nvme_ctrl_info_err(nvme_ctrl_info_t *ci) { return (ci->nci_err); } int32_t nvme_ctrl_info_syserr(nvme_ctrl_info_t *ci) { return (ci->nci_syserr); } const char * nvme_ctrl_info_errmsg(nvme_ctrl_info_t *ci) { return (ci->nci_errmsg); } size_t nvme_ctrl_info_errlen(nvme_ctrl_info_t *ci) { return (ci->nci_errlen); } /* * These errors are shared with the nvme_ns_info_t structures. While they both * allow for us to pass in their respective information objects, that's mostly * for future API changes. The namespace information variant of this just calls * this function with the control information set to NULL. */ const char * nvme_ctrl_info_errtostr(nvme_ctrl_info_t *ci, nvme_info_err_t err) { switch (err) { case NVME_INFO_ERR_OK: return ("NVME_INFO_ERR_OK"); case NVME_INFO_ERR_TRANSPORT: return ("NVME_INFO_ERR_TRANSPORT"); case NVME_INFO_ERR_VERSION: return ("NVME_INFO_ERR_VERSION"); case NVME_INFO_ERR_MISSING_CAP: return ("NVME_INFO_ERR_MISSING_CAP"); case NVME_INFO_ERR_BAD_LBA_FMT: return ("NVME_INFO_ERR_BAD_LBA_FMT"); case NVME_INFO_ERR_PERSIST_NVL: return ("NVME_INFO_ERR_PERSIST_NVL"); case NVME_INFO_ERR_BAD_FMT: return ("NVME_INFO_ERR_BAD_FMT"); case NVME_INFO_ERR_BAD_FMT_DATA: return ("NVME_INFO_ERR_BAD_FMT_DATA"); case NVME_INFO_ERR_NS_INACTIVE: return ("NVME_INFO_ERR_NS_INACTIVE"); case NVME_INFO_ERR_NS_NO_BLKDEV: return ("NVME_INFO_ERR_NS_NO_BLKDEV"); default: return ("unknown error"); } } void nvme_ctrl_info_free(nvme_ctrl_info_t *ci) { free(ci); } /* * The caller is required to ensure that out is at least max_src + 1 bytes long. */ static void nvme_ctrl_info_init_ident_str(const char *src, size_t max_src, char *out) { while (max_src > 0 && src[max_src - 1] == ' ') { max_src--; } if (max_src == 0) { *out = '\0'; return; } (void) memcpy(out, src, max_src); out[max_src] = '\0'; } static void nvme_ctrl_info_init_ident(nvme_ctrl_info_t *ci) { nvme_ctrl_info_init_ident_str(ci->nci_info.id_serial, sizeof (ci->nci_info.id_serial), ci->nci_serial); nvme_ctrl_info_init_ident_str(ci->nci_info.id_model, sizeof (ci->nci_info.id_model), ci->nci_model); nvme_ctrl_info_init_ident_str(ci->nci_info.id_fwrev, sizeof (ci->nci_info.id_fwrev), ci->nci_fwrev); } bool nvme_ctrl_info_restore(nvme_t *nvme, nvlist_t *nvl, nvme_ctrl_info_t **outp) { int ret; uint32_t vers; nvme_ctrl_info_t *ci; char *path; uchar_t *ctrl, *ns; uint_t ctrl_len, ns_len; if (nvl == NULL) { return (nvme_error(nvme, NVME_ERR_BAD_PTR, 0, "encountered " "invalid nvlist_t input pointer: %p", nvl)); } if (outp == NULL) { return (nvme_error(nvme, NVME_ERR_BAD_PTR, 0, "encountered " "invalid nvme_ctrl_info_t output pointer: %p", outp)); } *outp = NULL; ci = calloc(1, sizeof (nvme_ctrl_info_t)); if (ci == NULL) { int e = errno; return (nvme_error(nvme, NVME_ERR_NO_MEM, e, "failed to " "allocate memory for a new nvme_ctrl_info: %s", strerror(e))); } if ((ret = nvlist_lookup_uint32(nvl, NVME_NVL_CI_VERS, &vers)) != 0) { (void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, ret, "failed " "to get version key: %s", strerror(ret)); goto err; } if (vers != NVME_NVL_CI_VERS_0) { (void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "found unsupported version key: 0x%x", vers); goto err; } ret = nvlist_lookup_pairs(nvl, 0, NVME_NVL_CI_MAJOR, DATA_TYPE_UINT16, &ci->nci_vers.v_major, NVME_NVL_CI_MINOR, DATA_TYPE_UINT16, &ci->nci_vers.v_minor, NVME_NVL_CI_INST, DATA_TYPE_INT32, &ci->nci_inst, NVME_NVL_CI_DEV_PATH, DATA_TYPE_STRING, &path, NVME_NVL_CI_ID_CTRL, DATA_TYPE_BYTE_ARRAY, &ctrl, &ctrl_len, NVME_NVL_CI_ID_NS, DATA_TYPE_BYTE_ARRAY, &ns, &ns_len, NVME_NVL_CI_TPORT, DATA_TYPE_UINT32, &ci->nci_tport, NULL); if (ret != 0) { (void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, ret, "failed to retrieve required keys: %s", strerror(ret)); goto err; } if (ci->nci_inst < 0) { (void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "instance data is negative"); goto err; } if (ctrl_len != sizeof (ci->nci_info)) { (void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "identify " "controller information is the wrong length, expected " "0x%zx bytes, found 0x%x", sizeof (ci->nci_info), ctrl_len); goto err; } if (ns_len != sizeof (ci->nci_ns)) { (void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "identify " "namespace information is the wrong length, expected " "0x%zx bytes, found 0x%x", sizeof (ci->nci_info), ctrl_len); goto err; } (void) memcpy(&ci->nci_info, ctrl, ctrl_len); (void) memcpy(&ci->nci_ns, ns, ns_len); if (strlcpy(ci->nci_dev_path, path, sizeof (ci->nci_dev_path)) >= sizeof (ci->nci_dev_path)) { (void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "device " "path would have overflowed"); goto err; } if (ci->nci_tport != NVME_CTRL_TRANSPORT_PCI) { (void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, 0, "found " "unknown transport type: 0x%x", ci->nci_tport); goto err; } ret = nvlist_lookup_pairs(nvl, 0, NVME_NVL_CI_PCI_VID, DATA_TYPE_UINT16, &ci->nci_vid, NVME_NVL_CI_PCI_DID, DATA_TYPE_UINT16, &ci->nci_did, NVME_NVL_CI_PCI_SUBVID, DATA_TYPE_UINT16, &ci->nci_subvid, NVME_NVL_CI_PCI_SUBSYS, DATA_TYPE_UINT16, &ci->nci_subsys, NVME_NVL_CI_PCI_REV, DATA_TYPE_UINT8, &ci->nci_rev, NVME_NVL_CI_PCI_MPSMIN, DATA_TYPE_UINT32, &ci->nci_mps_min, NVME_NVL_CI_PCI_MPSMAX, DATA_TYPE_UINT32, &ci->nci_mps_max, NVME_NVL_CI_PCI_NINTRS, DATA_TYPE_UINT32, &ci->nci_nintrs, NULL); if (ret != 0) { (void) nvme_error(nvme, NVME_ERR_BAD_RESTORE, ret, "failed to retrieve required PCI-specific keys: %s", strerror(ret)); goto err; } nvme_ctrl_info_init_ident(ci); *outp = ci; return (true); err: nvme_ctrl_info_free(ci); return (false); } bool nvme_ctrl_info_persist(nvme_ctrl_info_t *ci, nvlist_t **nvlp) { int ret; nvlist_t *nvl; if ((ret = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) { return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret, "failed to create initial nvlist_t *: %s", strerror(ret))); } if ((ret = nvlist_add_uint32(nvl, NVME_NVL_CI_VERS, NVME_NVL_CI_VERS_0)) != 0) { return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret, "failed to persist %s to nvlist: %s", NVME_NVL_CI_VERS, strerror(ret))); } if ((ret = nvlist_add_int32(nvl, NVME_NVL_CI_INST, ci->nci_inst)) != 0) { return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret, "failed to persist %s to nvlist: %s", NVME_NVL_CI_INST, strerror(ret))); } if ((ret = nvlist_add_uint16(nvl, NVME_NVL_CI_MAJOR, ci->nci_vers.v_major)) != 0 || (ret = nvlist_add_uint16(nvl, NVME_NVL_CI_MINOR, ci->nci_vers.v_minor)) != 0) { return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret, "failed to persist version data to nvlist: %s", strerror(ret))); } if ((ret = nvlist_add_string(nvl, NVME_NVL_CI_DEV_PATH, ci->nci_dev_path)) != 0) { return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret, "failed to persist %s to nvlist: %s", NVME_NVL_CI_DEV_PATH, strerror(ret))); } if ((ret = nvlist_add_byte_array(nvl, NVME_NVL_CI_ID_CTRL, (void *)&ci->nci_info, sizeof (ci->nci_info))) != 0) { return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret, "failed to persist %s to nvlist: %s", NVME_NVL_CI_ID_CTRL, strerror(ret))); } if ((ret = nvlist_add_byte_array(nvl, NVME_NVL_CI_ID_NS, (void *)&ci->nci_ns, sizeof (ci->nci_ns))) != 0) { return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret, "failed to persist %s to nvlist: %s", NVME_NVL_CI_ID_NS, strerror(ret))); } if ((ret = nvlist_add_uint32(nvl, NVME_NVL_CI_TPORT, ci->nci_tport)) != 0) { return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret, "failed to persist %s to nvlist: %s", NVME_NVL_CI_TPORT, strerror(ret))); } if ((ret = nvlist_add_uint16(nvl, NVME_NVL_CI_PCI_VID, ci->nci_vid)) != 0 || (ret = nvlist_add_uint16(nvl, NVME_NVL_CI_PCI_DID, ci->nci_did)) != 0 || (ret = nvlist_add_uint16(nvl, NVME_NVL_CI_PCI_SUBVID, ci->nci_subvid)) != 0 || (ret = nvlist_add_uint16(nvl, NVME_NVL_CI_PCI_SUBSYS, ci->nci_subsys)) != 0 || (ret = nvlist_add_uint8(nvl, NVME_NVL_CI_PCI_REV, ci->nci_rev)) != 0 || (ret = nvlist_add_uint32(nvl, NVME_NVL_CI_PCI_MPSMIN, ci->nci_mps_min)) != 0 || (ret = nvlist_add_uint32(nvl, NVME_NVL_CI_PCI_MPSMAX, ci->nci_mps_max)) != 0 || (ret = nvlist_add_uint32(nvl, NVME_NVL_CI_PCI_NINTRS, ci->nci_nintrs)) != 0) { return (nvme_info_error(ci, NVME_INFO_ERR_PERSIST_NVL, ret, "failed to persist PCI data to nvlist: %s", strerror(ret))); } *nvlp = nvl; return (true); } static bool nvme_ctrl_get_udi(nvme_ctrl_t *ctrl, di_node_t di, const char *prop, uint32_t *outp, uint32_t max) { int *vals, nvals; nvals = di_prop_lookup_ints(DDI_DEV_T_ANY, di, prop, &vals); if (nvals < 0) { int e = errno; return (nvme_ctrl_error(ctrl, NVME_ERR_LIBDEVINFO, e, "failed " "to get property %s while constructing controller " "information: %s", prop, strerror(e))); } else if (nvals != 1) { return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_DEVI_PROP, 0, "found unexpected number of property values for %s while " "constructing controller information, expected 1, found %d", prop, nvals)); } if (vals[0] < 0 || vals[0] > max) { return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_DEVI_PROP, 0, "property %s has value 0x%x outside the allowed range " "[0x0, 0x%x]", prop, vals[0], max)); } *outp = (uint32_t)vals[0]; return (true); } bool nvme_ctrl_info_snap(nvme_ctrl_t *ctrl, nvme_ctrl_info_t **outp) { nvme_ctrl_info_t *ci; uint32_t val; nvme_ioctl_ctrl_info_t info; if (outp == NULL) { return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, "encountered invalid nvme_ctrl_info_t output pointer: %p", outp)); } *outp = NULL; ci = calloc(1, sizeof (nvme_ctrl_info_t)); if (ci == NULL) { int e = errno; return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " "allocate memory for a new nvme_ctrl_info: %s", strerror(e))); } if (!nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "vendor-id", &val, UINT16_MAX)) { goto err; } ci->nci_vid = (uint16_t)val; if (!nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "device-id", &val, UINT16_MAX)) { goto err; } ci->nci_did = (uint16_t)val; /* * The system will not create a subsystem-vendor-id or a subsystem-id if * the subsytem vendor is zero. This should not be a fatal error. * However, if a subsytem-vendor-id is present then we should expect a * subsystem-id. */ if (nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "subsystem-vendor-id", &val, UINT16_MAX)) { ci->nci_subvid = (uint16_t)val; if (!nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "subsystem-id", &val, UINT16_MAX)) { goto err; } } else { ci->nci_subvid = 0; ci->nci_subsys = 0; } if (!nvme_ctrl_get_udi(ctrl, ctrl->nc_devi, "revision-id", &val, UINT8_MAX)) { goto err; } ci->nci_rev = (uint8_t)val; /* * As we only support PCI based NVMe devices right now, we simply always * identify everything as PCI based. In the future, this would be * something we'd want to get from either an ioctl or a devinfo * property. */ ci->nci_tport = NVME_CTRL_TRANSPORT_PCI; if (!nvme_ioc_ctrl_info(ctrl, &info)) { goto err; } ci->nci_vers = info.nci_vers; ci->nci_info = info.nci_ctrl_id; ci->nci_ns = info.nci_common_ns; ci->nci_mps_min = info.nci_caps.cap_mpsmin; ci->nci_mps_max = info.nci_caps.cap_mpsmax; ci->nci_nintrs = info.nci_nintrs; nvme_ctrl_info_init_ident(ci); *outp = ci; return (nvme_ctrl_success(ctrl)); err: nvme_ctrl_info_free(ci); return (false); } uint16_t nvme_ctrl_info_vendor(nvme_ctrl_info_t *ci) { return (ci->nci_info.id_vid); } const char * nvme_ctrl_info_model(nvme_ctrl_info_t *ci) { return (ci->nci_model); } const char * nvme_ctrl_info_serial(nvme_ctrl_info_t *ci) { return (ci->nci_serial); } uint32_t nvme_ctrl_info_fwgran(nvme_ctrl_info_t *ci) { nvme_valid_ctrl_data_t data; data.vcd_vers = &ci->nci_vers; data.vcd_id = &ci->nci_info; return (nvme_fw_load_granularity(&data)); } const char * nvme_ctrl_info_fwrev(nvme_ctrl_info_t *ci) { return (ci->nci_fwrev); } const nvme_identify_ctrl_t * nvme_ctrl_info_identify(nvme_ctrl_info_t *ci) { return (&ci->nci_info); } const nvme_version_t * nvme_ctrl_info_version(nvme_ctrl_info_t *ci) { return (&ci->nci_vers); } nvme_ctrl_transport_t nvme_ctrl_info_transport(nvme_ctrl_info_t *ci) { return (ci->nci_tport); } nvme_ctrl_type_t nvme_ctrl_info_type(nvme_ctrl_info_t *ci) { if (nvme_vers_ctrl_info_atleast(ci, &nvme_vers_1v4)) { switch (ci->nci_info.id_cntrltype) { case NVME_CNTRLTYPE_IO: return (NVME_CTRL_TYPE_IO); case NVME_CNTRLTYPE_DISC: return (NVME_CTRL_TYPE_DISCOVERY); case NVME_CNTRLTYPE_ADMIN: return (NVME_CTRL_TYPE_ADMIN); default: return (NVME_CTRL_TYPE_UNKNOWN); } } else { return (NVME_CTRL_TYPE_IO); } } static bool nvme_ctrl_info_pci_tport(nvme_ctrl_info_t *ci) { if (ci->nci_tport != NVME_CTRL_TRANSPORT_PCI) { return (nvme_info_error(ci, NVME_INFO_ERR_TRANSPORT, 0, "cannot get PCI data from device with type %s (0x%x)", nvme_tporttostr(ci->nci_tport), ci->nci_tport)); } return (true); } bool nvme_ctrl_info_pci_vid(nvme_ctrl_info_t *ci, uint16_t *u16p) { if (!nvme_ctrl_info_pci_tport(ci)) return (false); *u16p = ci->nci_vid; return (nvme_info_success(ci)); } bool nvme_ctrl_info_pci_did(nvme_ctrl_info_t *ci, uint16_t *u16p) { if (!nvme_ctrl_info_pci_tport(ci)) return (false); *u16p = ci->nci_did; return (nvme_info_success(ci)); } bool nvme_ctrl_info_pci_subvid(nvme_ctrl_info_t *ci, uint16_t *u16p) { if (!nvme_ctrl_info_pci_tport(ci)) return (false); *u16p = ci->nci_subvid; return (nvme_info_success(ci)); } bool nvme_ctrl_info_pci_subsys(nvme_ctrl_info_t *ci, uint16_t *u16p) { if (!nvme_ctrl_info_pci_tport(ci)) return (false); *u16p = ci->nci_subsys; return (nvme_info_success(ci)); } bool nvme_ctrl_info_pci_rev(nvme_ctrl_info_t *ci, uint8_t *u8p) { if (!nvme_ctrl_info_pci_tport(ci)) return (false); *u8p = ci->nci_rev; return (nvme_info_success(ci)); } bool nvme_ctrl_info_pci_mps_min(nvme_ctrl_info_t *ci, uint32_t *u32p) { if (!nvme_ctrl_info_pci_tport(ci)) return (false); *u32p = ci->nci_mps_min; return (nvme_info_success(ci)); } bool nvme_ctrl_info_pci_mps_max(nvme_ctrl_info_t *ci, uint32_t *u32p) { if (!nvme_ctrl_info_pci_tport(ci)) return (false); *u32p = ci->nci_mps_max; return (nvme_info_success(ci)); } bool nvme_ctrl_info_pci_nintrs(nvme_ctrl_info_t *ci, uint32_t *u32p) { if (!nvme_ctrl_info_pci_tport(ci)) return (false); *u32p = ci->nci_nintrs; return (nvme_info_success(ci)); } static bool nvme_ctrl_info_nsmgmt(nvme_ctrl_info_t *ci) { if (!nvme_vers_ctrl_info_atleast(ci, &nvme_vers_1v2)) { return (nvme_info_error(ci, NVME_INFO_ERR_VERSION, 0, "cannot provide information, device must be at least " "version 1.2, but is %u.%u", ci->nci_vers.v_major, ci->nci_vers.v_minor)); } if (ci->nci_info.id_oacs.oa_nsmgmt == 0) { return (nvme_info_error(ci, NVME_INFO_ERR_MISSING_CAP, 0, "cannot provide information, device does not support " "namespace management")); } return (true); } bool nvme_ctrl_info_cap(nvme_ctrl_info_t *ci, nvme_uint128_t *u128p) { if (!nvme_ctrl_info_nsmgmt(ci)) { return (false); } (void) memcpy(u128p, &ci->nci_info.ap_tnvmcap, sizeof (nvme_uint128_t)); return (nvme_info_success(ci)); } bool nvme_ctrl_info_unalloc_cap(nvme_ctrl_info_t *ci, nvme_uint128_t *u128p) { if (!nvme_ctrl_info_nsmgmt(ci)) { return (false); } (void) memcpy(u128p, &ci->nci_info.ap_unvmcap, sizeof (nvme_uint128_t)); return (nvme_info_success(ci)); } bool nvme_ctrl_info_common_ns(nvme_ctrl_info_t *ci, const nvme_identify_nsid_t **idp) { if (!nvme_ctrl_info_nsmgmt(ci)) { return (false); } *idp = &ci->nci_ns; return (nvme_info_success(ci)); } uint32_t nvme_ctrl_info_nformats(nvme_ctrl_info_t *ci) { return (MIN(ci->nci_ns.id_nlbaf + 1, NVME_MAX_LBAF)); } uint32_t nvme_ctrl_info_nns(nvme_ctrl_info_t *ci) { return (ci->nci_info.id_nn); } bool nvme_ctrl_info_format(nvme_ctrl_info_t *ci, uint32_t idx, const nvme_nvm_lba_fmt_t **outp) { const uint32_t max = nvme_ctrl_info_nformats(ci); if (idx >= max) { return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT, 0, "requested index %u is invalid: valid range is [0, %u]", idx, max - 1)); } if (!ci->nci_lbaf_valid[idx]) { uint8_t lbads = ci->nci_ns.id_lbaf[idx].lbaf_lbads; if (lbads == 0) { return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT, 0, "format %u is not actually valid due to 0 LBA " "data size even though it is considered a valid " "LBA format by NLBAF", lbads)); } if (lbads < 9) { return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT_DATA, 0, "NVMe devices are not allowed to have a LBA " "data size of less than 512 bytes, found raw " "shift value of %u for format %u", lbads, idx)); } if (lbads >= 64) { return (nvme_info_error(ci, NVME_INFO_ERR_BAD_FMT_DATA, 0, "LBA format %u has LBA data size greater " "than 64 (%u), cannot be represented as a byte " "size", idx, lbads)); } ci->nci_lbaf[idx].nnlf_id = idx; ci->nci_lbaf[idx].nnlf_ms = ci->nci_ns.id_lbaf[idx].lbaf_ms; ci->nci_lbaf[idx].nnlf_lbasz = 1ULL << lbads; ci->nci_lbaf[idx].nnlf_rel = ci->nci_ns.id_lbaf[idx].lbaf_rp; ci->nci_lbaf_valid[idx] = true; } *outp = &ci->nci_lbaf[idx]; return (nvme_info_success(ci)); } uint32_t nvme_nvm_lba_fmt_id(const nvme_nvm_lba_fmt_t *lbaf) { return (lbaf->nnlf_id); } uint32_t nvme_nvm_lba_fmt_meta_size(const nvme_nvm_lba_fmt_t *lbaf) { return (lbaf->nnlf_ms); } uint64_t nvme_nvm_lba_fmt_data_size(const nvme_nvm_lba_fmt_t *lbaf) { return (lbaf->nnlf_lbasz); } uint32_t nvme_nvm_lba_fmt_rel_perf(const nvme_nvm_lba_fmt_t *lbaf) { return (lbaf->nnlf_rel); }