File Index
Files and directories
Sign in
FILE

fabrics-cmd.c

File preview with syntax highlighting.

linux-6.19.14/drivers/nvme/target/fabrics-cmd.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * NVMe Fabrics command implementation.
  4 * Copyright (c) 2015-2016 HGST, a Western Digital Company.
  5 */
  6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  7#include <linux/blkdev.h>
  8#include "nvmet.h"
  9
 10static void nvmet_execute_prop_set(struct nvmet_req *req)
 11{
 12	u64 val = le64_to_cpu(req->cmd->prop_set.value);
 13	u16 status = 0;
 14
 15	if (!nvmet_check_transfer_len(req, 0))
 16		return;
 17
 18	if (req->cmd->prop_set.attrib & 1) {
 19		req->error_loc =
 20			offsetof(struct nvmf_property_set_command, attrib);
 21		status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
 22		goto out;
 23	}
 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_STATUS_DNR;
 33	}
 34out:
 35	nvmet_req_complete(req, status);
 36}
 37
 38static void nvmet_execute_prop_get(struct nvmet_req *req)
 39{
 40	struct nvmet_ctrl *ctrl = req->sq->ctrl;
 41	u16 status = 0;
 42	u64 val = 0;
 43
 44	if (!nvmet_check_transfer_len(req, 0))
 45		return;
 46
 47	if (req->cmd->prop_get.attrib & 1) {
 48		switch (le32_to_cpu(req->cmd->prop_get.offset)) {
 49		case NVME_REG_CAP:
 50			val = ctrl->cap;
 51			break;
 52		default:
 53			status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
 54			break;
 55		}
 56	} else {
 57		switch (le32_to_cpu(req->cmd->prop_get.offset)) {
 58		case NVME_REG_VS:
 59			val = ctrl->subsys->ver;
 60			break;
 61		case NVME_REG_CC:
 62			val = ctrl->cc;
 63			break;
 64		case NVME_REG_CSTS:
 65			val = ctrl->csts;
 66			break;
 67		case NVME_REG_CRTO:
 68			val = NVME_CAP_TIMEOUT(ctrl->csts);
 69			break;
 70		default:
 71			status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
 72			break;
 73		}
 74	}
 75
 76	if (status && req->cmd->prop_get.attrib & 1) {
 77		req->error_loc =
 78			offsetof(struct nvmf_property_get_command, offset);
 79	} else {
 80		req->error_loc =
 81			offsetof(struct nvmf_property_get_command, attrib);
 82	}
 83
 84	req->cqe->result.u64 = cpu_to_le64(val);
 85	nvmet_req_complete(req, status);
 86}
 87
 88u32 nvmet_fabrics_admin_cmd_data_len(struct nvmet_req *req)
 89{
 90	struct nvme_command *cmd = req->cmd;
 91
 92	switch (cmd->fabrics.fctype) {
 93#ifdef CONFIG_NVME_TARGET_AUTH
 94	case nvme_fabrics_type_auth_send:
 95		return nvmet_auth_send_data_len(req);
 96	case nvme_fabrics_type_auth_receive:
 97		return nvmet_auth_receive_data_len(req);
 98#endif
 99	default:
100		return 0;
101	}
102}
103
104u16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req)
105{
106	struct nvme_command *cmd = req->cmd;
107
108	switch (cmd->fabrics.fctype) {
109	case nvme_fabrics_type_property_set:
110		req->execute = nvmet_execute_prop_set;
111		break;
112	case nvme_fabrics_type_property_get:
113		req->execute = nvmet_execute_prop_get;
114		break;
115#ifdef CONFIG_NVME_TARGET_AUTH
116	case nvme_fabrics_type_auth_send:
117		req->execute = nvmet_execute_auth_send;
118		break;
119	case nvme_fabrics_type_auth_receive:
120		req->execute = nvmet_execute_auth_receive;
121		break;
122#endif
123	default:
124		pr_debug("received unknown capsule type 0x%x\n",
125			cmd->fabrics.fctype);
126		req->error_loc = offsetof(struct nvmf_common_command, fctype);
127		return NVME_SC_INVALID_OPCODE | NVME_STATUS_DNR;
128	}
129
130	return 0;
131}
132
133u32 nvmet_fabrics_io_cmd_data_len(struct nvmet_req *req)
134{
135	struct nvme_command *cmd = req->cmd;
136
137	switch (cmd->fabrics.fctype) {
138#ifdef CONFIG_NVME_TARGET_AUTH
139	case nvme_fabrics_type_auth_send:
140		return nvmet_auth_send_data_len(req);
141	case nvme_fabrics_type_auth_receive:
142		return nvmet_auth_receive_data_len(req);
143#endif
144	default:
145		return 0;
146	}
147}
148
149u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req)
150{
151	struct nvme_command *cmd = req->cmd;
152
153	switch (cmd->fabrics.fctype) {
154#ifdef CONFIG_NVME_TARGET_AUTH
155	case nvme_fabrics_type_auth_send:
156		req->execute = nvmet_execute_auth_send;
157		break;
158	case nvme_fabrics_type_auth_receive:
159		req->execute = nvmet_execute_auth_receive;
160		break;
161#endif
162	default:
163		pr_debug("received unknown capsule type 0x%x\n",
164			cmd->fabrics.fctype);
165		req->error_loc = offsetof(struct nvmf_common_command, fctype);
166		return NVME_SC_INVALID_OPCODE | NVME_STATUS_DNR;
167	}
168
169	return 0;
170}
171
172static u16 nvmet_install_queue(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
173{
174	struct nvmf_connect_command *c = &req->cmd->connect;
175	u16 qid = le16_to_cpu(c->qid);
176	u16 sqsize = le16_to_cpu(c->sqsize);
177	struct nvmet_ctrl *old;
178	u16 mqes = NVME_CAP_MQES(ctrl->cap);
179	u16 ret;
180
181	if (!sqsize) {
182		pr_warn("queue size zero!\n");
183		req->error_loc = offsetof(struct nvmf_connect_command, sqsize);
184		req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(sqsize);
185		ret = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
186		goto err;
187	}
188
189	if (ctrl->sqs[qid] != NULL) {
190		pr_warn("qid %u has already been created\n", qid);
191		req->error_loc = offsetof(struct nvmf_connect_command, qid);
192		return NVME_SC_CMD_SEQ_ERROR | NVME_STATUS_DNR;
193	}
194
195	/* for fabrics, this value applies to only the I/O Submission Queues */
196	if (qid && sqsize > mqes) {
197		pr_warn("sqsize %u is larger than MQES supported %u cntlid %d\n",
198				sqsize, mqes, ctrl->cntlid);
199		req->error_loc = offsetof(struct nvmf_connect_command, sqsize);
200		req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(sqsize);
201		return NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
202	}
203
204	old = cmpxchg(&req->sq->ctrl, NULL, ctrl);
205	if (old) {
206		pr_warn("queue already connected!\n");
207		req->error_loc = offsetof(struct nvmf_connect_command, opcode);
208		return NVME_SC_CONNECT_CTRL_BUSY | NVME_STATUS_DNR;
209	}
210
211	kref_get(&ctrl->ref);
212	old = cmpxchg(&req->cq->ctrl, NULL, ctrl);
213	if (old) {
214		pr_warn("queue already connected!\n");
215		req->error_loc = offsetof(struct nvmf_connect_command, opcode);
216		return NVME_SC_CONNECT_CTRL_BUSY | NVME_STATUS_DNR;
217	}
218
219	/* note: convert queue size from 0's-based value to 1's-based value */
220	nvmet_cq_setup(ctrl, req->cq, qid, sqsize + 1);
221	nvmet_sq_setup(ctrl, req->sq, qid, sqsize + 1);
222
223	if (c->cattr & NVME_CONNECT_DISABLE_SQFLOW) {
224		req->sq->sqhd_disabled = true;
225		req->cqe->sq_head = cpu_to_le16(0xffff);
226	}
227
228	if (ctrl->ops->install_queue) {
229		ret = ctrl->ops->install_queue(req->sq);
230		if (ret) {
231			pr_err("failed to install queue %d cntlid %d ret %x\n",
232				qid, ctrl->cntlid, ret);
233			ctrl->sqs[qid] = NULL;
234			goto err;
235		}
236	}
237
238	return 0;
239
240err:
241	req->sq->ctrl = NULL;
242	return ret;
243}
244
245static u32 nvmet_connect_result(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
246{
247	bool needs_auth = nvmet_has_auth(ctrl, sq);
248	key_serial_t keyid = nvmet_queue_tls_keyid(sq);
249
250	/* Do not authenticate I/O queues */
251	if (sq->qid)
252		needs_auth = false;
253
254	if (keyid)
255		pr_debug("%s: ctrl %d qid %d should %sauthenticate, tls psk %08x\n",
256			 __func__, ctrl->cntlid, sq->qid,
257			 needs_auth ? "" : "not ", keyid);
258	else
259		pr_debug("%s: ctrl %d qid %d should %sauthenticate%s\n",
260			 __func__, ctrl->cntlid, sq->qid,
261			 needs_auth ? "" : "not ",
262			 ctrl->concat ? ", secure concatenation" : "");
263	return (u32)ctrl->cntlid |
264		(needs_auth ? NVME_CONNECT_AUTHREQ_ATR : 0);
265}
266
267static void nvmet_execute_admin_connect(struct nvmet_req *req)
268{
269	struct nvmf_connect_command *c = &req->cmd->connect;
270	struct nvmf_connect_data *d;
271	struct nvmet_ctrl *ctrl = NULL;
272	struct nvmet_alloc_ctrl_args args = {
273		.port = req->port,
274		.sq = req->sq,
275		.ops = req->ops,
276		.p2p_client = req->p2p_client,
277		.kato = le32_to_cpu(c->kato),
278	};
279
280	if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
281		return;
282
283	d = kmalloc(sizeof(*d), GFP_KERNEL);
284	if (!d) {
285		args.status = NVME_SC_INTERNAL;
286		goto complete;
287	}
288
289	args.status = nvmet_copy_from_sgl(req, 0, d, sizeof(*d));
290	if (args.status)
291		goto out;
292
293	if (c->recfmt != 0) {
294		pr_warn("invalid connect version (%d).\n",
295			le16_to_cpu(c->recfmt));
296		args.error_loc = offsetof(struct nvmf_connect_command, recfmt);
297		args.status = NVME_SC_CONNECT_FORMAT | NVME_STATUS_DNR;
298		goto out;
299	}
300
301	if (unlikely(d->cntlid != cpu_to_le16(0xffff))) {
302		pr_warn("connect attempt for invalid controller ID %#x\n",
303			d->cntlid);
304		args.status = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
305		args.result = IPO_IATTR_CONNECT_DATA(cntlid);
306		goto out;
307	}
308
309	d->subsysnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
310	d->hostnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
311
312	args.subsysnqn = d->subsysnqn;
313	args.hostnqn = d->hostnqn;
314	args.hostid = &d->hostid;
315	args.kato = le32_to_cpu(c->kato);
316
317	ctrl = nvmet_alloc_ctrl(&args);
318	if (!ctrl)
319		goto out;
320
321	args.status = nvmet_install_queue(ctrl, req);
322	if (args.status) {
323		nvmet_ctrl_put(ctrl);
324		goto out;
325	}
326
327	args.result = cpu_to_le32(nvmet_connect_result(ctrl, req->sq));
328out:
329	kfree(d);
330complete:
331	req->error_loc = args.error_loc;
332	req->cqe->result.u32 = args.result;
333	nvmet_req_complete(req, args.status);
334}
335
336static void nvmet_execute_io_connect(struct nvmet_req *req)
337{
338	struct nvmf_connect_command *c = &req->cmd->connect;
339	struct nvmf_connect_data *d;
340	struct nvmet_ctrl *ctrl;
341	u16 qid = le16_to_cpu(c->qid);
342	u16 status;
343
344	if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
345		return;
346
347	d = kmalloc(sizeof(*d), GFP_KERNEL);
348	if (!d) {
349		status = NVME_SC_INTERNAL;
350		goto complete;
351	}
352
353	status = nvmet_copy_from_sgl(req, 0, d, sizeof(*d));
354	if (status)
355		goto out;
356
357	if (c->recfmt != 0) {
358		pr_warn("invalid connect version (%d).\n",
359			le16_to_cpu(c->recfmt));
360		status = NVME_SC_CONNECT_FORMAT | NVME_STATUS_DNR;
361		goto out;
362	}
363
364	d->subsysnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
365	d->hostnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
366	ctrl = nvmet_ctrl_find_get(d->subsysnqn, d->hostnqn,
367				   le16_to_cpu(d->cntlid), req);
368	if (!ctrl) {
369		status = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
370		goto out;
371	}
372
373	if (unlikely(qid > ctrl->subsys->max_qid)) {
374		pr_warn("invalid queue id (%d)\n", qid);
375		status = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
376		req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(qid);
377		goto out_ctrl_put;
378	}
379
380	status = nvmet_install_queue(ctrl, req);
381	if (status)
382		goto out_ctrl_put;
383
384	pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid);
385	req->cqe->result.u32 = cpu_to_le32(nvmet_connect_result(ctrl, req->sq));
386out:
387	kfree(d);
388complete:
389	nvmet_req_complete(req, status);
390	return;
391
392out_ctrl_put:
393	nvmet_ctrl_put(ctrl);
394	goto out;
395}
396
397u32 nvmet_connect_cmd_data_len(struct nvmet_req *req)
398{
399	struct nvme_command *cmd = req->cmd;
400
401	if (!nvme_is_fabrics(cmd) ||
402	    cmd->fabrics.fctype != nvme_fabrics_type_connect)
403		return 0;
404
405	return sizeof(struct nvmf_connect_data);
406}
407
408u16 nvmet_parse_connect_cmd(struct nvmet_req *req)
409{
410	struct nvme_command *cmd = req->cmd;
411
412	if (!nvme_is_fabrics(cmd)) {
413		pr_debug("invalid command 0x%x on unconnected queue.\n",
414			cmd->fabrics.opcode);
415		req->error_loc = offsetof(struct nvme_common_command, opcode);
416		return NVME_SC_INVALID_OPCODE | NVME_STATUS_DNR;
417	}
418	if (cmd->fabrics.fctype != nvme_fabrics_type_connect) {
419		pr_debug("invalid capsule type 0x%x on unconnected queue.\n",
420			cmd->fabrics.fctype);
421		req->error_loc = offsetof(struct nvmf_common_command, fctype);
422		return NVME_SC_INVALID_OPCODE | NVME_STATUS_DNR;
423	}
424
425	if (cmd->connect.qid == 0)
426		req->execute = nvmet_execute_admin_connect;
427	else
428		req->execute = nvmet_execute_io_connect;
429	return 0;
430}
~/ Read-only file preview.