From: Ming Lin <min...@ssi.samsung.com>

Signed-off-by: Ming Lin <min...@ssi.samsung.com>
---
 drivers/nvme/target/vhost.c | 153 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 153 insertions(+)

diff --git a/drivers/nvme/target/vhost.c b/drivers/nvme/target/vhost.c
index 4a147d6..04ed0bc 100644
--- a/drivers/nvme/target/vhost.c
+++ b/drivers/nvme/target/vhost.c
@@ -39,6 +39,11 @@ enum NvmeAqaMask {
 #define NVME_AQA_ASQS(aqa) ((aqa >> AQA_ASQS_SHIFT) & AQA_ASQS_MASK)
 #define NVME_AQA_ACQS(aqa) ((aqa >> AQA_ACQS_SHIFT) & AQA_ACQS_MASK)
 
+#define NVME_CQ_FLAGS_PC(cq_flags)     (cq_flags & 0x1)
+#define NVME_CQ_FLAGS_IEN(cq_flags)    ((cq_flags >> 1) & 0x1)
+
+#define NVME_SQ_FLAGS_PC(sq_flags)     (sq_flags & 0x1)
+
 struct nvmet_vhost_ctrl_eventfd {
        struct file *call;
        struct eventfd_ctx *call_ctx;
@@ -90,6 +95,19 @@ struct nvmet_vhost_ctrl {
        u32 page_size;
 };
 
+#define sq_to_vsq(sq) container_of(sq, struct nvmet_vhost_sq, sq)
+#define cq_to_vcq(cq) container_of(cq, struct nvmet_vhost_cq, cq)
+
+static int nvmet_vhost_check_sqid(struct nvmet_ctrl *n, u16 sqid)
+{
+       return sqid <= n->subsys->max_qid && n->sqs[sqid] != NULL ? 0 : -1;
+}
+
+static int nvmet_vhost_check_cqid(struct nvmet_ctrl *n, u16 cqid)
+{
+       return cqid <= n->subsys->max_qid && n->cqs[cqid] != NULL ? 0 : -1;
+}
+
 static int nvmet_vhost_init_cq(struct nvmet_vhost_cq *cq,
                struct nvmet_vhost_ctrl *n, u64 dma_addr,
                u16 cqid, u16 size, struct eventfd_ctx *eventfd,
@@ -147,6 +165,140 @@ static void nvmet_vhost_start_ctrl(void *opaque)
        }
 }
 
+static void nvmet_vhost_create_cq(struct nvmet_req *req)
+{
+       struct nvmet_cq *cq;
+       struct nvmet_vhost_cq *vcq;
+       struct nvmet_vhost_ctrl *n;
+       struct nvme_create_cq *c;
+       u16 cqid;
+       u16 vector;
+       u16 qsize;
+       u16 qflags;
+       u64 prp1;
+       int status;
+       int ret;
+
+       cq = req->cq;
+       vcq = cq_to_vcq(cq);
+       n = vcq->ctrl;
+       c = &req->cmd->create_cq;
+       cqid = le16_to_cpu(c->cqid);
+       vector = le16_to_cpu(c->irq_vector);
+       qsize = le16_to_cpu(c->qsize);
+       qflags = le16_to_cpu(c->cq_flags);
+       prp1 = le64_to_cpu(c->prp1);
+       status = NVME_SC_SUCCESS;
+
+       if (!cqid || (cqid && !nvmet_vhost_check_cqid(n->ctrl, cqid))) {
+               status = NVME_SC_QID_INVALID | NVME_SC_DNR;
+               goto out;
+       }
+       if (!qsize || qsize > NVME_CAP_MQES(n->ctrl->cap)) {
+               status = NVME_SC_QUEUE_SIZE | NVME_SC_DNR;
+               goto out;
+       }
+       if (!prp1) {
+               status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+               goto out;
+       }
+       if (vector > n->num_queues) {
+               status = NVME_SC_INVALID_VECTOR | NVME_SC_DNR;
+               goto out;
+       }
+       if (!(NVME_CQ_FLAGS_PC(qflags))) {
+               status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+               goto out;
+       }
+
+       vcq = kmalloc(sizeof(*vcq), GFP_KERNEL);
+       if (!vcq) {
+               status = NVME_SC_INTERNAL | NVME_SC_DNR;
+               goto out;
+       }
+
+       ret = nvmet_vhost_init_cq(vcq, n, prp1, cqid, qsize+1,
+               n->eventfd[cqid].call_ctx, vector,
+               NVME_CQ_FLAGS_IEN(qflags));
+       if (ret)
+               status = NVME_SC_INTERNAL | NVME_SC_DNR;
+
+out:
+       nvmet_req_complete(req, status);
+}
+
+static void nvmet_vhost_create_sq(struct nvmet_req *req)
+{
+       struct nvme_create_sq *c = &req->cmd->create_sq;
+       u16 cqid = le16_to_cpu(c->cqid);
+       u16 sqid = le16_to_cpu(c->sqid);
+       u16 qsize = le16_to_cpu(c->qsize);
+       u16 qflags = le16_to_cpu(c->sq_flags);
+       u64 prp1 = le64_to_cpu(c->prp1);
+
+       struct nvmet_sq *sq = req->sq;
+       struct nvmet_vhost_sq *vsq;
+       struct nvmet_vhost_ctrl *n;
+       int status;
+       int ret;
+
+       status = NVME_SC_SUCCESS;
+       vsq = sq_to_vsq(sq);
+       n = vsq->ctrl;
+
+       if (!cqid || nvmet_vhost_check_cqid(n->ctrl, cqid)) {
+               status = NVME_SC_CQ_INVALID | NVME_SC_DNR;
+               goto out;
+       }
+       if (!sqid || (sqid && !nvmet_vhost_check_sqid(n->ctrl, sqid))) {
+               status = NVME_SC_QID_INVALID | NVME_SC_DNR;
+               goto out;
+       }
+       if (!qsize || qsize > NVME_CAP_MQES(n->ctrl->cap)) {
+               status = NVME_SC_QUEUE_SIZE | NVME_SC_DNR;
+               goto out;
+       }
+       if (!prp1 || prp1 & (n->page_size - 1)) {
+               status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+               goto out;
+       }
+       if (!(NVME_SQ_FLAGS_PC(qflags))) {
+               status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+               goto out;
+       }
+
+       vsq = kmalloc(sizeof(*vsq), GFP_KERNEL);
+       if (!sq) {
+               status = NVME_SC_INTERNAL | NVME_SC_DNR;
+               goto out;
+       }
+
+       ret = nvmet_vhost_init_sq(vsq, n, prp1, sqid, cqid, qsize + 1);
+       if (ret)
+               status = NVME_SC_INTERNAL | NVME_SC_DNR;
+
+out:
+       nvmet_req_complete(req, status);
+}
+
+static int nvmet_vhost_parse_admin_cmd(struct nvmet_req *req)
+{
+       struct nvme_command *cmd = req->cmd;
+
+       switch (cmd->common.opcode) {
+       case nvme_admin_create_cq:
+               req->execute = nvmet_vhost_create_cq;
+               req->data_len = 0;
+               return 0;
+       case nvme_admin_create_sq:
+               req->execute = nvmet_vhost_create_sq;
+               req->data_len = 0;
+               return 0;
+       }
+
+       return -1;
+}
+
 static int
 nvmet_vhost_set_endpoint(struct nvmet_vhost_ctrl *n,
                        struct vhost_nvme_target *c)
@@ -173,6 +325,7 @@ nvmet_vhost_set_endpoint(struct nvmet_vhost_ctrl *n,
        n->num_queues = subsys->max_qid + 1;
        ctrl->opaque = n;
        ctrl->start = nvmet_vhost_start_ctrl;
+       ctrl->parse_extra_admin_cmd = nvmet_vhost_parse_admin_cmd;
 
        num_queues = ctrl->subsys->max_qid + 1;
        n->cqs = kzalloc(sizeof(*n->cqs) * num_queues, GFP_KERNEL);
-- 
1.9.1


Reply via email to