Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba47e386 authored by Matthew Wilcox's avatar Matthew Wilcox
Browse files

NVMe: Wait for device to acknowledge shutdown



A recent update to the specification makes it clear that the host
is expected to wait for the device to acknowledge the Enable bit
transitioning to 0 as well as waiting for the device to acknowledge a
transition to 1.

Reported-by: default avatarKhosrow Panah <Khosrow.Panah@idt.com>
Signed-off-by: default avatarMatthew Wilcox <matthew.r.wilcox@intel.com>
Reviewed-by: default avatarKeith Busch <keith.busch@intel.com>
parent 78f8d257
Loading
Loading
Loading
Loading
+46 −19
Original line number Original line Diff line number Diff line
@@ -1108,15 +1108,57 @@ static struct nvme_queue *nvme_create_queue(struct nvme_dev *dev, int qid,
	return ERR_PTR(result);
	return ERR_PTR(result);
}
}


static int nvme_wait_ready(struct nvme_dev *dev, u64 cap, bool enabled)
{
	unsigned long timeout;
	u32 bit = enabled ? NVME_CSTS_RDY : 0;

	timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies;

	while ((readl(&dev->bar->csts) & NVME_CSTS_RDY) != bit) {
		msleep(100);
		if (fatal_signal_pending(current))
			return -EINTR;
		if (time_after(jiffies, timeout)) {
			dev_err(&dev->pci_dev->dev,
				"Device not ready; aborting initialisation\n");
			return -ENODEV;
		}
	}

	return 0;
}

/*
 * If the device has been passed off to us in an enabled state, just clear
 * the enabled bit.  The spec says we should set the 'shutdown notification
 * bits', but doing so may cause the device to complete commands to the
 * admin queue ... and we don't know what memory that might be pointing at!
 */
static int nvme_disable_ctrl(struct nvme_dev *dev, u64 cap)
{
	writel(0, &dev->bar->cc);
	return nvme_wait_ready(dev, cap, false);
}

static int nvme_enable_ctrl(struct nvme_dev *dev, u64 cap)
{
	return nvme_wait_ready(dev, cap, true);
}

static int nvme_configure_admin_queue(struct nvme_dev *dev)
static int nvme_configure_admin_queue(struct nvme_dev *dev)
{
{
	int result = 0;
	int result;
	u32 aqa;
	u32 aqa;
	u64 cap;
	u64 cap = readq(&dev->bar->cap);
	unsigned long timeout;
	struct nvme_queue *nvmeq;
	struct nvme_queue *nvmeq;


	dev->dbs = ((void __iomem *)dev->bar) + 4096;
	dev->dbs = ((void __iomem *)dev->bar) + 4096;
	dev->db_stride = NVME_CAP_STRIDE(cap);

	result = nvme_disable_ctrl(dev, cap);
	if (result < 0)
		return result;


	nvmeq = nvme_alloc_queue(dev, 0, 64, 0);
	nvmeq = nvme_alloc_queue(dev, 0, 64, 0);
	if (!nvmeq)
	if (!nvmeq)
@@ -1130,27 +1172,12 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
	dev->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE;
	dev->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE;
	dev->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES;
	dev->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES;


	writel(0, &dev->bar->cc);
	writel(aqa, &dev->bar->aqa);
	writel(aqa, &dev->bar->aqa);
	writeq(nvmeq->sq_dma_addr, &dev->bar->asq);
	writeq(nvmeq->sq_dma_addr, &dev->bar->asq);
	writeq(nvmeq->cq_dma_addr, &dev->bar->acq);
	writeq(nvmeq->cq_dma_addr, &dev->bar->acq);
	writel(dev->ctrl_config, &dev->bar->cc);
	writel(dev->ctrl_config, &dev->bar->cc);


	cap = readq(&dev->bar->cap);
	result = nvme_enable_ctrl(dev, cap);
	timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies;
	dev->db_stride = NVME_CAP_STRIDE(cap);

	while (!result && !(readl(&dev->bar->csts) & NVME_CSTS_RDY)) {
		msleep(100);
		if (fatal_signal_pending(current))
			result = -EINTR;
		if (time_after(jiffies, timeout)) {
			dev_err(&dev->pci_dev->dev,
				"Device not ready; aborting initialisation\n");
			result = -ENODEV;
		}
	}

	if (result)
	if (result)
		goto free_q;
		goto free_q;