diff --git a/CHANGELOG.md b/CHANGELOG.md index 62318a155..875c3ca4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,25 @@ parameter. `spdk_ftl_punit_range` and `ftl_module_init_opts` structures were removed. +### nvmf + +Support for custom NVMe admin command handlers and admin command passthru +in the NVMF subsystem. + +It is now possible to set a custom handler for a specific NVMe admin command. +For example, vendor specific admin commands can now be intercepted by implementing +a function handling the command. +Further NVMe admin commands can be forwarded straight to an underlying NVMe bdev. + +The functions `spdk_nvmf_set_custom_admin_cmd_hdlr` and `spdk_nvmf_set_passthru_admin_cmd` +in `spdk_internal/nvmf.h` expose this functionality. There is an example custom admin handler +for the NVMe IDENTIFY CTRLR in `lib/nvmf/custom_cmd_hdlr.c`. This handler gets the SN, MN, FR, IEEE, FGUID +attributes from the first NVMe drive in the NVMF subsystem and returns it to the NVMF initiator (sn and mn attributes +specified during NVMF subsystem creation RPC will be overwritten). + +This handler is enabled by default and can be disabled by adding +`spdk_nvmf_set_custom_admin_cmd_hdlr(SPDK_NVME_OPC_IDENTIFY, NULL);` to a target application. + ### sock Added spdk_sock_writev_async for performing asynchronous writes to sockets. This call will diff --git a/include/spdk/nvme_spec.h b/include/spdk/nvme_spec.h index d760eda1c..bf3b97d39 100644 --- a/include/spdk/nvme_spec.h +++ b/include/spdk/nvme_spec.h @@ -1221,6 +1221,8 @@ enum spdk_nvme_path_status_code { SPDK_NVME_SC_ABORTED_BY_HOST = 0x71, }; +#define SPDK_NVME_MAX_OPC 0xff + /** * Admin opcodes */ diff --git a/include/spdk/nvmf.h b/include/spdk/nvmf.h index 64805b41d..64b8d884a 100644 --- a/include/spdk/nvmf.h +++ b/include/spdk/nvmf.h @@ -974,6 +974,15 @@ const char *spdk_nvmf_subsystem_get_nqn(struct spdk_nvmf_subsystem *subsystem); */ enum spdk_nvmf_subtype spdk_nvmf_subsystem_get_type(struct spdk_nvmf_subsystem *subsystem); +/** + * Get maximum namespace id of the specified subsystem. + * + * \param subsystem Subsystem to query. + * + * \return maximum namespace id + */ +uint32_t spdk_nvmf_subsystem_get_max_nsid(struct spdk_nvmf_subsystem *subsystem); + /** * Initialize transport options * diff --git a/include/spdk_internal/nvmf.h b/include/spdk_internal/nvmf.h index 83c87de07..0bbdac36e 100644 --- a/include/spdk_internal/nvmf.h +++ b/include/spdk_internal/nvmf.h @@ -31,11 +31,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SPDK_NVMF_INTERNAL_H_ -#define SPDK_NVMF_INTERNAL_H_ +#ifndef SPDK_INTERNAL_NVMF_H_ +#define SPDK_INTERNAL_NVMF_H_ #include "spdk/stdinc.h" #include "spdk/nvmf.h" +#include "spdk/bdev.h" typedef enum _spdk_nvmf_request_exec_status { SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE, @@ -50,4 +51,25 @@ int spdk_nvmf_ctrlr_identify_ns(struct spdk_nvmf_ctrlr *ctrlr, struct spdk_nvme_cpl *rsp, struct spdk_nvme_ns_data *nsdata); -#endif /* SPDK_NVMF_INTERNAL_H_ */ +typedef int (*spdk_nvmf_custom_cmd_hdlr)(struct spdk_nvmf_request *req); +void spdk_nvmf_set_custom_admin_cmd_hdlr(uint8_t opc, spdk_nvmf_custom_cmd_hdlr hdlr); + +void spdk_nvmf_set_passthru_admin_cmd(uint8_t opc, uint32_t forward_nsid); + +typedef void (*spdk_nvmf_nvme_passthru_cmd_cb)(struct spdk_nvmf_request *req); +int spdk_nvmf_bdev_ctrlr_nvme_passthru_admin(struct spdk_bdev *bdev, struct spdk_bdev_desc *desc, + struct spdk_io_channel *ch, struct spdk_nvmf_request *req, spdk_nvmf_nvme_passthru_cmd_cb cb_fn); + +int spdk_nvmf_request_get_bdev(uint32_t nsid, + struct spdk_nvmf_request *req, + struct spdk_bdev **bdev, + struct spdk_bdev_desc **desc, + struct spdk_io_channel **ch); +struct spdk_nvmf_ctrlr *spdk_nvmf_request_get_ctrlr(struct spdk_nvmf_request *req); +struct spdk_nvmf_subsystem *spdk_nvmf_request_get_subsystem(struct spdk_nvmf_request *req); +void spdk_nvmf_request_get_data(struct spdk_nvmf_request *req, void **data, uint32_t *length); +struct spdk_nvme_cmd *spdk_nvmf_request_get_cmd(struct spdk_nvmf_request *req); +struct spdk_nvme_cpl *spdk_nvmf_request_get_response(struct spdk_nvmf_request *req); +int spdk_nvmf_custom_identify_hdlr(struct spdk_nvmf_request *req); + +#endif /* SPDK_INTERNAL_NVMF_H_ */ diff --git a/lib/nvmf/Makefile b/lib/nvmf/Makefile index 58952001f..a8c8e2075 100644 --- a/lib/nvmf/Makefile +++ b/lib/nvmf/Makefile @@ -35,7 +35,8 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk C_SRCS = ctrlr.c ctrlr_discovery.c ctrlr_bdev.c \ - subsystem.c nvmf.c nvmf_rpc.c transport.c tcp.c + subsystem.c nvmf.c nvmf_rpc.c transport.c tcp.c \ + custom_cmd_hdlr.c C_SRCS-$(CONFIG_RDMA) += rdma.c LIBNAME = nvmf diff --git a/lib/nvmf/ctrlr.c b/lib/nvmf/ctrlr.c index f546a2e03..6ade6f4cc 100644 --- a/lib/nvmf/ctrlr.c +++ b/lib/nvmf/ctrlr.c @@ -59,6 +59,16 @@ */ #define FW_VERSION SPDK_VERSION_MAJOR_STRING SPDK_VERSION_MINOR_STRING SPDK_VERSION_PATCH_STRING +/* + * Support for custom admin command handlers + */ +struct spdk_nvmf_custom_admin_cmd { + spdk_nvmf_custom_cmd_hdlr hdlr; + uint32_t nsid; /* nsid to forward */ +}; + +static struct spdk_nvmf_custom_admin_cmd g_nvmf_custom_admin_cmd_hdlrs[SPDK_NVME_MAX_OPC + 1]; + static inline void spdk_nvmf_invalid_connect_response(struct spdk_nvmf_fabric_connect_rsp *rsp, uint8_t iattr, uint16_t ipo) @@ -2017,6 +2027,7 @@ spdk_nvmf_ctrlr_process_admin_cmd(struct spdk_nvmf_request *req) struct spdk_nvmf_ctrlr *ctrlr = req->qpair->ctrlr; struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd; struct spdk_nvme_cpl *response = &req->rsp->nvme_cpl; + int rc; if (ctrlr == NULL) { SPDK_ERRLOG("Admin command sent before CONNECT\n"); @@ -2048,6 +2059,14 @@ spdk_nvmf_ctrlr_process_admin_cmd(struct spdk_nvmf_request *req) } } + if (g_nvmf_custom_admin_cmd_hdlrs[cmd->opc].hdlr) { + rc = g_nvmf_custom_admin_cmd_hdlrs[cmd->opc].hdlr(req); + if (rc >= SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE) { + /* The handler took care of this commmand */ + return rc; + } + } + switch (cmd->opc) { case SPDK_NVME_OPC_GET_LOG_PAGE: return spdk_nvmf_ctrlr_get_log_page(req); @@ -2694,3 +2713,95 @@ spdk_nvmf_request_get_dif_ctx(struct spdk_nvmf_request *req, struct spdk_dif_ctx return spdk_nvmf_ctrlr_get_dif_ctx(ctrlr, &req->cmd->nvme_cmd, dif_ctx); } + +void +spdk_nvmf_set_custom_admin_cmd_hdlr(uint8_t opc, spdk_nvmf_custom_cmd_hdlr hdlr) +{ + g_nvmf_custom_admin_cmd_hdlrs[opc].hdlr = hdlr; +} + +static int +nvmf_passthru_admin_cmd(struct spdk_nvmf_request *req) +{ + struct spdk_bdev *bdev; + struct spdk_bdev_desc *desc; + struct spdk_io_channel *ch; + struct spdk_nvme_cmd *cmd = spdk_nvmf_request_get_cmd(req); + struct spdk_nvme_cpl *response = spdk_nvmf_request_get_response(req); + uint32_t bdev_nsid; + int rc; + + if (g_nvmf_custom_admin_cmd_hdlrs[cmd->opc].nsid == 0) { + bdev_nsid = cmd->nsid; + } else { + bdev_nsid = g_nvmf_custom_admin_cmd_hdlrs[cmd->opc].nsid; + } + + rc = spdk_nvmf_request_get_bdev(bdev_nsid, req, &bdev, &desc, &ch); + if (rc) { + response->status.sct = SPDK_NVME_SCT_GENERIC; + response->status.sc = SPDK_NVME_SC_INVALID_NAMESPACE_OR_FORMAT; + return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE; + } + return spdk_nvmf_bdev_ctrlr_nvme_passthru_admin(bdev, desc, ch, req, NULL); +} + +void +spdk_nvmf_set_passthru_admin_cmd(uint8_t opc, uint32_t forward_nsid) +{ + g_nvmf_custom_admin_cmd_hdlrs[opc].hdlr = nvmf_passthru_admin_cmd; + g_nvmf_custom_admin_cmd_hdlrs[opc].nsid = forward_nsid; +} + +int +spdk_nvmf_request_get_bdev(uint32_t nsid, struct spdk_nvmf_request *req, + struct spdk_bdev **bdev, struct spdk_bdev_desc **desc, struct spdk_io_channel **ch) +{ + struct spdk_nvmf_ctrlr *ctrlr = req->qpair->ctrlr; + struct spdk_nvmf_ns *ns; + struct spdk_nvmf_poll_group *group = req->qpair->group; + struct spdk_nvmf_subsystem_pg_ns_info *ns_info; + + *bdev = NULL; + *desc = NULL; + *ch = NULL; + + ns = _spdk_nvmf_subsystem_get_ns(ctrlr->subsys, nsid); + if (ns == NULL || ns->bdev == NULL) { + return -EINVAL; + } + + assert(group != NULL && group->sgroups != NULL); + ns_info = &group->sgroups[ctrlr->subsys->id].ns_info[nsid - 1]; + *bdev = ns->bdev; + *desc = ns->desc; + *ch = ns_info->channel; + + return 0; +} + +struct spdk_nvmf_ctrlr *spdk_nvmf_request_get_ctrlr(struct spdk_nvmf_request *req) +{ + return req->qpair->ctrlr; +} + +struct spdk_nvme_cmd *spdk_nvmf_request_get_cmd(struct spdk_nvmf_request *req) +{ + return &req->cmd->nvme_cmd; +} + +struct spdk_nvme_cpl *spdk_nvmf_request_get_response(struct spdk_nvmf_request *req) +{ + return &req->rsp->nvme_cpl; +} + +struct spdk_nvmf_subsystem *spdk_nvmf_request_get_subsystem(struct spdk_nvmf_request *req) +{ + return req->qpair->ctrlr->subsys; +} + +void spdk_nvmf_request_get_data(struct spdk_nvmf_request *req, void **data, uint32_t *length) +{ + *data = req->data; + *length = req->length; +} diff --git a/lib/nvmf/ctrlr_bdev.c b/lib/nvmf/ctrlr_bdev.c index c1b23acd2..4b9370e12 100644 --- a/lib/nvmf/ctrlr_bdev.c +++ b/lib/nvmf/ctrlr_bdev.c @@ -105,6 +105,19 @@ nvmf_bdev_ctrlr_complete_cmd(struct spdk_bdev_io *bdev_io, bool success, spdk_bdev_free_io(bdev_io); } +static void +nvmf_bdev_ctrlr_complete_admin_cmd(struct spdk_bdev_io *bdev_io, bool success, + void *cb_arg) +{ + struct spdk_nvmf_request *req = cb_arg; + + if (req->cmd_cb_fn) { + req->cmd_cb_fn(req); + } + + nvmf_bdev_ctrlr_complete_cmd(bdev_io, success, req); +} + void spdk_nvmf_bdev_ctrlr_identify_ns(struct spdk_nvmf_ns *ns, struct spdk_nvme_ns_data *nsdata, bool dif_insert_or_strip) @@ -202,6 +215,14 @@ spdk_nvmf_ctrlr_process_io_cmd_resubmit(void *arg) spdk_nvmf_ctrlr_process_io_cmd(req); } +static void +spdk_nvmf_ctrlr_process_admin_cmd_resubmit(void *arg) +{ + struct spdk_nvmf_request *req = arg; + + spdk_nvmf_ctrlr_process_admin_cmd(req); +} + static void nvmf_bdev_ctrl_queue_io(struct spdk_nvmf_request *req, struct spdk_bdev *bdev, struct spdk_io_channel *ch, spdk_bdev_io_wait_cb cb_fn, void *cb_arg) @@ -532,6 +553,30 @@ spdk_nvmf_bdev_ctrlr_nvme_passthru_io(struct spdk_bdev *bdev, struct spdk_bdev_d return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS; } +int +spdk_nvmf_bdev_ctrlr_nvme_passthru_admin(struct spdk_bdev *bdev, struct spdk_bdev_desc *desc, + struct spdk_io_channel *ch, struct spdk_nvmf_request *req, + spdk_nvmf_nvme_passthru_cmd_cb cb_fn) +{ + int rc; + + req->cmd_cb_fn = cb_fn; + + rc = spdk_bdev_nvme_admin_passthru(desc, ch, &req->cmd->nvme_cmd, req->data, req->length, + nvmf_bdev_ctrlr_complete_admin_cmd, req); + if (spdk_unlikely(rc)) { + if (rc == -ENOMEM) { + nvmf_bdev_ctrl_queue_io(req, bdev, ch, spdk_nvmf_ctrlr_process_admin_cmd_resubmit, req); + return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS; + } + req->rsp->nvme_cpl.status.sct = SPDK_NVME_SCT_GENERIC; + req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR; + return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE; + } + + return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS; +} + bool spdk_nvmf_bdev_ctrlr_get_dif_ctx(struct spdk_bdev *bdev, struct spdk_nvme_cmd *cmd, struct spdk_dif_ctx *dif_ctx) diff --git a/lib/nvmf/custom_cmd_hdlr.c b/lib/nvmf/custom_cmd_hdlr.c new file mode 100644 index 000000000..08a04cf79 --- /dev/null +++ b/lib/nvmf/custom_cmd_hdlr.c @@ -0,0 +1,117 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Hewlett Packard Enterprise Development LP + * + * 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. + */ + +/* + * This is an example handler for modifying parts of the NVMF identify + * ctlr response based on the underlying NVMe device. + */ + +#include "spdk/stdinc.h" + +#include "spdk/bdev.h" +#include "spdk_internal/nvmf.h" +#include "spdk_internal/log.h" + +static void +fixup_identify_ctrlr(struct spdk_nvmf_request *req) +{ + uint32_t length; + int rc; + struct spdk_nvme_ctrlr_data *nvme_cdata; + struct spdk_nvme_ctrlr_data nvmf_cdata = {}; + struct spdk_nvmf_ctrlr *ctrlr = spdk_nvmf_request_get_ctrlr(req); + struct spdk_nvme_cpl *rsp = spdk_nvmf_request_get_response(req); + + /* This is the identify data from the NVMe drive */ + spdk_nvmf_request_get_data(req, (void **)&nvme_cdata, &length); + + /* Get the NVMF identify data */ + rc = spdk_nvmf_ctrlr_identify_ctrlr(ctrlr, &nvmf_cdata); + if (rc != SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE) { + rsp->status.sct = SPDK_NVME_SCT_GENERIC; + rsp->status.sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR; + return; + } + + /* Fixup NVMF identify data with NVMe identify data */ + + /* Serial Number (SN) */ + memcpy(&nvmf_cdata.sn[0], &nvme_cdata->sn[0], sizeof(nvmf_cdata.sn)); + /* Model Number (MN) */ + memcpy(&nvmf_cdata.mn[0], &nvme_cdata->mn[0], sizeof(nvmf_cdata.mn)); + /* Firmware Revision (FR) */ + memcpy(&nvmf_cdata.fr[0], &nvme_cdata->fr[0], sizeof(nvmf_cdata.fr)); + /* IEEE OUI Identifier (IEEE) */ + memcpy(&nvmf_cdata.ieee[0], &nvme_cdata->ieee[0], sizeof(nvmf_cdata.ieee)); + /* FRU Globally Unique Identifier (FGUID) */ + + /* Copy the fixed up data back to the response */ + memcpy(nvme_cdata, &nvmf_cdata, length); +} + +int +spdk_nvmf_custom_identify_hdlr(struct spdk_nvmf_request *req) +{ + struct spdk_nvme_cmd *cmd = spdk_nvmf_request_get_cmd(req); + struct spdk_bdev *bdev; + struct spdk_bdev_desc *desc; + struct spdk_io_channel *ch; + struct spdk_nvmf_subsystem *subsys; + int rc; + + if (cmd->cdw10_bits.identify.cns != SPDK_NVME_IDENTIFY_CTRLR) { + return -1; /* continue */ + } + + subsys = spdk_nvmf_request_get_subsystem(req); + if (subsys == NULL) { + return -1; + } + + /* Only procss this request if it has exactly one namespace */ + if (spdk_nvmf_subsystem_get_max_nsid(subsys) != 1) { + return -1; + } + + /* Forward to first namespace if it supports NVME admin commands */ + rc = spdk_nvmf_request_get_bdev(1, req, &bdev, &desc, &ch); + if (rc) { + /* No bdev found for this namespace. Continue. */ + return -1; + } + + if (!spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_NVME_ADMIN)) { + return -1; + } + + return spdk_nvmf_bdev_ctrlr_nvme_passthru_admin(bdev, desc, ch, req, fixup_identify_ctrlr); +} diff --git a/lib/nvmf/nvmf_internal.h b/lib/nvmf/nvmf_internal.h index c5538836b..06089dfa7 100644 --- a/lib/nvmf/nvmf_internal.h +++ b/lib/nvmf/nvmf_internal.h @@ -45,6 +45,8 @@ #include "spdk/util.h" #include "spdk/thread.h" +#include "spdk_internal/nvmf.h" + #define SPDK_NVMF_MAX_SGL_ENTRIES 16 /* The maximum number of buffers per request */ @@ -221,6 +223,7 @@ struct spdk_nvmf_request { bool data_from_pool; struct spdk_bdev_io_wait_entry bdev_io_wait; struct spdk_nvmf_dif_info dif; + spdk_nvmf_nvme_passthru_cmd_cb cmd_cb_fn; STAILQ_ENTRY(spdk_nvmf_request) buf_link; TAILQ_ENTRY(spdk_nvmf_request) link; diff --git a/lib/nvmf/subsystem.c b/lib/nvmf/subsystem.c index 151aee182..245a91bb6 100644 --- a/lib/nvmf/subsystem.c +++ b/lib/nvmf/subsystem.c @@ -1314,6 +1314,12 @@ spdk_nvmf_subsystem_get_type(struct spdk_nvmf_subsystem *subsystem) return subsystem->subtype; } +uint32_t +spdk_nvmf_subsystem_get_max_nsid(struct spdk_nvmf_subsystem *subsystem) +{ + return subsystem->max_nsid; +} + static uint16_t spdk_nvmf_subsystem_gen_cntlid(struct spdk_nvmf_subsystem *subsystem) { diff --git a/module/event/subsystems/nvmf/nvmf_tgt.c b/module/event/subsystems/nvmf/nvmf_tgt.c index bac92ad98..0a24a4a38 100644 --- a/module/event/subsystems/nvmf/nvmf_tgt.c +++ b/module/event/subsystems/nvmf/nvmf_tgt.c @@ -40,6 +40,8 @@ #include "spdk/nvme.h" #include "spdk/util.h" +#include "spdk_internal/nvmf.h" + enum nvmf_tgt_state { NVMF_TGT_INIT_NONE = 0, NVMF_TGT_INIT_PARSE_CONFIG, @@ -436,6 +438,7 @@ nvmf_tgt_advance_state(void) switch (g_tgt_state) { case NVMF_TGT_INIT_NONE: { + spdk_nvmf_set_custom_admin_cmd_hdlr(SPDK_NVME_OPC_IDENTIFY, spdk_nvmf_custom_identify_hdlr); g_tgt_state = NVMF_TGT_INIT_PARSE_CONFIG; break; } diff --git a/test/unit/lib/nvmf/ctrlr.c/ctrlr_ut.c b/test/unit/lib/nvmf/ctrlr.c/ctrlr_ut.c index dd712008b..39e3405f2 100644 --- a/test/unit/lib/nvmf/ctrlr.c/ctrlr_ut.c +++ b/test/unit/lib/nvmf/ctrlr.c/ctrlr_ut.c @@ -1346,6 +1346,68 @@ test_identify_ctrlr(void) CU_ASSERT(cdata.nvmf_specific.ioccsz == expected_ioccsz); } +static int +custom_admin_cmd_hdlr(struct spdk_nvmf_request *req) +{ + req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_SUCCESS; + + return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE; +}; + +static void +test_custom_admin_cmd(void) +{ + struct spdk_nvmf_subsystem subsystem; + struct spdk_nvmf_qpair qpair; + struct spdk_nvmf_ctrlr ctrlr; + struct spdk_nvmf_request req; + struct spdk_nvmf_ns *ns_ptrs[1]; + struct spdk_nvmf_ns ns; + union nvmf_h2c_msg cmd; + union nvmf_c2h_msg rsp; + struct spdk_bdev bdev; + uint8_t buf[4096]; + int rc; + + memset(&subsystem, 0, sizeof(subsystem)); + ns_ptrs[0] = &ns; + subsystem.ns = ns_ptrs; + subsystem.max_nsid = 1; + subsystem.subtype = SPDK_NVMF_SUBTYPE_NVME; + + memset(&ns, 0, sizeof(ns)); + ns.opts.nsid = 1; + ns.bdev = &bdev; + + memset(&qpair, 0, sizeof(qpair)); + qpair.ctrlr = &ctrlr; + + memset(&ctrlr, 0, sizeof(ctrlr)); + ctrlr.subsys = &subsystem; + ctrlr.vcprop.cc.bits.en = 1; + + memset(&req, 0, sizeof(req)); + req.qpair = &qpair; + req.cmd = &cmd; + req.rsp = &rsp; + req.xfer = SPDK_NVME_DATA_CONTROLLER_TO_HOST; + req.data = buf; + req.length = sizeof(buf); + + memset(&cmd, 0, sizeof(cmd)); + cmd.nvme_cmd.opc = 0xc1; + cmd.nvme_cmd.nsid = 0; + memset(&rsp, 0, sizeof(rsp)); + + spdk_nvmf_set_custom_admin_cmd_hdlr(cmd.nvme_cmd.opc, custom_admin_cmd_hdlr); + + /* Ensure that our hdlr is being called */ + rc = spdk_nvmf_ctrlr_process_admin_cmd(&req); + CU_ASSERT(rc == SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE); + CU_ASSERT(rsp.nvme_cpl.status.sct == SPDK_NVME_SCT_GENERIC); + CU_ASSERT(rsp.nvme_cpl.status.sc == SPDK_NVME_SC_SUCCESS); +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -1376,7 +1438,8 @@ int main(int argc, char **argv) test_reservation_notification_log_page) == NULL || CU_add_test(suite, "get_dif_ctx", test_get_dif_ctx) == NULL || CU_add_test(suite, "set_get_features", test_set_get_features) == NULL || - CU_add_test(suite, "identify_ctrlr", test_identify_ctrlr) == NULL) { + CU_add_test(suite, "identify_ctrlr", test_identify_ctrlr) == NULL || + CU_add_test(suite, "custom_admin_cmd", test_custom_admin_cmd) == NULL) { CU_cleanup_registry(); return CU_get_error(); }