From 98416108553bf521c15002068a65a0fc3e74e52e Mon Sep 17 00:00:00 2001 From: Cunyin Chang Date: Thu, 25 Feb 2016 11:44:44 +0800 Subject: [PATCH] spdk: Add namespace management interface and unit tests. Change-Id: I9d203bf7532d50b1f8c8ca50c50df09ded8b5256 Signed-off-by: Cunyin Chang --- examples/nvme/Makefile | 2 +- examples/nvme/nvme_manage/.gitignore | 1 + examples/nvme/nvme_manage/Makefile | 57 +++ examples/nvme/nvme_manage/nvme_manage.c | 464 ++++++++++++++++++ include/spdk/nvme.h | 58 +++ include/spdk/nvme_spec.h | 16 +- lib/nvme/nvme_ctrlr.c | 96 ++++ lib/nvme/nvme_ctrlr_cmd.c | 102 ++++ lib/nvme/nvme_internal.h | 9 +- .../nvme/unit/nvme_ctrlr_c/nvme_ctrlr_ut.c | 28 ++ .../unit/nvme_ctrlr_cmd_c/nvme_ctrlr_cmd_ut.c | 76 ++- 11 files changed, 905 insertions(+), 4 deletions(-) create mode 100644 examples/nvme/nvme_manage/.gitignore create mode 100644 examples/nvme/nvme_manage/Makefile create mode 100644 examples/nvme/nvme_manage/nvme_manage.c diff --git a/examples/nvme/Makefile b/examples/nvme/Makefile index 16321a7ac..3fe0e31d8 100644 --- a/examples/nvme/Makefile +++ b/examples/nvme/Makefile @@ -34,7 +34,7 @@ SPDK_ROOT_DIR := $(CURDIR)/../.. include $(SPDK_ROOT_DIR)/mk/spdk.common.mk -DIRS-y += identify perf reserve +DIRS-y += identify perf reserve nvme_manage .PHONY: all clean $(DIRS-y) diff --git a/examples/nvme/nvme_manage/.gitignore b/examples/nvme/nvme_manage/.gitignore new file mode 100644 index 000000000..cdc78a1a1 --- /dev/null +++ b/examples/nvme/nvme_manage/.gitignore @@ -0,0 +1 @@ +nvme_manage diff --git a/examples/nvme/nvme_manage/Makefile b/examples/nvme/nvme_manage/Makefile new file mode 100644 index 000000000..afdf91f73 --- /dev/null +++ b/examples/nvme/nvme_manage/Makefile @@ -0,0 +1,57 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(CURDIR)/../../.. +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +APP = nvme_manage + +C_SRCS := nvme_manage.c + +CFLAGS += -I. $(DPDK_INC) + +SPDK_LIBS += $(SPDK_ROOT_DIR)/lib/nvme/libspdk_nvme.a \ + $(SPDK_ROOT_DIR)/lib/util/libspdk_util.a \ + $(SPDK_ROOT_DIR)/lib/memory/libspdk_memory.a + +LIBS += $(SPDK_LIBS) -lpciaccess -lpthread $(DPDK_LIB) -lrt + +all : $(APP) + +$(APP) : $(OBJS) $(SPDK_LIBS) + $(LINK_C) + +clean : + $(CLEAN_C) $(APP) + +include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk diff --git a/examples/nvme/nvme_manage/nvme_manage.c b/examples/nvme/nvme_manage/nvme_manage.c new file mode 100644 index 000000000..bac7daf75 --- /dev/null +++ b/examples/nvme/nvme_manage/nvme_manage.c @@ -0,0 +1,464 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "spdk/nvme.h" +#include "spdk/pci.h" + +struct rte_mempool *request_mempool; + +#define MAX_DEVS 64 + +struct dev { + struct spdk_pci_device *pci_dev; + struct spdk_nvme_ctrlr *ctrlr; + struct spdk_nvme_ns_data *ndata; + const struct spdk_nvme_ctrlr_data *cdata; + struct spdk_nvme_ctrlr_list *ctrlr_list; +}; + +static struct dev devs[MAX_DEVS]; +static int num_devs = 0; + +#define foreach_dev(iter) \ + for (iter = devs; iter - devs < num_devs; iter++) + +static struct dev * +get_controller(void) +{ + int bus; + int devid; + int function; + struct dev *iter; + const struct spdk_nvme_ctrlr_data *cdata; + + printf("Please Input Bus ID: \n"); + if (!scanf("%d", &bus)) { + printf("Invalid Bus ID\n"); + while (getchar() != '\n'); + return NULL; + } + printf("Please Input Dev ID: \n"); + if (!scanf("%d", &devid)) { + printf("Invalid Dev ID\n"); + while (getchar() != '\n'); + return NULL; + } + printf("Please Input Function ID: \n"); + if (!scanf("%d", &function)) { + printf("Invalid Function ID\n"); + while (getchar() != '\n'); + return NULL; + } + + foreach_dev(iter) { + if (spdk_pci_device_get_bus(iter->pci_dev) == bus && + spdk_pci_device_get_dev(iter->pci_dev) == devid && + spdk_pci_device_get_func(iter->pci_dev) == function) { + cdata = spdk_nvme_ctrlr_get_data(iter->ctrlr); + iter->cdata = cdata; + return iter; + } + } + return NULL; +} + +static void +ns_attach(struct dev *device, int attachment_op, int ctrlr_id, int ns_id) +{ + int ret = 0; + + device->ctrlr_list = rte_zmalloc("nvme controller list", sizeof(struct spdk_nvme_ctrlr_list), + 4096); + if (device->ctrlr_list == NULL) { + printf("Allocation error (controller list)\n"); + exit(1); + } + + device->ctrlr_list->ctrlr_count = 1; + device->ctrlr_list->ctrlr_list[0] = ctrlr_id; + + if (attachment_op == SPDK_NVME_NS_CTRLR_ATTACH) { + ret = spdk_nvme_ctrlr_attach_ns(device->ctrlr, ns_id, device->ctrlr_list); + } else if (attachment_op == SPDK_NVME_NS_CTRLR_DETACH) { + ret = spdk_nvme_ctrlr_detach_ns(device->ctrlr, ns_id, device->ctrlr_list); + } + + if (ret) { + fprintf(stdout, "ns attach: Failed\n"); + } + + rte_free(device->ctrlr_list); +} + +static void +ns_manage_add(struct dev *device, uint64_t ns_size, uint64_t ns_capacity, int ns_lbasize) +{ + int ret = 0; + + device->ndata = rte_zmalloc("nvme namespace data", sizeof(struct spdk_nvme_ns_data), 4096); + if (device->ndata == NULL) { + printf("Allocation error (namespace data)\n"); + exit(1); + } + + device->ndata->nsze = ns_size; + device->ndata->ncap = ns_capacity; + device->ndata->flbas.format = ns_lbasize; + ret = spdk_nvme_ctrlr_create_ns(device->ctrlr, device->ndata); + if (ret) { + fprintf(stdout, "ns manage: Failed\n"); + } + + rte_free(device->ndata); +} + +static void +ns_manage_delete(struct dev *device, int ns_id) +{ + int ret = 0; + + ret = spdk_nvme_ctrlr_delete_ns(device->ctrlr, ns_id); + if (ret) { + fprintf(stdout, "ns manage: Failed\n"); + return; + } +} + +static bool +probe_cb(void *cb_ctx, struct spdk_pci_device *dev) +{ + if (spdk_pci_device_has_non_uio_driver(dev)) { + fprintf(stderr, "non-uio kernel driver attached to NVMe\n"); + fprintf(stderr, " controller at PCI address %04x:%02x:%02x.%02x\n", + spdk_pci_device_get_domain(dev), + spdk_pci_device_get_bus(dev), + spdk_pci_device_get_dev(dev), + spdk_pci_device_get_func(dev)); + fprintf(stderr, " skipping...\n"); + return false; + } + + return true; +} + +static void +attach_cb(void *cb_ctx, struct spdk_pci_device *pci_dev, struct spdk_nvme_ctrlr *ctrlr) +{ + struct dev *dev; + + /* add to dev list */ + dev = &devs[num_devs++]; + dev->pci_dev = pci_dev; + dev->ctrlr = ctrlr; +} + +static const char *ealargs[] = { + "nvme_manage", + "-c 0x1", + "-n 4", +}; + +static void usage(void) +{ + printf("NVMe Management Options"); + printf("\n"); + printf("\t[1: list controllers]\n"); + printf("\t[2: create namespace]\n"); + printf("\t[3: delete namespace]\n"); + printf("\t[4: attach namespace to controller]\n"); + printf("\t[5: detach namespace from controller]\n"); + printf("\t[6: quit]\n"); +} + +static void +display_namespace(struct spdk_nvme_ns *ns) +{ + const struct spdk_nvme_ns_data *nsdata; + uint32_t i; + + nsdata = spdk_nvme_ns_get_data(ns); + + printf("Namespace ID:%d\n", spdk_nvme_ns_get_id(ns)); + + printf("Size (in LBAs): %lld (%lldM)\n", + (long long)nsdata->nsze, + (long long)nsdata->nsze / 1024 / 1024); + printf("Capacity (in LBAs): %lld (%lldM)\n", + (long long)nsdata->ncap, + (long long)nsdata->ncap / 1024 / 1024); + printf("Utilization (in LBAs): %lld (%lldM)\n", + (long long)nsdata->nuse, + (long long)nsdata->nuse / 1024 / 1024); + + printf("Number of LBA Formats: %d\n", nsdata->nlbaf + 1); + printf("Current LBA Format: LBA Format #%02d\n", + nsdata->flbas.format); + for (i = 0; i <= nsdata->nlbaf; i++) + printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d\n", + i, 1 << nsdata->lbaf[i].lbads, nsdata->lbaf[i].ms); + printf("\n"); +} + +static void +display_controller(struct dev *dev) +{ + const struct spdk_nvme_ctrlr_data *cdata; + uint8_t str[128]; + uint32_t i; + + cdata = spdk_nvme_ctrlr_get_data(dev->ctrlr); + + printf("=====================================================\n"); + printf("NVMe Controller at PCI bus %d, device %d, function %d\n", + spdk_pci_device_get_bus(dev->pci_dev), spdk_pci_device_get_dev(dev->pci_dev), + spdk_pci_device_get_func(dev->pci_dev)); + printf("=====================================================\n"); + printf("Controller Capabilities/Features\n"); + printf("Controller ID: %d\n", cdata->cntlid); + snprintf(str, sizeof(cdata->sn) + 1, "%s", cdata->sn); + printf("Serial Number: %s\n", str); + printf("\n"); + printf("Admin Command Set Attributes\n"); + printf("============================\n"); + printf("Namespace Manage And Attach: %s\n", + cdata->oacs.ns_manage ? "Supported" : "Not Supported"); + printf("\n"); + printf("Namespace Attributes\n"); + printf("============================\n"); + for (i = 1; i <= spdk_nvme_ctrlr_get_num_ns(dev->ctrlr); i++) { + display_namespace(spdk_nvme_ctrlr_get_ns(dev->ctrlr, i)); + } +} + +static void +display_controller_list(void) +{ + struct dev *iter; + + foreach_dev(iter) { + display_controller(iter); + } +} + +static void +attach_and_detach_ns(int attachment_op) +{ + int ns_id; + struct dev *ctrlr; + + ctrlr = get_controller(); + if (ctrlr == NULL) { + printf("Invalid controller PCI BDF.\n"); + return; + } + + printf("Please Input Namespace ID: \n"); + if (!scanf("%d", &ns_id)) { + printf("Invalid Namespace ID\n"); + while (getchar() != '\n'); + return; + } + + if (ctrlr->cdata->oacs.ns_manage) { + ns_attach(ctrlr, attachment_op, ctrlr->cdata->cntlid, ns_id); + } else { + printf("Controller does not support ns management\n"); + } +} + +static void +add_ns(void) +{ + uint64_t ns_size; + uint64_t ns_capacity; + int ns_lbasize; + struct dev *ctrlr; + + ctrlr = get_controller(); + if (ctrlr == NULL) { + printf("Invalid controller PCI BDF.\n"); + return; + } + + printf("Please Input Namespace Size (in LBAs): \n"); + if (!scanf("%ld", &ns_size)) { + printf("Invalid Namespace Size\n"); + while (getchar() != '\n'); + return; + } + + printf("Please Input Namespace Capacity (in LBAs): \n"); + if (!scanf("%ld", &ns_capacity)) { + printf("Invalid Namespace Capacity\n"); + while (getchar() != '\n'); + return; + } + + printf("Please Input LBA Format Number (0 - 15): \n"); + if (!scanf("%d", &ns_lbasize)) { + printf("Invalid LBA format size\n"); + while (getchar() != '\n'); + return; + } + + if (ctrlr->cdata->oacs.ns_manage) { + ns_manage_add(ctrlr, ns_size, ns_capacity, ns_lbasize); + } else { + printf("Controller does not support ns management\n"); + } +} + +static void +delete_ns(void) +{ + int ns_id; + struct dev *ctrlr; + + ctrlr = get_controller(); + if (ctrlr == NULL) { + printf("Invalid controller PCI BDF.\n"); + return; + } + + printf("Please Input Namespace ID: \n"); + if (!scanf("%d", &ns_id)) { + printf("Invalid Namespace ID\n"); + while (getchar() != '\n'); + return; + } + + if (ctrlr->cdata->oacs.ns_manage) { + ns_manage_delete(ctrlr, ns_id); + } else { + printf("Controller does not support ns management\n"); + } +} + +int main(int argc, char **argv) +{ + int rc, i; + + rc = rte_eal_init(sizeof(ealargs) / sizeof(ealargs[0]), + (char **)(void *)(uintptr_t)ealargs); + + if (rc < 0) { + fprintf(stderr, "could not initialize dpdk\n"); + exit(1); + } + + request_mempool = rte_mempool_create("nvme_request", 8192, + spdk_nvme_request_size(), 128, 0, + NULL, NULL, NULL, NULL, + SOCKET_ID_ANY, 0); + + if (request_mempool == NULL) { + fprintf(stderr, "could not initialize request mempool\n"); + exit(1); + } + + if (spdk_nvme_probe(NULL, probe_cb, attach_cb) != 0) { + fprintf(stderr, "spdk_nvme_probe() failed\n"); + return 1; + } + + if (num_devs) { + rc = spdk_nvme_register_io_thread(); + if (rc != 0) + return rc; + } + + usage(); + + while (1) { + int cmd; + bool exit_flag = false; + + if (!scanf("%d", &cmd)) { + printf("Invalid Command\n"); + while (getchar() != '\n'); + return 0; + } + switch (cmd) { + case 1: + display_controller_list(); + break; + case 2: + add_ns(); + break; + case 3: + delete_ns(); + break; + case 4: + attach_and_detach_ns(SPDK_NVME_NS_CTRLR_ATTACH); + break; + case 5: + attach_and_detach_ns(SPDK_NVME_NS_CTRLR_DETACH); + break; + case 6: + exit_flag = true; + break; + default: + printf("Invalid Command\n"); + break; + } + + if (exit_flag) + break; + + while (getchar() != '\n'); + printf("press Enter to display cmd menu ...\n"); + while (getchar() != '\n'); + usage(); + } + + printf("Cleaning up...\n"); + + for (i = 0; i < num_devs; i++) { + struct dev *dev = &devs[i]; + spdk_nvme_detach(dev->ctrlr); + } + + if (num_devs) + spdk_nvme_unregister_io_thread(); + + return rc; +} diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h index 567ac52fc..74a0db071 100644 --- a/include/spdk/nvme.h +++ b/include/spdk/nvme.h @@ -340,6 +340,64 @@ int spdk_nvme_ctrlr_cmd_get_feature(struct spdk_nvme_ctrlr *ctrlr, void *payload, uint32_t payload_size, spdk_nvme_cmd_cb cb_fn, void *cb_arg); +/** + * \brief attach the specified namespace to controllers. + * + * \param payload The pointer to the controller list. + * + * \return 0 if successfully submitted, ENOMEM if resources could not be allocated for this request + * + * This function is thread safe and can be called at any point after spdk_nvme_attach(). + * + * Call \ref spdk_nvme_ctrlr_process_admin_completions() to poll for completion + * of commands submitted through this function. + */ +int spdk_nvme_ctrlr_attach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload); + +/** + * \brief detach the specified namespace from controllers. + * + * \param payload The pointer to the controller list. + * + * \return 0 if successfully submitted, ENOMEM if resources could not be allocated for this request + * + * This function is thread safe and can be called at any point after spdk_nvme_attach(). + * + * Call \ref spdk_nvme_ctrlr_process_admin_completions() to poll for completion + * of commands submitted through this function. + */ +int spdk_nvme_ctrlr_detach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload); + +/** + * \brief create a namespace. + * + * \param payload The pointer to the nvme namespace data. + * + * \return 0 if successfully submitted, ENOMEM if resources could not be allocated for this request + * + * This function is thread safe and can be called at any point after spdk_nvme_attach(). + * + * Call \ref spdk_nvme_ctrlr_process_admin_completions() to poll for completion + * of commands submitted through this function. + */ +int spdk_nvme_ctrlr_create_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns_data *payload); + +/** + * \brief delete a namespace. + * + * \param nsid the name space identifier. + * + * \return 0 if successfully submitted, ENOMEM if resources could not be allocated for this request + * + * This function is thread safe and can be called at any point after spdk_nvme_attach(). + * + * Call \ref spdk_nvme_ctrlr_process_admin_completions() to poll for completion + * of commands submitted through this function. + */ +int spdk_nvme_ctrlr_delete_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid); + /** * \brief Get the identify namespace data as defined by the NVMe specification. * diff --git a/include/spdk/nvme_spec.h b/include/spdk/nvme_spec.h index 2c5df546f..2074149d9 100644 --- a/include/spdk/nvme_spec.h +++ b/include/spdk/nvme_spec.h @@ -637,7 +637,10 @@ struct __attribute__((packed)) spdk_nvme_ctrlr_data { /* supports firmware activate/download commands */ uint16_t firmware : 1; - uint16_t oacs_rsvd : 13; + /* supports ns manage/ns attach commands */ + uint16_t ns_manage : 1; + + uint16_t oacs_rsvd : 12; } oacs; /** abort command limit */ @@ -1214,6 +1217,17 @@ enum spdk_nvme_ns_management_type { /* 0x2-0xF - Reserved */ }; +struct spdk_nvme_ns_list { + uint32_t ns_list[1024]; +}; +SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_ns_list) == 4096, "Incorrect size"); + +struct spdk_nvme_ctrlr_list { + uint16_t ctrlr_count; + uint16_t ctrlr_list[2047]; +}; +SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_ctrlr_list) == 4096, "Incorrect size"); + #define spdk_nvme_cpl_is_error(cpl) \ ((cpl)->status.sc != 0 || (cpl)->status.sct != 0) diff --git a/lib/nvme/nvme_ctrlr.c b/lib/nvme/nvme_ctrlr.c index 9247b3083..b55478344 100644 --- a/lib/nvme/nvme_ctrlr.c +++ b/lib/nvme/nvme_ctrlr.c @@ -1020,3 +1020,99 @@ spdk_nvme_ctrlr_is_feature_supported(struct spdk_nvme_ctrlr *ctrlr, uint8_t feat SPDK_STATIC_ASSERT(sizeof(ctrlr->feature_supported) == 256, "feature_supported size mismatch"); return ctrlr->feature_supported[feature_code]; } + +int +spdk_nvme_ctrlr_attach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload) +{ + struct nvme_completion_poll_status status; + int res; + + status.done = false; + res = nvme_ctrlr_cmd_attach_ns(ctrlr, nsid, payload, + nvme_completion_poll_cb, &status); + if (res) + return res; + while (status.done == false) { + nvme_mutex_lock(&ctrlr->ctrlr_lock); + spdk_nvme_qpair_process_completions(&ctrlr->adminq, 0); + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + } + if (spdk_nvme_cpl_is_error(&status.cpl)) { + nvme_printf(ctrlr, "spdk_nvme_ctrlr_attach_ns failed!\n"); + return ENXIO; + } + + return spdk_nvme_ctrlr_reset(ctrlr); +} + +int +spdk_nvme_ctrlr_detach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload) +{ + struct nvme_completion_poll_status status; + int res; + + status.done = false; + res = nvme_ctrlr_cmd_detach_ns(ctrlr, nsid, payload, + nvme_completion_poll_cb, &status); + if (res) + return res; + while (status.done == false) { + nvme_mutex_lock(&ctrlr->ctrlr_lock); + spdk_nvme_qpair_process_completions(&ctrlr->adminq, 0); + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + } + if (spdk_nvme_cpl_is_error(&status.cpl)) { + nvme_printf(ctrlr, "spdk_nvme_ctrlr_detach_ns failed!\n"); + return ENXIO; + } + + return spdk_nvme_ctrlr_reset(ctrlr); +} + +int +spdk_nvme_ctrlr_create_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns_data *payload) +{ + struct nvme_completion_poll_status status; + int res; + + status.done = false; + res = nvme_ctrlr_cmd_create_ns(ctrlr, payload, nvme_completion_poll_cb, &status); + if (res) + return res; + while (status.done == false) { + nvme_mutex_lock(&ctrlr->ctrlr_lock); + spdk_nvme_qpair_process_completions(&ctrlr->adminq, 0); + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + } + if (spdk_nvme_cpl_is_error(&status.cpl)) { + nvme_printf(ctrlr, "spdk_nvme_ctrlr_create_ns failed!\n"); + return ENXIO; + } + + return spdk_nvme_ctrlr_reset(ctrlr); +} + +int +spdk_nvme_ctrlr_delete_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid) +{ + struct nvme_completion_poll_status status; + int res; + + status.done = false; + res = nvme_ctrlr_cmd_delete_ns(ctrlr, nsid, nvme_completion_poll_cb, &status); + if (res) + return res; + while (status.done == false) { + nvme_mutex_lock(&ctrlr->ctrlr_lock); + spdk_nvme_qpair_process_completions(&ctrlr->adminq, 0); + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + } + if (spdk_nvme_cpl_is_error(&status.cpl)) { + nvme_printf(ctrlr, "spdk_nvme_ctrlr_delete_ns failed!\n"); + return ENXIO; + } + + return spdk_nvme_ctrlr_reset(ctrlr); +} diff --git a/lib/nvme/nvme_ctrlr_cmd.c b/lib/nvme/nvme_ctrlr_cmd.c index 4e919ba24..0971b610f 100644 --- a/lib/nvme/nvme_ctrlr_cmd.c +++ b/lib/nvme/nvme_ctrlr_cmd.c @@ -189,6 +189,108 @@ nvme_ctrlr_cmd_create_io_sq(struct spdk_nvme_ctrlr *ctrlr, return 0; } +int +nvme_ctrlr_cmd_attach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload, spdk_nvme_cmd_cb cb_fn, void *cb_arg) +{ + struct nvme_request *req; + struct spdk_nvme_cmd *cmd; + + nvme_mutex_lock(&ctrlr->ctrlr_lock); + req = nvme_allocate_request_contig(payload, sizeof(struct spdk_nvme_ctrlr_list), + cb_fn, cb_arg); + if (req == NULL) { + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + return ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = SPDK_NVME_OPC_NS_ATTACHMENT; + cmd->nsid = nsid; + cmd->cdw10 = SPDK_NVME_NS_CTRLR_ATTACH; + + nvme_ctrlr_submit_admin_request(ctrlr, req); + + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + return 0; +} + +int +nvme_ctrlr_cmd_detach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload, spdk_nvme_cmd_cb cb_fn, void *cb_arg) +{ + struct nvme_request *req; + struct spdk_nvme_cmd *cmd; + + nvme_mutex_lock(&ctrlr->ctrlr_lock); + req = nvme_allocate_request_contig(payload, sizeof(struct spdk_nvme_ctrlr_list), + cb_fn, cb_arg); + if (req == NULL) { + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + return ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = SPDK_NVME_OPC_NS_ATTACHMENT; + cmd->nsid = nsid; + cmd->cdw10 = SPDK_NVME_NS_CTRLR_DETACH; + + nvme_ctrlr_submit_admin_request(ctrlr, req); + + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + return 0; +} + +int +nvme_ctrlr_cmd_create_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns_data *payload, + spdk_nvme_cmd_cb cb_fn, void *cb_arg) +{ + struct nvme_request *req; + struct spdk_nvme_cmd *cmd; + + nvme_mutex_lock(&ctrlr->ctrlr_lock); + req = nvme_allocate_request_contig(payload, sizeof(struct spdk_nvme_ns_data), + cb_fn, cb_arg); + if (req == NULL) { + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + return ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = SPDK_NVME_OPC_NS_MANAGEMENT; + cmd->cdw10 = SPDK_NVME_NS_MANAGEMENT_CREATE; + + nvme_ctrlr_submit_admin_request(ctrlr, req); + + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + return 0; +} + +int +nvme_ctrlr_cmd_delete_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, spdk_nvme_cmd_cb cb_fn, + void *cb_arg) +{ + struct nvme_request *req; + struct spdk_nvme_cmd *cmd; + + nvme_mutex_lock(&ctrlr->ctrlr_lock); + req = nvme_allocate_request_null(cb_fn, cb_arg); + if (req == NULL) { + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + return ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = SPDK_NVME_OPC_NS_MANAGEMENT; + cmd->cdw10 = SPDK_NVME_NS_MANAGEMENT_DELETE; + cmd->nsid = nsid; + + nvme_ctrlr_submit_admin_request(ctrlr, req); + + nvme_mutex_unlock(&ctrlr->ctrlr_lock); + return 0; +} + int spdk_nvme_ctrlr_cmd_set_feature(struct spdk_nvme_ctrlr *ctrlr, uint8_t feature, uint32_t cdw11, uint32_t cdw12, void *payload, uint32_t payload_size, diff --git a/lib/nvme/nvme_internal.h b/lib/nvme/nvme_internal.h index 9e636b500..c1016fb73 100644 --- a/lib/nvme/nvme_internal.h +++ b/lib/nvme/nvme_internal.h @@ -465,7 +465,14 @@ int nvme_ctrlr_cmd_set_async_event_config(struct spdk_nvme_ctrlr *ctrlr, spdk_nvme_cmd_cb cb_fn, void *cb_arg); int nvme_ctrlr_cmd_abort(struct spdk_nvme_ctrlr *ctrlr, uint16_t cid, uint16_t sqid, spdk_nvme_cmd_cb cb_fn, void *cb_arg); - +int nvme_ctrlr_cmd_attach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload, spdk_nvme_cmd_cb cb_fn, void *cb_arg); +int nvme_ctrlr_cmd_detach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload, spdk_nvme_cmd_cb cb_fn, void *cb_arg); +int nvme_ctrlr_cmd_create_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns_data *payload, + spdk_nvme_cmd_cb cb_fn, void *cb_arg); +int nvme_ctrlr_cmd_delete_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, spdk_nvme_cmd_cb cb_fn, + void *cb_arg); void nvme_completion_poll_cb(void *arg, const struct spdk_nvme_cpl *cpl); int nvme_ctrlr_construct(struct spdk_nvme_ctrlr *ctrlr, void *devhandle); diff --git a/test/lib/nvme/unit/nvme_ctrlr_c/nvme_ctrlr_ut.c b/test/lib/nvme/unit/nvme_ctrlr_c/nvme_ctrlr_ut.c index 679ca8740..7d816d1c5 100644 --- a/test/lib/nvme/unit/nvme_ctrlr_c/nvme_ctrlr_ut.c +++ b/test/lib/nvme/unit/nvme_ctrlr_c/nvme_ctrlr_ut.c @@ -170,6 +170,34 @@ nvme_ctrlr_cmd_create_io_sq(struct spdk_nvme_ctrlr *ctrlr, return 0; } +int +nvme_ctrlr_cmd_attach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload, spdk_nvme_cmd_cb cb_fn, void *cb_arg) +{ + return 0; +} + +int +nvme_ctrlr_cmd_detach_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, + struct spdk_nvme_ctrlr_list *payload, spdk_nvme_cmd_cb cb_fn, void *cb_arg) +{ + return 0; +} + +int +nvme_ctrlr_cmd_create_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns_data *payload, + spdk_nvme_cmd_cb cb_fn, void *cb_arg) +{ + return 0; +} + +int +nvme_ctrlr_cmd_delete_ns(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, spdk_nvme_cmd_cb cb_fn, + void *cb_arg) +{ + return 0; +} + void nvme_ns_destruct(struct spdk_nvme_ns *ns) { diff --git a/test/lib/nvme/unit/nvme_ctrlr_cmd_c/nvme_ctrlr_cmd_ut.c b/test/lib/nvme/unit/nvme_ctrlr_cmd_c/nvme_ctrlr_cmd_ut.c index 42ffa9e53..3a05154d7 100644 --- a/test/lib/nvme/unit/nvme_ctrlr_cmd_c/nvme_ctrlr_cmd_ut.c +++ b/test/lib/nvme/unit/nvme_ctrlr_cmd_c/nvme_ctrlr_cmd_ut.c @@ -51,7 +51,7 @@ uint8_t get_feature = 1; uint32_t get_feature_cdw11 = 1; uint16_t abort_cid = 1; uint16_t abort_sqid = 1; - +uint32_t namespace_management_nsid = 1; typedef void (*verify_request_fn_t)(struct nvme_request *req); verify_request_fn_t verify_fn; @@ -177,6 +177,34 @@ static void verify_intel_get_log_page_directory(struct nvme_request *req) CU_ASSERT(req->cmd.cdw10 == temp_cdw10); } +static void verify_namespace_attach(struct nvme_request *req) +{ + CU_ASSERT(req->cmd.opc == SPDK_NVME_OPC_NS_ATTACHMENT); + CU_ASSERT(req->cmd.cdw10 == SPDK_NVME_NS_CTRLR_ATTACH); + CU_ASSERT(req->cmd.nsid == namespace_management_nsid); +} + +static void verify_namespace_detach(struct nvme_request *req) +{ + CU_ASSERT(req->cmd.opc == SPDK_NVME_OPC_NS_ATTACHMENT); + CU_ASSERT(req->cmd.cdw10 == SPDK_NVME_NS_CTRLR_DETACH); + CU_ASSERT(req->cmd.nsid == namespace_management_nsid); +} + +static void verify_namespace_create(struct nvme_request *req) +{ + CU_ASSERT(req->cmd.opc == SPDK_NVME_OPC_NS_MANAGEMENT); + CU_ASSERT(req->cmd.cdw10 == SPDK_NVME_NS_MANAGEMENT_CREATE); + CU_ASSERT(req->cmd.nsid == 0); +} + +static void verify_namespace_delete(struct nvme_request *req) +{ + CU_ASSERT(req->cmd.opc == SPDK_NVME_OPC_NS_MANAGEMENT); + CU_ASSERT(req->cmd.cdw10 == SPDK_NVME_NS_MANAGEMENT_DELETE); + CU_ASSERT(req->cmd.nsid == namespace_management_nsid); +} + struct nvme_request * nvme_allocate_request(const struct nvme_payload *payload, uint32_t payload_size, spdk_nvme_cmd_cb cb_fn, @@ -395,6 +423,48 @@ test_get_log_pages(void) test_generic_get_log_pages(); test_intel_get_log_pages(); } + +static void +test_namespace_attach(void) +{ + struct spdk_nvme_ctrlr ctrlr = {}; + struct spdk_nvme_ctrlr_list payload = {}; + + verify_fn = verify_namespace_attach; + + nvme_ctrlr_cmd_attach_ns(&ctrlr, namespace_management_nsid, &payload, NULL, NULL); +} + +static void +test_namespace_detach(void) +{ + struct spdk_nvme_ctrlr ctrlr = {}; + struct spdk_nvme_ctrlr_list payload = {}; + + verify_fn = verify_namespace_detach; + + nvme_ctrlr_cmd_detach_ns(&ctrlr, namespace_management_nsid, &payload, NULL, NULL); +} + +static void +test_namespace_create(void) +{ + struct spdk_nvme_ctrlr ctrlr = {}; + struct spdk_nvme_ns_data payload = {}; + + verify_fn = verify_namespace_create; + nvme_ctrlr_cmd_create_ns(&ctrlr, &payload, NULL, NULL); +} + +static void +test_namespace_delete(void) +{ + struct spdk_nvme_ctrlr ctrlr = {}; + + verify_fn = verify_namespace_delete; + nvme_ctrlr_cmd_delete_ns(&ctrlr, namespace_management_nsid, NULL, NULL); +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -416,6 +486,10 @@ int main(int argc, char **argv) || CU_add_test(suite, "test ctrlr cmd get_feature", test_get_feature_cmd) == NULL || CU_add_test(suite, "test ctrlr cmd abort_cmd", test_abort_cmd) == NULL || CU_add_test(suite, "test ctrlr cmd io_raw_cmd", test_io_raw_cmd) == NULL + || CU_add_test(suite, "test ctrlr cmd namespace_attach", test_namespace_attach) == NULL + || CU_add_test(suite, "test ctrlr cmd namespace_detach", test_namespace_detach) == NULL + || CU_add_test(suite, "test ctrlr cmd namespace_create", test_namespace_create) == NULL + || CU_add_test(suite, "test ctrlr cmd namespace_delete", test_namespace_delete) == NULL ) { CU_cleanup_registry(); return CU_get_error();