[PATCH RFC v3 06/11] nvmet: emulate basic support for cancel commands
Maurizio Lombardi
mlombard at redhat.com
Mon Mar 24 03:23:05 PDT 2025
Add a module parameter that allows the user to enable a basic
cancel command emulation, this could be useful for testing the host
driver's cancel command implementation.
It just reports that no abort has been executed, it will however
do some basic sanity check on the command sent by the host and return
an error if the fields are invalid.
Signed-off-by: Maurizio Lombardi <mlombard at redhat.com>
---
drivers/nvme/target/Makefile | 2 +-
drivers/nvme/target/admin-cmd.c | 4 +++
drivers/nvme/target/core.c | 11 ++++++
drivers/nvme/target/io-cmd-cancel.c | 52 +++++++++++++++++++++++++++++
drivers/nvme/target/nvmet.h | 4 +++
5 files changed, 72 insertions(+), 1 deletion(-)
create mode 100644 drivers/nvme/target/io-cmd-cancel.c
diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile
index ed8522911d1f..a533357e9be4 100644
--- a/drivers/nvme/target/Makefile
+++ b/drivers/nvme/target/Makefile
@@ -11,7 +11,7 @@ obj-$(CONFIG_NVME_TARGET_TCP) += nvmet-tcp.o
obj-$(CONFIG_NVME_TARGET_PCI_EPF) += nvmet-pci-epf.o
nvmet-y += core.o configfs.o admin-cmd.o fabrics-cmd.o \
- discovery.o io-cmd-file.o io-cmd-bdev.o pr.o
+ discovery.o io-cmd-file.o io-cmd-bdev.o pr.o io-cmd-cancel.o
nvmet-$(CONFIG_NVME_TARGET_DEBUGFS) += debugfs.o
nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o
nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index acc138bbf8f2..57b90263a7cb 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -398,6 +398,10 @@ static void nvmet_get_cmd_effects_nvm(struct nvme_effects_log *log)
log->iocs[nvme_cmd_resv_release] =
log->iocs[nvme_cmd_resv_report] =
cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);
+ if (emulate_cancel_support) {
+ log->iocs[nvme_cmd_cancel] =
+ cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);
+ }
log->iocs[nvme_cmd_write] =
log->iocs[nvme_cmd_write_zeroes] =
cpu_to_le32(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC);
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index a2b5319c37f3..3e22dacddd76 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -27,6 +27,10 @@ static DEFINE_IDA(cntlid_ida);
struct workqueue_struct *nvmet_wq;
EXPORT_SYMBOL_GPL(nvmet_wq);
+bool emulate_cancel_support;
+module_param(emulate_cancel_support, bool, 0644);
+MODULE_PARM_DESC(emulate_cancel_support, "Emulate the cancel command support. Default = false");
+
/*
* This read/write semaphore is used to synchronize access to configuration
* information on a target system that will result in discovery log page
@@ -1057,6 +1061,13 @@ static u16 nvmet_parse_io_cmd(struct nvmet_req *req)
if (nvmet_is_passthru_req(req))
return nvmet_parse_passthru_io_cmd(req);
+ if (emulate_cancel_support &&
+ req->cmd->common.opcode == nvme_cmd_cancel) {
+ req->execute = nvmet_execute_cancel;
+ if (req->cmd->cancel.nsid == NVME_NSID_ALL)
+ return 0;
+ }
+
ret = nvmet_req_find_ns(req);
if (unlikely(ret))
return ret;
diff --git a/drivers/nvme/target/io-cmd-cancel.c b/drivers/nvme/target/io-cmd-cancel.c
new file mode 100644
index 000000000000..8f55458e646a
--- /dev/null
+++ b/drivers/nvme/target/io-cmd-cancel.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NVMe I/O cancel command implementation.
+ * Copyright (c) 2023 Red Hat
+ */
+
+#include "nvmet.h"
+
+void nvmet_execute_cancel(struct nvmet_req *req)
+{
+ u16 cid;
+ __le16 sqid;
+ bool mult_cmds;
+ int ret = 0;
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ struct nvmet_sq *sq = req->sq;
+
+ if (!nvmet_check_transfer_len(req, 0))
+ return;
+
+ sqid = le16_to_cpu(req->cmd->cancel.sqid);
+ if (sqid > ctrl->subsys->max_qid) {
+ ret = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
+ goto exit;
+ }
+
+ mult_cmds = req->cmd->cancel.action & NVME_CANCEL_ACTION_MUL_CMD;
+ cid = req->cmd->cancel.cid;
+
+ if (cid == req->cmd->cancel.command_id && !mult_cmds) {
+ /* If action is set to "single command" and cid is
+ * set to the cid of this cancel command, then
+ * the controller shall abort the command with
+ * an "invalid cid" status code.
+ */
+ ret = NVME_SC_INVALID_CID | NVME_STATUS_DNR;
+ } else if ((cid != 0xFFFF && mult_cmds) || sqid != sq->qid) {
+ /* if action is set to "multiple commands" and
+ * cid isn't set to 0xFFFF, then abort the command
+ * with an "invalid field" status.
+ * if the sqid field doesn't match the sqid of
+ * the queue to which the cancel command is submitted,
+ * then abort the command with an "invalid field" status.
+ */
+ ret = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
+ }
+
+exit:
+ nvmet_set_result(req, 0);
+ nvmet_req_complete(req, ret);
+}
+
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 33fac9151b5b..8778fbe82e74 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -40,6 +40,8 @@
#define nvmet_for_each_enabled_ns(xa, index, entry) \
xa_for_each_marked(xa, index, entry, NVMET_NS_ENABLED)
+extern bool emulate_cancel_support;
+
/*
* Supported optional AENs:
*/
@@ -714,6 +716,8 @@ void nvmet_bdev_execute_zone_mgmt_recv(struct nvmet_req *req);
void nvmet_bdev_execute_zone_mgmt_send(struct nvmet_req *req);
void nvmet_bdev_execute_zone_append(struct nvmet_req *req);
+void nvmet_execute_cancel(struct nvmet_req *req);
+
static inline u32 nvmet_rw_data_len(struct nvmet_req *req)
{
return ((u32)le16_to_cpu(req->cmd->rw.length) + 1) <<
--
2.43.5
More information about the Linux-nvme
mailing list