nvme: introduce a controller state machine
Replace the adhoc flags in the PCI driver with a state machine in the core code. Based on code from Sagi Grimberg for the Fabrics driver. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Sagi Grimberg <sagi@grimberg.me> Acked-by Jon Derrick: <jonathan.derrick@intel.com> Signed-off-by: Jens Axboe <axboe@fb.com>
This commit is contained in:
parent
04a934d4c7
commit
bb8d261e08
|
@ -58,6 +58,55 @@ static DEFINE_SPINLOCK(dev_list_lock);
|
||||||
|
|
||||||
static struct class *nvme_class;
|
static struct class *nvme_class;
|
||||||
|
|
||||||
|
bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl,
|
||||||
|
enum nvme_ctrl_state new_state)
|
||||||
|
{
|
||||||
|
enum nvme_ctrl_state old_state = ctrl->state;
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
spin_lock_irq(&ctrl->lock);
|
||||||
|
switch (new_state) {
|
||||||
|
case NVME_CTRL_LIVE:
|
||||||
|
switch (old_state) {
|
||||||
|
case NVME_CTRL_RESETTING:
|
||||||
|
changed = true;
|
||||||
|
/* FALLTHRU */
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NVME_CTRL_RESETTING:
|
||||||
|
switch (old_state) {
|
||||||
|
case NVME_CTRL_NEW:
|
||||||
|
case NVME_CTRL_LIVE:
|
||||||
|
changed = true;
|
||||||
|
/* FALLTHRU */
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NVME_CTRL_DELETING:
|
||||||
|
switch (old_state) {
|
||||||
|
case NVME_CTRL_LIVE:
|
||||||
|
case NVME_CTRL_RESETTING:
|
||||||
|
changed = true;
|
||||||
|
/* FALLTHRU */
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spin_unlock_irq(&ctrl->lock);
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
ctrl->state = new_state;
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(nvme_change_ctrl_state);
|
||||||
|
|
||||||
static void nvme_free_ns(struct kref *kref)
|
static void nvme_free_ns(struct kref *kref)
|
||||||
{
|
{
|
||||||
struct nvme_ns *ns = container_of(kref, struct nvme_ns, kref);
|
struct nvme_ns *ns = container_of(kref, struct nvme_ns, kref);
|
||||||
|
@ -1583,6 +1632,8 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ctrl->state = NVME_CTRL_NEW;
|
||||||
|
spin_lock_init(&ctrl->lock);
|
||||||
INIT_LIST_HEAD(&ctrl->namespaces);
|
INIT_LIST_HEAD(&ctrl->namespaces);
|
||||||
mutex_init(&ctrl->namespaces_mutex);
|
mutex_init(&ctrl->namespaces_mutex);
|
||||||
kref_init(&ctrl->kref);
|
kref_init(&ctrl->kref);
|
||||||
|
|
|
@ -67,7 +67,16 @@ enum nvme_quirks {
|
||||||
NVME_QUIRK_DISCARD_ZEROES = (1 << 2),
|
NVME_QUIRK_DISCARD_ZEROES = (1 << 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum nvme_ctrl_state {
|
||||||
|
NVME_CTRL_NEW,
|
||||||
|
NVME_CTRL_LIVE,
|
||||||
|
NVME_CTRL_RESETTING,
|
||||||
|
NVME_CTRL_DELETING,
|
||||||
|
};
|
||||||
|
|
||||||
struct nvme_ctrl {
|
struct nvme_ctrl {
|
||||||
|
enum nvme_ctrl_state state;
|
||||||
|
spinlock_t lock;
|
||||||
const struct nvme_ctrl_ops *ops;
|
const struct nvme_ctrl_ops *ops;
|
||||||
struct request_queue *admin_q;
|
struct request_queue *admin_q;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
@ -187,6 +196,8 @@ static inline bool nvme_req_needs_retry(struct request *req, u16 status)
|
||||||
(jiffies - req->start_time) < req->timeout;
|
(jiffies - req->start_time) < req->timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl,
|
||||||
|
enum nvme_ctrl_state new_state);
|
||||||
int nvme_disable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
|
int nvme_disable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
|
||||||
int nvme_enable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
|
int nvme_enable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
|
||||||
int nvme_shutdown_ctrl(struct nvme_ctrl *ctrl);
|
int nvme_shutdown_ctrl(struct nvme_ctrl *ctrl);
|
||||||
|
|
|
@ -102,11 +102,6 @@ struct nvme_dev {
|
||||||
dma_addr_t cmb_dma_addr;
|
dma_addr_t cmb_dma_addr;
|
||||||
u64 cmb_size;
|
u64 cmb_size;
|
||||||
u32 cmbsz;
|
u32 cmbsz;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
#define NVME_CTRL_RESETTING 0
|
|
||||||
#define NVME_CTRL_REMOVING 1
|
|
||||||
|
|
||||||
struct nvme_ctrl ctrl;
|
struct nvme_ctrl ctrl;
|
||||||
struct completion ioq_wait;
|
struct completion ioq_wait;
|
||||||
};
|
};
|
||||||
|
@ -277,9 +272,8 @@ static void nvme_queue_scan(struct nvme_dev *dev)
|
||||||
* Do not queue new scan work when a controller is reset during
|
* Do not queue new scan work when a controller is reset during
|
||||||
* removal.
|
* removal.
|
||||||
*/
|
*/
|
||||||
if (test_bit(NVME_CTRL_REMOVING, &dev->flags))
|
if (dev->ctrl.state != NVME_CTRL_DELETING)
|
||||||
return;
|
queue_work(nvme_workq, &dev->scan_work);
|
||||||
queue_work(nvme_workq, &dev->scan_work);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_complete_async_event(struct nvme_dev *dev,
|
static void nvme_complete_async_event(struct nvme_dev *dev,
|
||||||
|
@ -901,7 +895,7 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req, bool reserved)
|
||||||
* cancellation error. All outstanding requests are completed on
|
* cancellation error. All outstanding requests are completed on
|
||||||
* shutdown, so we return BLK_EH_HANDLED.
|
* shutdown, so we return BLK_EH_HANDLED.
|
||||||
*/
|
*/
|
||||||
if (test_bit(NVME_CTRL_RESETTING, &dev->flags)) {
|
if (dev->ctrl.state == NVME_CTRL_RESETTING) {
|
||||||
dev_warn(dev->ctrl.device,
|
dev_warn(dev->ctrl.device,
|
||||||
"I/O %d QID %d timeout, disable controller\n",
|
"I/O %d QID %d timeout, disable controller\n",
|
||||||
req->tag, nvmeq->qid);
|
req->tag, nvmeq->qid);
|
||||||
|
@ -1835,7 +1829,7 @@ static void nvme_reset_work(struct work_struct *work)
|
||||||
struct nvme_dev *dev = container_of(work, struct nvme_dev, reset_work);
|
struct nvme_dev *dev = container_of(work, struct nvme_dev, reset_work);
|
||||||
int result = -ENODEV;
|
int result = -ENODEV;
|
||||||
|
|
||||||
if (WARN_ON(test_bit(NVME_CTRL_RESETTING, &dev->flags)))
|
if (WARN_ON(dev->ctrl.state == NVME_CTRL_RESETTING))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1845,7 +1839,8 @@ static void nvme_reset_work(struct work_struct *work)
|
||||||
if (dev->ctrl.ctrl_config & NVME_CC_ENABLE)
|
if (dev->ctrl.ctrl_config & NVME_CC_ENABLE)
|
||||||
nvme_dev_disable(dev, false);
|
nvme_dev_disable(dev, false);
|
||||||
|
|
||||||
set_bit(NVME_CTRL_RESETTING, &dev->flags);
|
if (!nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_RESETTING))
|
||||||
|
goto out;
|
||||||
|
|
||||||
result = nvme_pci_enable(dev);
|
result = nvme_pci_enable(dev);
|
||||||
if (result)
|
if (result)
|
||||||
|
@ -1894,7 +1889,10 @@ static void nvme_reset_work(struct work_struct *work)
|
||||||
nvme_dev_add(dev);
|
nvme_dev_add(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_bit(NVME_CTRL_RESETTING, &dev->flags);
|
if (!nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_LIVE)) {
|
||||||
|
dev_warn(dev->ctrl.device, "failed to mark controller live\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -2067,7 +2065,8 @@ static void nvme_remove(struct pci_dev *pdev)
|
||||||
|
|
||||||
del_timer_sync(&dev->watchdog_timer);
|
del_timer_sync(&dev->watchdog_timer);
|
||||||
|
|
||||||
set_bit(NVME_CTRL_REMOVING, &dev->flags);
|
nvme_change_ctrl_state(&dev->ctrl, NVME_CTRL_DELETING);
|
||||||
|
|
||||||
pci_set_drvdata(pdev, NULL);
|
pci_set_drvdata(pdev, NULL);
|
||||||
flush_work(&dev->async_work);
|
flush_work(&dev->async_work);
|
||||||
flush_work(&dev->scan_work);
|
flush_work(&dev->scan_work);
|
||||||
|
|
Loading…
Reference in New Issue