| .. | .. | 
|---|
 | 1 | +// SPDX-License-Identifier: GPL-2.0  | 
|---|
| 1 | 2 |  /* | 
|---|
| 2 | 3 |   * NVMe Fabrics command implementation. | 
|---|
| 3 | 4 |   * Copyright (c) 2015-2016 HGST, a Western Digital Company. | 
|---|
| 4 |  | - *  | 
|---|
| 5 |  | - * This program is free software; you can redistribute it and/or modify it  | 
|---|
| 6 |  | - * under the terms and conditions of the GNU General Public License,  | 
|---|
| 7 |  | - * version 2, as published by the Free Software Foundation.  | 
|---|
| 8 |  | - *  | 
|---|
| 9 |  | - * This program is distributed in the hope it will be useful, but WITHOUT  | 
|---|
| 10 |  | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or  | 
|---|
| 11 |  | - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for  | 
|---|
| 12 |  | - * more details.  | 
|---|
| 13 | 5 |   */ | 
|---|
| 14 | 6 |  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|---|
| 15 | 7 |  #include <linux/blkdev.h> | 
|---|
| .. | .. | 
|---|
| 17 | 9 |   | 
|---|
| 18 | 10 |  static void nvmet_execute_prop_set(struct nvmet_req *req) | 
|---|
| 19 | 11 |  { | 
|---|
 | 12 | +	u64 val = le64_to_cpu(req->cmd->prop_set.value);  | 
|---|
| 20 | 13 |  	u16 status = 0; | 
|---|
| 21 | 14 |   | 
|---|
| 22 |  | -	if (!(req->cmd->prop_set.attrib & 1)) {  | 
|---|
| 23 |  | -		u64 val = le64_to_cpu(req->cmd->prop_set.value);  | 
|---|
 | 15 | +	if (!nvmet_check_transfer_len(req, 0))  | 
|---|
 | 16 | +		return;  | 
|---|
| 24 | 17 |   | 
|---|
| 25 |  | -		switch (le32_to_cpu(req->cmd->prop_set.offset)) {  | 
|---|
| 26 |  | -		case NVME_REG_CC:  | 
|---|
| 27 |  | -			nvmet_update_cc(req->sq->ctrl, val);  | 
|---|
| 28 |  | -			break;  | 
|---|
| 29 |  | -		default:  | 
|---|
| 30 |  | -			status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;  | 
|---|
| 31 |  | -			break;  | 
|---|
| 32 |  | -		}  | 
|---|
| 33 |  | -	} else {  | 
|---|
 | 18 | +	if (req->cmd->prop_set.attrib & 1) {  | 
|---|
 | 19 | +		req->error_loc =  | 
|---|
 | 20 | +			offsetof(struct nvmf_property_set_command, attrib);  | 
|---|
| 34 | 21 |  		status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; | 
|---|
 | 22 | +		goto out;  | 
|---|
| 35 | 23 |  	} | 
|---|
| 36 | 24 |   | 
|---|
 | 25 | +	switch (le32_to_cpu(req->cmd->prop_set.offset)) {  | 
|---|
 | 26 | +	case NVME_REG_CC:  | 
|---|
 | 27 | +		nvmet_update_cc(req->sq->ctrl, val);  | 
|---|
 | 28 | +		break;  | 
|---|
 | 29 | +	default:  | 
|---|
 | 30 | +		req->error_loc =  | 
|---|
 | 31 | +			offsetof(struct nvmf_property_set_command, offset);  | 
|---|
 | 32 | +		status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;  | 
|---|
 | 33 | +	}  | 
|---|
 | 34 | +out:  | 
|---|
| 37 | 35 |  	nvmet_req_complete(req, status); | 
|---|
| 38 | 36 |  } | 
|---|
| 39 | 37 |   | 
|---|
| .. | .. | 
|---|
| 42 | 40 |  	struct nvmet_ctrl *ctrl = req->sq->ctrl; | 
|---|
| 43 | 41 |  	u16 status = 0; | 
|---|
| 44 | 42 |  	u64 val = 0; | 
|---|
 | 43 | +  | 
|---|
 | 44 | +	if (!nvmet_check_transfer_len(req, 0))  | 
|---|
 | 45 | +		return;  | 
|---|
| 45 | 46 |   | 
|---|
| 46 | 47 |  	if (req->cmd->prop_get.attrib & 1) { | 
|---|
| 47 | 48 |  		switch (le32_to_cpu(req->cmd->prop_get.offset)) { | 
|---|
| .. | .. | 
|---|
| 69 | 70 |  		} | 
|---|
| 70 | 71 |  	} | 
|---|
| 71 | 72 |   | 
|---|
| 72 |  | -	req->rsp->result.u64 = cpu_to_le64(val);  | 
|---|
 | 73 | +	if (status && req->cmd->prop_get.attrib & 1) {  | 
|---|
 | 74 | +		req->error_loc =  | 
|---|
 | 75 | +			offsetof(struct nvmf_property_get_command, offset);  | 
|---|
 | 76 | +	} else {  | 
|---|
 | 77 | +		req->error_loc =  | 
|---|
 | 78 | +			offsetof(struct nvmf_property_get_command, attrib);  | 
|---|
 | 79 | +	}  | 
|---|
 | 80 | +  | 
|---|
 | 81 | +	req->cqe->result.u64 = cpu_to_le64(val);  | 
|---|
| 73 | 82 |  	nvmet_req_complete(req, status); | 
|---|
| 74 | 83 |  } | 
|---|
| 75 | 84 |   | 
|---|
| .. | .. | 
|---|
| 79 | 88 |   | 
|---|
| 80 | 89 |  	switch (cmd->fabrics.fctype) { | 
|---|
| 81 | 90 |  	case nvme_fabrics_type_property_set: | 
|---|
| 82 |  | -		req->data_len = 0;  | 
|---|
| 83 | 91 |  		req->execute = nvmet_execute_prop_set; | 
|---|
| 84 | 92 |  		break; | 
|---|
| 85 | 93 |  	case nvme_fabrics_type_property_get: | 
|---|
| 86 |  | -		req->data_len = 0;  | 
|---|
| 87 | 94 |  		req->execute = nvmet_execute_prop_get; | 
|---|
| 88 | 95 |  		break; | 
|---|
| 89 | 96 |  	default: | 
|---|
| 90 | 97 |  		pr_err("received unknown capsule type 0x%x\n", | 
|---|
| 91 | 98 |  			cmd->fabrics.fctype); | 
|---|
 | 99 | +		req->error_loc = offsetof(struct nvmf_common_command, fctype);  | 
|---|
| 92 | 100 |  		return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; | 
|---|
| 93 | 101 |  	} | 
|---|
| 94 | 102 |   | 
|---|
| .. | .. | 
|---|
| 101 | 109 |  	u16 qid = le16_to_cpu(c->qid); | 
|---|
| 102 | 110 |  	u16 sqsize = le16_to_cpu(c->sqsize); | 
|---|
| 103 | 111 |  	struct nvmet_ctrl *old; | 
|---|
 | 112 | +	u16 ret;  | 
|---|
| 104 | 113 |   | 
|---|
| 105 | 114 |  	old = cmpxchg(&req->sq->ctrl, NULL, ctrl); | 
|---|
| 106 | 115 |  	if (old) { | 
|---|
| 107 | 116 |  		pr_warn("queue already connected!\n"); | 
|---|
 | 117 | +		req->error_loc = offsetof(struct nvmf_connect_command, opcode);  | 
|---|
| 108 | 118 |  		return NVME_SC_CONNECT_CTRL_BUSY | NVME_SC_DNR; | 
|---|
| 109 | 119 |  	} | 
|---|
| 110 | 120 |  	if (!sqsize) { | 
|---|
| 111 | 121 |  		pr_warn("queue size zero!\n"); | 
|---|
| 112 |  | -		return NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;  | 
|---|
 | 122 | +		req->error_loc = offsetof(struct nvmf_connect_command, sqsize);  | 
|---|
 | 123 | +		req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(sqsize);  | 
|---|
 | 124 | +		ret = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;  | 
|---|
 | 125 | +		goto err;  | 
|---|
| 113 | 126 |  	} | 
|---|
| 114 | 127 |   | 
|---|
| 115 | 128 |  	/* note: convert queue size from 0's-based value to 1's-based value */ | 
|---|
| 116 | 129 |  	nvmet_cq_setup(ctrl, req->cq, qid, sqsize + 1); | 
|---|
| 117 | 130 |  	nvmet_sq_setup(ctrl, req->sq, qid, sqsize + 1); | 
|---|
 | 131 | +  | 
|---|
 | 132 | +	if (c->cattr & NVME_CONNECT_DISABLE_SQFLOW) {  | 
|---|
 | 133 | +		req->sq->sqhd_disabled = true;  | 
|---|
 | 134 | +		req->cqe->sq_head = cpu_to_le16(0xffff);  | 
|---|
 | 135 | +	}  | 
|---|
 | 136 | +  | 
|---|
 | 137 | +	if (ctrl->ops->install_queue) {  | 
|---|
 | 138 | +		ret = ctrl->ops->install_queue(req->sq);  | 
|---|
 | 139 | +		if (ret) {  | 
|---|
 | 140 | +			pr_err("failed to install queue %d cntlid %d ret %x\n",  | 
|---|
 | 141 | +				qid, ctrl->cntlid, ret);  | 
|---|
 | 142 | +			goto err;  | 
|---|
 | 143 | +		}  | 
|---|
 | 144 | +	}  | 
|---|
 | 145 | +  | 
|---|
| 118 | 146 |  	return 0; | 
|---|
 | 147 | +  | 
|---|
 | 148 | +err:  | 
|---|
 | 149 | +	req->sq->ctrl = NULL;  | 
|---|
 | 150 | +	return ret;  | 
|---|
| 119 | 151 |  } | 
|---|
| 120 | 152 |   | 
|---|
| 121 | 153 |  static void nvmet_execute_admin_connect(struct nvmet_req *req) | 
|---|
| .. | .. | 
|---|
| 124 | 156 |  	struct nvmf_connect_data *d; | 
|---|
| 125 | 157 |  	struct nvmet_ctrl *ctrl = NULL; | 
|---|
| 126 | 158 |  	u16 status = 0; | 
|---|
 | 159 | +  | 
|---|
 | 160 | +	if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))  | 
|---|
 | 161 | +		return;  | 
|---|
| 127 | 162 |   | 
|---|
| 128 | 163 |  	d = kmalloc(sizeof(*d), GFP_KERNEL); | 
|---|
| 129 | 164 |  	if (!d) { | 
|---|
| .. | .. | 
|---|
| 136 | 171 |  		goto out; | 
|---|
| 137 | 172 |   | 
|---|
| 138 | 173 |  	/* zero out initial completion result, assign values as needed */ | 
|---|
| 139 |  | -	req->rsp->result.u32 = 0;  | 
|---|
 | 174 | +	req->cqe->result.u32 = 0;  | 
|---|
| 140 | 175 |   | 
|---|
| 141 | 176 |  	if (c->recfmt != 0) { | 
|---|
| 142 | 177 |  		pr_warn("invalid connect version (%d).\n", | 
|---|
| 143 | 178 |  			le16_to_cpu(c->recfmt)); | 
|---|
 | 179 | +		req->error_loc = offsetof(struct nvmf_connect_command, recfmt);  | 
|---|
| 144 | 180 |  		status = NVME_SC_CONNECT_FORMAT | NVME_SC_DNR; | 
|---|
| 145 | 181 |  		goto out; | 
|---|
| 146 | 182 |  	} | 
|---|
| .. | .. | 
|---|
| 149 | 185 |  		pr_warn("connect attempt for invalid controller ID %#x\n", | 
|---|
| 150 | 186 |  			d->cntlid); | 
|---|
| 151 | 187 |  		status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR; | 
|---|
| 152 |  | -		req->rsp->result.u32 = IPO_IATTR_CONNECT_DATA(cntlid);  | 
|---|
 | 188 | +		req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(cntlid);  | 
|---|
| 153 | 189 |  		goto out; | 
|---|
| 154 | 190 |  	} | 
|---|
| 155 | 191 |   | 
|---|
| 156 | 192 |  	status = nvmet_alloc_ctrl(d->subsysnqn, d->hostnqn, req, | 
|---|
| 157 | 193 |  				  le32_to_cpu(c->kato), &ctrl); | 
|---|
| 158 |  | -	if (status)  | 
|---|
 | 194 | +	if (status) {  | 
|---|
 | 195 | +		if (status == (NVME_SC_INVALID_FIELD | NVME_SC_DNR))  | 
|---|
 | 196 | +			req->error_loc =  | 
|---|
 | 197 | +				offsetof(struct nvme_common_command, opcode);  | 
|---|
| 159 | 198 |  		goto out; | 
|---|
 | 199 | +	}  | 
|---|
 | 200 | +  | 
|---|
 | 201 | +	ctrl->pi_support = ctrl->port->pi_enable && ctrl->subsys->pi_support;  | 
|---|
 | 202 | +  | 
|---|
| 160 | 203 |  	uuid_copy(&ctrl->hostid, &d->hostid); | 
|---|
| 161 | 204 |   | 
|---|
| 162 | 205 |  	status = nvmet_install_queue(ctrl, req); | 
|---|
| .. | .. | 
|---|
| 165 | 208 |  		goto out; | 
|---|
| 166 | 209 |  	} | 
|---|
| 167 | 210 |   | 
|---|
| 168 |  | -	pr_info("creating controller %d for subsystem %s for NQN %s.\n",  | 
|---|
| 169 |  | -		ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn);  | 
|---|
| 170 |  | -	req->rsp->result.u16 = cpu_to_le16(ctrl->cntlid);  | 
|---|
 | 211 | +	pr_info("creating controller %d for subsystem %s for NQN %s%s.\n",  | 
|---|
 | 212 | +		ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,  | 
|---|
 | 213 | +		ctrl->pi_support ? " T10-PI is enabled" : "");  | 
|---|
 | 214 | +	req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);  | 
|---|
| 171 | 215 |   | 
|---|
| 172 | 216 |  out: | 
|---|
| 173 | 217 |  	kfree(d); | 
|---|
| .. | .. | 
|---|
| 183 | 227 |  	u16 qid = le16_to_cpu(c->qid); | 
|---|
| 184 | 228 |  	u16 status = 0; | 
|---|
| 185 | 229 |   | 
|---|
 | 230 | +	if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))  | 
|---|
 | 231 | +		return;  | 
|---|
 | 232 | +  | 
|---|
| 186 | 233 |  	d = kmalloc(sizeof(*d), GFP_KERNEL); | 
|---|
| 187 | 234 |  	if (!d) { | 
|---|
| 188 | 235 |  		status = NVME_SC_INTERNAL; | 
|---|
| .. | .. | 
|---|
| 194 | 241 |  		goto out; | 
|---|
| 195 | 242 |   | 
|---|
| 196 | 243 |  	/* zero out initial completion result, assign values as needed */ | 
|---|
| 197 |  | -	req->rsp->result.u32 = 0;  | 
|---|
 | 244 | +	req->cqe->result.u32 = 0;  | 
|---|
| 198 | 245 |   | 
|---|
| 199 | 246 |  	if (c->recfmt != 0) { | 
|---|
| 200 | 247 |  		pr_warn("invalid connect version (%d).\n", | 
|---|
| .. | .. | 
|---|
| 212 | 259 |  	if (unlikely(qid > ctrl->subsys->max_qid)) { | 
|---|
| 213 | 260 |  		pr_warn("invalid queue id (%d)\n", qid); | 
|---|
| 214 | 261 |  		status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR; | 
|---|
| 215 |  | -		req->rsp->result.u32 = IPO_IATTR_CONNECT_SQE(qid);  | 
|---|
 | 262 | +		req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(qid);  | 
|---|
| 216 | 263 |  		goto out_ctrl_put; | 
|---|
| 217 | 264 |  	} | 
|---|
| 218 | 265 |   | 
|---|
| 219 | 266 |  	status = nvmet_install_queue(ctrl, req); | 
|---|
| 220 |  | -	if (status) {  | 
|---|
| 221 |  | -		/* pass back cntlid that had the issue of installing queue */  | 
|---|
| 222 |  | -		req->rsp->result.u16 = cpu_to_le16(ctrl->cntlid);  | 
|---|
 | 267 | +	if (status)  | 
|---|
| 223 | 268 |  		goto out_ctrl_put; | 
|---|
| 224 |  | -	}  | 
|---|
 | 269 | +  | 
|---|
 | 270 | +	/* pass back cntlid for successful completion */  | 
|---|
 | 271 | +	req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);  | 
|---|
| 225 | 272 |   | 
|---|
| 226 | 273 |  	pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid); | 
|---|
| 227 | 274 |   | 
|---|
| .. | .. | 
|---|
| 240 | 287 |  { | 
|---|
| 241 | 288 |  	struct nvme_command *cmd = req->cmd; | 
|---|
| 242 | 289 |   | 
|---|
| 243 |  | -	if (cmd->common.opcode != nvme_fabrics_command) {  | 
|---|
 | 290 | +	if (!nvme_is_fabrics(cmd)) {  | 
|---|
| 244 | 291 |  		pr_err("invalid command 0x%x on unconnected queue.\n", | 
|---|
| 245 | 292 |  			cmd->fabrics.opcode); | 
|---|
 | 293 | +		req->error_loc = offsetof(struct nvme_common_command, opcode);  | 
|---|
| 246 | 294 |  		return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; | 
|---|
| 247 | 295 |  	} | 
|---|
| 248 | 296 |  	if (cmd->fabrics.fctype != nvme_fabrics_type_connect) { | 
|---|
| 249 | 297 |  		pr_err("invalid capsule type 0x%x on unconnected queue.\n", | 
|---|
| 250 | 298 |  			cmd->fabrics.fctype); | 
|---|
 | 299 | +		req->error_loc = offsetof(struct nvmf_common_command, fctype);  | 
|---|
| 251 | 300 |  		return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; | 
|---|
| 252 | 301 |  	} | 
|---|
| 253 | 302 |   | 
|---|
| 254 |  | -	req->data_len = sizeof(struct nvmf_connect_data);  | 
|---|
| 255 | 303 |  	if (cmd->connect.qid == 0) | 
|---|
| 256 | 304 |  		req->execute = nvmet_execute_admin_connect; | 
|---|
| 257 | 305 |  	else | 
|---|