diff --git a/lib/nvme/nvme_ctrlr.c b/lib/nvme/nvme_ctrlr.c index d121ea8545..5e122c077b 100644 --- a/lib/nvme/nvme_ctrlr.c +++ b/lib/nvme/nvme_ctrlr.c @@ -678,22 +678,43 @@ nvme_ctrlr_alloc_ana_log_page(struct spdk_nvme_ctrlr *ctrlr) uint32_t ana_log_page_size; ana_log_page_size = sizeof(struct spdk_nvme_ana_page) + ctrlr->cdata.nanagrpid * - sizeof(struct spdk_nvme_ana_group_descriptor) + ctrlr->cdata.nn * + sizeof(struct spdk_nvme_ana_group_descriptor) + ctrlr->max_active_ns_idx * sizeof(uint32_t); - ctrlr->ana_log_page = calloc(1, ana_log_page_size); - if (ctrlr->ana_log_page == NULL) { - NVME_CTRLR_ERRLOG(ctrlr, "could not allocate ANA log page buffer\n"); - return -ENXIO; - } + /* Number of active namespaces may have changed. + * Check if ANA log page fits into existing buffer. + */ + if (ana_log_page_size > ctrlr->ana_log_page_size) { + void *new_buffer; - ctrlr->copied_ana_desc = calloc(1, ana_log_page_size); - if (ctrlr->copied_ana_desc == NULL) { - NVME_CTRLR_ERRLOG(ctrlr, "could not allocate a buffer to parse ANA descriptor\n"); - return -ENOMEM; - } + if (ctrlr->ana_log_page) { + new_buffer = realloc(ctrlr->ana_log_page, ana_log_page_size); + } else { + new_buffer = calloc(1, ana_log_page_size); + } - ctrlr->ana_log_page_size = ana_log_page_size; + if (!new_buffer) { + NVME_CTRLR_ERRLOG(ctrlr, "could not allocate ANA log page buffer, size %u\n", + ana_log_page_size); + return -ENXIO; + } + + ctrlr->ana_log_page = new_buffer; + if (ctrlr->copied_ana_desc) { + new_buffer = realloc(ctrlr->copied_ana_desc, ana_log_page_size); + } else { + new_buffer = calloc(1, ana_log_page_size); + } + + if (!new_buffer) { + NVME_CTRLR_ERRLOG(ctrlr, "could not allocate a buffer to parse ANA descriptor, size %u\n", + ana_log_page_size); + return -ENOMEM; + } + + ctrlr->copied_ana_desc = new_buffer; + ctrlr->ana_log_page_size = ana_log_page_size; + } return 0; } @@ -704,6 +725,11 @@ nvme_ctrlr_update_ana_log_page(struct spdk_nvme_ctrlr *ctrlr) struct nvme_completion_poll_status *status; int rc; + rc = nvme_ctrlr_alloc_ana_log_page(ctrlr); + if (rc != 0) { + return rc; + } + status = calloc(1, sizeof(*status)); if (status == NULL) { NVME_CTRLR_ERRLOG(ctrlr, "Failed to allocate status tracker\n"); diff --git a/test/unit/lib/nvme/nvme_ctrlr.c/nvme_ctrlr_ut.c b/test/unit/lib/nvme/nvme_ctrlr.c/nvme_ctrlr_ut.c index 34146b3986..0a1b8bd234 100644 --- a/test/unit/lib/nvme/nvme_ctrlr.c/nvme_ctrlr_ut.c +++ b/test/unit/lib/nvme/nvme_ctrlr.c/nvme_ctrlr_ut.c @@ -280,11 +280,29 @@ spdk_nvme_ctrlr_cmd_get_feature(struct spdk_nvme_ctrlr *ctrlr, uint8_t feature, return 0; } +struct spdk_nvme_ana_page *g_ana_hdr; +struct spdk_nvme_ana_group_descriptor **g_ana_descs; + int spdk_nvme_ctrlr_cmd_get_log_page(struct spdk_nvme_ctrlr *ctrlr, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size, uint64_t offset, spdk_nvme_cmd_cb cb_fn, void *cb_arg) { + if ((log_page == SPDK_NVME_LOG_ASYMMETRIC_NAMESPACE_ACCESS) && g_ana_hdr) { + uint32_t i; + uint8_t *ptr = payload; + + memset(payload, 0, payload_size); + memcpy(ptr, g_ana_hdr, sizeof(*g_ana_hdr)); + ptr += sizeof(*g_ana_hdr); + for (i = 0; i < g_ana_hdr->num_ana_group_desc; ++i) { + uint32_t desc_size = sizeof(**g_ana_descs) + + g_ana_descs[i]->num_of_nsid * sizeof(uint32_t); + memcpy(ptr, g_ana_descs[i], desc_size); + ptr += desc_size; + } + } + fake_cpl_sc(cb_fn, cb_arg); return 0; } @@ -2874,7 +2892,7 @@ test_nvme_ctrlr_set_supported_log_pages(void) ctrlr.cdata.cmic.ana_reporting = true; ctrlr.cdata.lpa.celp = 1; ctrlr.cdata.nanagrpid = 1; - ctrlr.cdata.nn = 1; + ctrlr.max_active_ns_idx = 1; rc = nvme_ctrlr_set_supported_log_pages(&ctrlr); CU_ASSERT(rc == 0); @@ -2905,6 +2923,7 @@ test_nvme_ctrlr_parse_ana_log_page(void) ctrlr.cdata.nn = 3; ctrlr.cdata.nanagrpid = 3; ctrlr.num_ns = 3; + ctrlr.max_active_ns_idx = 3; rc = nvme_ctrlr_init_ana_log_page(&ctrlr); CU_ASSERT(rc == 0); @@ -2964,6 +2983,90 @@ test_nvme_ctrlr_parse_ana_log_page(void) free(ctrlr.copied_ana_desc); } +static void +test_nvme_ctrlr_ana_resize(void) +{ + DECLARE_AND_CONSTRUCT_CTRLR(); + uint32_t active_ns_list[] = { 1, 2, 3, 4 }; + struct spdk_nvme_ana_page ana_hdr = { + .change_count = 0, + .num_ana_group_desc = 1 + }; + uint8_t ana_desc_buf[sizeof(struct spdk_nvme_ana_group_descriptor) + 4 * sizeof(uint32_t)] = {}; + struct spdk_nvme_ana_group_descriptor *ana_desc = + (struct spdk_nvme_ana_group_descriptor *)ana_desc_buf; + struct spdk_nvme_ns *ns; + union spdk_nvme_async_event_completion aer_event = { + .bits.async_event_type = SPDK_NVME_ASYNC_EVENT_TYPE_NOTICE, + .bits.async_event_info = SPDK_NVME_ASYNC_EVENT_NS_ATTR_CHANGED + }; + struct spdk_nvme_cpl aer_cpl = { + .status.sct = SPDK_NVME_SCT_GENERIC, + .status.sc = SPDK_NVME_SC_SUCCESS, + .cdw0 = aer_event.raw + }; + uint32_t i; + + SPDK_CU_ASSERT_FATAL(nvme_ctrlr_construct(&ctrlr) == 0); + + ctrlr.vs.bits.mjr = 1; + ctrlr.vs.bits.mnr = 4; + ctrlr.vs.bits.ter = 0; + ctrlr.cdata.nn = 4096; + ctrlr.cdata.cmic.ana_reporting = true; + ctrlr.cdata.nanagrpid = 1; + + ctrlr.state = NVME_CTRLR_STATE_IDENTIFY_ACTIVE_NS; + /* Start with 2 active namespaces */ + g_active_ns_list = active_ns_list; + g_active_ns_list_length = 2; + g_ana_hdr = &ana_hdr; + g_ana_descs = &ana_desc; + ana_desc->ana_group_id = 1; + ana_desc->ana_state = SPDK_NVME_ANA_NON_OPTIMIZED_STATE; + ana_desc->num_of_nsid = 2; + for (i = 0; i < ana_desc->num_of_nsid; ++i) { + ana_desc->nsid[i] = i + 1; + } + + /* Bring controller to ready state */ + while (ctrlr.state != NVME_CTRLR_STATE_READY) { + SPDK_CU_ASSERT_FATAL(nvme_ctrlr_process_init(&ctrlr) == 0); + } + + for (i = 0; i < ana_desc->num_of_nsid; ++i) { + ns = spdk_nvme_ctrlr_get_ns(&ctrlr, i + 1); + CU_ASSERT(ns->ana_state == SPDK_NVME_ANA_NON_OPTIMIZED_STATE); + } + + /* Add more namespaces */ + g_active_ns_list_length = 4; + nvme_ctrlr_async_event_cb(&ctrlr.aer[0], &aer_cpl); + nvme_ctrlr_complete_queued_async_events(&ctrlr); + + /* Update ANA log with new namespaces */ + ana_desc->ana_state = SPDK_NVME_ANA_OPTIMIZED_STATE; + ana_desc->num_of_nsid = 4; + for (i = 0; i < ana_desc->num_of_nsid; ++i) { + ana_desc->nsid[i] = i + 1; + } + aer_event.bits.async_event_info = SPDK_NVME_ASYNC_EVENT_ANA_CHANGE; + aer_cpl.cdw0 = aer_event.raw; + nvme_ctrlr_async_event_cb(&ctrlr.aer[0], &aer_cpl); + nvme_ctrlr_complete_queued_async_events(&ctrlr); + + for (i = 0; i < ana_desc->num_of_nsid; ++i) { + ns = spdk_nvme_ctrlr_get_ns(&ctrlr, i + 1); + CU_ASSERT(ns->ana_state == SPDK_NVME_ANA_OPTIMIZED_STATE); + } + + g_active_ns_list = 0; + g_active_ns_list_length = 0; + g_ana_hdr = NULL; + g_ana_descs = NULL; + nvme_ctrlr_destruct(&ctrlr); +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -3015,6 +3118,7 @@ int main(int argc, char **argv) CU_ADD_TEST(suite, test_nvme_ctrlr_identify_namespaces_iocs_specific_next); CU_ADD_TEST(suite, test_nvme_ctrlr_set_supported_log_pages); CU_ADD_TEST(suite, test_nvme_ctrlr_parse_ana_log_page); + CU_ADD_TEST(suite, test_nvme_ctrlr_ana_resize); CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests();