Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * NVMe Fabrics command implementation.
0004  * Copyright (c) 2015-2016 HGST, a Western Digital Company.
0005  */
0006 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0007 #include <linux/blkdev.h>
0008 #include "nvmet.h"
0009 
0010 static void nvmet_execute_prop_set(struct nvmet_req *req)
0011 {
0012     u64 val = le64_to_cpu(req->cmd->prop_set.value);
0013     u16 status = 0;
0014 
0015     if (!nvmet_check_transfer_len(req, 0))
0016         return;
0017 
0018     if (req->cmd->prop_set.attrib & 1) {
0019         req->error_loc =
0020             offsetof(struct nvmf_property_set_command, attrib);
0021         status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
0022         goto out;
0023     }
0024 
0025     switch (le32_to_cpu(req->cmd->prop_set.offset)) {
0026     case NVME_REG_CC:
0027         nvmet_update_cc(req->sq->ctrl, val);
0028         break;
0029     default:
0030         req->error_loc =
0031             offsetof(struct nvmf_property_set_command, offset);
0032         status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
0033     }
0034 out:
0035     nvmet_req_complete(req, status);
0036 }
0037 
0038 static void nvmet_execute_prop_get(struct nvmet_req *req)
0039 {
0040     struct nvmet_ctrl *ctrl = req->sq->ctrl;
0041     u16 status = 0;
0042     u64 val = 0;
0043 
0044     if (!nvmet_check_transfer_len(req, 0))
0045         return;
0046 
0047     if (req->cmd->prop_get.attrib & 1) {
0048         switch (le32_to_cpu(req->cmd->prop_get.offset)) {
0049         case NVME_REG_CAP:
0050             val = ctrl->cap;
0051             break;
0052         default:
0053             status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
0054             break;
0055         }
0056     } else {
0057         switch (le32_to_cpu(req->cmd->prop_get.offset)) {
0058         case NVME_REG_VS:
0059             val = ctrl->subsys->ver;
0060             break;
0061         case NVME_REG_CC:
0062             val = ctrl->cc;
0063             break;
0064         case NVME_REG_CSTS:
0065             val = ctrl->csts;
0066             break;
0067         default:
0068             status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
0069             break;
0070         }
0071     }
0072 
0073     if (status && req->cmd->prop_get.attrib & 1) {
0074         req->error_loc =
0075             offsetof(struct nvmf_property_get_command, offset);
0076     } else {
0077         req->error_loc =
0078             offsetof(struct nvmf_property_get_command, attrib);
0079     }
0080 
0081     req->cqe->result.u64 = cpu_to_le64(val);
0082     nvmet_req_complete(req, status);
0083 }
0084 
0085 u16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req)
0086 {
0087     struct nvme_command *cmd = req->cmd;
0088 
0089     switch (cmd->fabrics.fctype) {
0090     case nvme_fabrics_type_property_set:
0091         req->execute = nvmet_execute_prop_set;
0092         break;
0093     case nvme_fabrics_type_property_get:
0094         req->execute = nvmet_execute_prop_get;
0095         break;
0096 #ifdef CONFIG_NVME_TARGET_AUTH
0097     case nvme_fabrics_type_auth_send:
0098         req->execute = nvmet_execute_auth_send;
0099         break;
0100     case nvme_fabrics_type_auth_receive:
0101         req->execute = nvmet_execute_auth_receive;
0102         break;
0103 #endif
0104     default:
0105         pr_debug("received unknown capsule type 0x%x\n",
0106             cmd->fabrics.fctype);
0107         req->error_loc = offsetof(struct nvmf_common_command, fctype);
0108         return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
0109     }
0110 
0111     return 0;
0112 }
0113 
0114 u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req)
0115 {
0116     struct nvme_command *cmd = req->cmd;
0117 
0118     switch (cmd->fabrics.fctype) {
0119 #ifdef CONFIG_NVME_TARGET_AUTH
0120     case nvme_fabrics_type_auth_send:
0121         req->execute = nvmet_execute_auth_send;
0122         break;
0123     case nvme_fabrics_type_auth_receive:
0124         req->execute = nvmet_execute_auth_receive;
0125         break;
0126 #endif
0127     default:
0128         pr_debug("received unknown capsule type 0x%x\n",
0129             cmd->fabrics.fctype);
0130         req->error_loc = offsetof(struct nvmf_common_command, fctype);
0131         return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
0132     }
0133 
0134     return 0;
0135 }
0136 
0137 static u16 nvmet_install_queue(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
0138 {
0139     struct nvmf_connect_command *c = &req->cmd->connect;
0140     u16 qid = le16_to_cpu(c->qid);
0141     u16 sqsize = le16_to_cpu(c->sqsize);
0142     struct nvmet_ctrl *old;
0143     u16 mqes = NVME_CAP_MQES(ctrl->cap);
0144     u16 ret;
0145 
0146     if (!sqsize) {
0147         pr_warn("queue size zero!\n");
0148         req->error_loc = offsetof(struct nvmf_connect_command, sqsize);
0149         req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(sqsize);
0150         ret = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
0151         goto err;
0152     }
0153 
0154     if (ctrl->sqs[qid] != NULL) {
0155         pr_warn("qid %u has already been created\n", qid);
0156         req->error_loc = offsetof(struct nvmf_connect_command, qid);
0157         return NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR;
0158     }
0159 
0160     if (sqsize > mqes) {
0161         pr_warn("sqsize %u is larger than MQES supported %u cntlid %d\n",
0162                 sqsize, mqes, ctrl->cntlid);
0163         req->error_loc = offsetof(struct nvmf_connect_command, sqsize);
0164         req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(sqsize);
0165         return NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
0166     }
0167 
0168     old = cmpxchg(&req->sq->ctrl, NULL, ctrl);
0169     if (old) {
0170         pr_warn("queue already connected!\n");
0171         req->error_loc = offsetof(struct nvmf_connect_command, opcode);
0172         return NVME_SC_CONNECT_CTRL_BUSY | NVME_SC_DNR;
0173     }
0174 
0175     /* note: convert queue size from 0's-based value to 1's-based value */
0176     nvmet_cq_setup(ctrl, req->cq, qid, sqsize + 1);
0177     nvmet_sq_setup(ctrl, req->sq, qid, sqsize + 1);
0178 
0179     if (c->cattr & NVME_CONNECT_DISABLE_SQFLOW) {
0180         req->sq->sqhd_disabled = true;
0181         req->cqe->sq_head = cpu_to_le16(0xffff);
0182     }
0183 
0184     if (ctrl->ops->install_queue) {
0185         ret = ctrl->ops->install_queue(req->sq);
0186         if (ret) {
0187             pr_err("failed to install queue %d cntlid %d ret %x\n",
0188                 qid, ctrl->cntlid, ret);
0189             ctrl->sqs[qid] = NULL;
0190             goto err;
0191         }
0192     }
0193 
0194     return 0;
0195 
0196 err:
0197     req->sq->ctrl = NULL;
0198     return ret;
0199 }
0200 
0201 static void nvmet_execute_admin_connect(struct nvmet_req *req)
0202 {
0203     struct nvmf_connect_command *c = &req->cmd->connect;
0204     struct nvmf_connect_data *d;
0205     struct nvmet_ctrl *ctrl = NULL;
0206     u16 status = 0;
0207     int ret;
0208 
0209     if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
0210         return;
0211 
0212     d = kmalloc(sizeof(*d), GFP_KERNEL);
0213     if (!d) {
0214         status = NVME_SC_INTERNAL;
0215         goto complete;
0216     }
0217 
0218     status = nvmet_copy_from_sgl(req, 0, d, sizeof(*d));
0219     if (status)
0220         goto out;
0221 
0222     /* zero out initial completion result, assign values as needed */
0223     req->cqe->result.u32 = 0;
0224 
0225     if (c->recfmt != 0) {
0226         pr_warn("invalid connect version (%d).\n",
0227             le16_to_cpu(c->recfmt));
0228         req->error_loc = offsetof(struct nvmf_connect_command, recfmt);
0229         status = NVME_SC_CONNECT_FORMAT | NVME_SC_DNR;
0230         goto out;
0231     }
0232 
0233     if (unlikely(d->cntlid != cpu_to_le16(0xffff))) {
0234         pr_warn("connect attempt for invalid controller ID %#x\n",
0235             d->cntlid);
0236         status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
0237         req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(cntlid);
0238         goto out;
0239     }
0240 
0241     status = nvmet_alloc_ctrl(d->subsysnqn, d->hostnqn, req,
0242                   le32_to_cpu(c->kato), &ctrl);
0243     if (status)
0244         goto out;
0245 
0246     ctrl->pi_support = ctrl->port->pi_enable && ctrl->subsys->pi_support;
0247 
0248     uuid_copy(&ctrl->hostid, &d->hostid);
0249 
0250     ret = nvmet_setup_auth(ctrl);
0251     if (ret < 0) {
0252         pr_err("Failed to setup authentication, error %d\n", ret);
0253         nvmet_ctrl_put(ctrl);
0254         if (ret == -EPERM)
0255             status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR);
0256         else
0257             status = NVME_SC_INTERNAL;
0258         goto out;
0259     }
0260 
0261     status = nvmet_install_queue(ctrl, req);
0262     if (status) {
0263         nvmet_ctrl_put(ctrl);
0264         goto out;
0265     }
0266 
0267     pr_info("creating %s controller %d for subsystem %s for NQN %s%s%s.\n",
0268         nvmet_is_disc_subsys(ctrl->subsys) ? "discovery" : "nvm",
0269         ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
0270         ctrl->pi_support ? " T10-PI is enabled" : "",
0271         nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : "");
0272     req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
0273 
0274     if (nvmet_has_auth(ctrl))
0275         nvmet_init_auth(ctrl, req);
0276 out:
0277     kfree(d);
0278 complete:
0279     nvmet_req_complete(req, status);
0280 }
0281 
0282 static void nvmet_execute_io_connect(struct nvmet_req *req)
0283 {
0284     struct nvmf_connect_command *c = &req->cmd->connect;
0285     struct nvmf_connect_data *d;
0286     struct nvmet_ctrl *ctrl;
0287     u16 qid = le16_to_cpu(c->qid);
0288     u16 status = 0;
0289 
0290     if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
0291         return;
0292 
0293     d = kmalloc(sizeof(*d), GFP_KERNEL);
0294     if (!d) {
0295         status = NVME_SC_INTERNAL;
0296         goto complete;
0297     }
0298 
0299     status = nvmet_copy_from_sgl(req, 0, d, sizeof(*d));
0300     if (status)
0301         goto out;
0302 
0303     /* zero out initial completion result, assign values as needed */
0304     req->cqe->result.u32 = 0;
0305 
0306     if (c->recfmt != 0) {
0307         pr_warn("invalid connect version (%d).\n",
0308             le16_to_cpu(c->recfmt));
0309         status = NVME_SC_CONNECT_FORMAT | NVME_SC_DNR;
0310         goto out;
0311     }
0312 
0313     ctrl = nvmet_ctrl_find_get(d->subsysnqn, d->hostnqn,
0314                    le16_to_cpu(d->cntlid), req);
0315     if (!ctrl) {
0316         status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
0317         goto out;
0318     }
0319 
0320     if (unlikely(qid > ctrl->subsys->max_qid)) {
0321         pr_warn("invalid queue id (%d)\n", qid);
0322         status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
0323         req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(qid);
0324         goto out_ctrl_put;
0325     }
0326 
0327     status = nvmet_install_queue(ctrl, req);
0328     if (status)
0329         goto out_ctrl_put;
0330 
0331     /* pass back cntlid for successful completion */
0332     req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
0333 
0334     pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid);
0335     req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
0336     if (nvmet_has_auth(ctrl))
0337         nvmet_init_auth(ctrl, req);
0338 
0339 out:
0340     kfree(d);
0341 complete:
0342     nvmet_req_complete(req, status);
0343     return;
0344 
0345 out_ctrl_put:
0346     nvmet_ctrl_put(ctrl);
0347     goto out;
0348 }
0349 
0350 u16 nvmet_parse_connect_cmd(struct nvmet_req *req)
0351 {
0352     struct nvme_command *cmd = req->cmd;
0353 
0354     if (!nvme_is_fabrics(cmd)) {
0355         pr_debug("invalid command 0x%x on unconnected queue.\n",
0356             cmd->fabrics.opcode);
0357         req->error_loc = offsetof(struct nvme_common_command, opcode);
0358         return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
0359     }
0360     if (cmd->fabrics.fctype != nvme_fabrics_type_connect) {
0361         pr_debug("invalid capsule type 0x%x on unconnected queue.\n",
0362             cmd->fabrics.fctype);
0363         req->error_loc = offsetof(struct nvmf_common_command, fctype);
0364         return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
0365     }
0366 
0367     if (cmd->connect.qid == 0)
0368         req->execute = nvmet_execute_admin_connect;
0369     else
0370         req->execute = nvmet_execute_io_connect;
0371     return 0;
0372 }