Commit bb8d261e authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe
Browse files

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: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarSagi Grimberg <sagi@grimberg.me>
Acked-by Jon Derrick: <jonathan.derrick@intel.com>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent 04a934d4
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -58,6 +58,55 @@ static DEFINE_SPINLOCK(dev_list_lock);

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)
{
	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;

	ctrl->state = NVME_CTRL_NEW;
	spin_lock_init(&ctrl->lock);
	INIT_LIST_HEAD(&ctrl->namespaces);
	mutex_init(&ctrl->namespaces_mutex);
	kref_init(&ctrl->kref);
+11 −0
Original line number Diff line number Diff line
@@ -67,7 +67,16 @@ enum nvme_quirks {
	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 {
	enum nvme_ctrl_state state;
	spinlock_t lock;
	const struct nvme_ctrl_ops *ops;
	struct request_queue *admin_q;
	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;
}

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_enable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
int nvme_shutdown_ctrl(struct nvme_ctrl *ctrl);
+12 −13
Original line number Diff line number Diff line
@@ -102,11 +102,6 @@ struct nvme_dev {
	dma_addr_t cmb_dma_addr;
	u64 cmb_size;
	u32 cmbsz;
	unsigned long flags;

#define NVME_CTRL_RESETTING    0
#define NVME_CTRL_REMOVING     1

	struct nvme_ctrl ctrl;
	struct completion ioq_wait;
};
@@ -277,8 +272,7 @@ static void nvme_queue_scan(struct nvme_dev *dev)
	 * Do not queue new scan work when a controller is reset during
	 * removal.
	 */
	if (test_bit(NVME_CTRL_REMOVING, &dev->flags))
		return;
	if (dev->ctrl.state != NVME_CTRL_DELETING)
		queue_work(nvme_workq, &dev->scan_work);
}

@@ -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
	 * 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,
			 "I/O %d QID %d timeout, disable controller\n",
			 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);
	int result = -ENODEV;

	if (WARN_ON(test_bit(NVME_CTRL_RESETTING, &dev->flags)))
	if (WARN_ON(dev->ctrl.state == NVME_CTRL_RESETTING))
		goto out;

	/*
@@ -1845,7 +1839,8 @@ static void nvme_reset_work(struct work_struct *work)
	if (dev->ctrl.ctrl_config & NVME_CC_ENABLE)
		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);
	if (result)
@@ -1894,7 +1889,10 @@ static void nvme_reset_work(struct work_struct *work)
		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;

 out:
@@ -2067,7 +2065,8 @@ static void nvme_remove(struct pci_dev *pdev)

	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);
	flush_work(&dev->async_work);
	flush_work(&dev->scan_work);