diff options
author | Keith Busch <keith.busch@intel.com> | 2013-07-15 23:02:22 +0200 |
---|---|---|
committer | Matthew Wilcox <matthew.r.wilcox@intel.com> | 2013-09-03 22:40:32 +0200 |
commit | 1894d8f16afe5ad54b732f0fa6c4e80bd4d40b91 (patch) | |
tree | f1451bcf63fc1ddc60b10ba0f502029da1119e7e | |
parent | NVMe: Separate controller init from disk discovery (diff) | |
download | linux-1894d8f16afe5ad54b732f0fa6c4e80bd4d40b91.tar.xz linux-1894d8f16afe5ad54b732f0fa6c4e80bd4d40b91.zip |
NVMe: Use normal shutdown
The NVMe spec recommends using the shutdown normal sequence when safely
taking the controller offline instead of hitting CC.EN on the next
start-up to reset the controller. The spec recommends a minimum of 1
second for the shutdown complete. This patch waits 2 seconds to be on
the safe side.
Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
-rw-r--r-- | drivers/block/nvme-core.c | 26 | ||||
-rw-r--r-- | include/linux/nvme.h | 2 |
2 files changed, 28 insertions, 0 deletions
diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index 1595ffba8a66..23bb5a70d810 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -1221,6 +1221,30 @@ static int nvme_enable_ctrl(struct nvme_dev *dev, u64 cap) return nvme_wait_ready(dev, cap, true); } +static int nvme_shutdown_ctrl(struct nvme_dev *dev) +{ + unsigned long timeout; + u32 cc; + + cc = (readl(&dev->bar->cc) & ~NVME_CC_SHN_MASK) | NVME_CC_SHN_NORMAL; + writel(cc, &dev->bar->cc); + + timeout = 2 * HZ + jiffies; + while ((readl(&dev->bar->csts) & NVME_CSTS_SHST_MASK) != + NVME_CSTS_SHST_CMPLT) { + msleep(100); + if (fatal_signal_pending(current)) + return -EINTR; + if (time_after(jiffies, timeout)) { + dev_err(&dev->pci_dev->dev, + "Device shutdown incomplete; abort shutdown\n"); + return -ENODEV; + } + } + + return 0; +} + static int nvme_configure_admin_queue(struct nvme_dev *dev) { int result; @@ -1943,6 +1967,8 @@ static void nvme_dev_shutdown(struct nvme_dev *dev) list_del_init(&dev->node); spin_unlock(&dev_list_lock); + if (dev->bar) + nvme_shutdown_ctrl(dev); nvme_dev_unmap(dev); } diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 3403c8f06fa0..26ebcf41c213 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -53,6 +53,7 @@ enum { NVME_CC_SHN_NONE = 0 << 14, NVME_CC_SHN_NORMAL = 1 << 14, NVME_CC_SHN_ABRUPT = 2 << 14, + NVME_CC_SHN_MASK = 3 << 14, NVME_CC_IOSQES = 6 << 16, NVME_CC_IOCQES = 4 << 20, NVME_CSTS_RDY = 1 << 0, @@ -60,6 +61,7 @@ enum { NVME_CSTS_SHST_NORMAL = 0 << 2, NVME_CSTS_SHST_OCCUR = 1 << 2, NVME_CSTS_SHST_CMPLT = 2 << 2, + NVME_CSTS_SHST_MASK = 3 << 2, }; #define NVME_VS(major, minor) (major << 16 | minor) |