162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Discovery service for the NVMe over Fabrics target.
462306a36Sopenharmony_ci * Copyright (C) 2016 Intel Corporation. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <generated/utsrelease.h>
962306a36Sopenharmony_ci#include "nvmet.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct nvmet_subsys *nvmet_disc_subsys;
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic u64 nvmet_genctr;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic void __nvmet_disc_changed(struct nvmet_port *port,
1662306a36Sopenharmony_ci				 struct nvmet_ctrl *ctrl)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	if (ctrl->port != port)
1962306a36Sopenharmony_ci		return;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	if (nvmet_aen_bit_disabled(ctrl, NVME_AEN_BIT_DISC_CHANGE))
2262306a36Sopenharmony_ci		return;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	nvmet_add_async_event(ctrl, NVME_AER_TYPE_NOTICE,
2562306a36Sopenharmony_ci			      NVME_AER_NOTICE_DISC_CHANGED, NVME_LOG_DISC);
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_civoid nvmet_port_disc_changed(struct nvmet_port *port,
2962306a36Sopenharmony_ci			     struct nvmet_subsys *subsys)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct nvmet_ctrl *ctrl;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	lockdep_assert_held(&nvmet_config_sem);
3462306a36Sopenharmony_ci	nvmet_genctr++;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	mutex_lock(&nvmet_disc_subsys->lock);
3762306a36Sopenharmony_ci	list_for_each_entry(ctrl, &nvmet_disc_subsys->ctrls, subsys_entry) {
3862306a36Sopenharmony_ci		if (subsys && !nvmet_host_allowed(subsys, ctrl->hostnqn))
3962306a36Sopenharmony_ci			continue;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		__nvmet_disc_changed(port, ctrl);
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci	mutex_unlock(&nvmet_disc_subsys->lock);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* If transport can signal change, notify transport */
4662306a36Sopenharmony_ci	if (port->tr_ops && port->tr_ops->discovery_chg)
4762306a36Sopenharmony_ci		port->tr_ops->discovery_chg(port);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void __nvmet_subsys_disc_changed(struct nvmet_port *port,
5162306a36Sopenharmony_ci					struct nvmet_subsys *subsys,
5262306a36Sopenharmony_ci					struct nvmet_host *host)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct nvmet_ctrl *ctrl;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	mutex_lock(&nvmet_disc_subsys->lock);
5762306a36Sopenharmony_ci	list_for_each_entry(ctrl, &nvmet_disc_subsys->ctrls, subsys_entry) {
5862306a36Sopenharmony_ci		if (host && strcmp(nvmet_host_name(host), ctrl->hostnqn))
5962306a36Sopenharmony_ci			continue;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		__nvmet_disc_changed(port, ctrl);
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	mutex_unlock(&nvmet_disc_subsys->lock);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_civoid nvmet_subsys_disc_changed(struct nvmet_subsys *subsys,
6762306a36Sopenharmony_ci			       struct nvmet_host *host)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct nvmet_port *port;
7062306a36Sopenharmony_ci	struct nvmet_subsys_link *s;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	lockdep_assert_held(&nvmet_config_sem);
7362306a36Sopenharmony_ci	nvmet_genctr++;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	list_for_each_entry(port, nvmet_ports, global_entry)
7662306a36Sopenharmony_ci		list_for_each_entry(s, &port->subsystems, entry) {
7762306a36Sopenharmony_ci			if (s->subsys != subsys)
7862306a36Sopenharmony_ci				continue;
7962306a36Sopenharmony_ci			__nvmet_subsys_disc_changed(port, subsys, host);
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_civoid nvmet_referral_enable(struct nvmet_port *parent, struct nvmet_port *port)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	down_write(&nvmet_config_sem);
8662306a36Sopenharmony_ci	if (list_empty(&port->entry)) {
8762306a36Sopenharmony_ci		list_add_tail(&port->entry, &parent->referrals);
8862306a36Sopenharmony_ci		port->enabled = true;
8962306a36Sopenharmony_ci		nvmet_port_disc_changed(parent, NULL);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	up_write(&nvmet_config_sem);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_civoid nvmet_referral_disable(struct nvmet_port *parent, struct nvmet_port *port)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	down_write(&nvmet_config_sem);
9762306a36Sopenharmony_ci	if (!list_empty(&port->entry)) {
9862306a36Sopenharmony_ci		port->enabled = false;
9962306a36Sopenharmony_ci		list_del_init(&port->entry);
10062306a36Sopenharmony_ci		nvmet_port_disc_changed(parent, NULL);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci	up_write(&nvmet_config_sem);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void nvmet_format_discovery_entry(struct nvmf_disc_rsp_page_hdr *hdr,
10662306a36Sopenharmony_ci		struct nvmet_port *port, char *subsys_nqn, char *traddr,
10762306a36Sopenharmony_ci		u8 type, u32 numrec)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct nvmf_disc_rsp_page_entry *e = &hdr->entries[numrec];
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	e->trtype = port->disc_addr.trtype;
11262306a36Sopenharmony_ci	e->adrfam = port->disc_addr.adrfam;
11362306a36Sopenharmony_ci	e->treq = port->disc_addr.treq;
11462306a36Sopenharmony_ci	e->portid = port->disc_addr.portid;
11562306a36Sopenharmony_ci	/* we support only dynamic controllers */
11662306a36Sopenharmony_ci	e->cntlid = cpu_to_le16(NVME_CNTLID_DYNAMIC);
11762306a36Sopenharmony_ci	e->asqsz = cpu_to_le16(NVME_AQ_DEPTH);
11862306a36Sopenharmony_ci	e->subtype = type;
11962306a36Sopenharmony_ci	memcpy(e->trsvcid, port->disc_addr.trsvcid, NVMF_TRSVCID_SIZE);
12062306a36Sopenharmony_ci	memcpy(e->traddr, traddr, NVMF_TRADDR_SIZE);
12162306a36Sopenharmony_ci	memcpy(e->tsas.common, port->disc_addr.tsas.common, NVMF_TSAS_SIZE);
12262306a36Sopenharmony_ci	strncpy(e->subnqn, subsys_nqn, NVMF_NQN_SIZE);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/*
12662306a36Sopenharmony_ci * nvmet_set_disc_traddr - set a correct discovery log entry traddr
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * IP based transports (e.g RDMA) can listen on "any" ipv4/ipv6 addresses
12962306a36Sopenharmony_ci * (INADDR_ANY or IN6ADDR_ANY_INIT). The discovery log page traddr reply
13062306a36Sopenharmony_ci * must not contain that "any" IP address. If the transport implements
13162306a36Sopenharmony_ci * .disc_traddr, use it. this callback will set the discovery traddr
13262306a36Sopenharmony_ci * from the req->port address in case the port in question listens
13362306a36Sopenharmony_ci * "any" IP address.
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_cistatic void nvmet_set_disc_traddr(struct nvmet_req *req, struct nvmet_port *port,
13662306a36Sopenharmony_ci		char *traddr)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	if (req->ops->disc_traddr)
13962306a36Sopenharmony_ci		req->ops->disc_traddr(req, port, traddr);
14062306a36Sopenharmony_ci	else
14162306a36Sopenharmony_ci		memcpy(traddr, port->disc_addr.traddr, NVMF_TRADDR_SIZE);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic size_t discovery_log_entries(struct nvmet_req *req)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct nvmet_ctrl *ctrl = req->sq->ctrl;
14762306a36Sopenharmony_ci	struct nvmet_subsys_link *p;
14862306a36Sopenharmony_ci	struct nvmet_port *r;
14962306a36Sopenharmony_ci	size_t entries = 1;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	list_for_each_entry(p, &req->port->subsystems, entry) {
15262306a36Sopenharmony_ci		if (!nvmet_host_allowed(p->subsys, ctrl->hostnqn))
15362306a36Sopenharmony_ci			continue;
15462306a36Sopenharmony_ci		entries++;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	list_for_each_entry(r, &req->port->referrals, entry)
15762306a36Sopenharmony_ci		entries++;
15862306a36Sopenharmony_ci	return entries;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void nvmet_execute_disc_get_log_page(struct nvmet_req *req)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	const int entry_size = sizeof(struct nvmf_disc_rsp_page_entry);
16462306a36Sopenharmony_ci	struct nvmet_ctrl *ctrl = req->sq->ctrl;
16562306a36Sopenharmony_ci	struct nvmf_disc_rsp_page_hdr *hdr;
16662306a36Sopenharmony_ci	u64 offset = nvmet_get_log_page_offset(req->cmd);
16762306a36Sopenharmony_ci	size_t data_len = nvmet_get_log_page_len(req->cmd);
16862306a36Sopenharmony_ci	size_t alloc_len;
16962306a36Sopenharmony_ci	struct nvmet_subsys_link *p;
17062306a36Sopenharmony_ci	struct nvmet_port *r;
17162306a36Sopenharmony_ci	u32 numrec = 0;
17262306a36Sopenharmony_ci	u16 status = 0;
17362306a36Sopenharmony_ci	void *buffer;
17462306a36Sopenharmony_ci	char traddr[NVMF_TRADDR_SIZE];
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, data_len))
17762306a36Sopenharmony_ci		return;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (req->cmd->get_log_page.lid != NVME_LOG_DISC) {
18062306a36Sopenharmony_ci		req->error_loc =
18162306a36Sopenharmony_ci			offsetof(struct nvme_get_log_page_command, lid);
18262306a36Sopenharmony_ci		status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
18362306a36Sopenharmony_ci		goto out;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Spec requires dword aligned offsets */
18762306a36Sopenharmony_ci	if (offset & 0x3) {
18862306a36Sopenharmony_ci		req->error_loc =
18962306a36Sopenharmony_ci			offsetof(struct nvme_get_log_page_command, lpo);
19062306a36Sopenharmony_ci		status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
19162306a36Sopenharmony_ci		goto out;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/*
19562306a36Sopenharmony_ci	 * Make sure we're passing at least a buffer of response header size.
19662306a36Sopenharmony_ci	 * If host provided data len is less than the header size, only the
19762306a36Sopenharmony_ci	 * number of bytes requested by host will be sent to host.
19862306a36Sopenharmony_ci	 */
19962306a36Sopenharmony_ci	down_read(&nvmet_config_sem);
20062306a36Sopenharmony_ci	alloc_len = sizeof(*hdr) + entry_size * discovery_log_entries(req);
20162306a36Sopenharmony_ci	buffer = kzalloc(alloc_len, GFP_KERNEL);
20262306a36Sopenharmony_ci	if (!buffer) {
20362306a36Sopenharmony_ci		up_read(&nvmet_config_sem);
20462306a36Sopenharmony_ci		status = NVME_SC_INTERNAL;
20562306a36Sopenharmony_ci		goto out;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci	hdr = buffer;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	nvmet_set_disc_traddr(req, req->port, traddr);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	nvmet_format_discovery_entry(hdr, req->port,
21262306a36Sopenharmony_ci				     nvmet_disc_subsys->subsysnqn,
21362306a36Sopenharmony_ci				     traddr, NVME_NQN_CURR, numrec);
21462306a36Sopenharmony_ci	numrec++;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	list_for_each_entry(p, &req->port->subsystems, entry) {
21762306a36Sopenharmony_ci		if (!nvmet_host_allowed(p->subsys, ctrl->hostnqn))
21862306a36Sopenharmony_ci			continue;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		nvmet_format_discovery_entry(hdr, req->port,
22162306a36Sopenharmony_ci				p->subsys->subsysnqn, traddr,
22262306a36Sopenharmony_ci				NVME_NQN_NVME, numrec);
22362306a36Sopenharmony_ci		numrec++;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	list_for_each_entry(r, &req->port->referrals, entry) {
22762306a36Sopenharmony_ci		nvmet_format_discovery_entry(hdr, r,
22862306a36Sopenharmony_ci				NVME_DISC_SUBSYS_NAME,
22962306a36Sopenharmony_ci				r->disc_addr.traddr,
23062306a36Sopenharmony_ci				NVME_NQN_DISC, numrec);
23162306a36Sopenharmony_ci		numrec++;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	hdr->genctr = cpu_to_le64(nvmet_genctr);
23562306a36Sopenharmony_ci	hdr->numrec = cpu_to_le64(numrec);
23662306a36Sopenharmony_ci	hdr->recfmt = cpu_to_le16(0);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	nvmet_clear_aen_bit(req, NVME_AEN_BIT_DISC_CHANGE);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	up_read(&nvmet_config_sem);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	status = nvmet_copy_to_sgl(req, 0, buffer + offset, data_len);
24362306a36Sopenharmony_ci	kfree(buffer);
24462306a36Sopenharmony_ciout:
24562306a36Sopenharmony_ci	nvmet_req_complete(req, status);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic void nvmet_execute_disc_identify(struct nvmet_req *req)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct nvmet_ctrl *ctrl = req->sq->ctrl;
25162306a36Sopenharmony_ci	struct nvme_id_ctrl *id;
25262306a36Sopenharmony_ci	u16 status = 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, NVME_IDENTIFY_DATA_SIZE))
25562306a36Sopenharmony_ci		return;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (req->cmd->identify.cns != NVME_ID_CNS_CTRL) {
25862306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvme_identify, cns);
25962306a36Sopenharmony_ci		status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
26062306a36Sopenharmony_ci		goto out;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	id = kzalloc(sizeof(*id), GFP_KERNEL);
26462306a36Sopenharmony_ci	if (!id) {
26562306a36Sopenharmony_ci		status = NVME_SC_INTERNAL;
26662306a36Sopenharmony_ci		goto out;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	memcpy(id->sn, ctrl->subsys->serial, NVMET_SN_MAX_SIZE);
27062306a36Sopenharmony_ci	memset(id->fr, ' ', sizeof(id->fr));
27162306a36Sopenharmony_ci	memcpy_and_pad(id->mn, sizeof(id->mn), ctrl->subsys->model_number,
27262306a36Sopenharmony_ci		       strlen(ctrl->subsys->model_number), ' ');
27362306a36Sopenharmony_ci	memcpy_and_pad(id->fr, sizeof(id->fr),
27462306a36Sopenharmony_ci		       UTS_RELEASE, strlen(UTS_RELEASE), ' ');
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	id->cntrltype = NVME_CTRL_DISC;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* no limit on data transfer sizes for now */
27962306a36Sopenharmony_ci	id->mdts = 0;
28062306a36Sopenharmony_ci	id->cntlid = cpu_to_le16(ctrl->cntlid);
28162306a36Sopenharmony_ci	id->ver = cpu_to_le32(ctrl->subsys->ver);
28262306a36Sopenharmony_ci	id->lpa = (1 << 2);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* no enforcement soft-limit for maxcmd - pick arbitrary high value */
28562306a36Sopenharmony_ci	id->maxcmd = cpu_to_le16(NVMET_MAX_CMD);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	id->sgls = cpu_to_le32(1 << 0);	/* we always support SGLs */
28862306a36Sopenharmony_ci	if (ctrl->ops->flags & NVMF_KEYED_SGLS)
28962306a36Sopenharmony_ci		id->sgls |= cpu_to_le32(1 << 2);
29062306a36Sopenharmony_ci	if (req->port->inline_data_size)
29162306a36Sopenharmony_ci		id->sgls |= cpu_to_le32(1 << 20);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	id->oaes = cpu_to_le32(NVMET_DISC_AEN_CFG_OPTIONAL);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	strscpy(id->subnqn, ctrl->subsys->subsysnqn, sizeof(id->subnqn));
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	status = nvmet_copy_to_sgl(req, 0, id, sizeof(*id));
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	kfree(id);
30062306a36Sopenharmony_ciout:
30162306a36Sopenharmony_ci	nvmet_req_complete(req, status);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void nvmet_execute_disc_set_features(struct nvmet_req *req)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10);
30762306a36Sopenharmony_ci	u16 stat;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, 0))
31062306a36Sopenharmony_ci		return;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	switch (cdw10 & 0xff) {
31362306a36Sopenharmony_ci	case NVME_FEAT_KATO:
31462306a36Sopenharmony_ci		stat = nvmet_set_feat_kato(req);
31562306a36Sopenharmony_ci		break;
31662306a36Sopenharmony_ci	case NVME_FEAT_ASYNC_EVENT:
31762306a36Sopenharmony_ci		stat = nvmet_set_feat_async_event(req,
31862306a36Sopenharmony_ci						  NVMET_DISC_AEN_CFG_OPTIONAL);
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci	default:
32162306a36Sopenharmony_ci		req->error_loc =
32262306a36Sopenharmony_ci			offsetof(struct nvme_common_command, cdw10);
32362306a36Sopenharmony_ci		stat = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
32462306a36Sopenharmony_ci		break;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	nvmet_req_complete(req, stat);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic void nvmet_execute_disc_get_features(struct nvmet_req *req)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10);
33362306a36Sopenharmony_ci	u16 stat = 0;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (!nvmet_check_transfer_len(req, 0))
33662306a36Sopenharmony_ci		return;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	switch (cdw10 & 0xff) {
33962306a36Sopenharmony_ci	case NVME_FEAT_KATO:
34062306a36Sopenharmony_ci		nvmet_get_feat_kato(req);
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	case NVME_FEAT_ASYNC_EVENT:
34362306a36Sopenharmony_ci		nvmet_get_feat_async_event(req);
34462306a36Sopenharmony_ci		break;
34562306a36Sopenharmony_ci	default:
34662306a36Sopenharmony_ci		req->error_loc =
34762306a36Sopenharmony_ci			offsetof(struct nvme_common_command, cdw10);
34862306a36Sopenharmony_ci		stat = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
34962306a36Sopenharmony_ci		break;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	nvmet_req_complete(req, stat);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ciu16 nvmet_parse_discovery_cmd(struct nvmet_req *req)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct nvme_command *cmd = req->cmd;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (unlikely(!(req->sq->ctrl->csts & NVME_CSTS_RDY))) {
36062306a36Sopenharmony_ci		pr_err("got cmd %d while not ready\n",
36162306a36Sopenharmony_ci		       cmd->common.opcode);
36262306a36Sopenharmony_ci		req->error_loc =
36362306a36Sopenharmony_ci			offsetof(struct nvme_common_command, opcode);
36462306a36Sopenharmony_ci		return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	switch (cmd->common.opcode) {
36862306a36Sopenharmony_ci	case nvme_admin_set_features:
36962306a36Sopenharmony_ci		req->execute = nvmet_execute_disc_set_features;
37062306a36Sopenharmony_ci		return 0;
37162306a36Sopenharmony_ci	case nvme_admin_get_features:
37262306a36Sopenharmony_ci		req->execute = nvmet_execute_disc_get_features;
37362306a36Sopenharmony_ci		return 0;
37462306a36Sopenharmony_ci	case nvme_admin_async_event:
37562306a36Sopenharmony_ci		req->execute = nvmet_execute_async_event;
37662306a36Sopenharmony_ci		return 0;
37762306a36Sopenharmony_ci	case nvme_admin_keep_alive:
37862306a36Sopenharmony_ci		req->execute = nvmet_execute_keep_alive;
37962306a36Sopenharmony_ci		return 0;
38062306a36Sopenharmony_ci	case nvme_admin_get_log_page:
38162306a36Sopenharmony_ci		req->execute = nvmet_execute_disc_get_log_page;
38262306a36Sopenharmony_ci		return 0;
38362306a36Sopenharmony_ci	case nvme_admin_identify:
38462306a36Sopenharmony_ci		req->execute = nvmet_execute_disc_identify;
38562306a36Sopenharmony_ci		return 0;
38662306a36Sopenharmony_ci	default:
38762306a36Sopenharmony_ci		pr_debug("unhandled cmd %d\n", cmd->common.opcode);
38862306a36Sopenharmony_ci		req->error_loc = offsetof(struct nvme_common_command, opcode);
38962306a36Sopenharmony_ci		return NVME_SC_INVALID_OPCODE | NVME_SC_DNR;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ciint __init nvmet_init_discovery(void)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	nvmet_disc_subsys =
39762306a36Sopenharmony_ci		nvmet_subsys_alloc(NVME_DISC_SUBSYS_NAME, NVME_NQN_CURR);
39862306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(nvmet_disc_subsys);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_civoid nvmet_exit_discovery(void)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	nvmet_subsys_put(nvmet_disc_subsys);
40462306a36Sopenharmony_ci}
405