]> www.infradead.org Git - nvme.git/commitdiff
nvmet: Add support for I/O queue management admin commands
authorDamien Le Moal <dlemoal@kernel.org>
Sat, 4 Jan 2025 04:59:43 +0000 (13:59 +0900)
committerKeith Busch <kbusch@kernel.org>
Sat, 11 Jan 2025 03:30:48 +0000 (19:30 -0800)
The I/O submission queue management admin commands
(nvme_admin_delete_sq, nvme_admin_create_sq, nvme_admin_delete_cq,
and nvme_admin_create_cq) are mandatory admin commands for I/O
controllers using the PCI transport, that is, support for these commands
is mandatory for a a PCI target I/O controller.

Implement support for these commands by adding the functions
nvmet_execute_delete_sq(), nvmet_execute_create_sq(),
nvmet_execute_delete_cq() and nvmet_execute_create_cq() to set as the
execute method of requests for these commands. These functions will
return an invalid opcode error for any controller that is not a PCI
target controller. Support for the I/O queue management commands is also
reported in the command effect log  of PCI target controllers (using
nvmet_get_cmd_effects_admin()).

Each management command is backed by a controller fabric operation
that can be defined by a PCI target controller driver to setup I/O
queues using nvmet_sq_create() and nvmet_cq_create() or delete I/O
queues using nvmet_sq_destroy().

As noted in a comment in nvmet_execute_create_sq(), we do not yet
support sharing a single CQ between multiple SQs.

Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Tested-by: Rick Wertenbroek <rick.wertenbroek@gmail.com>
Tested-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Keith Busch <kbusch@kernel.org>
drivers/nvme/target/admin-cmd.c
drivers/nvme/target/nvmet.h

index 6f7e5b0c91c7279f3ee578f57e19bfe2438d0182..c91864c185fc0db44b7a23263d1a142720e7f58c 100644 (file)
 #include <linux/unaligned.h>
 #include "nvmet.h"
 
+static void nvmet_execute_delete_sq(struct nvmet_req *req)
+{
+       struct nvmet_ctrl *ctrl = req->sq->ctrl;
+       u16 sqid = le16_to_cpu(req->cmd->delete_queue.qid);
+       u16 status;
+
+       if (!nvmet_is_pci_ctrl(ctrl)) {
+               status = nvmet_report_invalid_opcode(req);
+               goto complete;
+       }
+
+       if (!sqid) {
+               status = NVME_SC_QID_INVALID | NVME_STATUS_DNR;
+               goto complete;
+       }
+
+       status = nvmet_check_sqid(ctrl, sqid, false);
+       if (status != NVME_SC_SUCCESS)
+               goto complete;
+
+       status = ctrl->ops->delete_sq(ctrl, sqid);
+
+complete:
+       nvmet_req_complete(req, status);
+}
+
+static void nvmet_execute_create_sq(struct nvmet_req *req)
+{
+       struct nvmet_ctrl *ctrl = req->sq->ctrl;
+       struct nvme_command *cmd = req->cmd;
+       u16 sqid = le16_to_cpu(cmd->create_sq.sqid);
+       u16 cqid = le16_to_cpu(cmd->create_sq.cqid);
+       u16 sq_flags = le16_to_cpu(cmd->create_sq.sq_flags);
+       u16 qsize = le16_to_cpu(cmd->create_sq.qsize);
+       u64 prp1 = le64_to_cpu(cmd->create_sq.prp1);
+       u16 status;
+
+       if (!nvmet_is_pci_ctrl(ctrl)) {
+               status = nvmet_report_invalid_opcode(req);
+               goto complete;
+       }
+
+       if (!sqid) {
+               status = NVME_SC_QID_INVALID | NVME_STATUS_DNR;
+               goto complete;
+       }
+
+       status = nvmet_check_sqid(ctrl, sqid, true);
+       if (status != NVME_SC_SUCCESS)
+               goto complete;
+
+       /*
+        * Note: The NVMe specification allows multiple SQs to use the same CQ.
+        * However, the target code does not really support that. So for now,
+        * prevent this and fail the command if sqid and cqid are different.
+        */
+       if (!cqid || cqid != sqid) {
+               pr_err("SQ %u: Unsupported CQID %u\n", sqid, cqid);
+               status = NVME_SC_CQ_INVALID | NVME_STATUS_DNR;
+               goto complete;
+       }
+
+       if (!qsize || qsize > NVME_CAP_MQES(ctrl->cap)) {
+               status = NVME_SC_QUEUE_SIZE | NVME_STATUS_DNR;
+               goto complete;
+       }
+
+       status = ctrl->ops->create_sq(ctrl, sqid, sq_flags, qsize, prp1);
+
+complete:
+       nvmet_req_complete(req, status);
+}
+
+static void nvmet_execute_delete_cq(struct nvmet_req *req)
+{
+       struct nvmet_ctrl *ctrl = req->sq->ctrl;
+       u16 cqid = le16_to_cpu(req->cmd->delete_queue.qid);
+       u16 status;
+
+       if (!nvmet_is_pci_ctrl(ctrl)) {
+               status = nvmet_report_invalid_opcode(req);
+               goto complete;
+       }
+
+       if (!cqid) {
+               status = NVME_SC_QID_INVALID | NVME_STATUS_DNR;
+               goto complete;
+       }
+
+       status = nvmet_check_cqid(ctrl, cqid);
+       if (status != NVME_SC_SUCCESS)
+               goto complete;
+
+       status = ctrl->ops->delete_cq(ctrl, cqid);
+
+complete:
+       nvmet_req_complete(req, status);
+}
+
+static void nvmet_execute_create_cq(struct nvmet_req *req)
+{
+       struct nvmet_ctrl *ctrl = req->sq->ctrl;
+       struct nvme_command *cmd = req->cmd;
+       u16 cqid = le16_to_cpu(cmd->create_cq.cqid);
+       u16 cq_flags = le16_to_cpu(cmd->create_cq.cq_flags);
+       u16 qsize = le16_to_cpu(cmd->create_cq.qsize);
+       u16 irq_vector = le16_to_cpu(cmd->create_cq.irq_vector);
+       u64 prp1 = le64_to_cpu(cmd->create_cq.prp1);
+       u16 status;
+
+       if (!nvmet_is_pci_ctrl(ctrl)) {
+               status = nvmet_report_invalid_opcode(req);
+               goto complete;
+       }
+
+       if (!cqid) {
+               status = NVME_SC_QID_INVALID | NVME_STATUS_DNR;
+               goto complete;
+       }
+
+       status = nvmet_check_cqid(ctrl, cqid);
+       if (status != NVME_SC_SUCCESS)
+               goto complete;
+
+       if (!qsize || qsize > NVME_CAP_MQES(ctrl->cap)) {
+               status = NVME_SC_QUEUE_SIZE | NVME_STATUS_DNR;
+               goto complete;
+       }
+
+       status = ctrl->ops->create_cq(ctrl, cqid, cq_flags, qsize,
+                                     prp1, irq_vector);
+
+complete:
+       nvmet_req_complete(req, status);
+}
+
 u32 nvmet_get_log_page_len(struct nvme_command *cmd)
 {
        u32 len = le16_to_cpu(cmd->get_log_page.numdu);
@@ -230,8 +366,18 @@ out:
        nvmet_req_complete(req, status);
 }
 
-static void nvmet_get_cmd_effects_admin(struct nvme_effects_log *log)
+static void nvmet_get_cmd_effects_admin(struct nvmet_ctrl *ctrl,
+                                       struct nvme_effects_log *log)
 {
+       /* For a PCI target controller, advertize support for the . */
+       if (nvmet_is_pci_ctrl(ctrl)) {
+               log->acs[nvme_admin_delete_sq] =
+               log->acs[nvme_admin_create_sq] =
+               log->acs[nvme_admin_delete_cq] =
+               log->acs[nvme_admin_create_cq] =
+                       cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);
+       }
+
        log->acs[nvme_admin_get_log_page] =
        log->acs[nvme_admin_identify] =
        log->acs[nvme_admin_abort_cmd] =
@@ -268,6 +414,7 @@ static void nvmet_get_cmd_effects_zns(struct nvme_effects_log *log)
 
 static void nvmet_execute_get_log_cmd_effects_ns(struct nvmet_req *req)
 {
+       struct nvmet_ctrl *ctrl = req->sq->ctrl;
        struct nvme_effects_log *log;
        u16 status = NVME_SC_SUCCESS;
 
@@ -279,7 +426,7 @@ static void nvmet_execute_get_log_cmd_effects_ns(struct nvmet_req *req)
 
        switch (req->cmd->get_log_page.csi) {
        case NVME_CSI_NVM:
-               nvmet_get_cmd_effects_admin(log);
+               nvmet_get_cmd_effects_admin(ctrl, log);
                nvmet_get_cmd_effects_nvm(log);
                break;
        case NVME_CSI_ZNS:
@@ -287,7 +434,7 @@ static void nvmet_execute_get_log_cmd_effects_ns(struct nvmet_req *req)
                        status = NVME_SC_INVALID_IO_CMD_SET;
                        goto free;
                }
-               nvmet_get_cmd_effects_admin(log);
+               nvmet_get_cmd_effects_admin(ctrl, log);
                nvmet_get_cmd_effects_nvm(log);
                nvmet_get_cmd_effects_zns(log);
                break;
@@ -1335,9 +1482,21 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
                return nvmet_parse_passthru_admin_cmd(req);
 
        switch (cmd->common.opcode) {
+       case nvme_admin_delete_sq:
+               req->execute = nvmet_execute_delete_sq;
+               return 0;
+       case nvme_admin_create_sq:
+               req->execute = nvmet_execute_create_sq;
+               return 0;
        case nvme_admin_get_log_page:
                req->execute = nvmet_execute_get_log_page;
                return 0;
+       case nvme_admin_delete_cq:
+               req->execute = nvmet_execute_delete_cq;
+               return 0;
+       case nvme_admin_create_cq:
+               req->execute = nvmet_execute_create_cq;
+               return 0;
        case nvme_admin_identify:
                req->execute = nvmet_execute_identify;
                return 0;
index 5c8ed8f9391817710c7dfc94e0c33314b8925c3b..86bb2852a63ba0f567360d9a36e1ab58bdc66b57 100644 (file)
@@ -408,6 +408,14 @@ struct nvmet_fabrics_ops {
        void (*discovery_chg)(struct nvmet_port *port);
        u8 (*get_mdts)(const struct nvmet_ctrl *ctrl);
        u16 (*get_max_queue_size)(const struct nvmet_ctrl *ctrl);
+
+       /* Operations mandatory for PCI target controllers */
+       u16 (*create_sq)(struct nvmet_ctrl *ctrl, u16 sqid, u16 flags,
+                        u16 qsize, u64 prp1);
+       u16 (*delete_sq)(struct nvmet_ctrl *ctrl, u16 sqid);
+       u16 (*create_cq)(struct nvmet_ctrl *ctrl, u16 cqid, u16 flags,
+                        u16 qsize, u64 prp1, u16 irq_vector);
+       u16 (*delete_cq)(struct nvmet_ctrl *ctrl, u16 cqid);
 };
 
 #define NVMET_MAX_INLINE_BIOVEC        8