From 92fa3ec5374f73cf51e2b31eee5b2e27d35118a9 Mon Sep 17 00:00:00 2001 From: Changpeng Liu Date: Fri, 22 Jan 2016 16:39:55 +0800 Subject: [PATCH] spdk: add NVMe reservation commands support NVMe reservations provide capabilities that may be used by two or more hosts to coordinate access to a shared namespace, here we add the 4 reservation commands: reservation register/acquire/release/report. Change-Id: Ib03ae2120a57dd14aa64311a6ffeb39fda73018c Signed-off-by: Changpeng Liu Signed-off-by: Ziye Yang --- examples/nvme/Makefile | 2 +- examples/nvme/reserve/.gitignore | 1 + examples/nvme/reserve/Makefile | 57 ++++ examples/nvme/reserve/reservation.c | 475 ++++++++++++++++++++++++++++ include/spdk/nvme.h | 90 ++++++ include/spdk/nvme_spec.h | 119 ++++++- lib/nvme/nvme_ns_cmd.c | 128 ++++++++ test/lib/nvme/nvme.sh | 4 + 8 files changed, 874 insertions(+), 2 deletions(-) create mode 100644 examples/nvme/reserve/.gitignore create mode 100644 examples/nvme/reserve/Makefile create mode 100644 examples/nvme/reserve/reservation.c diff --git a/examples/nvme/Makefile b/examples/nvme/Makefile index 9cf98aef7..bd9ea481c 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 +DIRS-y += identify perf reserve .PHONY: all clean $(DIRS-y) diff --git a/examples/nvme/reserve/.gitignore b/examples/nvme/reserve/.gitignore new file mode 100644 index 000000000..c58b368cf --- /dev/null +++ b/examples/nvme/reserve/.gitignore @@ -0,0 +1 @@ +reserve diff --git a/examples/nvme/reserve/Makefile b/examples/nvme/reserve/Makefile new file mode 100644 index 000000000..e1fbf1019 --- /dev/null +++ b/examples/nvme/reserve/Makefile @@ -0,0 +1,57 @@ +# +# BSD LICENSE +# +# Copyright(c) 2010-2015 Intel Corporation. All rights reserved. +# 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 = reserve + +C_SRCS := reservation.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/reserve/reservation.c b/examples/nvme/reserve/reservation.c new file mode 100644 index 000000000..64838a766 --- /dev/null +++ b/examples/nvme/reserve/reservation.c @@ -0,0 +1,475 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2015 Intel Corporation. All rights reserved. + * 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 + +#include "spdk/nvme.h" +#include "spdk/pci.h" + +struct rte_mempool *request_mempool; + +#define MAX_DEVS 64 + +struct dev { + struct pci_device *pci_dev; + struct nvme_controller *ctrlr; + char name[100]; +}; + +static struct dev devs[MAX_DEVS]; +static int num_devs = 0; + +#define foreach_dev(iter) \ + for (iter = devs; iter - devs < num_devs; iter++) + +static int outstanding_commands; +static int reserve_command_result; +static int set_feature_result; + +struct feature { + uint32_t result; + bool valid; +}; + +static struct feature features[256]; + +#define HOST_ID 0xABABABABCDCDCDCD +#define CR_KEY 0xDEADBEAF5A5A5A5B + +static void +get_feature_completion(void *cb_arg, const struct nvme_completion *cpl) +{ + struct feature *feature = cb_arg; + int fid = feature - features; + if (nvme_completion_is_error(cpl)) { + fprintf(stdout, "get_feature(0x%02X) failed\n", fid); + } else { + feature->result = cpl->cdw0; + feature->valid = true; + } + outstanding_commands--; +} + +static void +set_feature_completion(void *cb_arg, const struct nvme_completion *cpl) +{ + struct feature *feature = cb_arg; + int fid = feature - features; + if (nvme_completion_is_error(cpl)) { + fprintf(stdout, "set_feature(0x%02X) failed\n", fid); + set_feature_result = -1; + } else { + set_feature_result = 0; + } + outstanding_commands--; +} + +static int +get_host_identifier(struct nvme_controller *ctrlr) +{ + int ret; + uint64_t *host_id; + struct nvme_command cmd = {}; + + cmd.opc = NVME_OPC_GET_FEATURES; + cmd.cdw10 = NVME_FEAT_HOST_IDENTIFIER; + + outstanding_commands = 0; + + host_id = rte_malloc(NULL, 8, 0); + ret = nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, host_id, 8, + get_feature_completion, &features[NVME_FEAT_HOST_IDENTIFIER]); + if (ret) { + fprintf(stdout, "Get Feature: Failed\n"); + return -1; + } + + outstanding_commands++; + + while (outstanding_commands) { + nvme_ctrlr_process_admin_completions(ctrlr); + } + + if (features[NVME_FEAT_HOST_IDENTIFIER].valid) { + fprintf(stdout, "Get Feature: Host Identifier 0x%"PRIx64"\n", *host_id); + } + + return 0; +} + +static int +set_host_identifier(struct nvme_controller *ctrlr) +{ + int ret; + uint64_t *host_id; + struct nvme_command cmd = {}; + + cmd.opc = NVME_OPC_SET_FEATURES; + cmd.cdw10 = NVME_FEAT_HOST_IDENTIFIER; + + host_id = rte_malloc(NULL, 8, 0); + *host_id = HOST_ID; + + outstanding_commands = 0; + set_feature_result = -1; + + fprintf(stdout, "Set Feature: Host Identifier 0x%"PRIx64"\n", *host_id); + ret = nvme_ctrlr_cmd_admin_raw(ctrlr, &cmd, host_id, 8, + set_feature_completion, &features[NVME_FEAT_HOST_IDENTIFIER]); + if (ret) { + fprintf(stdout, "Set Feature: Failed\n"); + rte_free(host_id); + return -1; + } + + outstanding_commands++; + + while (outstanding_commands) { + nvme_ctrlr_process_admin_completions(ctrlr); + } + + if (set_feature_result) + fprintf(stdout, "Set Feature: Host Identifier Failed\n"); + + rte_free(host_id); + return 0; +} + +static void +reservation_ns_completion(void *cb_arg, const struct nvme_completion *cpl) +{ + if (nvme_completion_is_error(cpl)) { + reserve_command_result = -1; + } else { + reserve_command_result = 0; + } + + outstanding_commands--; +} + +static int +reservation_ns_register(struct nvme_controller *ctrlr, uint16_t ns_id) +{ + int ret; + struct nvme_reservation_register_data *rr_data; + struct nvme_namespace *ns; + + ns = nvme_ctrlr_get_ns(ctrlr, ns_id); + + rr_data = rte_zmalloc(NULL, sizeof(struct nvme_reservation_register_data), 0); + rr_data->crkey = CR_KEY; + rr_data->nrkey = CR_KEY; + + outstanding_commands = 0; + reserve_command_result = -1; + + ret = nvme_ns_cmd_reservation_register(ns, rr_data, 1, + NVME_RESERVE_REGISTER_KEY, + NVME_RESERVE_PTPL_NO_CHANGES, + reservation_ns_completion, NULL); + if (ret) { + fprintf(stderr, "Reservation Register Failed\n"); + rte_free(rr_data); + return -1; + } + + outstanding_commands++; + while (outstanding_commands) { + nvme_ctrlr_process_io_completions(ctrlr, 100); + } + + if (reserve_command_result) + fprintf(stderr, "Reservation Register Failed\n"); + + rte_free(rr_data); + return 0; +} + +static int +reservation_ns_report(struct nvme_controller *ctrlr, uint16_t ns_id) +{ + int ret, i; + uint8_t *payload; + struct nvme_reservation_status_data *status; + struct nvme_reservation_controller_data *cdata; + struct nvme_namespace *ns; + + ns = nvme_ctrlr_get_ns(ctrlr, ns_id); + payload = rte_zmalloc(NULL, 0x1000, 0x1000); + + outstanding_commands = 0; + reserve_command_result = -1; + + ret = nvme_ns_cmd_reservation_report(ns, payload, 0x1000, + reservation_ns_completion, NULL); + if (ret) { + fprintf(stderr, "Reservation Report Failed\n"); + rte_free(payload); + return -1; + } + + outstanding_commands++; + while (outstanding_commands) { + nvme_ctrlr_process_io_completions(ctrlr, 100); + } + + if (reserve_command_result) { + fprintf(stderr, "Reservation Report Failed\n"); + rte_free(payload); + return 0; + } + + status = (struct nvme_reservation_status_data *)payload; + fprintf(stdout, "Reservation Generation Counter %u\n", status->generation); + fprintf(stdout, "Reservation type %u\n", status->type); + fprintf(stdout, "Reservation Number of Registered Controllers %u\n", status->nr_regctl); + fprintf(stdout, "Reservation Persist Through Power Loss State %u\n", status->ptpl_state); + for (i = 0; i < status->nr_regctl; i++) { + cdata = (struct nvme_reservation_controller_data *)(payload + sizeof(struct + nvme_reservation_status_data) * (i + 1)); + fprintf(stdout, "Controller ID %u\n", cdata->ctrlr_id); + fprintf(stdout, "Controller Reservation Status %u\n", cdata->rcsts.status); + fprintf(stdout, "Controller Host ID 0x%"PRIx64"\n", cdata->host_id); + fprintf(stdout, "Controller Reservation Key 0x%"PRIx64"\n", cdata->key); + } + + rte_free(payload); + return 0; +} + +static int +reservation_ns_acquire(struct nvme_controller *ctrlr, uint16_t ns_id) +{ + int ret; + struct nvme_reservation_acquire_data *cdata; + struct nvme_namespace *ns; + + ns = nvme_ctrlr_get_ns(ctrlr, ns_id); + cdata = rte_zmalloc(NULL, sizeof(struct nvme_reservation_acquire_data), 0); + cdata->crkey = CR_KEY; + + outstanding_commands = 0; + reserve_command_result = -1; + + ret = nvme_ns_cmd_reservation_acquire(ns, cdata, + 0, + NVME_RESERVE_ACQUIRE, + NVME_RESERVE_WRITE_EXCLUSIVE, + reservation_ns_completion, NULL); + if (ret) { + fprintf(stderr, "Reservation Acquire Failed\n"); + rte_free(cdata); + return -1; + } + + outstanding_commands++; + while (outstanding_commands) { + nvme_ctrlr_process_io_completions(ctrlr, 100); + } + + if (reserve_command_result) + fprintf(stderr, "Reservation Acquire Failed\n"); + + rte_free(cdata); + return 0; +} + +static int +reservation_ns_release(struct nvme_controller *ctrlr, uint16_t ns_id) +{ + int ret; + struct nvme_reservation_key_data *cdata; + struct nvme_namespace *ns; + + ns = nvme_ctrlr_get_ns(ctrlr, ns_id); + cdata = rte_zmalloc(NULL, sizeof(struct nvme_reservation_key_data), 0); + cdata->crkey = CR_KEY; + + outstanding_commands = 0; + reserve_command_result = -1; + + ret = nvme_ns_cmd_reservation_release(ns, cdata, + 0, + NVME_RESERVE_RELEASE, + NVME_RESERVE_WRITE_EXCLUSIVE, + reservation_ns_completion, NULL); + if (ret) { + fprintf(stderr, "Reservation Release Failed\n"); + rte_free(cdata); + return -1; + } + + outstanding_commands++; + while (outstanding_commands) { + nvme_ctrlr_process_io_completions(ctrlr, 100); + } + + if (reserve_command_result) + fprintf(stderr, "Reservation Release Failed\n"); + + rte_free(cdata); + return 0; +} + +static void +reserve_controller(struct nvme_controller *ctrlr, struct pci_device *pci_dev) +{ + const struct nvme_controller_data *cdata; + + cdata = nvme_ctrlr_get_data(ctrlr); + + printf("=====================================================\n"); + printf("NVMe Controller at PCI bus %d, device %d, function %d\n", + pci_dev->bus, pci_dev->dev, pci_dev->func); + printf("=====================================================\n"); + + printf("Reservations: %s\n", + cdata->oncs.reservations ? "Supported" : "Not Supported"); + + if (!cdata->oncs.reservations) + return; + + set_host_identifier(ctrlr); + get_host_identifier(ctrlr); + + /* tested 1 namespace */ + reservation_ns_register(ctrlr, 1); + reservation_ns_acquire(ctrlr, 1); + reservation_ns_report(ctrlr, 1); + reservation_ns_release(ctrlr, 1); +} + +static const char *ealargs[] = { + "reserve", + "-c 0x1", + "-n 4", +}; + +int main(int argc, char **argv) +{ + struct pci_device_iterator *pci_dev_iter; + struct pci_device *pci_dev; + struct dev *iter; + struct pci_id_match match; + 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, + 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); + } + + pci_system_init(); + + match.vendor_id = PCI_MATCH_ANY; + match.subvendor_id = PCI_MATCH_ANY; + match.subdevice_id = PCI_MATCH_ANY; + match.device_id = PCI_MATCH_ANY; + match.device_class = NVME_CLASS_CODE; + match.device_class_mask = 0xFFFFFF; + + pci_dev_iter = pci_id_match_iterator_create(&match); + + rc = 0; + while ((pci_dev = pci_device_next(pci_dev_iter))) { + struct nvme_controller *ctrlr; + struct dev *dev; + + if (pci_device_has_non_uio_driver(pci_dev)) { + fprintf(stderr, "non-uio kernel driver attached to nvme\n"); + fprintf(stderr, " controller at pci bdf %d:%d:%d\n", + pci_dev->bus, pci_dev->dev, pci_dev->func); + fprintf(stderr, " skipping...\n"); + continue; + } + + pci_device_probe(pci_dev); + + ctrlr = nvme_attach(pci_dev); + if (ctrlr == NULL) { + fprintf(stderr, "failed to attach to NVMe controller at PCI BDF %d:%d:%d\n", + pci_dev->bus, pci_dev->dev, pci_dev->func); + rc = 1; + continue; + } + /* add to dev list */ + dev = &devs[num_devs++]; + dev->pci_dev = pci_dev; + dev->ctrlr = ctrlr; + } + + pci_iterator_destroy(pci_dev_iter); + + if (num_devs) { + rc = nvme_register_io_thread(); + if (rc != 0) + return rc; + } + + foreach_dev(iter) { + reserve_controller(iter->ctrlr, iter->pci_dev); + } + + printf("Cleaning up...\n"); + + for (i = 0; i < num_devs; i++) { + struct dev *dev = &devs[i]; + nvme_detach(dev->ctrlr); + } + + if (num_devs) + nvme_unregister_io_thread(); + + return rc; +} diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h index ba3d5f1d3..d309f0062 100644 --- a/include/spdk/nvme.h +++ b/include/spdk/nvme.h @@ -505,6 +505,96 @@ int nvme_ns_cmd_deallocate(struct nvme_namespace *ns, void *payload, int nvme_ns_cmd_flush(struct nvme_namespace *ns, nvme_cb_fn_t cb_fn, void *cb_arg); +/** + * \brief Submits a reservation register to the specified NVMe namespace. + * + * \param ns NVMe namespace to submit the reservation register request + * \param payload virtual address pointer to the reservation register data + * \param ignore_key '1' the current reservation key check is disabled + * \param action specifies the registration action + * \param cptpl change the Persist Through Power Loss state + * \param cb_fn callback function to invoke when the I/O is completed + * \param cb_arg argument to pass to the callback function + * + * \return 0 if successfully submitted, ENOMEM if an nvme_request + * structure cannot be allocated for the I/O request + * + * This function is thread safe and can be called at any point after + * nvme_register_io_thread(). + */ +int nvme_ns_cmd_reservation_register(struct nvme_namespace *ns, + struct nvme_reservation_register_data *payload, + bool ignore_key, + enum nvme_reservation_register_action action, + enum nvme_reservation_register_cptpl cptpl, + nvme_cb_fn_t cb_fn, void *cb_arg); + +/** + * \brief Submits a reservation release to the specified NVMe namespace. + * + * \param ns NVMe namespace to submit the reservation release request + * \param payload virtual address pointer to current reservation key + * \param ignore_key '1' the current reservation key check is disabled + * \param action specifies the reservation release action + * \param type reservation type for the namespace + * \param cb_fn callback function to invoke when the I/O is completed + * \param cb_arg argument to pass to the callback function + * + * \return 0 if successfully submitted, ENOMEM if an nvme_request + * structure cannot be allocated for the I/O request + * + * This function is thread safe and can be called at any point after + * nvme_register_io_thread(). + */ +int nvme_ns_cmd_reservation_release(struct nvme_namespace *ns, + struct nvme_reservation_key_data *payload, + bool ignore_key, + enum nvme_reservation_release_action action, + enum nvme_reservation_type type, + nvme_cb_fn_t cb_fn, void *cb_arg); + +/** + * \brief Submits a reservation acquire to the specified NVMe namespace. + * + * \param ns NVMe namespace to submit the reservation acquire request + * \param payload virtual address pointer to reservation acquire data + * \param ignore_key '1' the current reservation key check is disabled + * \param action specifies the reservation acquire action + * \param type reservation type for the namespace + * \param cb_fn callback function to invoke when the I/O is completed + * \param cb_arg argument to pass to the callback function + * + * \return 0 if successfully submitted, ENOMEM if an nvme_request + * structure cannot be allocated for the I/O request + * + * This function is thread safe and can be called at any point after + * nvme_register_io_thread(). + */ +int nvme_ns_cmd_reservation_acquire(struct nvme_namespace *ns, + struct nvme_reservation_acquire_data *payload, + bool ignore_key, + enum nvme_reservation_acquire_action action, + enum nvme_reservation_type type, + nvme_cb_fn_t cb_fn, void *cb_arg); + +/** + * \brief Submits a reservation report to the specified NVMe namespace. + * + * \param ns NVMe namespace to submit the reservation report request + * \param payload virtual address pointer for reservation status data + * \param len length bytes for reservation status data structure + * \param cb_fn callback function to invoke when the I/O is completed + * \param cb_arg argument to pass to the callback function + * + * \return 0 if successfully submitted, ENOMEM if an nvme_request + * structure cannot be allocated for the I/O request + * + * This function is thread safe and can be called at any point after + * nvme_register_io_thread(). + */ +int nvme_ns_cmd_reservation_report(struct nvme_namespace *ns, void *payload, + uint32_t len, nvme_cb_fn_t cb_fn, void *cb_arg); + /** * \brief Get the size, in bytes, of an nvme_request. * diff --git a/include/spdk/nvme_spec.h b/include/spdk/nvme_spec.h index 9a1848585..fa14c46d8 100644 --- a/include/spdk/nvme_spec.h +++ b/include/spdk/nvme_spec.h @@ -451,7 +451,10 @@ enum nvme_feature { NVME_FEAT_ASYNC_EVENT_CONFIGURATION = 0x0B, /* 0x0C-0x7F - reserved */ NVME_FEAT_SOFTWARE_PROGRESS_MARKER = 0x80, - /* 0x81-0xBF - command set specific (reserved) */ + /* 0x81-0xBF - command set specific */ + NVME_FEAT_HOST_IDENTIFIER = 0x81, + NVME_FEAT_HOST_RESERVE_MASK = 0x82, + NVME_FEAT_HOST_RESERVE_PERSIST = 0x83, /* 0xC0-0xFF - vendor specific */ }; @@ -872,6 +875,120 @@ struct nvme_namespace_data { }; SPDK_STATIC_ASSERT(sizeof(struct nvme_namespace_data) == 4096, "Incorrect size"); +/** + * Reservation Type Encoding + */ +enum nvme_reservation_type { + /* 0x00 - reserved */ + + /* Write Exclusive Reservation */ + NVME_RESERVE_WRITE_EXCLUSIVE = 0x1, + + /* Exclusive Access Reservation */ + NVME_RESERVE_EXCLUSIVE_ACCESS = 0x2, + + /* Write Exclusive - Registrants Only Reservation */ + NVME_RESERVE_WRITE_EXCLUSIVE_REG_ONLY = 0x3, + + /* Exclusive Access - Registrants Only Reservation */ + NVME_RESERVE_EXCLUSIVE_ACCESS_REG_ONLY = 0x4, + + /* Write Exclusive - All Registrants Reservation */ + NVME_RESERVE_WRITE_EXCLUSIVE_ALL_REGS = 0x5, + + /* Exclusive Access - All Registrants Reservation */ + NVME_RESERVE_EXCLUSIVE_ACCESS_ALL_REGS = 0x6, + + /* 0x7-0xFF - Reserved */ +}; + +struct nvme_reservation_acquire_data { + /** current reservation key */ + uint64_t crkey; + /** preempt reservation key */ + uint64_t prkey; +}; +SPDK_STATIC_ASSERT(sizeof(struct nvme_reservation_acquire_data) == 16, "Incorrect size"); + +/** + * Reservation Acquire action + */ +enum nvme_reservation_acquire_action { + NVME_RESERVE_ACQUIRE = 0x0, + NVME_RESERVE_PREEMPT = 0x1, + NVME_RESERVE_PREEMPT_ABORT = 0x2, +}; + +struct __attribute__((packed)) nvme_reservation_status_data { + /** reservation action generation counter */ + uint32_t generation; + /** reservation type */ + uint8_t type; + /** number of registered controllers */ + uint16_t nr_regctl; + uint16_t reserved1; + /** persist through power loss state */ + uint8_t ptpl_state; + uint8_t reserved[14]; +}; +SPDK_STATIC_ASSERT(sizeof(struct nvme_reservation_status_data) == 24, "Incorrect size"); + +struct __attribute__((packed)) nvme_reservation_controller_data { + uint16_t ctrlr_id; + /** reservation status */ + struct { + uint8_t status : 1; + uint8_t reserved1 : 7; + } rcsts; + uint8_t reserved2[5]; + /** host identifier */ + uint64_t host_id; + /** reservation key */ + uint64_t key; +}; +SPDK_STATIC_ASSERT(sizeof(struct nvme_reservation_controller_data) == 24, "Incorrect size"); + +/** + * Change persist through power loss state for + * Reservation Register command + */ +enum nvme_reservation_register_cptpl { + NVME_RESERVE_PTPL_NO_CHANGES = 0x0, + NVME_RESERVE_PTPL_CLEAR_POWER_ON = 0x2, + NVME_RESERVE_PTPL_PERSIST_POWER_LOSS = 0x3, +}; + +/** + * Registration action for Reservation Register command + */ +enum nvme_reservation_register_action { + NVME_RESERVE_REGISTER_KEY = 0x0, + NVME_RESERVE_UNREGISTER_KEY = 0x1, + NVME_RESERVE_REPLACE_KEY = 0x2, +}; + +struct nvme_reservation_register_data { + /** current reservation key */ + uint64_t crkey; + /** new reservation key */ + uint64_t nrkey; +}; +SPDK_STATIC_ASSERT(sizeof(struct nvme_reservation_register_data) == 16, "Incorrect size"); + +struct nvme_reservation_key_data { + /** current reservation key */ + uint64_t crkey; +}; +SPDK_STATIC_ASSERT(sizeof(struct nvme_reservation_key_data) == 8, "Incorrect size"); + +/** + * Reservation Release action + */ +enum nvme_reservation_release_action { + NVME_RESERVE_RELEASE = 0x0, + NVME_RESERVE_CLEAR = 0x1, +}; + /** * Log page identifiers for NVME_OPC_GET_LOG_PAGE */ diff --git a/lib/nvme/nvme_ns_cmd.c b/lib/nvme/nvme_ns_cmd.c index 3e3ec4749..357d45de3 100644 --- a/lib/nvme/nvme_ns_cmd.c +++ b/lib/nvme/nvme_ns_cmd.c @@ -294,3 +294,131 @@ nvme_ns_cmd_flush(struct nvme_namespace *ns, nvme_cb_fn_t cb_fn, void *cb_arg) return 0; } + +int +nvme_ns_cmd_reservation_register(struct nvme_namespace *ns, + struct nvme_reservation_register_data *payload, + bool ignore_key, + enum nvme_reservation_register_action action, + enum nvme_reservation_register_cptpl cptpl, + nvme_cb_fn_t cb_fn, void *cb_arg) +{ + struct nvme_request *req; + struct nvme_command *cmd; + + req = nvme_allocate_request(payload, + sizeof(struct nvme_reservation_register_data), + cb_fn, cb_arg); + if (req == NULL) { + return ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = NVME_OPC_RESERVATION_REGISTER; + cmd->nsid = ns->id; + + /* Bits 0-2 */ + cmd->cdw10 = action; + /* Bit 3 */ + cmd->cdw10 |= ignore_key ? 1 << 3 : 0; + /* Bits 30-31 */ + cmd->cdw10 |= (uint32_t)cptpl << 30; + + nvme_ctrlr_submit_io_request(ns->ctrlr, req); + + return 0; +} + +int +nvme_ns_cmd_reservation_release(struct nvme_namespace *ns, + struct nvme_reservation_key_data *payload, + bool ignore_key, + enum nvme_reservation_release_action action, + enum nvme_reservation_type type, + nvme_cb_fn_t cb_fn, void *cb_arg) +{ + struct nvme_request *req; + struct nvme_command *cmd; + + req = nvme_allocate_request(payload, sizeof(struct nvme_reservation_key_data), cb_fn, cb_arg); + if (req == NULL) { + return ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = NVME_OPC_RESERVATION_RELEASE; + cmd->nsid = ns->id; + + /* Bits 0-2 */ + cmd->cdw10 = action; + /* Bit 3 */ + cmd->cdw10 |= ignore_key ? 1 << 3 : 0; + /* Bits 8-15 */ + cmd->cdw10 |= (uint32_t)type << 8; + + nvme_ctrlr_submit_io_request(ns->ctrlr, req); + + return 0; +} + +int +nvme_ns_cmd_reservation_acquire(struct nvme_namespace *ns, + struct nvme_reservation_acquire_data *payload, + bool ignore_key, + enum nvme_reservation_acquire_action action, + enum nvme_reservation_type type, + nvme_cb_fn_t cb_fn, void *cb_arg) +{ + struct nvme_request *req; + struct nvme_command *cmd; + + req = nvme_allocate_request(payload, + sizeof(struct nvme_reservation_acquire_data), + cb_fn, cb_arg); + if (req == NULL) { + return ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = NVME_OPC_RESERVATION_ACQUIRE; + cmd->nsid = ns->id; + + /* Bits 0-2 */ + cmd->cdw10 = action; + /* Bit 3 */ + cmd->cdw10 |= ignore_key ? 1 << 3 : 0; + /* Bits 8-15 */ + cmd->cdw10 |= (uint32_t)type << 8; + + nvme_ctrlr_submit_io_request(ns->ctrlr, req); + + return 0; +} + +int +nvme_ns_cmd_reservation_report(struct nvme_namespace *ns, void *payload, + uint32_t len, nvme_cb_fn_t cb_fn, void *cb_arg) +{ + uint32_t num_dwords; + struct nvme_request *req; + struct nvme_command *cmd; + + if (len % 4) + return EINVAL; + num_dwords = len / 4; + + req = nvme_allocate_request(payload, num_dwords, cb_fn, cb_arg); + if (req == NULL) { + return ENOMEM; + } + + cmd = &req->cmd; + cmd->opc = NVME_OPC_RESERVATION_REPORT; + cmd->nsid = ns->id; + + cmd->cdw10 = num_dwords; + + nvme_ctrlr_submit_io_request(ns->ctrlr, req); + + return 0; +} diff --git a/test/lib/nvme/nvme.sh b/test/lib/nvme/nvme.sh index e8557e78a..8917677d8 100755 --- a/test/lib/nvme/nvme.sh +++ b/test/lib/nvme/nvme.sh @@ -28,6 +28,10 @@ timing_enter perf $rootdir/examples/nvme/perf/perf -q 128 -w read -s 12288 -t 5 timing_exit perf +timing_enter reserve +$rootdir/examples/nvme/reserve/reserve +timing_exit reserve + #Now test nvme reset function timing_enter reset $testdir/reset/reset -q 64 -w write -s 4096 -t 15