Commit f54f0d0e authored by Keith Busch's avatar Keith Busch
Browse files

nvme: enhance cns version checking



The number of CNS bits in the command is specific to the nvme spec
version compliance. The existing check is not sufficient for possible
CNS values the driver uses that may create confusion between host and
device, so enhance the check to consider the version and desired CNS
value.

Reviewed-by: default avatarSagi Grimberg <sagi@grimberg.me>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarKeith Busch <kbusch@kernel.org>
parent 49c234b5
Loading
Loading
Loading
Loading
+25 −12
Original line number Diff line number Diff line
@@ -1390,17 +1390,30 @@ static void nvme_update_keep_alive(struct nvme_ctrl *ctrl,
	nvme_start_keep_alive(ctrl);
}

static bool nvme_id_cns_ok(struct nvme_ctrl *ctrl, u8 cns)
{
	/*
 * In NVMe 1.0 the CNS field was just a binary controller or namespace
 * flag, thus sending any new CNS opcodes has a big chance of not working.
 * Qemu unfortunately had that bug after reporting a 1.1 version compliance
 * (but not for any later version).
	 * The CNS field occupies a full byte starting with NVMe 1.2
	 */
static bool nvme_ctrl_limited_cns(struct nvme_ctrl *ctrl)
{
	if (ctrl->quirks & NVME_QUIRK_IDENTIFY_CNS)
		return ctrl->vs < NVME_VS(1, 2, 0);
	return ctrl->vs < NVME_VS(1, 1, 0);
	if (ctrl->vs >= NVME_VS(1, 2, 0))
		return true;

	/*
	 * NVMe 1.1 expanded the CNS value to two bits, which means values
	 * larger than that could get truncated and treated as an incorrect
	 * value.
	 *
	 * Qemu implemented 1.0 behavior for controllers claiming 1.1
	 * compliance, so they need to be quirked here.
	 */
	if (ctrl->vs >= NVME_VS(1, 1, 0) &&
	    !(ctrl->quirks & NVME_QUIRK_IDENTIFY_CNS))
		return cns <= 3;

	/*
	 * NVMe 1.0 used a single bit for the CNS value.
	 */
	return cns <= 1;
}

static int nvme_identify_ctrl(struct nvme_ctrl *dev, struct nvme_id_ctrl **id)
@@ -3104,7 +3117,7 @@ static int nvme_init_non_mdts_limits(struct nvme_ctrl *ctrl)
		ctrl->max_zeroes_sectors = 0;

	if (ctrl->subsys->subtype != NVME_NQN_NVME ||
	    nvme_ctrl_limited_cns(ctrl) ||
	    !nvme_id_cns_ok(ctrl, NVME_ID_CNS_CS_CTRL) ||
	    test_bit(NVME_CTRL_SKIP_ID_CNS_CS, &ctrl->flags))
		return 0;

@@ -4200,7 +4213,7 @@ static void nvme_scan_work(struct work_struct *work)
	}

	mutex_lock(&ctrl->scan_lock);
	if (nvme_ctrl_limited_cns(ctrl)) {
	if (!nvme_id_cns_ok(ctrl, NVME_ID_CNS_NS_ACTIVE_LIST)) {
		nvme_scan_ns_sequential(ctrl);
	} else {
		/*