* [RFC PATCH 3/5] nvme: to add support for nvme reset functions
@ 2019-12-11 2:34 tsutomu.owa
0 siblings, 0 replies; only message in thread
From: tsutomu.owa @ 2019-12-11 2:34 UTC (permalink / raw)
To: linux-nvme, sagi, kbusch, hch; +Cc: tsutomu.owa
This patch adds support for nvme reset functions described in NVMe spec
1.3c "7.3.2 Controller Level Reset" (conventional hot reset,
link down reset and function level reset).
Signed-off-by: Tsutomu OWA <tsutomu.owa@kioxia.com>
---
drivers/nvme/host/core.c | 122 ++++++++++++++++++++++++++++++++
drivers/nvme/host/nvme.h | 4 ++
drivers/nvme/host/pci.c | 11 +++
include/linux/nvme.h | 4 ++
include/uapi/linux/nvme_ioctl.h | 43 +++++++++++
5 files changed, 184 insertions(+)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index dfe37a525..65e3ef820 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -94,6 +94,113 @@ static void nvme_put_subsystem(struct nvme_subsystem *subsys);
static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
unsigned nsid);
+#ifdef CONFIG_NVME_PCI_RESET
+static int nvme_conventional_hot_reset(struct pci_dev *bridge)
+{
+ return pci_bridge_secondary_bus_reset(bridge);
+}
+
+#define PCIE_RESET_READY_POLL_MS 60000
+static int nvme_link_down_reset(struct pci_dev *pdev, struct pci_dev *bridge)
+{
+ /* link down */
+ pcie_capability_set_word(bridge, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_LD);
+
+ msleep(100);
+
+ /* link up */
+ pcie_capability_clear_word(bridge, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_LD);
+
+ return pci_dev_wait(pdev, "LNK", PCIE_RESET_READY_POLL_MS);
+}
+
+static int __pci_reset_dev_locked(struct nvme_ctrl *ctrl,
+ struct pci_dev *pdev, unsigned int cmd)
+{
+ int rc;
+ struct pci_dev *bridge = pci_upstream_bridge(pdev);
+
+ might_sleep();
+
+ if (!pci_is_pcie(pdev) || !bridge || !pci_is_pcie(bridge)) {
+ dev_err(ctrl->device,
+ " no bridge found (or not PCIe dev)\n");
+ return -ENOTTY;
+ }
+
+ if (!pci_wait_for_pending_transaction(pdev))
+ dev_warn(ctrl->device,
+ "timed out waiting for pending transaction\n");
+ switch (cmd) {
+ case NVME_IOCTL_CONVENTIONAL_HOT_RESET:
+ rc = nvme_conventional_hot_reset(bridge);
+ break;
+ case NVME_IOCTL_LINK_DOWN_RESET:
+ rc = nvme_link_down_reset(pdev, bridge);
+ break;
+ default:
+ dev_err(ctrl->device,
+ "%s- unknown reset (%u)\n", __func__, cmd);
+ rc = -ENOTTY;
+ }
+
+ if (rc) {
+ dev_err(ctrl->device,
+ "%s: reset (%s) failed. rc=%d\n",
+ __func__, get_nvme_ioctl_name(cmd), rc);
+ return rc;
+ }
+
+ /* to reset mps (and misc.) */
+ pci_configure_device(pdev);
+
+ return rc;
+}
+
+static int nvme_pci_reset_dev(struct nvme_ctrl *ctrl,
+ struct pci_dev *pdev, unsigned int cmd)
+{
+ int rc;
+
+ pci_dev_lock(pdev);
+
+ /*
+ * do not disable the device
+ * as pci_dev_save_and_disable() calls nvme_disable_device().
+ */
+ ctrl->ctrl_config |= NVME_CC_NO_ENABLE;
+
+ pci_dev_save_and_disable(pdev); /* to save pcie status */
+
+ rc = __pci_reset_dev_locked(ctrl, pdev, cmd);
+
+ pci_dev_restore(pdev);
+
+ /* play safe */
+ ctrl->ctrl_config &= ~NVME_CC_NO_ENABLE;
+ pci_dev_unlock(pdev);
+
+ return rc;
+}
+
+static int nvme_pci_reset_common(struct nvme_ctrl *ctrl, unsigned int cmd)
+{
+ struct pci_dev *pdev;
+
+ dev_info(ctrl->device, "reset %s\n", get_nvme_ioctl_name(cmd));
+
+ if (!ctrl->ops->pci_dev)
+ return -ENOTTY;
+
+ pdev = ctrl->ops->pci_dev(ctrl);
+ if (cmd == NVME_IOCTL_FUNCTION_LEVEL_RESET)
+ return pci_reset_function(pdev);
+ else
+ return nvme_pci_reset_dev(ctrl, pdev, cmd);
+}
+
+#endif /* CONFIG_NVME_PCI_RESET */
+
static void nvme_set_queue_dying(struct nvme_ns *ns)
{
/*
@@ -2097,6 +2204,15 @@ int nvme_disable_ctrl(struct nvme_ctrl *ctrl)
ctrl->ctrl_config &= ~NVME_CC_SHN_MASK;
ctrl->ctrl_config &= ~NVME_CC_ENABLE;
+#ifdef CONFIG_NVME_PCI_RESET
+ if (ctrl->ctrl_config & NVME_CC_NO_ENABLE) {
+ dev_dbg(ctrl->device,
+ "%s: NVME_CC_NO_ENABLE set\n", __func__);
+ ctrl->ctrl_config &= ~NVME_CC_NO_ENABLE;
+ return 0;
+ }
+#endif /* CONFIG_NVME_PCI_RESET */
+
ret = ctrl->ops->reg_write32(ctrl, NVME_REG_CC, ctrl->ctrl_config);
if (ret)
return ret;
@@ -2979,6 +3095,12 @@ static long nvme_dev_ioctl(struct file *file, unsigned int cmd,
case NVME_IOCTL_RESCAN:
nvme_queue_scan(ctrl);
return 0;
+#ifdef CONFIG_NVME_PCI_RESET
+ case NVME_IOCTL_CONVENTIONAL_HOT_RESET: /* fall thru */
+ case NVME_IOCTL_LINK_DOWN_RESET:
+ case NVME_IOCTL_FUNCTION_LEVEL_RESET:
+ return nvme_pci_reset_common(ctrl, cmd);
+#endif /* CONFIG_NVME_PCI_RESET */
default:
return -ENOTTY;
}
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 3b9cbe066..0b5f59b50 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -401,6 +401,10 @@ struct nvme_ctrl_ops {
void (*submit_async_event)(struct nvme_ctrl *ctrl);
void (*delete_ctrl)(struct nvme_ctrl *ctrl);
int (*get_address)(struct nvme_ctrl *ctrl, char *buf, int size);
+
+#ifdef CONFIG_NVME_PCI_RESET
+ struct pci_dev *(*pci_dev)(struct nvme_ctrl *ctrl);
+#endif /* CONFIG_NVME_PCI_RESET */
};
#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index dcaad5831..5a78165ec 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -2694,6 +2694,13 @@ static int nvme_pci_get_address(struct nvme_ctrl *ctrl, char *buf, int size)
return snprintf(buf, size, "%s", dev_name(&pdev->dev));
}
+#ifdef CONFIG_NVME_PCI_RESET
+static struct pci_dev *nvme_pci_to_pci_dev(struct nvme_ctrl *ctrl)
+{
+ return to_pci_dev(to_nvme_dev(ctrl)->dev);
+}
+#endif /* CONFIG_NVME_PCI_RESET */
+
static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {
.name = "pcie",
.module = THIS_MODULE,
@@ -2705,6 +2712,10 @@ static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {
.free_ctrl = nvme_pci_free_ctrl,
.submit_async_event = nvme_pci_submit_async_event,
.get_address = nvme_pci_get_address,
+
+#ifdef CONFIG_NVME_PCI_RESET
+ .pci_dev = nvme_pci_to_pci_dev,
+#endif /* CONFIG_NVME_PCI_RESET */
};
static int nvme_dev_map(struct nvme_dev *dev)
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 3d5189f46..5c2ce7374 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -160,6 +160,10 @@ enum {
enum {
NVME_CC_ENABLE = 1 << 0,
+#ifdef CONFIG_NVME_PCI_RESET
+ NVME_CC_NO_ENABLE = 1 << 30, /* conventional hot reset or */
+ /* link down reset is going. */
+#endif /* CONFIG_NVME_PCI_RESET */
NVME_CC_CSS_NVM = 0 << 4,
NVME_CC_EN_SHIFT = 0,
NVME_CC_CSS_SHIFT = 4,
diff --git a/include/uapi/linux/nvme_ioctl.h b/include/uapi/linux/nvme_ioctl.h
index d99b5a772..36a77ff75 100644
--- a/include/uapi/linux/nvme_ioctl.h
+++ b/include/uapi/linux/nvme_ioctl.h
@@ -79,4 +79,47 @@ struct nvme_passthru_cmd64 {
#define NVME_IOCTL_ADMIN64_CMD _IOWR('N', 0x47, struct nvme_passthru_cmd64)
#define NVME_IOCTL_IO64_CMD _IOWR('N', 0x48, struct nvme_passthru_cmd64)
+#ifdef CONFIG_NVME_PCI_RESET
+#define NVME_IOCTL_CONVENTIONAL_HOT_RESET _IO('N', 0x49)
+#define NVME_IOCTL_LINK_DOWN_RESET _IO('N', 0x4a)
+#define NVME_IOCTL_FUNCTION_LEVEL_RESET _IO('N', 0x4b)
+#endif /* CONFIG_NVME_PCI_RESET */
+
+static inline char *get_nvme_ioctl_name(unsigned int cmd)
+{
+ switch (cmd) {
+ case NVME_IOCTL_ID:
+ return "id";
+ case NVME_IOCTL_ADMIN_CMD:
+ return "admin cmd";
+ case NVME_IOCTL_SUBMIT_IO:
+ return "submit io";
+ case NVME_IOCTL_IO_CMD:
+ return "io cmd";
+ case NVME_IOCTL_RESET:
+ return "controller reset";
+ case NVME_IOCTL_SUBSYS_RESET:
+ return "subsystem reset";
+ case NVME_IOCTL_RESCAN:
+ return "rescan";
+ case NVME_IOCTL_ADMIN64_CMD:
+ return "admin64 cmd";
+ case NVME_IOCTL_IO64_CMD:
+ return "io64 cmd";
+#ifdef CONFIG_NVME_PCI_RESET
+ case NVME_IOCTL_CONVENTIONAL_HOT_RESET:
+ return "conventional hot reset";
+ case NVME_IOCTL_LINK_DOWN_RESET:
+ return "link down reset";
+ case NVME_IOCTL_FUNCTION_LEVEL_RESET:
+ return "function level reset";
+#endif /* CONFIG_NVME_PCI_RESET */
+ default:
+ return "Unknown";
+ }
+
+ // should not reach here
+ return "Unknown";
+}
+
#endif /* _UAPI_LINUX_NVME_IOCTL_H */
--
2.17.1
_______________________________________________
linux-nvme mailing list
linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2019-12-11 2:35 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-11 2:34 [RFC PATCH 3/5] nvme: to add support for nvme reset functions tsutomu.owa
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).