1691 lines · 43.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * NVMe admin command implementation. * Copyright (c) 2015-2016 HGST, a Western Digital Company. */#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt#include <linux/module.h>#include <linux/rculist.h>#include <linux/part_stat.h> #include <generated/utsrelease.h>#include <linux/unaligned.h>#include "nvmet.h" static void nvmet_execute_delete_sq(struct nvmet_req *req){	struct nvmet_ctrl *ctrl = req->sq->ctrl;	u16 sqid = le16_to_cpu(req->cmd->delete_queue.qid);	u16 status; 	if (!nvmet_is_pci_ctrl(ctrl)) {		status = nvmet_report_invalid_opcode(req);		goto complete;	} 	if (!sqid) {		status = NVME_SC_QID_INVALID | NVME_STATUS_DNR;		goto complete;	} 	status = nvmet_check_sqid(ctrl, sqid, false);	if (status != NVME_SC_SUCCESS)		goto complete; 	status = ctrl->ops->delete_sq(ctrl, sqid); complete:	nvmet_req_complete(req, status);} static void nvmet_execute_create_sq(struct nvmet_req *req){	struct nvmet_ctrl *ctrl = req->sq->ctrl;	struct nvme_command *cmd = req->cmd;	u16 sqid = le16_to_cpu(cmd->create_sq.sqid);	u16 cqid = le16_to_cpu(cmd->create_sq.cqid);	u16 sq_flags = le16_to_cpu(cmd->create_sq.sq_flags);	u16 qsize = le16_to_cpu(cmd->create_sq.qsize);	u64 prp1 = le64_to_cpu(cmd->create_sq.prp1);	u16 status; 	if (!nvmet_is_pci_ctrl(ctrl)) {		status = nvmet_report_invalid_opcode(req);		goto complete;	} 	if (!sqid) {		status = NVME_SC_QID_INVALID | NVME_STATUS_DNR;		goto complete;	} 	status = nvmet_check_sqid(ctrl, sqid, true);	if (status != NVME_SC_SUCCESS)		goto complete; 	status = nvmet_check_io_cqid(ctrl, cqid, false);	if (status != NVME_SC_SUCCESS) {		pr_err("SQ %u: Invalid CQID %u\n", sqid, cqid);		goto complete;	} 	if (!qsize || qsize > NVME_CAP_MQES(ctrl->cap)) {		status = NVME_SC_QUEUE_SIZE | NVME_STATUS_DNR;		goto complete;	} 	status = ctrl->ops->create_sq(ctrl, sqid, cqid, sq_flags, qsize, prp1); complete:	nvmet_req_complete(req, status);} static void nvmet_execute_delete_cq(struct nvmet_req *req){	struct nvmet_ctrl *ctrl = req->sq->ctrl;	u16 cqid = le16_to_cpu(req->cmd->delete_queue.qid);	u16 status; 	if (!nvmet_is_pci_ctrl(ctrl)) {		status = nvmet_report_invalid_opcode(req);		goto complete;	} 	status = nvmet_check_io_cqid(ctrl, cqid, false);	if (status != NVME_SC_SUCCESS)		goto complete; 	if (!ctrl->cqs[cqid] || nvmet_cq_in_use(ctrl->cqs[cqid])) {		/* Some SQs are still using this CQ */		status = NVME_SC_QID_INVALID | NVME_STATUS_DNR;		goto complete;	} 	status = ctrl->ops->delete_cq(ctrl, cqid); complete:	nvmet_req_complete(req, status);} static void nvmet_execute_create_cq(struct nvmet_req *req){	struct nvmet_ctrl *ctrl = req->sq->ctrl;	struct nvme_command *cmd = req->cmd;	u16 cqid = le16_to_cpu(cmd->create_cq.cqid);	u16 cq_flags = le16_to_cpu(cmd->create_cq.cq_flags);	u16 qsize = le16_to_cpu(cmd->create_cq.qsize);	u16 irq_vector = le16_to_cpu(cmd->create_cq.irq_vector);	u64 prp1 = le64_to_cpu(cmd->create_cq.prp1);	u16 status; 	if (!nvmet_is_pci_ctrl(ctrl)) {		status = nvmet_report_invalid_opcode(req);		goto complete;	} 	status = nvmet_check_io_cqid(ctrl, cqid, true);	if (status != NVME_SC_SUCCESS)		goto complete; 	if (!qsize || qsize > NVME_CAP_MQES(ctrl->cap)) {		status = NVME_SC_QUEUE_SIZE | NVME_STATUS_DNR;		goto complete;	} 	status = ctrl->ops->create_cq(ctrl, cqid, cq_flags, qsize,				      prp1, irq_vector); complete:	nvmet_req_complete(req, status);} u32 nvmet_get_log_page_len(struct nvme_command *cmd){	u32 len = le16_to_cpu(cmd->get_log_page.numdu); 	len <<= 16;	len += le16_to_cpu(cmd->get_log_page.numdl);	/* NUMD is a 0's based value */	len += 1;	len *= sizeof(u32); 	return len;} static u32 nvmet_feat_data_len(struct nvmet_req *req, u32 cdw10){	switch (cdw10 & 0xff) {	case NVME_FEAT_HOST_ID:		return sizeof(req->sq->ctrl->hostid);	default:		return 0;	}} u64 nvmet_get_log_page_offset(struct nvme_command *cmd){	return le64_to_cpu(cmd->get_log_page.lpo);} static void nvmet_execute_get_log_page_noop(struct nvmet_req *req){	nvmet_req_complete(req, nvmet_zero_sgl(req, 0, req->transfer_len));} static void nvmet_execute_get_log_page_error(struct nvmet_req *req){	struct nvmet_ctrl *ctrl = req->sq->ctrl;	unsigned long flags;	off_t offset = 0;	u64 slot;	u64 i; 	spin_lock_irqsave(&ctrl->error_lock, flags);	slot = ctrl->err_counter % NVMET_ERROR_LOG_SLOTS; 	for (i = 0; i < NVMET_ERROR_LOG_SLOTS; i++) {		if (nvmet_copy_to_sgl(req, offset, &ctrl->slots[slot],				sizeof(struct nvme_error_slot)))			break; 		if (slot == 0)			slot = NVMET_ERROR_LOG_SLOTS - 1;		else			slot--;		offset += sizeof(struct nvme_error_slot);	}	spin_unlock_irqrestore(&ctrl->error_lock, flags);	nvmet_req_complete(req, 0);} static void nvmet_execute_get_supported_log_pages(struct nvmet_req *req){	struct nvme_supported_log *logs;	u16 status; 	logs = kzalloc_obj(*logs);	if (!logs) {		status = NVME_SC_INTERNAL;		goto out;	} 	logs->lids[NVME_LOG_SUPPORTED] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_ERROR] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_SMART] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_FW_SLOT] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_CHANGED_NS] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_CMD_EFFECTS] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_ENDURANCE_GROUP] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_ANA] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_FEATURES] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_RMI] = cpu_to_le32(NVME_LIDS_LSUPP);	logs->lids[NVME_LOG_RESERVATION] = cpu_to_le32(NVME_LIDS_LSUPP); 	status = nvmet_copy_to_sgl(req, 0, logs, sizeof(*logs));	kfree(logs);out:	nvmet_req_complete(req, status);} static u16 nvmet_get_smart_log_nsid(struct nvmet_req *req,		struct nvme_smart_log *slog){	u64 host_reads, host_writes, data_units_read, data_units_written;	u16 status; 	status = nvmet_req_find_ns(req);	if (status)		return status; 	/* we don't have the right data for file backed ns */	if (!req->ns->bdev)		return NVME_SC_SUCCESS; 	host_reads = part_stat_read(req->ns->bdev, ios[READ]);	data_units_read =		DIV_ROUND_UP(part_stat_read(req->ns->bdev, sectors[READ]), 1000);	host_writes = part_stat_read(req->ns->bdev, ios[WRITE]);	data_units_written =		DIV_ROUND_UP(part_stat_read(req->ns->bdev, sectors[WRITE]), 1000); 	put_unaligned_le64(host_reads, &slog->host_reads[0]);	put_unaligned_le64(data_units_read, &slog->data_units_read[0]);	put_unaligned_le64(host_writes, &slog->host_writes[0]);	put_unaligned_le64(data_units_written, &slog->data_units_written[0]); 	return NVME_SC_SUCCESS;} static u16 nvmet_get_smart_log_all(struct nvmet_req *req,		struct nvme_smart_log *slog){	u64 host_reads = 0, host_writes = 0;	u64 data_units_read = 0, data_units_written = 0;	struct nvmet_ns *ns;	struct nvmet_ctrl *ctrl;	unsigned long idx; 	ctrl = req->sq->ctrl;	nvmet_for_each_enabled_ns(&ctrl->subsys->namespaces, idx, ns) {		/* we don't have the right data for file backed ns */		if (!ns->bdev)			continue;		host_reads += part_stat_read(ns->bdev, ios[READ]);		data_units_read += DIV_ROUND_UP(			part_stat_read(ns->bdev, sectors[READ]), 1000);		host_writes += part_stat_read(ns->bdev, ios[WRITE]);		data_units_written += DIV_ROUND_UP(			part_stat_read(ns->bdev, sectors[WRITE]), 1000);	} 	put_unaligned_le64(host_reads, &slog->host_reads[0]);	put_unaligned_le64(data_units_read, &slog->data_units_read[0]);	put_unaligned_le64(host_writes, &slog->host_writes[0]);	put_unaligned_le64(data_units_written, &slog->data_units_written[0]); 	return NVME_SC_SUCCESS;} static void nvmet_execute_get_log_page_rmi(struct nvmet_req *req){	struct nvme_rotational_media_log *log;	struct gendisk *disk;	u16 status; 	req->cmd->common.nsid = cpu_to_le32(le16_to_cpu(					    req->cmd->get_log_page.lsi));	status = nvmet_req_find_ns(req);	if (status)		goto out; 	if (!req->ns->bdev || !bdev_rot(req->ns->bdev)) {		status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;		goto out;	} 	if (req->transfer_len != sizeof(*log)) {		status = NVME_SC_SGL_INVALID_DATA | NVME_STATUS_DNR;		goto out;	} 	log = kzalloc_obj(*log);	if (!log) {		status = NVME_SC_INTERNAL;		goto out;	} 	log->endgid = req->cmd->get_log_page.lsi;	disk = req->ns->bdev->bd_disk;	if (disk && disk->ia_ranges)		log->numa = cpu_to_le16(disk->ia_ranges->nr_ia_ranges);	else		log->numa = cpu_to_le16(1); 	status = nvmet_copy_to_sgl(req, 0, log, sizeof(*log));	kfree(log);out:	nvmet_req_complete(req, status);} static void nvmet_execute_get_log_page_smart(struct nvmet_req *req){	struct nvme_smart_log *log;	u16 status = NVME_SC_INTERNAL;	unsigned long flags; 	if (req->transfer_len != sizeof(*log))		goto out; 	log = kzalloc_obj(*log);	if (!log)		goto out; 	if (req->cmd->get_log_page.nsid == cpu_to_le32(NVME_NSID_ALL))		status = nvmet_get_smart_log_all(req, log);	else		status = nvmet_get_smart_log_nsid(req, log);	if (status)		goto out_free_log; 	spin_lock_irqsave(&req->sq->ctrl->error_lock, flags);	put_unaligned_le64(req->sq->ctrl->err_counter,			&log->num_err_log_entries);	spin_unlock_irqrestore(&req->sq->ctrl->error_lock, flags); 	status = nvmet_copy_to_sgl(req, 0, log, sizeof(*log));out_free_log:	kfree(log);out:	nvmet_req_complete(req, status);} static void nvmet_get_cmd_effects_admin(struct nvmet_ctrl *ctrl,					struct nvme_effects_log *log){	/* For a PCI target controller, advertize support for the . */	if (nvmet_is_pci_ctrl(ctrl)) {		log->acs[nvme_admin_delete_sq] =		log->acs[nvme_admin_create_sq] =		log->acs[nvme_admin_delete_cq] =		log->acs[nvme_admin_create_cq] =			cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);	} 	log->acs[nvme_admin_get_log_page] =	log->acs[nvme_admin_identify] =	log->acs[nvme_admin_abort_cmd] =	log->acs[nvme_admin_set_features] =	log->acs[nvme_admin_get_features] =	log->acs[nvme_admin_async_event] =	log->acs[nvme_admin_keep_alive] =		cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);} static void nvmet_get_cmd_effects_nvm(struct nvme_effects_log *log){	log->iocs[nvme_cmd_read] =	log->iocs[nvme_cmd_flush] =	log->iocs[nvme_cmd_dsm]	=	log->iocs[nvme_cmd_resv_acquire] =	log->iocs[nvme_cmd_resv_register] =	log->iocs[nvme_cmd_resv_release] =	log->iocs[nvme_cmd_resv_report] =		cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);	log->iocs[nvme_cmd_write] =	log->iocs[nvme_cmd_write_zeroes] =		cpu_to_le32(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC);} static void nvmet_get_cmd_effects_zns(struct nvme_effects_log *log){	log->iocs[nvme_cmd_zone_append] =	log->iocs[nvme_cmd_zone_mgmt_send] =		cpu_to_le32(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC);	log->iocs[nvme_cmd_zone_mgmt_recv] =		cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);} static void nvmet_execute_get_log_cmd_effects_ns(struct nvmet_req *req){	struct nvmet_ctrl *ctrl = req->sq->ctrl;	struct nvme_effects_log *log;	u16 status = NVME_SC_SUCCESS; 	log = kzalloc_obj(*log);	if (!log) {		status = NVME_SC_INTERNAL;		goto out;	} 	switch (req->cmd->get_log_page.csi) {	case NVME_CSI_NVM:		nvmet_get_cmd_effects_admin(ctrl, log);		nvmet_get_cmd_effects_nvm(log);		break;	case NVME_CSI_ZNS:		if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED)) {			status = NVME_SC_INVALID_IO_CMD_SET;			goto free;		}		nvmet_get_cmd_effects_admin(ctrl, log);		nvmet_get_cmd_effects_nvm(log);		nvmet_get_cmd_effects_zns(log);		break;	default:		status = NVME_SC_INVALID_LOG_PAGE;		goto free;	} 	status = nvmet_copy_to_sgl(req, 0, log, sizeof(*log));free:	kfree(log);out:	nvmet_req_complete(req, status);} static void nvmet_execute_get_log_changed_ns(struct nvmet_req *req){	struct nvmet_ctrl *ctrl = req->sq->ctrl;	u16 status = NVME_SC_INTERNAL;	size_t len; 	if (req->transfer_len != NVME_MAX_CHANGED_NAMESPACES * sizeof(__le32))		goto out; 	mutex_lock(&ctrl->lock);	if (ctrl->nr_changed_ns == U32_MAX)		len = sizeof(__le32);	else		len = ctrl->nr_changed_ns * sizeof(__le32);	status = nvmet_copy_to_sgl(req, 0, ctrl->changed_ns_list, len);	if (!status)		status = nvmet_zero_sgl(req, len, req->transfer_len - len);	ctrl->nr_changed_ns = 0;	nvmet_clear_aen_bit(req, NVME_AEN_BIT_NS_ATTR);	mutex_unlock(&ctrl->lock);out:	nvmet_req_complete(req, status);} static u32 nvmet_format_ana_group(struct nvmet_req *req, u32 grpid,		struct nvme_ana_group_desc *desc){	struct nvmet_ctrl *ctrl = req->sq->ctrl;	struct nvmet_ns *ns;	unsigned long idx;	u32 count = 0; 	if (!(req->cmd->get_log_page.lsp & NVME_ANA_LOG_RGO)) {		nvmet_for_each_enabled_ns(&ctrl->subsys->namespaces, idx, ns) {			if (ns->anagrpid == grpid)				desc->nsids[count++] = cpu_to_le32(ns->nsid);		}	} 	desc->grpid = cpu_to_le32(grpid);	desc->nnsids = cpu_to_le32(count);	desc->chgcnt = cpu_to_le64(nvmet_ana_chgcnt);	desc->state = req->port->ana_state[grpid];	memset(desc->rsvd17, 0, sizeof(desc->rsvd17));	return struct_size(desc, nsids, count);} static void nvmet_execute_get_log_page_endgrp(struct nvmet_req *req){	u64 host_reads, host_writes, data_units_read, data_units_written;	struct nvme_endurance_group_log *log;	u16 status; 	/*	 * The target driver emulates each endurance group as its own

Lines 1–500 of 1691.