diff options
Diffstat (limited to 'drivers/nvme/target/admin-cmd.c')
| -rw-r--r-- | drivers/nvme/target/admin-cmd.c | 160 | 
1 files changed, 91 insertions, 69 deletions
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index 5e0e9fcc0d4d..38803576d5e1 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c @@ -32,6 +32,11 @@ u32 nvmet_get_log_page_len(struct nvme_command *cmd)  	return len;  } +static void nvmet_execute_get_log_page_noop(struct nvmet_req *req) +{ +	nvmet_req_complete(req, nvmet_zero_sgl(req, 0, req->data_len)); +} +  static u16 nvmet_get_smart_log_nsid(struct nvmet_req *req,  		struct nvme_smart_log *slog)  { @@ -45,6 +50,10 @@ static u16 nvmet_get_smart_log_nsid(struct nvmet_req *req,  		return NVME_SC_INVALID_NS;  	} +	/* we don't have the right data for file backed ns */ +	if (!ns->bdev) +		goto out; +  	host_reads = part_stat_read(ns->bdev->bd_part, ios[READ]);  	data_units_read = part_stat_read(ns->bdev->bd_part, sectors[READ]);  	host_writes = part_stat_read(ns->bdev->bd_part, ios[WRITE]); @@ -54,6 +63,7 @@ static u16 nvmet_get_smart_log_nsid(struct nvmet_req *req,  	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]); +out:  	nvmet_put_namespace(ns);  	return NVME_SC_SUCCESS; @@ -71,6 +81,9 @@ static u16 nvmet_get_smart_log_all(struct nvmet_req *req,  	rcu_read_lock();  	list_for_each_entry_rcu(ns, &ctrl->subsys->namespaces, dev_link) { +		/* we don't have the right data for file backed ns */ +		if (!ns->bdev) +			continue;  		host_reads += part_stat_read(ns->bdev->bd_part, ios[READ]);  		data_units_read +=  			part_stat_read(ns->bdev->bd_part, sectors[READ]); @@ -89,74 +102,52 @@ static u16 nvmet_get_smart_log_all(struct nvmet_req *req,  	return NVME_SC_SUCCESS;  } -static u16 nvmet_get_smart_log(struct nvmet_req *req, -		struct nvme_smart_log *slog) +static void nvmet_execute_get_log_page_smart(struct nvmet_req *req)  { -	u16 status; +	struct nvme_smart_log *log; +	u16 status = NVME_SC_INTERNAL; + +	if (req->data_len != sizeof(*log)) +		goto out; + +	log = kzalloc(sizeof(*log), GFP_KERNEL); +	if (!log) +		goto out; -	WARN_ON(req == NULL || slog == NULL);  	if (req->cmd->get_log_page.nsid == cpu_to_le32(NVME_NSID_ALL)) -		status = nvmet_get_smart_log_all(req, slog); +		status = nvmet_get_smart_log_all(req, log);  	else -		status = nvmet_get_smart_log_nsid(req, slog); -	return status; +		status = nvmet_get_smart_log_nsid(req, log); +	if (status) +		goto out_free_log; + +	status = nvmet_copy_to_sgl(req, 0, log, sizeof(*log)); +out_free_log: +	kfree(log); +out: +	nvmet_req_complete(req, status);  } -static void nvmet_execute_get_log_page(struct nvmet_req *req) +static void nvmet_execute_get_log_changed_ns(struct nvmet_req *req)  { -	struct nvme_smart_log *smart_log; -	size_t data_len = nvmet_get_log_page_len(req->cmd); -	void *buf; -	u16 status = 0; +	struct nvmet_ctrl *ctrl = req->sq->ctrl; +	u16 status = NVME_SC_INTERNAL; +	size_t len; -	buf = kzalloc(data_len, GFP_KERNEL); -	if (!buf) { -		status = NVME_SC_INTERNAL; +	if (req->data_len != NVME_MAX_CHANGED_NAMESPACES * sizeof(__le32))  		goto out; -	} -	switch (req->cmd->get_log_page.lid) { -	case NVME_LOG_ERROR: -		/* -		 * We currently never set the More bit in the status field, -		 * so all error log entries are invalid and can be zeroed out. -		 * This is called a minum viable implementation (TM) of this -		 * mandatory log page. -		 */ -		break; -	case NVME_LOG_SMART: -		/* -		 * XXX: fill out actual smart log -		 * -		 * We might have a hard time coming up with useful values for -		 * many of the fields, and even when we have useful data -		 * available (e.g. units or commands read/written) those aren't -		 * persistent over power loss. -		 */ -		if (data_len != sizeof(*smart_log)) { -			status = NVME_SC_INTERNAL; -			goto err; -		} -		smart_log = buf; -		status = nvmet_get_smart_log(req, smart_log); -		if (status) -			goto err; -		break; -	case NVME_LOG_FW_SLOT: -		/* -		 * We only support a single firmware slot which always is -		 * active, so we can zero out the whole firmware slot log and -		 * still claim to fully implement this mandatory log page. -		 */ -		break; -	default: -		BUG(); -	} - -	status = nvmet_copy_to_sgl(req, 0, buf, data_len); - -err: -	kfree(buf); +	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->data_len - len); +	ctrl->nr_changed_ns = 0; +	clear_bit(NVME_AEN_CFG_NS_ATTR, &ctrl->aen_masked); +	mutex_unlock(&ctrl->lock);  out:  	nvmet_req_complete(req, status);  } @@ -201,7 +192,7 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)  	id->ver = cpu_to_le32(ctrl->subsys->ver);  	/* XXX: figure out what to do about RTD3R/RTD3 */ -	id->oaes = cpu_to_le32(1 << 8); +	id->oaes = cpu_to_le32(NVMET_AEN_CFG_OPTIONAL);  	id->ctratt = cpu_to_le32(1 << 0);  	id->oacs = 0; @@ -281,8 +272,7 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)  	struct nvme_id_ns *id;  	u16 status = 0; -	ns = nvmet_find_namespace(req->sq->ctrl, req->cmd->identify.nsid); -	if (!ns) { +	if (le32_to_cpu(req->cmd->identify.nsid) == NVME_NSID_ALL) {  		status = NVME_SC_INVALID_NS | NVME_SC_DNR;  		goto out;  	} @@ -290,9 +280,14 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)  	id = kzalloc(sizeof(*id), GFP_KERNEL);  	if (!id) {  		status = NVME_SC_INTERNAL; -		goto out_put_ns; +		goto out;  	} +	/* return an all zeroed buffer if we can't find an active namespace */ +	ns = nvmet_find_namespace(req->sq->ctrl, req->cmd->identify.nsid); +	if (!ns) +		goto done; +  	/*  	 * nuse = ncap = nsze isn't always true, but we have no way to find  	 * that out from the underlying device. @@ -317,11 +312,10 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)  	id->lbaf[0].ds = ns->blksize_shift; +	nvmet_put_namespace(ns); +done:  	status = nvmet_copy_to_sgl(req, 0, id, sizeof(*id)); -  	kfree(id); -out_put_ns: -	nvmet_put_namespace(ns);  out:  	nvmet_req_complete(req, status);  } @@ -447,6 +441,16 @@ static void nvmet_execute_set_features(struct nvmet_req *req)  		req->sq->ctrl->kato = DIV_ROUND_UP(val32, 1000);  		nvmet_set_result(req, req->sq->ctrl->kato);  		break; +	case NVME_FEAT_ASYNC_EVENT: +		val32 = le32_to_cpu(req->cmd->common.cdw10[1]); +		if (val32 & ~NVMET_AEN_CFG_ALL) { +			status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; +			break; +		} + +		WRITE_ONCE(req->sq->ctrl->aen_enabled, val32); +		nvmet_set_result(req, val32); +		break;  	case NVME_FEAT_HOST_ID:  		status = NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR;  		break; @@ -485,9 +489,10 @@ static void nvmet_execute_get_features(struct nvmet_req *req)  		break;  	case NVME_FEAT_WRITE_ATOMIC:  		break; +#endif  	case NVME_FEAT_ASYNC_EVENT: +		nvmet_set_result(req, READ_ONCE(req->sq->ctrl->aen_enabled));  		break; -#endif  	case NVME_FEAT_VOLATILE_WC:  		nvmet_set_result(req, 1);  		break; @@ -548,8 +553,6 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)  	struct nvme_command *cmd = req->cmd;  	u16 ret; -	req->ns = NULL; -  	ret = nvmet_check_ctrl_status(req, cmd);  	if (unlikely(ret))  		return ret; @@ -560,9 +563,28 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)  		switch (cmd->get_log_page.lid) {  		case NVME_LOG_ERROR: +			/* +			 * We currently never set the More bit in the status +			 * field, so all error log entries are invalid and can +			 * be zeroed out.  This is called a minum viable +			 * implementation (TM) of this mandatory log page. +			 */ +			req->execute = nvmet_execute_get_log_page_noop; +			return 0;  		case NVME_LOG_SMART: +			req->execute = nvmet_execute_get_log_page_smart; +			return 0;  		case NVME_LOG_FW_SLOT: -			req->execute = nvmet_execute_get_log_page; +			/* +			 * We only support a single firmware slot which always +			 * is active, so we can zero out the whole firmware slot +			 * log and still claim to fully implement this mandatory +			 * log page. +			 */ +			req->execute = nvmet_execute_get_log_page_noop; +			return 0; +		case NVME_LOG_CHANGED_NS: +			req->execute = nvmet_execute_get_log_changed_ns;  			return 0;  		}  		break;  | 
