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