2024-02-07 18:43:30

by David Jeffery

[permalink] [raw]
Subject: [RFC PATCH 4/6] pci nvme async shutdown support

Alter nvme so that the pci nvme driver may shut down the hardware
asynchronously, providing a large benefit to shutdown time with large numbers
of nvme devices in some configurations.

The functionality of nvme_disable_ctrl is split in separate functions for
starting disabling the hardware and for waiting for the hardware to report
success. A wrapper is created which provides the same interface for the
other nvme types so they remain unchanged.

And new field is added to nvme-pci's nvme_dev to track the nvme shutdown
state so that the nvme async_shutdown_end call knows what if anything it
still needs to do.

Signed-off-by: David Jeffery <[email protected]>
Tested-by: Laurence Oberman <[email protected]>

---
drivers/nvme/host/core.c | 26 +++++++++++++++-----
drivers/nvme/host/nvme.h | 2 ++
drivers/nvme/host/pci.c | 53 ++++++++++++++++++++++++++++++++++++++--
3 files changed, 73 insertions(+), 8 deletions(-)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 85ab0fcf9e88..b24985a843a8 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2242,20 +2242,21 @@ static int nvme_wait_ready(struct nvme_ctrl *ctrl, u32 mask, u32 val,
return ret;
}

-int nvme_disable_ctrl(struct nvme_ctrl *ctrl, bool shutdown)
+int nvme_disable_ctrl_send(struct nvme_ctrl *ctrl, bool shutdown)
{
- int ret;
-
ctrl->ctrl_config &= ~NVME_CC_SHN_MASK;
if (shutdown)
ctrl->ctrl_config |= NVME_CC_SHN_NORMAL;
else
ctrl->ctrl_config &= ~NVME_CC_ENABLE;

- ret = ctrl->ops->reg_write32(ctrl, NVME_REG_CC, ctrl->ctrl_config);
- if (ret)
- return ret;
+ return ctrl->ops->reg_write32(ctrl, NVME_REG_CC, ctrl->ctrl_config);
+}
+EXPORT_SYMBOL_GPL(nvme_disable_ctrl_send);

+
+int nvme_disable_ctrl_wait(struct nvme_ctrl *ctrl, bool shutdown)
+{
if (shutdown) {
return nvme_wait_ready(ctrl, NVME_CSTS_SHST_MASK,
NVME_CSTS_SHST_CMPLT,
@@ -2266,6 +2267,19 @@ int nvme_disable_ctrl(struct nvme_ctrl *ctrl, bool shutdown)
return nvme_wait_ready(ctrl, NVME_CSTS_RDY, 0,
(NVME_CAP_TIMEOUT(ctrl->cap) + 1) / 2, "reset");
}
+EXPORT_SYMBOL_GPL(nvme_disable_ctrl_wait);
+
+
+int nvme_disable_ctrl(struct nvme_ctrl *ctrl, bool shutdown)
+{
+ int ret;
+
+ ret = nvme_disable_ctrl_send(ctrl, shutdown);
+ if (!ret)
+ ret = nvme_disable_ctrl_wait(ctrl, shutdown);
+
+ return ret;
+}
EXPORT_SYMBOL_GPL(nvme_disable_ctrl);

int nvme_enable_ctrl(struct nvme_ctrl *ctrl)
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 030c80818240..5bdd862328d4 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -761,6 +761,8 @@ void nvme_cancel_admin_tagset(struct nvme_ctrl *ctrl);
bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl,
enum nvme_ctrl_state new_state);
int nvme_disable_ctrl(struct nvme_ctrl *ctrl, bool shutdown);
+int nvme_disable_ctrl_send(struct nvme_ctrl *ctrl, bool shutdown);
+int nvme_disable_ctrl_wait(struct nvme_ctrl *ctrl, bool shutdown);
int nvme_enable_ctrl(struct nvme_ctrl *ctrl);
int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
const struct nvme_ctrl_ops *ops, unsigned long quirks);
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index c1d6357ec98a..c2a7b3d28a56 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -112,6 +112,12 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown);
static void nvme_delete_io_queues(struct nvme_dev *dev);
static void nvme_update_attrs(struct nvme_dev *dev);

+enum nvme_disable_state {
+ NVME_DISABLE_START = 0,
+ NVME_DISABLE_WAIT = 1,
+ NVME_DISABLE_DONE = 2,
+};
+
/*
* Represents an NVM Express device. Each nvme_dev is a PCI function.
*/
@@ -159,6 +165,7 @@ struct nvme_dev {
unsigned int nr_allocated_queues;
unsigned int nr_write_queues;
unsigned int nr_poll_queues;
+ enum nvme_disable_state disable_state;
};

static int io_queue_depth_set(const char *val, const struct kernel_param *kp)
@@ -2574,12 +2581,14 @@ static bool nvme_pci_ctrl_is_dead(struct nvme_dev *dev)
return (csts & NVME_CSTS_CFS) || !(csts & NVME_CSTS_RDY);
}

-static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown)
+static void nvme_dev_disable_start(struct nvme_dev *dev, bool shutdown)
{
enum nvme_ctrl_state state = nvme_ctrl_state(&dev->ctrl);
struct pci_dev *pdev = to_pci_dev(dev->dev);
bool dead;

+ dev->disable_state = NVME_DISABLE_START;
+
mutex_lock(&dev->shutdown_lock);
dead = nvme_pci_ctrl_is_dead(dev);
if (state == NVME_CTRL_LIVE || state == NVME_CTRL_RESETTING) {
@@ -2597,7 +2606,20 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown)

if (!dead && dev->ctrl.queue_count > 0) {
nvme_delete_io_queues(dev);
- nvme_disable_ctrl(&dev->ctrl, shutdown);
+ nvme_disable_ctrl_send(&dev->ctrl, shutdown);
+ dev->disable_state = NVME_DISABLE_WAIT;
+ }
+}
+
+static void nvme_dev_disable_end(struct nvme_dev *dev, bool shutdown)
+{
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
+
+ if (dev->disable_state == NVME_DISABLE_DONE)
+ return;
+
+ if (dev->disable_state == NVME_DISABLE_WAIT) {
+ nvme_disable_ctrl_wait(&dev->ctrl, shutdown);
nvme_poll_irqdisable(&dev->queues[0]);
}
nvme_suspend_io_queues(dev);
@@ -2623,6 +2645,12 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown)
mutex_unlock(&dev->shutdown_lock);
}

+static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown)
+{
+ nvme_dev_disable_start(dev, shutdown);
+ nvme_dev_disable_end(dev, shutdown);
+}
+
static int nvme_disable_prepare_reset(struct nvme_dev *dev, bool shutdown)
{
if (!nvme_wait_reset(&dev->ctrl))
@@ -3120,6 +3148,25 @@ static void nvme_shutdown(struct pci_dev *pdev)
nvme_disable_prepare_reset(dev, true);
}

+static void nvme_shutdown_start(struct pci_dev *pdev)
+{
+ struct nvme_dev *dev = pci_get_drvdata(pdev);
+
+ if (!nvme_wait_reset(&dev->ctrl)) {
+ dev->disable_state = NVME_DISABLE_DONE;
+ return;
+ }
+ nvme_dev_disable_start(dev, true);
+}
+
+static void nvme_shutdown_end(struct pci_dev *pdev)
+{
+ struct nvme_dev *dev = pci_get_drvdata(pdev);
+
+ nvme_dev_disable_end(dev, true);
+}
+
+
/*
* The driver's remove may be called on a device in a partially initialized
* state. This function must not have any dependencies on the device state in
@@ -3511,6 +3558,8 @@ static struct pci_driver nvme_driver = {
.probe = nvme_probe,
.remove = nvme_remove,
.shutdown = nvme_shutdown,
+ .async_shutdown_start = nvme_shutdown_start,
+ .async_shutdown_end = nvme_shutdown_end,
.driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
#ifdef CONFIG_PM_SLEEP
--
2.43.0