mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
nvmet: Add support for I/O queue management admin commands
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>
This commit is contained in:
committed by
Keith Busch
parent
1eb380caf5
commit
60d3cd8561
@@ -12,6 +12,142 @@
|
||||
#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;
|
||||
|
||||
Reference in New Issue
Block a user