nvmf: Add a req_release callback to the transport layer

This can be used to release requests that don't
require a completion to be sent.

Change-Id: I8fb932ea8569bf3c45342d9fa4e270af5510c60c
Signed-off-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Ben Walker 2016-07-25 08:58:13 -07:00
parent 052be2f540
commit a6135981e8
5 changed files with 73 additions and 75 deletions

View File

@ -1104,19 +1104,6 @@ spdk_nvmf_rdma_poll(struct spdk_nvmf_conn *conn)
return -1; return -1;
} }
{
/* TEMPORARY SPECIAL CASE: For asynchronous event requests, just immediately
* re-post the capsule. */
struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
if (conn->type == CONN_TYPE_AQ &&
cmd->opc == SPDK_NVME_OPC_ASYNC_EVENT_REQUEST) {
spdk_nvmf_rdma_request_release(req);
break;
}
}
rdma_conn->outstanding_reqs++; rdma_conn->outstanding_reqs++;
SPDK_TRACELOG(SPDK_TRACE_RDMA, SPDK_TRACELOG(SPDK_TRACE_RDMA,
"RDMA RECV Complete. Request: %p Connection: %p Outstanding I/O: %d\n", "RDMA RECV Complete. Request: %p Connection: %p Outstanding I/O: %d\n",
@ -1183,6 +1170,7 @@ const struct spdk_nvmf_transport spdk_nvmf_transport_rdma = {
.transport_stop = spdk_nvmf_rdma_acceptor_stop, .transport_stop = spdk_nvmf_rdma_acceptor_stop,
.req_complete = spdk_nvmf_rdma_request_complete, .req_complete = spdk_nvmf_rdma_request_complete,
.req_release = spdk_nvmf_rdma_request_release,
.conn_fini = spdk_nvmf_rdma_conn_destroy, .conn_fini = spdk_nvmf_rdma_conn_destroy,
.conn_poll = spdk_nvmf_rdma_poll, .conn_poll = spdk_nvmf_rdma_poll,

View File

@ -44,6 +44,12 @@
#include "spdk/nvmf_spec.h" #include "spdk/nvmf_spec.h"
#include "spdk/trace.h" #include "spdk/trace.h"
typedef enum _spdk_nvmf_request_exec_status {
SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE,
SPDK_NVMF_REQUEST_EXEC_STATUS_RELEASE,
SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS,
} spdk_nvmf_request_exec_status;
int int
spdk_nvmf_request_complete(struct spdk_nvmf_request *req) spdk_nvmf_request_complete(struct spdk_nvmf_request *req)
{ {
@ -67,7 +73,7 @@ spdk_nvmf_request_complete(struct spdk_nvmf_request *req)
return 0; return 0;
} }
static bool static spdk_nvmf_request_exec_status
nvmf_process_discovery_cmd(struct spdk_nvmf_request *req) nvmf_process_discovery_cmd(struct spdk_nvmf_request *req)
{ {
struct nvmf_session *session = req->conn->sess; struct nvmf_session *session = req->conn->sess;
@ -81,7 +87,7 @@ nvmf_process_discovery_cmd(struct spdk_nvmf_request *req)
if (req->data == NULL) { if (req->data == NULL) {
SPDK_ERRLOG("discovery command with no buffer\n"); SPDK_ERRLOG("discovery command with no buffer\n");
response->status.sc = SPDK_NVME_SC_INVALID_FIELD; response->status.sc = SPDK_NVME_SC_INVALID_FIELD;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
switch (cmd->opc) { switch (cmd->opc) {
@ -90,11 +96,11 @@ nvmf_process_discovery_cmd(struct spdk_nvmf_request *req)
if ((cmd->cdw10 & 0xFF) == SPDK_NVME_IDENTIFY_CTRLR) { if ((cmd->cdw10 & 0xFF) == SPDK_NVME_IDENTIFY_CTRLR) {
SPDK_TRACELOG(SPDK_TRACE_NVMF, "Identify Controller\n"); SPDK_TRACELOG(SPDK_TRACE_NVMF, "Identify Controller\n");
memcpy(req->data, (char *)&session->vcdata, sizeof(struct spdk_nvme_ctrlr_data)); memcpy(req->data, (char *)&session->vcdata, sizeof(struct spdk_nvme_ctrlr_data));
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} else { } else {
SPDK_ERRLOG("Unsupported identify command\n"); SPDK_ERRLOG("Unsupported identify command\n");
response->status.sc = SPDK_NVME_SC_INVALID_FIELD; response->status.sc = SPDK_NVME_SC_INVALID_FIELD;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
break; break;
case SPDK_NVME_OPC_GET_LOG_PAGE: case SPDK_NVME_OPC_GET_LOG_PAGE:
@ -107,20 +113,20 @@ nvmf_process_discovery_cmd(struct spdk_nvmf_request *req)
log->genctr = 0; log->genctr = 0;
log->numrec = 0; log->numrec = 0;
spdk_format_discovery_log(log, req->length); spdk_format_discovery_log(log, req->length);
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} else { } else {
SPDK_ERRLOG("Unsupported log page %u\n", cmd->cdw10 & 0xFF); SPDK_ERRLOG("Unsupported log page %u\n", cmd->cdw10 & 0xFF);
response->status.sc = SPDK_NVME_SC_INVALID_FIELD; response->status.sc = SPDK_NVME_SC_INVALID_FIELD;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
break; break;
default: default:
SPDK_ERRLOG("Unsupported Opcode 0x%x for Discovery service\n", cmd->opc); SPDK_ERRLOG("Unsupported Opcode 0x%x for Discovery service\n", cmd->opc);
response->status.sc = SPDK_NVME_SC_INVALID_FIELD; response->status.sc = SPDK_NVME_SC_INVALID_FIELD;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
static void static void
@ -137,7 +143,7 @@ nvmf_complete_cmd(void *ctx, const struct spdk_nvme_cpl *cmp)
spdk_nvmf_request_complete(req); spdk_nvmf_request_complete(req);
} }
static bool static spdk_nvmf_request_exec_status
nvmf_process_admin_cmd(struct spdk_nvmf_request *req) nvmf_process_admin_cmd(struct spdk_nvmf_request *req)
{ {
struct nvmf_session *session = req->conn->sess; struct nvmf_session *session = req->conn->sess;
@ -156,13 +162,13 @@ nvmf_process_admin_cmd(struct spdk_nvmf_request *req)
if (req->data == NULL || req->length < sizeof(struct spdk_nvme_ctrlr_data)) { if (req->data == NULL || req->length < sizeof(struct spdk_nvme_ctrlr_data)) {
SPDK_ERRLOG("identify command with no buffer\n"); SPDK_ERRLOG("identify command with no buffer\n");
response->status.sc = SPDK_NVME_SC_INVALID_FIELD; response->status.sc = SPDK_NVME_SC_INVALID_FIELD;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
SPDK_TRACELOG(SPDK_TRACE_NVMF, "Identify Controller\n"); SPDK_TRACELOG(SPDK_TRACE_NVMF, "Identify Controller\n");
/* pull from virtual controller context */ /* pull from virtual controller context */
memcpy(req->data, &session->vcdata, sizeof(struct spdk_nvme_ctrlr_data)); memcpy(req->data, &session->vcdata, sizeof(struct spdk_nvme_ctrlr_data));
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
goto passthrough; goto passthrough;
@ -173,7 +179,7 @@ nvmf_process_admin_cmd(struct spdk_nvmf_request *req)
SPDK_TRACELOG(SPDK_TRACE_NVMF, "Get Features - Number of Queues\n"); SPDK_TRACELOG(SPDK_TRACE_NVMF, "Get Features - Number of Queues\n");
response->cdw0 = ((session->max_connections_allowed - 1) << 16) | response->cdw0 = ((session->max_connections_allowed - 1) << 16) |
(session->max_connections_allowed - 1); (session->max_connections_allowed - 1);
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
default: default:
goto passthrough; goto passthrough;
} }
@ -192,27 +198,16 @@ nvmf_process_admin_cmd(struct spdk_nvmf_request *req)
response->cdw0 = ((session->max_connections_allowed - 1) << 16) | response->cdw0 = ((session->max_connections_allowed - 1) << 16) |
(session->max_connections_allowed - 1); (session->max_connections_allowed - 1);
} }
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
default: default:
goto passthrough; goto passthrough;
} }
break; break;
case SPDK_NVME_OPC_ASYNC_EVENT_REQUEST: case SPDK_NVME_OPC_ASYNC_EVENT_REQUEST:
SPDK_TRACELOG(SPDK_TRACE_NVMF, "Async Event Request\n"); SPDK_TRACELOG(SPDK_TRACE_NVMF, "Async Event Request\n");
/* /* TODO: Just release the request as consumed. AER events will never
Trap request here and save in the session context * be triggered. */
until NVMe library indicates some event. return SPDK_NVMF_REQUEST_EXEC_STATUS_RELEASE;
*/
if (session->aer_req == NULL) {
session->aer_req = req;
return false;
} else {
/* AER already recorded, send error response */
SPDK_TRACELOG(SPDK_TRACE_NVMF, "AER already active!\n");
response->status.sc = SPDK_NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED;
return true;
}
break;
case SPDK_NVME_OPC_KEEP_ALIVE: case SPDK_NVME_OPC_KEEP_ALIVE:
SPDK_TRACELOG(SPDK_TRACE_NVMF, "Keep Alive\n"); SPDK_TRACELOG(SPDK_TRACE_NVMF, "Keep Alive\n");
/* /*
@ -224,7 +219,7 @@ nvmf_process_admin_cmd(struct spdk_nvmf_request *req)
take appropriate action. take appropriate action.
*/ */
//session->keep_alive_timestamp = ; //session->keep_alive_timestamp = ;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
case SPDK_NVME_OPC_CREATE_IO_SQ: case SPDK_NVME_OPC_CREATE_IO_SQ:
case SPDK_NVME_OPC_CREATE_IO_CQ: case SPDK_NVME_OPC_CREATE_IO_CQ:
@ -232,7 +227,7 @@ nvmf_process_admin_cmd(struct spdk_nvmf_request *req)
case SPDK_NVME_OPC_DELETE_IO_CQ: case SPDK_NVME_OPC_DELETE_IO_CQ:
SPDK_ERRLOG("Admin opc 0x%02X not allowed in NVMf\n", cmd->opc); SPDK_ERRLOG("Admin opc 0x%02X not allowed in NVMf\n", cmd->opc);
response->status.sc = SPDK_NVME_SC_INVALID_OPCODE; response->status.sc = SPDK_NVME_SC_INVALID_OPCODE;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
default: default:
passthrough: passthrough:
@ -245,14 +240,14 @@ passthrough:
if (rc) { if (rc) {
SPDK_ERRLOG("Error submitting admin opc 0x%02x\n", cmd->opc); SPDK_ERRLOG("Error submitting admin opc 0x%02x\n", cmd->opc);
response->status.sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR; response->status.sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
return false; return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS;
} }
} }
static bool static spdk_nvmf_request_exec_status
nvmf_process_io_cmd(struct spdk_nvmf_request *req) nvmf_process_io_cmd(struct spdk_nvmf_request *req)
{ {
struct spdk_nvmf_subsystem *subsystem = req->conn->sess->subsys; struct spdk_nvmf_subsystem *subsystem = req->conn->sess->subsys;
@ -267,13 +262,13 @@ nvmf_process_io_cmd(struct spdk_nvmf_request *req)
if (rc) { if (rc) {
SPDK_ERRLOG("Failed to submit request %p\n", req); SPDK_ERRLOG("Failed to submit request %p\n", req);
req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR; req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INTERNAL_DEVICE_ERROR;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
return false; return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS;
} }
static bool static spdk_nvmf_request_exec_status
nvmf_process_property_get(struct spdk_nvmf_request *req) nvmf_process_property_get(struct spdk_nvmf_request *req)
{ {
struct spdk_nvmf_fabric_prop_get_rsp *response; struct spdk_nvmf_fabric_prop_get_rsp *response;
@ -284,10 +279,10 @@ nvmf_process_property_get(struct spdk_nvmf_request *req)
nvmf_property_get(req->conn->sess, cmd, response); nvmf_property_get(req->conn->sess, cmd, response);
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
static bool static spdk_nvmf_request_exec_status
nvmf_process_property_set(struct spdk_nvmf_request *req) nvmf_process_property_set(struct spdk_nvmf_request *req)
{ {
struct spdk_nvmf_fabric_prop_set_cmd *cmd; struct spdk_nvmf_fabric_prop_set_cmd *cmd;
@ -296,7 +291,7 @@ nvmf_process_property_set(struct spdk_nvmf_request *req)
nvmf_property_set(req->conn->sess, cmd, &req->rsp->nvme_cpl); nvmf_property_set(req->conn->sess, cmd, &req->rsp->nvme_cpl);
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
static void static void
@ -327,7 +322,7 @@ invalid_connect_response(struct spdk_nvmf_fabric_connect_rsp *rsp, uint8_t iattr
rsp->status_code_specific.invalid.ipo = ipo; rsp->status_code_specific.invalid.ipo = ipo;
} }
static bool static spdk_nvmf_request_exec_status
nvmf_process_connect(struct spdk_nvmf_request *req) nvmf_process_connect(struct spdk_nvmf_request *req)
{ {
struct spdk_nvmf_subsystem *subsystem; struct spdk_nvmf_subsystem *subsystem;
@ -341,7 +336,7 @@ nvmf_process_connect(struct spdk_nvmf_request *req)
if (req->length < sizeof(struct spdk_nvmf_fabric_connect_data)) { if (req->length < sizeof(struct spdk_nvmf_fabric_connect_data)) {
SPDK_ERRLOG("Connect command data length 0x%x too small\n", req->length); SPDK_ERRLOG("Connect command data length 0x%x too small\n", req->length);
req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INVALID_FIELD; req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INVALID_FIELD;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
/* Look up the requested subsystem */ /* Look up the requested subsystem */
@ -349,17 +344,17 @@ nvmf_process_connect(struct spdk_nvmf_request *req)
if (subsystem == NULL) { if (subsystem == NULL) {
SPDK_ERRLOG("Could not find subsystem '%s'\n", data->subnqn); SPDK_ERRLOG("Could not find subsystem '%s'\n", data->subnqn);
INVALID_CONNECT_DATA(subnqn); INVALID_CONNECT_DATA(subnqn);
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
/* Pass an event to the lcore that owns this subsystem */ /* Pass an event to the lcore that owns this subsystem */
event = spdk_event_allocate(subsystem->poller.lcore, nvmf_handle_connect, req, NULL, NULL); event = spdk_event_allocate(subsystem->poller.lcore, nvmf_handle_connect, req, NULL, NULL);
spdk_event_call(event); spdk_event_call(event);
return false; return SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS;
} }
static bool static spdk_nvmf_request_exec_status
nvmf_process_fabrics_command(struct spdk_nvmf_request *req) nvmf_process_fabrics_command(struct spdk_nvmf_request *req)
{ {
struct spdk_nvmf_conn *conn = req->conn; struct spdk_nvmf_conn *conn = req->conn;
@ -375,7 +370,7 @@ nvmf_process_fabrics_command(struct spdk_nvmf_request *req)
SPDK_TRACELOG(SPDK_TRACE_NVMF, "Got fctype 0x%x, expected Connect\n", SPDK_TRACELOG(SPDK_TRACE_NVMF, "Got fctype 0x%x, expected Connect\n",
cap_hdr->fctype); cap_hdr->fctype);
req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_COMMAND_SEQUENCE_ERROR; req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_COMMAND_SEQUENCE_ERROR;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
} else if (conn->type == CONN_TYPE_AQ) { } else if (conn->type == CONN_TYPE_AQ) {
/* /*
@ -391,14 +386,14 @@ nvmf_process_fabrics_command(struct spdk_nvmf_request *req)
SPDK_TRACELOG(SPDK_TRACE_NVMF, "recv capsule header type invalid [%x]!\n", SPDK_TRACELOG(SPDK_TRACE_NVMF, "recv capsule header type invalid [%x]!\n",
cap_hdr->fctype); cap_hdr->fctype);
req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INVALID_OPCODE; req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INVALID_OPCODE;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
} else { } else {
/* Session is established, and this is an I/O queue */ /* Session is established, and this is an I/O queue */
/* For now, no I/O-specific Fabrics commands are implemented (other than Connect) */ /* For now, no I/O-specific Fabrics commands are implemented (other than Connect) */
SPDK_TRACELOG(SPDK_TRACE_NVMF, "Unexpected I/O fctype 0x%x\n", cap_hdr->fctype); SPDK_TRACELOG(SPDK_TRACE_NVMF, "Unexpected I/O fctype 0x%x\n", cap_hdr->fctype);
req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INVALID_OPCODE; req->rsp->nvme_cpl.status.sc = SPDK_NVME_SC_INVALID_OPCODE;
return true; return SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} }
} }
@ -452,39 +447,47 @@ spdk_nvmf_request_exec(struct spdk_nvmf_request *req)
struct nvmf_session *session = req->conn->sess; struct nvmf_session *session = req->conn->sess;
struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd; struct spdk_nvme_cmd *cmd = &req->cmd->nvme_cmd;
struct spdk_nvme_cpl *rsp = &req->rsp->nvme_cpl; struct spdk_nvme_cpl *rsp = &req->rsp->nvme_cpl;
bool done; spdk_nvmf_request_exec_status status;
nvmf_trace_command(req->cmd, req->conn->type); nvmf_trace_command(req->cmd, req->conn->type);
if (cmd->opc == SPDK_NVME_OPC_FABRIC) { if (cmd->opc == SPDK_NVME_OPC_FABRIC) {
done = nvmf_process_fabrics_command(req); status = nvmf_process_fabrics_command(req);
} else if (session == NULL || !session->vcprop.cc.bits.en) { } else if (session == NULL || !session->vcprop.cc.bits.en) {
/* Only Fabric commands are allowed when the controller is disabled */ /* Only Fabric commands are allowed when the controller is disabled */
SPDK_ERRLOG("Non-Fabric command sent to disabled controller\n"); SPDK_ERRLOG("Non-Fabric command sent to disabled controller\n");
rsp->status.sc = SPDK_NVME_SC_COMMAND_SEQUENCE_ERROR; rsp->status.sc = SPDK_NVME_SC_COMMAND_SEQUENCE_ERROR;
done = true; status = SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE;
} else if (req->conn->type == CONN_TYPE_AQ) { } else if (req->conn->type == CONN_TYPE_AQ) {
struct spdk_nvmf_subsystem *subsystem; struct spdk_nvmf_subsystem *subsystem;
subsystem = session->subsys; subsystem = session->subsys;
assert(subsystem != NULL); assert(subsystem != NULL);
if (subsystem->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY) { if (subsystem->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY) {
done = nvmf_process_discovery_cmd(req); status = nvmf_process_discovery_cmd(req);
} else { } else {
done = nvmf_process_admin_cmd(req); status = nvmf_process_admin_cmd(req);
} }
} else { } else {
done = nvmf_process_io_cmd(req); status = nvmf_process_io_cmd(req);
} }
if (done) { switch (status) {
/* Synchronous command - response is already filled out */ case SPDK_NVMF_REQUEST_EXEC_STATUS_COMPLETE:
return spdk_nvmf_request_complete(req); return spdk_nvmf_request_complete(req);
case SPDK_NVMF_REQUEST_EXEC_STATUS_RELEASE:
if (req->conn->transport->req_release(req)) {
SPDK_ERRLOG("Transport request release error!\n");
return -1;
}
return 0;
case SPDK_NVMF_REQUEST_EXEC_STATUS_ASYNCHRONOUS:
return 0;
default:
SPDK_ERRLOG("Unknown request exec status: 0x%x\n", status);
return -1;
} }
/*
* Asynchronous command.
* The completion callback will call spdk_nvmf_request_complete().
*/
return 0; return 0;
} }

View File

@ -36,6 +36,7 @@
#include "session.h" #include "session.h"
#include "nvmf_internal.h" #include "nvmf_internal.h"
#include "request.h"
#include "subsystem.h" #include "subsystem.h"
#include "transport.h" #include "transport.h"
#include "spdk/log.h" #include "spdk/log.h"

View File

@ -37,7 +37,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "request.h"
#include "spdk/nvmf_spec.h" #include "spdk/nvmf_spec.h"
#include "spdk/queue.h" #include "spdk/queue.h"
@ -80,8 +79,6 @@ struct nvmf_session {
TAILQ_HEAD(connection_q, spdk_nvmf_conn) connections; TAILQ_HEAD(connection_q, spdk_nvmf_conn) connections;
int num_connections; int num_connections;
int max_connections_allowed; int max_connections_allowed;
struct spdk_nvmf_request *aer_req;
}; };
void spdk_nvmf_session_connect(struct spdk_nvmf_conn *conn, void spdk_nvmf_session_connect(struct spdk_nvmf_conn *conn,

View File

@ -66,10 +66,19 @@ struct spdk_nvmf_transport {
void (*transport_stop)(void); void (*transport_stop)(void);
/* /*
* Signal request completion. * Signal request completion, which sends a response
* to the originator. A request can either
* be completed or released, but not both.
*/ */
int (*req_complete)(struct spdk_nvmf_request *req); int (*req_complete)(struct spdk_nvmf_request *req);
/*
* Signal that the request can be released without sending
* a response. A request can either be completed or release,
* but not both.
*/
int (*req_release)(struct spdk_nvmf_request *req);
/* /*
* Deinitialize a connection. * Deinitialize a connection.
*/ */