162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NVMe Fabrics command implementation.
462306a36Sopenharmony_ci * Copyright (c) 2015-2016 HGST, a Western Digital Company.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
762306a36Sopenharmony_ci#include <linux/blkdev.h>
862306a36Sopenharmony_ci#include "nvmet.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic void nvmet_execute_prop_set(struct nvmet_req *req)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	u64 val = le64_to_cpu(req->cmd->prop_set.value);
1362306a36Sopenharmony_ci	u16 status = 0;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, 0))
1662306a36Sopenharmony_ci		return;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	if (req->cmd->prop_set.attrib & 1) {
1962306a36Sopenharmony_ci		req->error_loc =
2062306a36Sopenharmony_ci			offsetof(struct nvmf_property_set_command, attrib);
2162306a36Sopenharmony_ci		status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
2262306a36Sopenharmony_ci		goto out;
2362306a36Sopenharmony_ci	}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	switch (le32_to_cpu(req->cmd->prop_set.offset)) {
2662306a36Sopenharmony_ci	case NVME_REG_CC:
2762306a36Sopenharmony_ci		nvmet_update_cc(req->sq->ctrl, val);
2862306a36Sopenharmony_ci		break;
2962306a36Sopenharmony_ci	default:
3062306a36Sopenharmony_ci		req->error_loc =
3162306a36Sopenharmony_ci			offsetof(struct nvmf_property_set_command, offset);
3262306a36Sopenharmony_ci		status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ciout:
3562306a36Sopenharmony_ci	nvmet_req_complete(req, status);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void nvmet_execute_prop_get(struct nvmet_req *req)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct nvmet_ctrl *ctrl = req->sq->ctrl;
4162306a36Sopenharmony_ci	u16 status = 0;
4262306a36Sopenharmony_ci	u64 val = 0;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, 0))
4562306a36Sopenharmony_ci		return;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (req->cmd->prop_get.attrib & 1) {
4862306a36Sopenharmony_ci		switch (le32_to_cpu(req->cmd->prop_get.offset)) {
4962306a36Sopenharmony_ci		case NVME_REG_CAP:
5062306a36Sopenharmony_ci			val = ctrl->cap;
5162306a36Sopenharmony_ci			break;
5262306a36Sopenharmony_ci		default:
5362306a36Sopenharmony_ci			status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
5462306a36Sopenharmony_ci			break;
5562306a36Sopenharmony_ci		}
5662306a36Sopenharmony_ci	} else {
5762306a36Sopenharmony_ci		switch (le32_to_cpu(req->cmd->prop_get.offset)) {
5862306a36Sopenharmony_ci		case NVME_REG_VS:
5962306a36Sopenharmony_ci			val = ctrl->subsys->ver;
6062306a36Sopenharmony_ci			break;
6162306a36Sopenharmony_ci		case NVME_REG_CC:
6262306a36Sopenharmony_ci			val = ctrl->cc;
6362306a36Sopenharmony_ci			break;
6462306a36Sopenharmony_ci		case NVME_REG_CSTS:
6562306a36Sopenharmony_ci			val = ctrl->csts;
6662306a36Sopenharmony_ci			break;
6762306a36Sopenharmony_ci		default:
6862306a36Sopenharmony_ci			status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
6962306a36Sopenharmony_ci			break;
7062306a36Sopenharmony_ci		}
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (status && req->cmd->prop_get.attrib & 1) {
7462306a36Sopenharmony_ci		req->error_loc =
7562306a36Sopenharmony_ci			offsetof(struct nvmf_property_get_command, offset);
7662306a36Sopenharmony_ci	} else {
7762306a36Sopenharmony_ci		req->error_loc =
7862306a36Sopenharmony_ci			offsetof(struct nvmf_property_get_command, attrib);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	req->cqe->result.u64 = cpu_to_le64(val);
8262306a36Sopenharmony_ci	nvmet_req_complete(req, status);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciu16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct nvme_command *cmd = req->cmd;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	switch (cmd->fabrics.fctype) {
9062306a36Sopenharmony_ci	case nvme_fabrics_type_property_set:
9162306a36Sopenharmony_ci		req->execute = nvmet_execute_prop_set;
9262306a36Sopenharmony_ci		break;
9362306a36Sopenharmony_ci	case nvme_fabrics_type_property_get:
9462306a36Sopenharmony_ci		req->execute = nvmet_execute_prop_get;
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci#ifdef CONFIG_NVME_TARGET_AUTH
9762306a36Sopenharmony_ci	case nvme_fabrics_type_auth_send:
9862306a36Sopenharmony_ci		req->execute = nvmet_execute_auth_send;
9962306a36Sopenharmony_ci		break;
10062306a36Sopenharmony_ci	case nvme_fabrics_type_auth_receive:
10162306a36Sopenharmony_ci		req->execute = nvmet_execute_auth_receive;
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci#endif
10462306a36Sopenharmony_ci	default:
10562306a36Sopenharmony_ci		pr_debug("received unknown capsule type 0x%x\n",
10662306a36Sopenharmony_ci			cmd->fabrics.fctype);
10762306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvmf_common_command, fctype);
10862306a36Sopenharmony_ci		return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciu16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct nvme_command *cmd = req->cmd;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	switch (cmd->fabrics.fctype) {
11962306a36Sopenharmony_ci#ifdef CONFIG_NVME_TARGET_AUTH
12062306a36Sopenharmony_ci	case nvme_fabrics_type_auth_send:
12162306a36Sopenharmony_ci		req->execute = nvmet_execute_auth_send;
12262306a36Sopenharmony_ci		break;
12362306a36Sopenharmony_ci	case nvme_fabrics_type_auth_receive:
12462306a36Sopenharmony_ci		req->execute = nvmet_execute_auth_receive;
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci#endif
12762306a36Sopenharmony_ci	default:
12862306a36Sopenharmony_ci		pr_debug("received unknown capsule type 0x%x\n",
12962306a36Sopenharmony_ci			cmd->fabrics.fctype);
13062306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvmf_common_command, fctype);
13162306a36Sopenharmony_ci		return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic u16 nvmet_install_queue(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct nvmf_connect_command *c = &req->cmd->connect;
14062306a36Sopenharmony_ci	u16 qid = le16_to_cpu(c->qid);
14162306a36Sopenharmony_ci	u16 sqsize = le16_to_cpu(c->sqsize);
14262306a36Sopenharmony_ci	struct nvmet_ctrl *old;
14362306a36Sopenharmony_ci	u16 mqes = NVME_CAP_MQES(ctrl->cap);
14462306a36Sopenharmony_ci	u16 ret;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (!sqsize) {
14762306a36Sopenharmony_ci		pr_warn("queue size zero!\n");
14862306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvmf_connect_command, sqsize);
14962306a36Sopenharmony_ci		req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(sqsize);
15062306a36Sopenharmony_ci		ret = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
15162306a36Sopenharmony_ci		goto err;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (ctrl->sqs[qid] != NULL) {
15562306a36Sopenharmony_ci		pr_warn("qid %u has already been created\n", qid);
15662306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvmf_connect_command, qid);
15762306a36Sopenharmony_ci		return NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (sqsize > mqes) {
16162306a36Sopenharmony_ci		pr_warn("sqsize %u is larger than MQES supported %u cntlid %d\n",
16262306a36Sopenharmony_ci				sqsize, mqes, ctrl->cntlid);
16362306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvmf_connect_command, sqsize);
16462306a36Sopenharmony_ci		req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(sqsize);
16562306a36Sopenharmony_ci		return NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	old = cmpxchg(&req->sq->ctrl, NULL, ctrl);
16962306a36Sopenharmony_ci	if (old) {
17062306a36Sopenharmony_ci		pr_warn("queue already connected!\n");
17162306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvmf_connect_command, opcode);
17262306a36Sopenharmony_ci		return NVME_SC_CONNECT_CTRL_BUSY | NVME_SC_DNR;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* note: convert queue size from 0's-based value to 1's-based value */
17662306a36Sopenharmony_ci	nvmet_cq_setup(ctrl, req->cq, qid, sqsize + 1);
17762306a36Sopenharmony_ci	nvmet_sq_setup(ctrl, req->sq, qid, sqsize + 1);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (c->cattr & NVME_CONNECT_DISABLE_SQFLOW) {
18062306a36Sopenharmony_ci		req->sq->sqhd_disabled = true;
18162306a36Sopenharmony_ci		req->cqe->sq_head = cpu_to_le16(0xffff);
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (ctrl->ops->install_queue) {
18562306a36Sopenharmony_ci		ret = ctrl->ops->install_queue(req->sq);
18662306a36Sopenharmony_ci		if (ret) {
18762306a36Sopenharmony_ci			pr_err("failed to install queue %d cntlid %d ret %x\n",
18862306a36Sopenharmony_ci				qid, ctrl->cntlid, ret);
18962306a36Sopenharmony_ci			ctrl->sqs[qid] = NULL;
19062306a36Sopenharmony_ci			goto err;
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cierr:
19762306a36Sopenharmony_ci	req->sq->ctrl = NULL;
19862306a36Sopenharmony_ci	return ret;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic u32 nvmet_connect_result(struct nvmet_ctrl *ctrl)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	return (u32)ctrl->cntlid |
20462306a36Sopenharmony_ci		(nvmet_has_auth(ctrl) ? NVME_CONNECT_AUTHREQ_ATR : 0);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void nvmet_execute_admin_connect(struct nvmet_req *req)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct nvmf_connect_command *c = &req->cmd->connect;
21062306a36Sopenharmony_ci	struct nvmf_connect_data *d;
21162306a36Sopenharmony_ci	struct nvmet_ctrl *ctrl = NULL;
21262306a36Sopenharmony_ci	u16 status = 0;
21362306a36Sopenharmony_ci	int ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
21662306a36Sopenharmony_ci		return;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	d = kmalloc(sizeof(*d), GFP_KERNEL);
21962306a36Sopenharmony_ci	if (!d) {
22062306a36Sopenharmony_ci		status = NVME_SC_INTERNAL;
22162306a36Sopenharmony_ci		goto complete;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	status = nvmet_copy_from_sgl(req, 0, d, sizeof(*d));
22562306a36Sopenharmony_ci	if (status)
22662306a36Sopenharmony_ci		goto out;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* zero out initial completion result, assign values as needed */
22962306a36Sopenharmony_ci	req->cqe->result.u32 = 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (c->recfmt != 0) {
23262306a36Sopenharmony_ci		pr_warn("invalid connect version (%d).\n",
23362306a36Sopenharmony_ci			le16_to_cpu(c->recfmt));
23462306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvmf_connect_command, recfmt);
23562306a36Sopenharmony_ci		status = NVME_SC_CONNECT_FORMAT | NVME_SC_DNR;
23662306a36Sopenharmony_ci		goto out;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (unlikely(d->cntlid != cpu_to_le16(0xffff))) {
24062306a36Sopenharmony_ci		pr_warn("connect attempt for invalid controller ID %#x\n",
24162306a36Sopenharmony_ci			d->cntlid);
24262306a36Sopenharmony_ci		status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
24362306a36Sopenharmony_ci		req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(cntlid);
24462306a36Sopenharmony_ci		goto out;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	d->subsysnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
24862306a36Sopenharmony_ci	d->hostnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
24962306a36Sopenharmony_ci	status = nvmet_alloc_ctrl(d->subsysnqn, d->hostnqn, req,
25062306a36Sopenharmony_ci				  le32_to_cpu(c->kato), &ctrl);
25162306a36Sopenharmony_ci	if (status)
25262306a36Sopenharmony_ci		goto out;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ctrl->pi_support = ctrl->port->pi_enable && ctrl->subsys->pi_support;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	uuid_copy(&ctrl->hostid, &d->hostid);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	ret = nvmet_setup_auth(ctrl);
25962306a36Sopenharmony_ci	if (ret < 0) {
26062306a36Sopenharmony_ci		pr_err("Failed to setup authentication, error %d\n", ret);
26162306a36Sopenharmony_ci		nvmet_ctrl_put(ctrl);
26262306a36Sopenharmony_ci		if (ret == -EPERM)
26362306a36Sopenharmony_ci			status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR);
26462306a36Sopenharmony_ci		else
26562306a36Sopenharmony_ci			status = NVME_SC_INTERNAL;
26662306a36Sopenharmony_ci		goto out;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	status = nvmet_install_queue(ctrl, req);
27062306a36Sopenharmony_ci	if (status) {
27162306a36Sopenharmony_ci		nvmet_ctrl_put(ctrl);
27262306a36Sopenharmony_ci		goto out;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	pr_info("creating %s controller %d for subsystem %s for NQN %s%s%s.\n",
27662306a36Sopenharmony_ci		nvmet_is_disc_subsys(ctrl->subsys) ? "discovery" : "nvm",
27762306a36Sopenharmony_ci		ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
27862306a36Sopenharmony_ci		ctrl->pi_support ? " T10-PI is enabled" : "",
27962306a36Sopenharmony_ci		nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : "");
28062306a36Sopenharmony_ci	req->cqe->result.u32 = cpu_to_le32(nvmet_connect_result(ctrl));
28162306a36Sopenharmony_ciout:
28262306a36Sopenharmony_ci	kfree(d);
28362306a36Sopenharmony_cicomplete:
28462306a36Sopenharmony_ci	nvmet_req_complete(req, status);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic void nvmet_execute_io_connect(struct nvmet_req *req)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct nvmf_connect_command *c = &req->cmd->connect;
29062306a36Sopenharmony_ci	struct nvmf_connect_data *d;
29162306a36Sopenharmony_ci	struct nvmet_ctrl *ctrl;
29262306a36Sopenharmony_ci	u16 qid = le16_to_cpu(c->qid);
29362306a36Sopenharmony_ci	u16 status = 0;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
29662306a36Sopenharmony_ci		return;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	d = kmalloc(sizeof(*d), GFP_KERNEL);
29962306a36Sopenharmony_ci	if (!d) {
30062306a36Sopenharmony_ci		status = NVME_SC_INTERNAL;
30162306a36Sopenharmony_ci		goto complete;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	status = nvmet_copy_from_sgl(req, 0, d, sizeof(*d));
30562306a36Sopenharmony_ci	if (status)
30662306a36Sopenharmony_ci		goto out;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* zero out initial completion result, assign values as needed */
30962306a36Sopenharmony_ci	req->cqe->result.u32 = 0;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (c->recfmt != 0) {
31262306a36Sopenharmony_ci		pr_warn("invalid connect version (%d).\n",
31362306a36Sopenharmony_ci			le16_to_cpu(c->recfmt));
31462306a36Sopenharmony_ci		status = NVME_SC_CONNECT_FORMAT | NVME_SC_DNR;
31562306a36Sopenharmony_ci		goto out;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	d->subsysnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
31962306a36Sopenharmony_ci	d->hostnqn[NVMF_NQN_FIELD_LEN - 1] = '\0';
32062306a36Sopenharmony_ci	ctrl = nvmet_ctrl_find_get(d->subsysnqn, d->hostnqn,
32162306a36Sopenharmony_ci				   le16_to_cpu(d->cntlid), req);
32262306a36Sopenharmony_ci	if (!ctrl) {
32362306a36Sopenharmony_ci		status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
32462306a36Sopenharmony_ci		goto out;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (unlikely(qid > ctrl->subsys->max_qid)) {
32862306a36Sopenharmony_ci		pr_warn("invalid queue id (%d)\n", qid);
32962306a36Sopenharmony_ci		status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
33062306a36Sopenharmony_ci		req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(qid);
33162306a36Sopenharmony_ci		goto out_ctrl_put;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	status = nvmet_install_queue(ctrl, req);
33562306a36Sopenharmony_ci	if (status)
33662306a36Sopenharmony_ci		goto out_ctrl_put;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid);
33962306a36Sopenharmony_ci	req->cqe->result.u32 = cpu_to_le32(nvmet_connect_result(ctrl));
34062306a36Sopenharmony_ciout:
34162306a36Sopenharmony_ci	kfree(d);
34262306a36Sopenharmony_cicomplete:
34362306a36Sopenharmony_ci	nvmet_req_complete(req, status);
34462306a36Sopenharmony_ci	return;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ciout_ctrl_put:
34762306a36Sopenharmony_ci	nvmet_ctrl_put(ctrl);
34862306a36Sopenharmony_ci	goto out;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ciu16 nvmet_parse_connect_cmd(struct nvmet_req *req)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct nvme_command *cmd = req->cmd;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!nvme_is_fabrics(cmd)) {
35662306a36Sopenharmony_ci		pr_debug("invalid command 0x%x on unconnected queue.\n",
35762306a36Sopenharmony_ci			cmd->fabrics.opcode);
35862306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvme_common_command, opcode);
35962306a36Sopenharmony_ci		return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci	if (cmd->fabrics.fctype != nvme_fabrics_type_connect) {
36262306a36Sopenharmony_ci		pr_debug("invalid capsule type 0x%x on unconnected queue.\n",
36362306a36Sopenharmony_ci			cmd->fabrics.fctype);
36462306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvmf_common_command, fctype);
36562306a36Sopenharmony_ci		return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (cmd->connect.qid == 0)
36962306a36Sopenharmony_ci		req->execute = nvmet_execute_admin_connect;
37062306a36Sopenharmony_ci	else
37162306a36Sopenharmony_ci		req->execute = nvmet_execute_io_connect;
37262306a36Sopenharmony_ci	return 0;
37362306a36Sopenharmony_ci}
374