lib/nvmf: custom admin cmd handler
This commit provides the capability to install a custom admin command handler for NVMF. It can be used to implement or replace NVMe admin commands that are currently not handled by the NVMF subsystem. The handler implementation is pretty generic and the handler function has to figure out what to do with the command based on the bdevs that are configured for the subsystem. In cases where admin commands need to be forwarded to an NVMe bdev, the commit provides functions that allow access to the underlying bdev. There is an example handler in lib/nvmf/custom_cmd_hdlr.c. Change-Id: I4f9d538c53669c176a836e8bdd379db0070a87dc Signed-off-by: Michael Haeuptle <michael.haeuptle@hpe.com> Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/479167 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: <jacek.kalwas@intel.com>
This commit is contained in:
parent
d0cbb46875
commit
3fa2205647
19
CHANGELOG.md
19
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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
111
lib/nvmf/ctrlr.c
111
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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
117
lib/nvmf/custom_cmd_hdlr.c
Normal file
117
lib/nvmf/custom_cmd_hdlr.c
Normal file
@ -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);
|
||||
}
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user