From 2a66baec62c7cd3b31bb0154f8d73695a41f0ede Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Thu, 3 Aug 2023 14:37:30 +0200 Subject: [PATCH] abort tp4097 Signed-off-by: Maurizio Lombardi --- drivers/nvme/host/constants.c | 1 + drivers/nvme/host/core.c | 1 + drivers/nvme/target/Makefile | 2 +- drivers/nvme/target/admin-cmd.c | 41 +++++++++++++- drivers/nvme/target/core.c | 43 +++++++++++++++ drivers/nvme/target/io-cmd-cancel.c | 83 +++++++++++++++++++++++++++++ drivers/nvme/target/io-cmd-file.c | 19 +++++++ drivers/nvme/target/nvmet.h | 10 ++++ include/linux/nvme.h | 17 ++++++ 9 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 drivers/nvme/target/io-cmd-cancel.c diff --git a/drivers/nvme/host/constants.c b/drivers/nvme/host/constants.c index 20f46c230885..a10a45f87a78 100644 --- a/drivers/nvme/host/constants.c +++ b/drivers/nvme/host/constants.c @@ -19,6 +19,7 @@ static const char * const nvme_ops[] = { [nvme_cmd_resv_report] = "Reservation Report", [nvme_cmd_resv_acquire] = "Reservation Acquire", [nvme_cmd_resv_release] = "Reservation Release", + [nvme_cmd_cancel] = "Cancel", [nvme_cmd_zone_mgmt_send] = "Zone Management Send", [nvme_cmd_zone_mgmt_recv] = "Zone Management Receive", [nvme_cmd_zone_append] = "Zone Append", diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 21783aa2ee8e..3311a991ca4e 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -4656,6 +4656,7 @@ static inline void _nvme_check_size(void) BUILD_BUG_ON(sizeof(struct nvme_dsm_cmd) != 64); BUILD_BUG_ON(sizeof(struct nvme_write_zeroes_cmd) != 64); BUILD_BUG_ON(sizeof(struct nvme_abort_cmd) != 64); + BUILD_BUG_ON(sizeof(struct nvme_cancel_cmd) != 64); BUILD_BUG_ON(sizeof(struct nvme_get_log_page_command) != 64); BUILD_BUG_ON(sizeof(struct nvme_command) != 64); BUILD_BUG_ON(sizeof(struct nvme_id_ctrl) != NVME_IDENTIFY_DATA_SIZE); diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile index c66820102493..6541808579ae 100644 --- a/drivers/nvme/target/Makefile +++ b/drivers/nvme/target/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_NVME_TARGET_FCLOOP) += nvme-fcloop.o obj-$(CONFIG_NVME_TARGET_TCP) += nvmet-tcp.o nvmet-y += core.o configfs.o admin-cmd.o fabrics-cmd.o \ - discovery.o io-cmd-file.o io-cmd-bdev.o + discovery.o io-cmd-file.o io-cmd-bdev.o io-cmd-cancel.o nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index 39cb570f833d..10e168feda6e 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c @@ -176,6 +176,7 @@ static void nvmet_get_cmd_effects_nvm(struct nvme_effects_log *log) log->iocs[nvme_cmd_read] = log->iocs[nvme_cmd_flush] = log->iocs[nvme_cmd_dsm] = + log->iocs[nvme_cmd_cancel] = cpu_to_le32(NVME_CMD_EFFECTS_CSUPP); log->iocs[nvme_cmd_write] = log->iocs[nvme_cmd_write_zeroes] = @@ -736,9 +737,47 @@ static void nvmet_execute_identify(struct nvmet_req *req) */ static void nvmet_execute_abort(struct nvmet_req *req) { + u16 cid; + __le16 sqid; + unsigned long flags; + bool abort_success = false; + struct nvmet_sq *sq; + struct nvmet_ctrl *ctrl = req->sq->ctrl; + struct nvmet_req *r, *next; + if (!nvmet_check_transfer_len(req, 0)) return; - nvmet_set_result(req, 1); + + cid = req->cmd->abort.cid; + sqid = le16_to_cpu(req->cmd->abort.sqid); + if (sqid > ctrl->subsys->max_qid) { + /* Invalid queue id */ + goto error; + } + + sq = ctrl->sqs[sqid]; + if (!sq) + goto error; + + spin_lock_irqsave(&sq->state_lock, flags); + list_for_each_entry_safe(r, next, &sq->state_list, state_list) { + if (cid != r->cmd->common.command_id) + continue; + + if (r == req) { + /* Abort command can't abort itself */ + continue; + } + + printk("Found command to abort (cid = %u)\n", (unsigned int) cid); + nvmet_req_abort(r); + abort_success = true; + break; + } + spin_unlock_irqrestore(&sq->state_lock, flags); + +error: + nvmet_set_result(req, abort_success ? 0 : 1); nvmet_req_complete(req, 0); } diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 3935165048e7..dd6163566a44 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -757,12 +757,33 @@ static void __nvmet_req_complete(struct nvmet_req *req, u16 status) void nvmet_req_complete(struct nvmet_req *req, u16 status) { struct nvmet_sq *sq = req->sq; + unsigned long flags; + + spin_lock_irqsave(&sq->state_lock, flags); + + if (unlikely(req->aborted)) + status = NVME_SC_ABORT_REQ; + else + list_del(&req->state_list); + + spin_unlock_irqrestore(&sq->state_lock, flags); __nvmet_req_complete(req, status); percpu_ref_put(&sq->ref); } EXPORT_SYMBOL_GPL(nvmet_req_complete); +void nvmet_req_abort(struct nvmet_req *req) +{ + lockdep_assert_held(&req->sq->state_lock); + + if (req->abort) + req->abort(req); + + req->aborted = true; + list_del_init(&req->state_list); +} + void nvmet_cq_setup(struct nvmet_ctrl *ctrl, struct nvmet_cq *cq, u16 qid, u16 size) { @@ -803,6 +824,8 @@ void nvmet_sq_destroy(struct nvmet_sq *sq) percpu_ref_exit(&sq->ref); nvmet_auth_sq_free(sq); + WARN_ON_ONCE(!list_empty(&sq->state_list)); + if (ctrl) { /* * The teardown flow may take some time, and the host may not @@ -836,6 +859,8 @@ int nvmet_sq_init(struct nvmet_sq *sq) } init_completion(&sq->free_done); init_completion(&sq->confirm_done); + INIT_LIST_HEAD(&sq->state_list); + spin_lock_init(&sq->state_lock); nvmet_auth_sq_init(sq); return 0; @@ -904,6 +929,11 @@ static u16 nvmet_parse_io_cmd(struct nvmet_req *req) return ret; } + if (req->cmd->common.opcode == nvme_cmd_cancel) { + req->execute = nvmet_execute_cancel; + return 0; + } + switch (req->ns->csi) { case NVME_CSI_NVM: if (req->ns->file) @@ -922,6 +952,7 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq, struct nvmet_sq *sq, const struct nvmet_fabrics_ops *ops) { u8 flags = req->cmd->common.flags; + unsigned long sflags; u16 status; req->cq = cq; @@ -978,6 +1009,12 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq, if (sq->ctrl) sq->ctrl->reset_tbkas = true; + INIT_LIST_HEAD(&req->state_list); + + spin_lock_irqsave(&sq->state_lock, sflags); + list_add_tail(&req->state_list, &sq->state_list); + spin_unlock_irqrestore(&sq->state_lock, sflags); + return true; fail: @@ -988,6 +1025,12 @@ EXPORT_SYMBOL_GPL(nvmet_req_init); void nvmet_req_uninit(struct nvmet_req *req) { + unsigned long flags; + + spin_lock_irqsave(&req->sq->state_lock, flags); + list_del(&req->state_list); + spin_unlock_irqrestore(&req->sq->state_lock, flags); + percpu_ref_put(&req->sq->ref); if (req->ns) nvmet_put_namespace(req->ns); diff --git a/drivers/nvme/target/io-cmd-cancel.c b/drivers/nvme/target/io-cmd-cancel.c new file mode 100644 index 000000000000..4ae1ad1a81c6 --- /dev/null +++ b/drivers/nvme/target/io-cmd-cancel.c @@ -0,0 +1,83 @@ +// 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; + __le32 nsid; + struct nvmet_sq *sq; + struct nvmet_ctrl *ctrl = req->sq->ctrl; + struct nvmet_req *r, *next; + unsigned long flags; + int ret = 0; + u16 imm_abrts = 0; + u16 def_abrts = 0; + bool mult_cmds; + + if (!nvmet_check_transfer_len(req, 0)) + return; + + cid = req->cmd->cancel.cid; + sqid = le16_to_cpu(req->cmd->cancel.sqid); + nsid = le32_to_cpu(req->cmd->cancel.nsid); + mult_cmds = le32_to_cpu(req->cmd->cancel.action) & + NVME_CANCEL_ACTION_MUL_COMMAND; + + if (sqid > ctrl->subsys->max_qid) { + ret = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + goto error; + } + + sq = req->sq; + + if (cid == req->cmd->cancel.command_id && !mult_cmds) { + ret = NVME_SC_INVALID_CID | NVME_SC_DNR; + goto error; + } else if ((cid != 0xFFFF && mult_cmds) || sqid != sq->qid) { + ret = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + goto error; + } + + spin_lock_irqsave(&sq->state_lock, flags); + list_for_each_entry_safe(r, next, &sq->state_list, state_list) { + if (r == req) { + /* Cancel command can't abort itself */ + continue; + } + + if (mult_cmds) { + if (r->cmd->common.nsid != 0xFFFFFFFF && + r->cmd->common.nsid != nsid) { + continue; + } + + nvmet_req_abort(req); + def_abrts++; + } else { + if (cid != r->cmd->common.command_id) + continue; + + if (r->cmd->common.nsid != 0xFFFFFFFF && + nsid != r->cmd->common.nsid) { + ret = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + break; + } + + nvmet_req_abort(req); + def_abrts++; + break; + } + } + spin_unlock_irqrestore(&sq->state_lock, flags); + +error: + nvmet_set_result(req, def_abrts | (imm_abrts << 16)); + nvmet_req_complete(req, ret); +} + diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c index 2d068439b129..f01033481718 100644 --- a/drivers/nvme/target/io-cmd-file.c +++ b/drivers/nvme/target/io-cmd-file.c @@ -330,6 +330,21 @@ static void nvmet_file_execute_dsm(struct nvmet_req *req) queue_work(nvmet_wq, &req->f.work); } +static void nvmet_file_abort_work(struct work_struct *w) +{ + struct nvmet_req *req = container_of(w, struct nvmet_req, f.work); + + nvmet_req_complete(req, NVME_SC_ABORT_REQ); +} + +static void nvmet_file_cancel_work(struct nvmet_req *req) +{ + if (cancel_work(&req->f.work)) { + INIT_WORK(&req->f.work, nvmet_file_abort_work); + queue_work(nvmet_wq, &req->f.work); + } +} + static void nvmet_file_write_zeroes_work(struct work_struct *w) { struct nvmet_req *req = container_of(w, struct nvmet_req, f.work); @@ -366,15 +381,19 @@ u16 nvmet_file_parse_io_cmd(struct nvmet_req *req) case nvme_cmd_read: case nvme_cmd_write: req->execute = nvmet_file_execute_rw; + req->abort = nvmet_file_cancel_work; return 0; case nvme_cmd_flush: req->execute = nvmet_file_execute_flush; + req->abort = nvmet_file_cancel_work; return 0; case nvme_cmd_dsm: req->execute = nvmet_file_execute_dsm; + req->abort = nvmet_file_cancel_work; return 0; case nvme_cmd_write_zeroes: req->execute = nvmet_file_execute_write_zeroes; + req->abort = nvmet_file_cancel_work; return 0; default: return nvmet_report_invalid_opcode(req); diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index 8cfd60f3b564..3cf500067af9 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -123,6 +123,9 @@ struct nvmet_sq { #endif struct completion free_done; struct completion confirm_done; + + spinlock_t state_lock; + struct list_head state_list; }; struct nvmet_ana_group { @@ -387,12 +390,16 @@ struct nvmet_req { struct nvmet_port *port; void (*execute)(struct nvmet_req *req); + void (*abort)(struct nvmet_req *req); const struct nvmet_fabrics_ops *ops; struct pci_dev *p2p_dev; struct device *p2p_client; u16 error_loc; u64 error_slba; + + struct list_head state_list; + bool aborted; }; #define NVMET_MAX_MPOOL_BVEC 16 @@ -455,12 +462,15 @@ u16 nvmet_parse_discovery_cmd(struct nvmet_req *req); u16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req); u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req); +void nvmet_execute_cancel(struct nvmet_req *req); + bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq, struct nvmet_sq *sq, const struct nvmet_fabrics_ops *ops); void nvmet_req_uninit(struct nvmet_req *req); bool nvmet_check_transfer_len(struct nvmet_req *req, size_t len); bool nvmet_check_data_len_lte(struct nvmet_req *req, size_t data_len); void nvmet_req_complete(struct nvmet_req *req, u16 status); +void nvmet_req_abort(struct nvmet_req *req); int nvmet_req_alloc_sgls(struct nvmet_req *req); void nvmet_req_free_sgls(struct nvmet_req *req); diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 26dd3f859d9d..4654e096fd37 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -831,6 +831,7 @@ enum nvme_opcode { nvme_cmd_resv_report = 0x0e, nvme_cmd_resv_acquire = 0x11, nvme_cmd_resv_release = 0x15, + nvme_cmd_cancel = 0x18, nvme_cmd_zone_mgmt_send = 0x79, nvme_cmd_zone_mgmt_recv = 0x7a, nvme_cmd_zone_append = 0x7d, @@ -1344,6 +1345,20 @@ struct nvme_abort_cmd { __u32 rsvd11[5]; }; +struct nvme_cancel_cmd { + __u8 opcode; + __u8 flags; + __u16 command_id; + __le32 nsid; + __u32 rsvd1[8]; + __le16 sqid; + __u16 cid; + __le32 action; + __u32 rsvd11[4]; +}; + +#define NVME_CANCEL_ACTION_MUL_COMMAND (1 << 0) + struct nvme_download_firmware { __u8 opcode; __u8 flags; @@ -1796,6 +1811,7 @@ struct nvme_command { struct nvme_zone_mgmt_send_cmd zms; struct nvme_zone_mgmt_recv_cmd zmr; struct nvme_abort_cmd abort; + struct nvme_cancel_cmd cancel; struct nvme_get_log_page_command get_log_page; struct nvmf_common_command fabrics; struct nvmf_connect_command connect; @@ -1936,6 +1952,7 @@ enum { NVME_SC_INVALID_PI = 0x181, NVME_SC_READ_ONLY = 0x182, NVME_SC_ONCS_NOT_SUPPORTED = 0x183, + NVME_SC_INVALID_CID = 0x184, /* * I/O Command Set Specific - Fabrics commands: -- 2.39.3