Commit 60d3cd85 authored by Damien Le Moal's avatar Damien Le Moal Committed by Keith Busch
Browse files

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: default avatarDamien Le Moal <dlemoal@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Tested-by: default avatarRick Wertenbroek <rick.wertenbroek@gmail.com>
Tested-by: default avatarManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: default avatarKeith Busch <kbusch@kernel.org>
parent 1eb380ca
Loading
Loading
Loading
Loading
+162 −3
Original line number Diff line number Diff line
@@ -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 @@ static void nvmet_execute_get_log_page_smart(struct nvmet_req *req)
	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;
+8 −0
Original line number Diff line number Diff line
@@ -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