162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NVMe admin 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/module.h> 862306a36Sopenharmony_ci#include <linux/rculist.h> 962306a36Sopenharmony_ci#include <linux/part_stat.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <generated/utsrelease.h> 1262306a36Sopenharmony_ci#include <asm/unaligned.h> 1362306a36Sopenharmony_ci#include "nvmet.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciu32 nvmet_get_log_page_len(struct nvme_command *cmd) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci u32 len = le16_to_cpu(cmd->get_log_page.numdu); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci len <<= 16; 2062306a36Sopenharmony_ci len += le16_to_cpu(cmd->get_log_page.numdl); 2162306a36Sopenharmony_ci /* NUMD is a 0's based value */ 2262306a36Sopenharmony_ci len += 1; 2362306a36Sopenharmony_ci len *= sizeof(u32); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci return len; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic u32 nvmet_feat_data_len(struct nvmet_req *req, u32 cdw10) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci switch (cdw10 & 0xff) { 3162306a36Sopenharmony_ci case NVME_FEAT_HOST_ID: 3262306a36Sopenharmony_ci return sizeof(req->sq->ctrl->hostid); 3362306a36Sopenharmony_ci default: 3462306a36Sopenharmony_ci return 0; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciu64 nvmet_get_log_page_offset(struct nvme_command *cmd) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return le64_to_cpu(cmd->get_log_page.lpo); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void nvmet_execute_get_log_page_noop(struct nvmet_req *req) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci nvmet_req_complete(req, nvmet_zero_sgl(req, 0, req->transfer_len)); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void nvmet_execute_get_log_page_error(struct nvmet_req *req) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 5162306a36Sopenharmony_ci unsigned long flags; 5262306a36Sopenharmony_ci off_t offset = 0; 5362306a36Sopenharmony_ci u64 slot; 5462306a36Sopenharmony_ci u64 i; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->error_lock, flags); 5762306a36Sopenharmony_ci slot = ctrl->err_counter % NVMET_ERROR_LOG_SLOTS; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (i = 0; i < NVMET_ERROR_LOG_SLOTS; i++) { 6062306a36Sopenharmony_ci if (nvmet_copy_to_sgl(req, offset, &ctrl->slots[slot], 6162306a36Sopenharmony_ci sizeof(struct nvme_error_slot))) 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (slot == 0) 6562306a36Sopenharmony_ci slot = NVMET_ERROR_LOG_SLOTS - 1; 6662306a36Sopenharmony_ci else 6762306a36Sopenharmony_ci slot--; 6862306a36Sopenharmony_ci offset += sizeof(struct nvme_error_slot); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->error_lock, flags); 7162306a36Sopenharmony_ci nvmet_req_complete(req, 0); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic u16 nvmet_get_smart_log_nsid(struct nvmet_req *req, 7562306a36Sopenharmony_ci struct nvme_smart_log *slog) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci u64 host_reads, host_writes, data_units_read, data_units_written; 7862306a36Sopenharmony_ci u16 status; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci status = nvmet_req_find_ns(req); 8162306a36Sopenharmony_ci if (status) 8262306a36Sopenharmony_ci return status; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* we don't have the right data for file backed ns */ 8562306a36Sopenharmony_ci if (!req->ns->bdev) 8662306a36Sopenharmony_ci return NVME_SC_SUCCESS; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci host_reads = part_stat_read(req->ns->bdev, ios[READ]); 8962306a36Sopenharmony_ci data_units_read = 9062306a36Sopenharmony_ci DIV_ROUND_UP(part_stat_read(req->ns->bdev, sectors[READ]), 1000); 9162306a36Sopenharmony_ci host_writes = part_stat_read(req->ns->bdev, ios[WRITE]); 9262306a36Sopenharmony_ci data_units_written = 9362306a36Sopenharmony_ci DIV_ROUND_UP(part_stat_read(req->ns->bdev, sectors[WRITE]), 1000); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci put_unaligned_le64(host_reads, &slog->host_reads[0]); 9662306a36Sopenharmony_ci put_unaligned_le64(data_units_read, &slog->data_units_read[0]); 9762306a36Sopenharmony_ci put_unaligned_le64(host_writes, &slog->host_writes[0]); 9862306a36Sopenharmony_ci put_unaligned_le64(data_units_written, &slog->data_units_written[0]); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return NVME_SC_SUCCESS; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic u16 nvmet_get_smart_log_all(struct nvmet_req *req, 10462306a36Sopenharmony_ci struct nvme_smart_log *slog) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci u64 host_reads = 0, host_writes = 0; 10762306a36Sopenharmony_ci u64 data_units_read = 0, data_units_written = 0; 10862306a36Sopenharmony_ci struct nvmet_ns *ns; 10962306a36Sopenharmony_ci struct nvmet_ctrl *ctrl; 11062306a36Sopenharmony_ci unsigned long idx; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ctrl = req->sq->ctrl; 11362306a36Sopenharmony_ci xa_for_each(&ctrl->subsys->namespaces, idx, ns) { 11462306a36Sopenharmony_ci /* we don't have the right data for file backed ns */ 11562306a36Sopenharmony_ci if (!ns->bdev) 11662306a36Sopenharmony_ci continue; 11762306a36Sopenharmony_ci host_reads += part_stat_read(ns->bdev, ios[READ]); 11862306a36Sopenharmony_ci data_units_read += DIV_ROUND_UP( 11962306a36Sopenharmony_ci part_stat_read(ns->bdev, sectors[READ]), 1000); 12062306a36Sopenharmony_ci host_writes += part_stat_read(ns->bdev, ios[WRITE]); 12162306a36Sopenharmony_ci data_units_written += DIV_ROUND_UP( 12262306a36Sopenharmony_ci part_stat_read(ns->bdev, sectors[WRITE]), 1000); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci put_unaligned_le64(host_reads, &slog->host_reads[0]); 12662306a36Sopenharmony_ci put_unaligned_le64(data_units_read, &slog->data_units_read[0]); 12762306a36Sopenharmony_ci put_unaligned_le64(host_writes, &slog->host_writes[0]); 12862306a36Sopenharmony_ci put_unaligned_le64(data_units_written, &slog->data_units_written[0]); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return NVME_SC_SUCCESS; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void nvmet_execute_get_log_page_smart(struct nvmet_req *req) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct nvme_smart_log *log; 13662306a36Sopenharmony_ci u16 status = NVME_SC_INTERNAL; 13762306a36Sopenharmony_ci unsigned long flags; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (req->transfer_len != sizeof(*log)) 14062306a36Sopenharmony_ci goto out; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci log = kzalloc(sizeof(*log), GFP_KERNEL); 14362306a36Sopenharmony_ci if (!log) 14462306a36Sopenharmony_ci goto out; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (req->cmd->get_log_page.nsid == cpu_to_le32(NVME_NSID_ALL)) 14762306a36Sopenharmony_ci status = nvmet_get_smart_log_all(req, log); 14862306a36Sopenharmony_ci else 14962306a36Sopenharmony_ci status = nvmet_get_smart_log_nsid(req, log); 15062306a36Sopenharmony_ci if (status) 15162306a36Sopenharmony_ci goto out_free_log; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci spin_lock_irqsave(&req->sq->ctrl->error_lock, flags); 15462306a36Sopenharmony_ci put_unaligned_le64(req->sq->ctrl->err_counter, 15562306a36Sopenharmony_ci &log->num_err_log_entries); 15662306a36Sopenharmony_ci spin_unlock_irqrestore(&req->sq->ctrl->error_lock, flags); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, 0, log, sizeof(*log)); 15962306a36Sopenharmony_ciout_free_log: 16062306a36Sopenharmony_ci kfree(log); 16162306a36Sopenharmony_ciout: 16262306a36Sopenharmony_ci nvmet_req_complete(req, status); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void nvmet_get_cmd_effects_nvm(struct nvme_effects_log *log) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci log->acs[nvme_admin_get_log_page] = 16862306a36Sopenharmony_ci log->acs[nvme_admin_identify] = 16962306a36Sopenharmony_ci log->acs[nvme_admin_abort_cmd] = 17062306a36Sopenharmony_ci log->acs[nvme_admin_set_features] = 17162306a36Sopenharmony_ci log->acs[nvme_admin_get_features] = 17262306a36Sopenharmony_ci log->acs[nvme_admin_async_event] = 17362306a36Sopenharmony_ci log->acs[nvme_admin_keep_alive] = 17462306a36Sopenharmony_ci cpu_to_le32(NVME_CMD_EFFECTS_CSUPP); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci log->iocs[nvme_cmd_read] = 17762306a36Sopenharmony_ci log->iocs[nvme_cmd_flush] = 17862306a36Sopenharmony_ci log->iocs[nvme_cmd_dsm] = 17962306a36Sopenharmony_ci cpu_to_le32(NVME_CMD_EFFECTS_CSUPP); 18062306a36Sopenharmony_ci log->iocs[nvme_cmd_write] = 18162306a36Sopenharmony_ci log->iocs[nvme_cmd_write_zeroes] = 18262306a36Sopenharmony_ci cpu_to_le32(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void nvmet_get_cmd_effects_zns(struct nvme_effects_log *log) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci log->iocs[nvme_cmd_zone_append] = 18862306a36Sopenharmony_ci log->iocs[nvme_cmd_zone_mgmt_send] = 18962306a36Sopenharmony_ci cpu_to_le32(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC); 19062306a36Sopenharmony_ci log->iocs[nvme_cmd_zone_mgmt_recv] = 19162306a36Sopenharmony_ci cpu_to_le32(NVME_CMD_EFFECTS_CSUPP); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void nvmet_execute_get_log_cmd_effects_ns(struct nvmet_req *req) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct nvme_effects_log *log; 19762306a36Sopenharmony_ci u16 status = NVME_SC_SUCCESS; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci log = kzalloc(sizeof(*log), GFP_KERNEL); 20062306a36Sopenharmony_ci if (!log) { 20162306a36Sopenharmony_ci status = NVME_SC_INTERNAL; 20262306a36Sopenharmony_ci goto out; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci switch (req->cmd->get_log_page.csi) { 20662306a36Sopenharmony_ci case NVME_CSI_NVM: 20762306a36Sopenharmony_ci nvmet_get_cmd_effects_nvm(log); 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci case NVME_CSI_ZNS: 21062306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED)) { 21162306a36Sopenharmony_ci status = NVME_SC_INVALID_IO_CMD_SET; 21262306a36Sopenharmony_ci goto free; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci nvmet_get_cmd_effects_nvm(log); 21562306a36Sopenharmony_ci nvmet_get_cmd_effects_zns(log); 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci default: 21862306a36Sopenharmony_ci status = NVME_SC_INVALID_LOG_PAGE; 21962306a36Sopenharmony_ci goto free; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, 0, log, sizeof(*log)); 22362306a36Sopenharmony_cifree: 22462306a36Sopenharmony_ci kfree(log); 22562306a36Sopenharmony_ciout: 22662306a36Sopenharmony_ci nvmet_req_complete(req, status); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void nvmet_execute_get_log_changed_ns(struct nvmet_req *req) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 23262306a36Sopenharmony_ci u16 status = NVME_SC_INTERNAL; 23362306a36Sopenharmony_ci size_t len; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (req->transfer_len != NVME_MAX_CHANGED_NAMESPACES * sizeof(__le32)) 23662306a36Sopenharmony_ci goto out; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci mutex_lock(&ctrl->lock); 23962306a36Sopenharmony_ci if (ctrl->nr_changed_ns == U32_MAX) 24062306a36Sopenharmony_ci len = sizeof(__le32); 24162306a36Sopenharmony_ci else 24262306a36Sopenharmony_ci len = ctrl->nr_changed_ns * sizeof(__le32); 24362306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, 0, ctrl->changed_ns_list, len); 24462306a36Sopenharmony_ci if (!status) 24562306a36Sopenharmony_ci status = nvmet_zero_sgl(req, len, req->transfer_len - len); 24662306a36Sopenharmony_ci ctrl->nr_changed_ns = 0; 24762306a36Sopenharmony_ci nvmet_clear_aen_bit(req, NVME_AEN_BIT_NS_ATTR); 24862306a36Sopenharmony_ci mutex_unlock(&ctrl->lock); 24962306a36Sopenharmony_ciout: 25062306a36Sopenharmony_ci nvmet_req_complete(req, status); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic u32 nvmet_format_ana_group(struct nvmet_req *req, u32 grpid, 25462306a36Sopenharmony_ci struct nvme_ana_group_desc *desc) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 25762306a36Sopenharmony_ci struct nvmet_ns *ns; 25862306a36Sopenharmony_ci unsigned long idx; 25962306a36Sopenharmony_ci u32 count = 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!(req->cmd->get_log_page.lsp & NVME_ANA_LOG_RGO)) { 26262306a36Sopenharmony_ci xa_for_each(&ctrl->subsys->namespaces, idx, ns) 26362306a36Sopenharmony_ci if (ns->anagrpid == grpid) 26462306a36Sopenharmony_ci desc->nsids[count++] = cpu_to_le32(ns->nsid); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci desc->grpid = cpu_to_le32(grpid); 26862306a36Sopenharmony_ci desc->nnsids = cpu_to_le32(count); 26962306a36Sopenharmony_ci desc->chgcnt = cpu_to_le64(nvmet_ana_chgcnt); 27062306a36Sopenharmony_ci desc->state = req->port->ana_state[grpid]; 27162306a36Sopenharmony_ci memset(desc->rsvd17, 0, sizeof(desc->rsvd17)); 27262306a36Sopenharmony_ci return struct_size(desc, nsids, count); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void nvmet_execute_get_log_page_ana(struct nvmet_req *req) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct nvme_ana_rsp_hdr hdr = { 0, }; 27862306a36Sopenharmony_ci struct nvme_ana_group_desc *desc; 27962306a36Sopenharmony_ci size_t offset = sizeof(struct nvme_ana_rsp_hdr); /* start beyond hdr */ 28062306a36Sopenharmony_ci size_t len; 28162306a36Sopenharmony_ci u32 grpid; 28262306a36Sopenharmony_ci u16 ngrps = 0; 28362306a36Sopenharmony_ci u16 status; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci status = NVME_SC_INTERNAL; 28662306a36Sopenharmony_ci desc = kmalloc(struct_size(desc, nsids, NVMET_MAX_NAMESPACES), 28762306a36Sopenharmony_ci GFP_KERNEL); 28862306a36Sopenharmony_ci if (!desc) 28962306a36Sopenharmony_ci goto out; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci down_read(&nvmet_ana_sem); 29262306a36Sopenharmony_ci for (grpid = 1; grpid <= NVMET_MAX_ANAGRPS; grpid++) { 29362306a36Sopenharmony_ci if (!nvmet_ana_group_enabled[grpid]) 29462306a36Sopenharmony_ci continue; 29562306a36Sopenharmony_ci len = nvmet_format_ana_group(req, grpid, desc); 29662306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, offset, desc, len); 29762306a36Sopenharmony_ci if (status) 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci offset += len; 30062306a36Sopenharmony_ci ngrps++; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci for ( ; grpid <= NVMET_MAX_ANAGRPS; grpid++) { 30362306a36Sopenharmony_ci if (nvmet_ana_group_enabled[grpid]) 30462306a36Sopenharmony_ci ngrps++; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci hdr.chgcnt = cpu_to_le64(nvmet_ana_chgcnt); 30862306a36Sopenharmony_ci hdr.ngrps = cpu_to_le16(ngrps); 30962306a36Sopenharmony_ci nvmet_clear_aen_bit(req, NVME_AEN_BIT_ANA_CHANGE); 31062306a36Sopenharmony_ci up_read(&nvmet_ana_sem); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci kfree(desc); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* copy the header last once we know the number of groups */ 31562306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, 0, &hdr, sizeof(hdr)); 31662306a36Sopenharmony_ciout: 31762306a36Sopenharmony_ci nvmet_req_complete(req, status); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void nvmet_execute_get_log_page(struct nvmet_req *req) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci if (!nvmet_check_transfer_len(req, nvmet_get_log_page_len(req->cmd))) 32362306a36Sopenharmony_ci return; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci switch (req->cmd->get_log_page.lid) { 32662306a36Sopenharmony_ci case NVME_LOG_ERROR: 32762306a36Sopenharmony_ci return nvmet_execute_get_log_page_error(req); 32862306a36Sopenharmony_ci case NVME_LOG_SMART: 32962306a36Sopenharmony_ci return nvmet_execute_get_log_page_smart(req); 33062306a36Sopenharmony_ci case NVME_LOG_FW_SLOT: 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * We only support a single firmware slot which always is 33362306a36Sopenharmony_ci * active, so we can zero out the whole firmware slot log and 33462306a36Sopenharmony_ci * still claim to fully implement this mandatory log page. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci return nvmet_execute_get_log_page_noop(req); 33762306a36Sopenharmony_ci case NVME_LOG_CHANGED_NS: 33862306a36Sopenharmony_ci return nvmet_execute_get_log_changed_ns(req); 33962306a36Sopenharmony_ci case NVME_LOG_CMD_EFFECTS: 34062306a36Sopenharmony_ci return nvmet_execute_get_log_cmd_effects_ns(req); 34162306a36Sopenharmony_ci case NVME_LOG_ANA: 34262306a36Sopenharmony_ci return nvmet_execute_get_log_page_ana(req); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci pr_debug("unhandled lid %d on qid %d\n", 34562306a36Sopenharmony_ci req->cmd->get_log_page.lid, req->sq->qid); 34662306a36Sopenharmony_ci req->error_loc = offsetof(struct nvme_get_log_page_command, lid); 34762306a36Sopenharmony_ci nvmet_req_complete(req, NVME_SC_INVALID_FIELD | NVME_SC_DNR); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void nvmet_execute_identify_ctrl(struct nvmet_req *req) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 35362306a36Sopenharmony_ci struct nvmet_subsys *subsys = ctrl->subsys; 35462306a36Sopenharmony_ci struct nvme_id_ctrl *id; 35562306a36Sopenharmony_ci u32 cmd_capsule_size; 35662306a36Sopenharmony_ci u16 status = 0; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!subsys->subsys_discovered) { 35962306a36Sopenharmony_ci mutex_lock(&subsys->lock); 36062306a36Sopenharmony_ci subsys->subsys_discovered = true; 36162306a36Sopenharmony_ci mutex_unlock(&subsys->lock); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci id = kzalloc(sizeof(*id), GFP_KERNEL); 36562306a36Sopenharmony_ci if (!id) { 36662306a36Sopenharmony_ci status = NVME_SC_INTERNAL; 36762306a36Sopenharmony_ci goto out; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* XXX: figure out how to assign real vendors IDs. */ 37162306a36Sopenharmony_ci id->vid = 0; 37262306a36Sopenharmony_ci id->ssvid = 0; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci memcpy(id->sn, ctrl->subsys->serial, NVMET_SN_MAX_SIZE); 37562306a36Sopenharmony_ci memcpy_and_pad(id->mn, sizeof(id->mn), subsys->model_number, 37662306a36Sopenharmony_ci strlen(subsys->model_number), ' '); 37762306a36Sopenharmony_ci memcpy_and_pad(id->fr, sizeof(id->fr), 37862306a36Sopenharmony_ci subsys->firmware_rev, strlen(subsys->firmware_rev), ' '); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci put_unaligned_le24(subsys->ieee_oui, id->ieee); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci id->rab = 6; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (nvmet_is_disc_subsys(ctrl->subsys)) 38562306a36Sopenharmony_ci id->cntrltype = NVME_CTRL_DISC; 38662306a36Sopenharmony_ci else 38762306a36Sopenharmony_ci id->cntrltype = NVME_CTRL_IO; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* we support multiple ports, multiples hosts and ANA: */ 39062306a36Sopenharmony_ci id->cmic = NVME_CTRL_CMIC_MULTI_PORT | NVME_CTRL_CMIC_MULTI_CTRL | 39162306a36Sopenharmony_ci NVME_CTRL_CMIC_ANA; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* Limit MDTS according to transport capability */ 39462306a36Sopenharmony_ci if (ctrl->ops->get_mdts) 39562306a36Sopenharmony_ci id->mdts = ctrl->ops->get_mdts(ctrl); 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci id->mdts = 0; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci id->cntlid = cpu_to_le16(ctrl->cntlid); 40062306a36Sopenharmony_ci id->ver = cpu_to_le32(ctrl->subsys->ver); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* XXX: figure out what to do about RTD3R/RTD3 */ 40362306a36Sopenharmony_ci id->oaes = cpu_to_le32(NVMET_AEN_CFG_OPTIONAL); 40462306a36Sopenharmony_ci id->ctratt = cpu_to_le32(NVME_CTRL_ATTR_HID_128_BIT | 40562306a36Sopenharmony_ci NVME_CTRL_ATTR_TBKAS); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci id->oacs = 0; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* 41062306a36Sopenharmony_ci * We don't really have a practical limit on the number of abort 41162306a36Sopenharmony_ci * comands. But we don't do anything useful for abort either, so 41262306a36Sopenharmony_ci * no point in allowing more abort commands than the spec requires. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci id->acl = 3; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci id->aerl = NVMET_ASYNC_EVENTS - 1; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* first slot is read-only, only one slot supported */ 41962306a36Sopenharmony_ci id->frmw = (1 << 0) | (1 << 1); 42062306a36Sopenharmony_ci id->lpa = (1 << 0) | (1 << 1) | (1 << 2); 42162306a36Sopenharmony_ci id->elpe = NVMET_ERROR_LOG_SLOTS - 1; 42262306a36Sopenharmony_ci id->npss = 0; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* We support keep-alive timeout in granularity of seconds */ 42562306a36Sopenharmony_ci id->kas = cpu_to_le16(NVMET_KAS); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci id->sqes = (0x6 << 4) | 0x6; 42862306a36Sopenharmony_ci id->cqes = (0x4 << 4) | 0x4; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* no enforcement soft-limit for maxcmd - pick arbitrary high value */ 43162306a36Sopenharmony_ci id->maxcmd = cpu_to_le16(NVMET_MAX_CMD); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci id->nn = cpu_to_le32(NVMET_MAX_NAMESPACES); 43462306a36Sopenharmony_ci id->mnan = cpu_to_le32(NVMET_MAX_NAMESPACES); 43562306a36Sopenharmony_ci id->oncs = cpu_to_le16(NVME_CTRL_ONCS_DSM | 43662306a36Sopenharmony_ci NVME_CTRL_ONCS_WRITE_ZEROES); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* XXX: don't report vwc if the underlying device is write through */ 43962306a36Sopenharmony_ci id->vwc = NVME_CTRL_VWC_PRESENT; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* 44262306a36Sopenharmony_ci * We can't support atomic writes bigger than a LBA without support 44362306a36Sopenharmony_ci * from the backend device. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci id->awun = 0; 44662306a36Sopenharmony_ci id->awupf = 0; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci id->sgls = cpu_to_le32(1 << 0); /* we always support SGLs */ 44962306a36Sopenharmony_ci if (ctrl->ops->flags & NVMF_KEYED_SGLS) 45062306a36Sopenharmony_ci id->sgls |= cpu_to_le32(1 << 2); 45162306a36Sopenharmony_ci if (req->port->inline_data_size) 45262306a36Sopenharmony_ci id->sgls |= cpu_to_le32(1 << 20); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci strscpy(id->subnqn, ctrl->subsys->subsysnqn, sizeof(id->subnqn)); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * Max command capsule size is sqe + in-capsule data size. 45862306a36Sopenharmony_ci * Disable in-capsule data for Metadata capable controllers. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci cmd_capsule_size = sizeof(struct nvme_command); 46162306a36Sopenharmony_ci if (!ctrl->pi_support) 46262306a36Sopenharmony_ci cmd_capsule_size += req->port->inline_data_size; 46362306a36Sopenharmony_ci id->ioccsz = cpu_to_le32(cmd_capsule_size / 16); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Max response capsule size is cqe */ 46662306a36Sopenharmony_ci id->iorcsz = cpu_to_le32(sizeof(struct nvme_completion) / 16); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci id->msdbd = ctrl->ops->msdbd; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci id->anacap = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); 47162306a36Sopenharmony_ci id->anatt = 10; /* random value */ 47262306a36Sopenharmony_ci id->anagrpmax = cpu_to_le32(NVMET_MAX_ANAGRPS); 47362306a36Sopenharmony_ci id->nanagrpid = cpu_to_le32(NVMET_MAX_ANAGRPS); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * Meh, we don't really support any power state. Fake up the same 47762306a36Sopenharmony_ci * values that qemu does. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci id->psd[0].max_power = cpu_to_le16(0x9c4); 48062306a36Sopenharmony_ci id->psd[0].entry_lat = cpu_to_le32(0x10); 48162306a36Sopenharmony_ci id->psd[0].exit_lat = cpu_to_le32(0x4); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci id->nwpc = 1 << 0; /* write protect and no write protect */ 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, 0, id, sizeof(*id)); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci kfree(id); 48862306a36Sopenharmony_ciout: 48962306a36Sopenharmony_ci nvmet_req_complete(req, status); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic void nvmet_execute_identify_ns(struct nvmet_req *req) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct nvme_id_ns *id; 49562306a36Sopenharmony_ci u16 status; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (le32_to_cpu(req->cmd->identify.nsid) == NVME_NSID_ALL) { 49862306a36Sopenharmony_ci req->error_loc = offsetof(struct nvme_identify, nsid); 49962306a36Sopenharmony_ci status = NVME_SC_INVALID_NS | NVME_SC_DNR; 50062306a36Sopenharmony_ci goto out; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci id = kzalloc(sizeof(*id), GFP_KERNEL); 50462306a36Sopenharmony_ci if (!id) { 50562306a36Sopenharmony_ci status = NVME_SC_INTERNAL; 50662306a36Sopenharmony_ci goto out; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* return an all zeroed buffer if we can't find an active namespace */ 51062306a36Sopenharmony_ci status = nvmet_req_find_ns(req); 51162306a36Sopenharmony_ci if (status) { 51262306a36Sopenharmony_ci status = 0; 51362306a36Sopenharmony_ci goto done; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (nvmet_ns_revalidate(req->ns)) { 51762306a36Sopenharmony_ci mutex_lock(&req->ns->subsys->lock); 51862306a36Sopenharmony_ci nvmet_ns_changed(req->ns->subsys, req->ns->nsid); 51962306a36Sopenharmony_ci mutex_unlock(&req->ns->subsys->lock); 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* 52362306a36Sopenharmony_ci * nuse = ncap = nsze isn't always true, but we have no way to find 52462306a36Sopenharmony_ci * that out from the underlying device. 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ci id->ncap = id->nsze = 52762306a36Sopenharmony_ci cpu_to_le64(req->ns->size >> req->ns->blksize_shift); 52862306a36Sopenharmony_ci switch (req->port->ana_state[req->ns->anagrpid]) { 52962306a36Sopenharmony_ci case NVME_ANA_INACCESSIBLE: 53062306a36Sopenharmony_ci case NVME_ANA_PERSISTENT_LOSS: 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci default: 53362306a36Sopenharmony_ci id->nuse = id->nsze; 53462306a36Sopenharmony_ci break; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (req->ns->bdev) 53862306a36Sopenharmony_ci nvmet_bdev_set_limits(req->ns->bdev, id); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* 54162306a36Sopenharmony_ci * We just provide a single LBA format that matches what the 54262306a36Sopenharmony_ci * underlying device reports. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci id->nlbaf = 0; 54562306a36Sopenharmony_ci id->flbas = 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* 54862306a36Sopenharmony_ci * Our namespace might always be shared. Not just with other 54962306a36Sopenharmony_ci * controllers, but also with any other user of the block device. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci id->nmic = NVME_NS_NMIC_SHARED; 55262306a36Sopenharmony_ci id->anagrpid = cpu_to_le32(req->ns->anagrpid); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci memcpy(&id->nguid, &req->ns->nguid, sizeof(id->nguid)); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci id->lbaf[0].ds = req->ns->blksize_shift; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (req->sq->ctrl->pi_support && nvmet_ns_has_pi(req->ns)) { 55962306a36Sopenharmony_ci id->dpc = NVME_NS_DPC_PI_FIRST | NVME_NS_DPC_PI_LAST | 56062306a36Sopenharmony_ci NVME_NS_DPC_PI_TYPE1 | NVME_NS_DPC_PI_TYPE2 | 56162306a36Sopenharmony_ci NVME_NS_DPC_PI_TYPE3; 56262306a36Sopenharmony_ci id->mc = NVME_MC_EXTENDED_LBA; 56362306a36Sopenharmony_ci id->dps = req->ns->pi_type; 56462306a36Sopenharmony_ci id->flbas = NVME_NS_FLBAS_META_EXT; 56562306a36Sopenharmony_ci id->lbaf[0].ms = cpu_to_le16(req->ns->metadata_size); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (req->ns->readonly) 56962306a36Sopenharmony_ci id->nsattr |= NVME_NS_ATTR_RO; 57062306a36Sopenharmony_cidone: 57162306a36Sopenharmony_ci if (!status) 57262306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, 0, id, sizeof(*id)); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci kfree(id); 57562306a36Sopenharmony_ciout: 57662306a36Sopenharmony_ci nvmet_req_complete(req, status); 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic void nvmet_execute_identify_nslist(struct nvmet_req *req) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci static const int buf_size = NVME_IDENTIFY_DATA_SIZE; 58262306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 58362306a36Sopenharmony_ci struct nvmet_ns *ns; 58462306a36Sopenharmony_ci unsigned long idx; 58562306a36Sopenharmony_ci u32 min_nsid = le32_to_cpu(req->cmd->identify.nsid); 58662306a36Sopenharmony_ci __le32 *list; 58762306a36Sopenharmony_ci u16 status = 0; 58862306a36Sopenharmony_ci int i = 0; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci list = kzalloc(buf_size, GFP_KERNEL); 59162306a36Sopenharmony_ci if (!list) { 59262306a36Sopenharmony_ci status = NVME_SC_INTERNAL; 59362306a36Sopenharmony_ci goto out; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci xa_for_each(&ctrl->subsys->namespaces, idx, ns) { 59762306a36Sopenharmony_ci if (ns->nsid <= min_nsid) 59862306a36Sopenharmony_ci continue; 59962306a36Sopenharmony_ci list[i++] = cpu_to_le32(ns->nsid); 60062306a36Sopenharmony_ci if (i == buf_size / sizeof(__le32)) 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, 0, list, buf_size); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci kfree(list); 60762306a36Sopenharmony_ciout: 60862306a36Sopenharmony_ci nvmet_req_complete(req, status); 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic u16 nvmet_copy_ns_identifier(struct nvmet_req *req, u8 type, u8 len, 61262306a36Sopenharmony_ci void *id, off_t *off) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct nvme_ns_id_desc desc = { 61562306a36Sopenharmony_ci .nidt = type, 61662306a36Sopenharmony_ci .nidl = len, 61762306a36Sopenharmony_ci }; 61862306a36Sopenharmony_ci u16 status; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, *off, &desc, sizeof(desc)); 62162306a36Sopenharmony_ci if (status) 62262306a36Sopenharmony_ci return status; 62362306a36Sopenharmony_ci *off += sizeof(desc); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, *off, id, len); 62662306a36Sopenharmony_ci if (status) 62762306a36Sopenharmony_ci return status; 62862306a36Sopenharmony_ci *off += len; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic void nvmet_execute_identify_desclist(struct nvmet_req *req) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci off_t off = 0; 63662306a36Sopenharmony_ci u16 status; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci status = nvmet_req_find_ns(req); 63962306a36Sopenharmony_ci if (status) 64062306a36Sopenharmony_ci goto out; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (memchr_inv(&req->ns->uuid, 0, sizeof(req->ns->uuid))) { 64362306a36Sopenharmony_ci status = nvmet_copy_ns_identifier(req, NVME_NIDT_UUID, 64462306a36Sopenharmony_ci NVME_NIDT_UUID_LEN, 64562306a36Sopenharmony_ci &req->ns->uuid, &off); 64662306a36Sopenharmony_ci if (status) 64762306a36Sopenharmony_ci goto out; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci if (memchr_inv(req->ns->nguid, 0, sizeof(req->ns->nguid))) { 65062306a36Sopenharmony_ci status = nvmet_copy_ns_identifier(req, NVME_NIDT_NGUID, 65162306a36Sopenharmony_ci NVME_NIDT_NGUID_LEN, 65262306a36Sopenharmony_ci &req->ns->nguid, &off); 65362306a36Sopenharmony_ci if (status) 65462306a36Sopenharmony_ci goto out; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci status = nvmet_copy_ns_identifier(req, NVME_NIDT_CSI, 65862306a36Sopenharmony_ci NVME_NIDT_CSI_LEN, 65962306a36Sopenharmony_ci &req->ns->csi, &off); 66062306a36Sopenharmony_ci if (status) 66162306a36Sopenharmony_ci goto out; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (sg_zero_buffer(req->sg, req->sg_cnt, NVME_IDENTIFY_DATA_SIZE - off, 66462306a36Sopenharmony_ci off) != NVME_IDENTIFY_DATA_SIZE - off) 66562306a36Sopenharmony_ci status = NVME_SC_INTERNAL | NVME_SC_DNR; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ciout: 66862306a36Sopenharmony_ci nvmet_req_complete(req, status); 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic void nvmet_execute_identify_ctrl_nvm(struct nvmet_req *req) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci /* Not supported: return zeroes */ 67462306a36Sopenharmony_ci nvmet_req_complete(req, 67562306a36Sopenharmony_ci nvmet_zero_sgl(req, 0, sizeof(struct nvme_id_ctrl_nvm))); 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic void nvmet_execute_identify(struct nvmet_req *req) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci if (!nvmet_check_transfer_len(req, NVME_IDENTIFY_DATA_SIZE)) 68162306a36Sopenharmony_ci return; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci switch (req->cmd->identify.cns) { 68462306a36Sopenharmony_ci case NVME_ID_CNS_NS: 68562306a36Sopenharmony_ci nvmet_execute_identify_ns(req); 68662306a36Sopenharmony_ci return; 68762306a36Sopenharmony_ci case NVME_ID_CNS_CTRL: 68862306a36Sopenharmony_ci nvmet_execute_identify_ctrl(req); 68962306a36Sopenharmony_ci return; 69062306a36Sopenharmony_ci case NVME_ID_CNS_NS_ACTIVE_LIST: 69162306a36Sopenharmony_ci nvmet_execute_identify_nslist(req); 69262306a36Sopenharmony_ci return; 69362306a36Sopenharmony_ci case NVME_ID_CNS_NS_DESC_LIST: 69462306a36Sopenharmony_ci nvmet_execute_identify_desclist(req); 69562306a36Sopenharmony_ci return; 69662306a36Sopenharmony_ci case NVME_ID_CNS_CS_NS: 69762306a36Sopenharmony_ci switch (req->cmd->identify.csi) { 69862306a36Sopenharmony_ci case NVME_CSI_NVM: 69962306a36Sopenharmony_ci /* Not supported */ 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci case NVME_CSI_ZNS: 70262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_BLK_DEV_ZONED)) { 70362306a36Sopenharmony_ci nvmet_execute_identify_ns_zns(req); 70462306a36Sopenharmony_ci return; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci break; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci case NVME_ID_CNS_CS_CTRL: 71062306a36Sopenharmony_ci switch (req->cmd->identify.csi) { 71162306a36Sopenharmony_ci case NVME_CSI_NVM: 71262306a36Sopenharmony_ci nvmet_execute_identify_ctrl_nvm(req); 71362306a36Sopenharmony_ci return; 71462306a36Sopenharmony_ci case NVME_CSI_ZNS: 71562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_BLK_DEV_ZONED)) { 71662306a36Sopenharmony_ci nvmet_execute_identify_ctrl_zns(req); 71762306a36Sopenharmony_ci return; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci pr_debug("unhandled identify cns %d on qid %d\n", 72562306a36Sopenharmony_ci req->cmd->identify.cns, req->sq->qid); 72662306a36Sopenharmony_ci req->error_loc = offsetof(struct nvme_identify, cns); 72762306a36Sopenharmony_ci nvmet_req_complete(req, NVME_SC_INVALID_FIELD | NVME_SC_DNR); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci/* 73162306a36Sopenharmony_ci * A "minimum viable" abort implementation: the command is mandatory in the 73262306a36Sopenharmony_ci * spec, but we are not required to do any useful work. We couldn't really 73362306a36Sopenharmony_ci * do a useful abort, so don't bother even with waiting for the command 73462306a36Sopenharmony_ci * to be exectuted and return immediately telling the command to abort 73562306a36Sopenharmony_ci * wasn't found. 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_cistatic void nvmet_execute_abort(struct nvmet_req *req) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci if (!nvmet_check_transfer_len(req, 0)) 74062306a36Sopenharmony_ci return; 74162306a36Sopenharmony_ci nvmet_set_result(req, 1); 74262306a36Sopenharmony_ci nvmet_req_complete(req, 0); 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic u16 nvmet_write_protect_flush_sync(struct nvmet_req *req) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci u16 status; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (req->ns->file) 75062306a36Sopenharmony_ci status = nvmet_file_flush(req); 75162306a36Sopenharmony_ci else 75262306a36Sopenharmony_ci status = nvmet_bdev_flush(req); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (status) 75562306a36Sopenharmony_ci pr_err("write protect flush failed nsid: %u\n", req->ns->nsid); 75662306a36Sopenharmony_ci return status; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic u16 nvmet_set_feat_write_protect(struct nvmet_req *req) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci u32 write_protect = le32_to_cpu(req->cmd->common.cdw11); 76262306a36Sopenharmony_ci struct nvmet_subsys *subsys = nvmet_req_subsys(req); 76362306a36Sopenharmony_ci u16 status; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci status = nvmet_req_find_ns(req); 76662306a36Sopenharmony_ci if (status) 76762306a36Sopenharmony_ci return status; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci mutex_lock(&subsys->lock); 77062306a36Sopenharmony_ci switch (write_protect) { 77162306a36Sopenharmony_ci case NVME_NS_WRITE_PROTECT: 77262306a36Sopenharmony_ci req->ns->readonly = true; 77362306a36Sopenharmony_ci status = nvmet_write_protect_flush_sync(req); 77462306a36Sopenharmony_ci if (status) 77562306a36Sopenharmony_ci req->ns->readonly = false; 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci case NVME_NS_NO_WRITE_PROTECT: 77862306a36Sopenharmony_ci req->ns->readonly = false; 77962306a36Sopenharmony_ci status = 0; 78062306a36Sopenharmony_ci break; 78162306a36Sopenharmony_ci default: 78262306a36Sopenharmony_ci break; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (!status) 78662306a36Sopenharmony_ci nvmet_ns_changed(subsys, req->ns->nsid); 78762306a36Sopenharmony_ci mutex_unlock(&subsys->lock); 78862306a36Sopenharmony_ci return status; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ciu16 nvmet_set_feat_kato(struct nvmet_req *req) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci u32 val32 = le32_to_cpu(req->cmd->common.cdw11); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci nvmet_stop_keep_alive_timer(req->sq->ctrl); 79662306a36Sopenharmony_ci req->sq->ctrl->kato = DIV_ROUND_UP(val32, 1000); 79762306a36Sopenharmony_ci nvmet_start_keep_alive_timer(req->sq->ctrl); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci nvmet_set_result(req, req->sq->ctrl->kato); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci return 0; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ciu16 nvmet_set_feat_async_event(struct nvmet_req *req, u32 mask) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci u32 val32 = le32_to_cpu(req->cmd->common.cdw11); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (val32 & ~mask) { 80962306a36Sopenharmony_ci req->error_loc = offsetof(struct nvme_common_command, cdw11); 81062306a36Sopenharmony_ci return NVME_SC_INVALID_FIELD | NVME_SC_DNR; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci WRITE_ONCE(req->sq->ctrl->aen_enabled, val32); 81462306a36Sopenharmony_ci nvmet_set_result(req, val32); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return 0; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_civoid nvmet_execute_set_features(struct nvmet_req *req) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct nvmet_subsys *subsys = nvmet_req_subsys(req); 82262306a36Sopenharmony_ci u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10); 82362306a36Sopenharmony_ci u32 cdw11 = le32_to_cpu(req->cmd->common.cdw11); 82462306a36Sopenharmony_ci u16 status = 0; 82562306a36Sopenharmony_ci u16 nsqr; 82662306a36Sopenharmony_ci u16 ncqr; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (!nvmet_check_data_len_lte(req, 0)) 82962306a36Sopenharmony_ci return; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci switch (cdw10 & 0xff) { 83262306a36Sopenharmony_ci case NVME_FEAT_NUM_QUEUES: 83362306a36Sopenharmony_ci ncqr = (cdw11 >> 16) & 0xffff; 83462306a36Sopenharmony_ci nsqr = cdw11 & 0xffff; 83562306a36Sopenharmony_ci if (ncqr == 0xffff || nsqr == 0xffff) { 83662306a36Sopenharmony_ci status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci nvmet_set_result(req, 84062306a36Sopenharmony_ci (subsys->max_qid - 1) | ((subsys->max_qid - 1) << 16)); 84162306a36Sopenharmony_ci break; 84262306a36Sopenharmony_ci case NVME_FEAT_KATO: 84362306a36Sopenharmony_ci status = nvmet_set_feat_kato(req); 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci case NVME_FEAT_ASYNC_EVENT: 84662306a36Sopenharmony_ci status = nvmet_set_feat_async_event(req, NVMET_AEN_CFG_ALL); 84762306a36Sopenharmony_ci break; 84862306a36Sopenharmony_ci case NVME_FEAT_HOST_ID: 84962306a36Sopenharmony_ci status = NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR; 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci case NVME_FEAT_WRITE_PROTECT: 85262306a36Sopenharmony_ci status = nvmet_set_feat_write_protect(req); 85362306a36Sopenharmony_ci break; 85462306a36Sopenharmony_ci default: 85562306a36Sopenharmony_ci req->error_loc = offsetof(struct nvme_common_command, cdw10); 85662306a36Sopenharmony_ci status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci nvmet_req_complete(req, status); 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic u16 nvmet_get_feat_write_protect(struct nvmet_req *req) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct nvmet_subsys *subsys = nvmet_req_subsys(req); 86662306a36Sopenharmony_ci u32 result; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci result = nvmet_req_find_ns(req); 86962306a36Sopenharmony_ci if (result) 87062306a36Sopenharmony_ci return result; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci mutex_lock(&subsys->lock); 87362306a36Sopenharmony_ci if (req->ns->readonly == true) 87462306a36Sopenharmony_ci result = NVME_NS_WRITE_PROTECT; 87562306a36Sopenharmony_ci else 87662306a36Sopenharmony_ci result = NVME_NS_NO_WRITE_PROTECT; 87762306a36Sopenharmony_ci nvmet_set_result(req, result); 87862306a36Sopenharmony_ci mutex_unlock(&subsys->lock); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci return 0; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_civoid nvmet_get_feat_kato(struct nvmet_req *req) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci nvmet_set_result(req, req->sq->ctrl->kato * 1000); 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_civoid nvmet_get_feat_async_event(struct nvmet_req *req) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci nvmet_set_result(req, READ_ONCE(req->sq->ctrl->aen_enabled)); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_civoid nvmet_execute_get_features(struct nvmet_req *req) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci struct nvmet_subsys *subsys = nvmet_req_subsys(req); 89662306a36Sopenharmony_ci u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10); 89762306a36Sopenharmony_ci u16 status = 0; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (!nvmet_check_transfer_len(req, nvmet_feat_data_len(req, cdw10))) 90062306a36Sopenharmony_ci return; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci switch (cdw10 & 0xff) { 90362306a36Sopenharmony_ci /* 90462306a36Sopenharmony_ci * These features are mandatory in the spec, but we don't 90562306a36Sopenharmony_ci * have a useful way to implement them. We'll eventually 90662306a36Sopenharmony_ci * need to come up with some fake values for these. 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_ci#if 0 90962306a36Sopenharmony_ci case NVME_FEAT_ARBITRATION: 91062306a36Sopenharmony_ci break; 91162306a36Sopenharmony_ci case NVME_FEAT_POWER_MGMT: 91262306a36Sopenharmony_ci break; 91362306a36Sopenharmony_ci case NVME_FEAT_TEMP_THRESH: 91462306a36Sopenharmony_ci break; 91562306a36Sopenharmony_ci case NVME_FEAT_ERR_RECOVERY: 91662306a36Sopenharmony_ci break; 91762306a36Sopenharmony_ci case NVME_FEAT_IRQ_COALESCE: 91862306a36Sopenharmony_ci break; 91962306a36Sopenharmony_ci case NVME_FEAT_IRQ_CONFIG: 92062306a36Sopenharmony_ci break; 92162306a36Sopenharmony_ci case NVME_FEAT_WRITE_ATOMIC: 92262306a36Sopenharmony_ci break; 92362306a36Sopenharmony_ci#endif 92462306a36Sopenharmony_ci case NVME_FEAT_ASYNC_EVENT: 92562306a36Sopenharmony_ci nvmet_get_feat_async_event(req); 92662306a36Sopenharmony_ci break; 92762306a36Sopenharmony_ci case NVME_FEAT_VOLATILE_WC: 92862306a36Sopenharmony_ci nvmet_set_result(req, 1); 92962306a36Sopenharmony_ci break; 93062306a36Sopenharmony_ci case NVME_FEAT_NUM_QUEUES: 93162306a36Sopenharmony_ci nvmet_set_result(req, 93262306a36Sopenharmony_ci (subsys->max_qid-1) | ((subsys->max_qid-1) << 16)); 93362306a36Sopenharmony_ci break; 93462306a36Sopenharmony_ci case NVME_FEAT_KATO: 93562306a36Sopenharmony_ci nvmet_get_feat_kato(req); 93662306a36Sopenharmony_ci break; 93762306a36Sopenharmony_ci case NVME_FEAT_HOST_ID: 93862306a36Sopenharmony_ci /* need 128-bit host identifier flag */ 93962306a36Sopenharmony_ci if (!(req->cmd->common.cdw11 & cpu_to_le32(1 << 0))) { 94062306a36Sopenharmony_ci req->error_loc = 94162306a36Sopenharmony_ci offsetof(struct nvme_common_command, cdw11); 94262306a36Sopenharmony_ci status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; 94362306a36Sopenharmony_ci break; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci status = nvmet_copy_to_sgl(req, 0, &req->sq->ctrl->hostid, 94762306a36Sopenharmony_ci sizeof(req->sq->ctrl->hostid)); 94862306a36Sopenharmony_ci break; 94962306a36Sopenharmony_ci case NVME_FEAT_WRITE_PROTECT: 95062306a36Sopenharmony_ci status = nvmet_get_feat_write_protect(req); 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci default: 95362306a36Sopenharmony_ci req->error_loc = 95462306a36Sopenharmony_ci offsetof(struct nvme_common_command, cdw10); 95562306a36Sopenharmony_ci status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; 95662306a36Sopenharmony_ci break; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci nvmet_req_complete(req, status); 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_civoid nvmet_execute_async_event(struct nvmet_req *req) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (!nvmet_check_transfer_len(req, 0)) 96762306a36Sopenharmony_ci return; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci mutex_lock(&ctrl->lock); 97062306a36Sopenharmony_ci if (ctrl->nr_async_event_cmds >= NVMET_ASYNC_EVENTS) { 97162306a36Sopenharmony_ci mutex_unlock(&ctrl->lock); 97262306a36Sopenharmony_ci nvmet_req_complete(req, NVME_SC_ASYNC_LIMIT | NVME_SC_DNR); 97362306a36Sopenharmony_ci return; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci ctrl->async_event_cmds[ctrl->nr_async_event_cmds++] = req; 97662306a36Sopenharmony_ci mutex_unlock(&ctrl->lock); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci queue_work(nvmet_wq, &ctrl->async_event_work); 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_civoid nvmet_execute_keep_alive(struct nvmet_req *req) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 98462306a36Sopenharmony_ci u16 status = 0; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (!nvmet_check_transfer_len(req, 0)) 98762306a36Sopenharmony_ci return; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (!ctrl->kato) { 99062306a36Sopenharmony_ci status = NVME_SC_KA_TIMEOUT_INVALID; 99162306a36Sopenharmony_ci goto out; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci pr_debug("ctrl %d update keep-alive timer for %d secs\n", 99562306a36Sopenharmony_ci ctrl->cntlid, ctrl->kato); 99662306a36Sopenharmony_ci mod_delayed_work(system_wq, &ctrl->ka_work, ctrl->kato * HZ); 99762306a36Sopenharmony_ciout: 99862306a36Sopenharmony_ci nvmet_req_complete(req, status); 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ciu16 nvmet_parse_admin_cmd(struct nvmet_req *req) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct nvme_command *cmd = req->cmd; 100462306a36Sopenharmony_ci u16 ret; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci if (nvme_is_fabrics(cmd)) 100762306a36Sopenharmony_ci return nvmet_parse_fabrics_admin_cmd(req); 100862306a36Sopenharmony_ci if (unlikely(!nvmet_check_auth_status(req))) 100962306a36Sopenharmony_ci return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR; 101062306a36Sopenharmony_ci if (nvmet_is_disc_subsys(nvmet_req_subsys(req))) 101162306a36Sopenharmony_ci return nvmet_parse_discovery_cmd(req); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci ret = nvmet_check_ctrl_status(req); 101462306a36Sopenharmony_ci if (unlikely(ret)) 101562306a36Sopenharmony_ci return ret; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci if (nvmet_is_passthru_req(req)) 101862306a36Sopenharmony_ci return nvmet_parse_passthru_admin_cmd(req); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci switch (cmd->common.opcode) { 102162306a36Sopenharmony_ci case nvme_admin_get_log_page: 102262306a36Sopenharmony_ci req->execute = nvmet_execute_get_log_page; 102362306a36Sopenharmony_ci return 0; 102462306a36Sopenharmony_ci case nvme_admin_identify: 102562306a36Sopenharmony_ci req->execute = nvmet_execute_identify; 102662306a36Sopenharmony_ci return 0; 102762306a36Sopenharmony_ci case nvme_admin_abort_cmd: 102862306a36Sopenharmony_ci req->execute = nvmet_execute_abort; 102962306a36Sopenharmony_ci return 0; 103062306a36Sopenharmony_ci case nvme_admin_set_features: 103162306a36Sopenharmony_ci req->execute = nvmet_execute_set_features; 103262306a36Sopenharmony_ci return 0; 103362306a36Sopenharmony_ci case nvme_admin_get_features: 103462306a36Sopenharmony_ci req->execute = nvmet_execute_get_features; 103562306a36Sopenharmony_ci return 0; 103662306a36Sopenharmony_ci case nvme_admin_async_event: 103762306a36Sopenharmony_ci req->execute = nvmet_execute_async_event; 103862306a36Sopenharmony_ci return 0; 103962306a36Sopenharmony_ci case nvme_admin_keep_alive: 104062306a36Sopenharmony_ci req->execute = nvmet_execute_keep_alive; 104162306a36Sopenharmony_ci return 0; 104262306a36Sopenharmony_ci default: 104362306a36Sopenharmony_ci return nvmet_report_invalid_opcode(req); 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci} 1046