diff --git a/CHANGELOG.md b/CHANGELOG.md index 023814093..68cc962a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ needed; previously, the response was limited to 32 kilobytes. EXPERIMENTAL: Adds support for WDS and RDS capable CMBs in NVMe controllers. This support is experimental pending a functional allocator to free and reallocate CMB buffers. +spdk_nvme_ns_get_uuid() has been added to allow retrieval of per-namespace UUIDs when available. + ### NVMe-oF Target Namespaces may now be assigned unique identifiers via new optional "eui64" and "nguid" parameters diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h index d674138f0..ebc9ae8ad 100644 --- a/include/spdk/nvme.h +++ b/include/spdk/nvme.h @@ -1052,6 +1052,15 @@ enum spdk_nvme_dealloc_logical_block_read_value spdk_nvme_ns_get_dealloc_logical */ uint32_t spdk_nvme_ns_get_optimal_io_boundary(struct spdk_nvme_ns *ns); +/** + * Get the UUID for the given namespace. + * + * \param ns Namespace to query. + * + * \return Pointer to namespace UUID, or NULL if ns does not have a UUID. + */ +const struct spdk_uuid *spdk_nvme_ns_get_uuid(const struct spdk_nvme_ns *ns); + /** * \brief Namespace command support flags. */ diff --git a/lib/nvme/nvme_internal.h b/lib/nvme/nvme_internal.h index d9790fdcc..adc47bc2d 100644 --- a/lib/nvme/nvme_internal.h +++ b/lib/nvme/nvme_internal.h @@ -302,6 +302,9 @@ struct spdk_nvme_ns { uint32_t sectors_per_stripe; uint32_t id; uint16_t flags; + + /* Namespace Identification Descriptor List (CNS = 03h) */ + uint8_t id_desc_list[4096]; }; /** diff --git a/lib/nvme/nvme_ns.c b/lib/nvme/nvme_ns.c index 8745cec1d..05e3f4da8 100644 --- a/lib/nvme/nvme_ns.c +++ b/lib/nvme/nvme_ns.c @@ -122,6 +122,30 @@ int nvme_ns_identify_update(struct spdk_nvme_ns *ns) ns->flags |= SPDK_NVME_NS_DPS_PI_SUPPORTED; ns->pi_type = nsdata->dps.pit; } + + memset(ns->id_desc_list, 0, sizeof(ns->id_desc_list)); + if (ns->ctrlr->cdata.ver.raw >= SPDK_NVME_VERSION(1, 3, 0)) { + SPDK_DEBUGLOG(SPDK_LOG_NVME, "Attempting to retrieve NS ID Descriptor List\n"); + status.done = false; + rc = nvme_ctrlr_cmd_identify(ns->ctrlr, SPDK_NVME_IDENTIFY_NS_ID_DESCRIPTOR_LIST, 0, ns->id, + ns->id_desc_list, sizeof(ns->id_desc_list), + nvme_completion_poll_cb, &status); + if (rc == 0) { + while (status.done == false) { + nvme_robust_mutex_lock(&ns->ctrlr->ctrlr_lock); + spdk_nvme_qpair_process_completions(ns->ctrlr->adminq, 0); + nvme_robust_mutex_unlock(&ns->ctrlr->ctrlr_lock); + } + } + + if (rc != 0 || spdk_nvme_cpl_is_error(&status.cpl)) { + SPDK_WARNLOG("Failed to retrieve NS ID Descriptor List\n"); + memset(ns->id_desc_list, 0, sizeof(ns->id_desc_list)); + } + } else { + SPDK_DEBUGLOG(SPDK_LOG_NVME, "Version < 1.3; not attempting to retrieve NS ID Descriptor List\n"); + } + return rc; } @@ -223,6 +247,55 @@ spdk_nvme_ns_get_optimal_io_boundary(struct spdk_nvme_ns *ns) return ns->sectors_per_stripe; } +static const void * +_spdk_nvme_ns_find_id_desc(const struct spdk_nvme_ns *ns, enum spdk_nvme_nidt type, size_t *length) +{ + const struct spdk_nvme_ns_id_desc *desc; + size_t offset; + + offset = 0; + while (offset + 4 < sizeof(ns->id_desc_list)) { + desc = (const struct spdk_nvme_ns_id_desc *)&ns->id_desc_list[offset]; + + if (desc->nidl == 0) { + /* End of list */ + return NULL; + } + + /* + * Check if this descriptor fits within the list. + * 4 is the fixed-size descriptor header (not counted in NIDL). + */ + if (offset + desc->nidl + 4 > sizeof(ns->id_desc_list)) { + /* Descriptor longer than remaining space in list (invalid) */ + return NULL; + } + + if (desc->nidt == type) { + *length = desc->nidl; + return &desc->nid[0]; + } + + offset += 4 + desc->nidl; + } + + return NULL; +} + +const struct spdk_uuid * +spdk_nvme_ns_get_uuid(const struct spdk_nvme_ns *ns) +{ + const struct spdk_uuid *uuid; + size_t uuid_size; + + uuid = _spdk_nvme_ns_find_id_desc(ns, SPDK_NVME_NIDT_UUID, &uuid_size); + if (uuid == NULL || uuid_size != sizeof(*uuid)) { + return NULL; + } + + return uuid; +} + int nvme_ns_construct(struct spdk_nvme_ns *ns, uint32_t id, struct spdk_nvme_ctrlr *ctrlr) { diff --git a/test/unit/lib/nvme/nvme_ns.c/nvme_ns_ut.c b/test/unit/lib/nvme/nvme_ns.c/nvme_ns_ut.c index 61c18bbab..bb35c8d69 100644 --- a/test/unit/lib/nvme/nvme_ns.c/nvme_ns_ut.c +++ b/test/unit/lib/nvme/nvme_ns.c/nvme_ns_ut.c @@ -71,6 +71,62 @@ test_nvme_ns_construct(void) CU_ASSERT(ns.id == 1); } +static void +test_nvme_ns_uuid(void) +{ + struct spdk_nvme_ns ns = {}; + const struct spdk_uuid *uuid; + struct spdk_uuid expected_uuid; + + memset(&expected_uuid, 0xA5, sizeof(expected_uuid)); + + /* Empty list - no UUID should be found */ + memset(ns.id_desc_list, 0, sizeof(ns.id_desc_list)); + uuid = spdk_nvme_ns_get_uuid(&ns); + CU_ASSERT(uuid == NULL); + + /* NGUID only (no UUID in list) */ + memset(ns.id_desc_list, 0, sizeof(ns.id_desc_list)); + ns.id_desc_list[0] = 0x02; /* NIDT == NGUID */ + ns.id_desc_list[1] = 0x10; /* NIDL */ + memset(&ns.id_desc_list[4], 0xCC, 0x10); + uuid = spdk_nvme_ns_get_uuid(&ns); + CU_ASSERT(uuid == NULL); + + /* Just UUID in the list */ + memset(ns.id_desc_list, 0, sizeof(ns.id_desc_list)); + ns.id_desc_list[0] = 0x03; /* NIDT == UUID */ + ns.id_desc_list[1] = 0x10; /* NIDL */ + memcpy(&ns.id_desc_list[4], &expected_uuid, sizeof(expected_uuid)); + uuid = spdk_nvme_ns_get_uuid(&ns); + SPDK_CU_ASSERT_FATAL(uuid != NULL); + CU_ASSERT(memcmp(uuid, &expected_uuid, sizeof(*uuid)) == 0); + + /* UUID followed by NGUID */ + memset(ns.id_desc_list, 0, sizeof(ns.id_desc_list)); + ns.id_desc_list[0] = 0x03; /* NIDT == UUID */ + ns.id_desc_list[1] = 0x10; /* NIDL */ + memcpy(&ns.id_desc_list[4], &expected_uuid, sizeof(expected_uuid)); + ns.id_desc_list[20] = 0x02; /* NIDT == NGUID */ + ns.id_desc_list[21] = 0x10; /* NIDL */ + memset(&ns.id_desc_list[24], 0xCC, 0x10); + uuid = spdk_nvme_ns_get_uuid(&ns); + SPDK_CU_ASSERT_FATAL(uuid != NULL); + CU_ASSERT(memcmp(uuid, &expected_uuid, sizeof(*uuid)) == 0); + + /* NGUID followed by UUID */ + memset(ns.id_desc_list, 0, sizeof(ns.id_desc_list)); + ns.id_desc_list[0] = 0x02; /* NIDT == NGUID */ + ns.id_desc_list[1] = 0x10; /* NIDL */ + memset(&ns.id_desc_list[4], 0xCC, 0x10); + ns.id_desc_list[20] = 0x03; /* NIDT = UUID */ + ns.id_desc_list[21] = 0x10; /* NIDL */ + memcpy(&ns.id_desc_list[24], &expected_uuid, sizeof(expected_uuid)); + uuid = spdk_nvme_ns_get_uuid(&ns); + SPDK_CU_ASSERT_FATAL(uuid != NULL); + CU_ASSERT(memcmp(uuid, &expected_uuid, sizeof(*uuid)) == 0); +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -87,7 +143,8 @@ int main(int argc, char **argv) } if ( - CU_add_test(suite, "test_nvme_ns", test_nvme_ns_construct) == NULL + CU_add_test(suite, "test_nvme_ns", test_nvme_ns_construct) == NULL || + CU_add_test(suite, "test_nvme_ns_uuid", test_nvme_ns_uuid) == NULL ) { CU_cleanup_registry(); return CU_get_error();