Skip to content

Commit

Permalink
NVMe: Start and stop h/w queues on reset
Browse files Browse the repository at this point in the history
This freezes and stops all the queues on device shutdown and restarts
them on resume. This fixes hotplug and reset issues when the controller
is actively being used.

Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
  • Loading branch information
Keith Busch authored and Jens Axboe committed Jan 8, 2015
1 parent cef6a94 commit c9d3bf8
Showing 1 changed file with 41 additions and 3 deletions.
44 changes: 41 additions & 3 deletions drivers/block/nvme-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,13 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx,
if (unlikely(status)) {
if (!(status & NVME_SC_DNR || blk_noretry_request(req))
&& (jiffies - req->start_time) < req->timeout) {
unsigned long flags;

blk_mq_requeue_request(req);
blk_mq_kick_requeue_list(req->q);
spin_lock_irqsave(req->q->queue_lock, flags);
if (!blk_queue_stopped(req->q))
blk_mq_kick_requeue_list(req->q);
spin_unlock_irqrestore(req->q->queue_lock, flags);
return;
}
req->errors = nvme_error_status(status);
Expand Down Expand Up @@ -2405,6 +2410,34 @@ static void nvme_dev_list_remove(struct nvme_dev *dev)
kthread_stop(tmp);
}

static void nvme_freeze_queues(struct nvme_dev *dev)
{
struct nvme_ns *ns;

list_for_each_entry(ns, &dev->namespaces, list) {
blk_mq_freeze_queue_start(ns->queue);

spin_lock(ns->queue->queue_lock);
queue_flag_set(QUEUE_FLAG_STOPPED, ns->queue);
spin_unlock(ns->queue->queue_lock);

blk_mq_cancel_requeue_work(ns->queue);
blk_mq_stop_hw_queues(ns->queue);
}
}

static void nvme_unfreeze_queues(struct nvme_dev *dev)
{
struct nvme_ns *ns;

list_for_each_entry(ns, &dev->namespaces, list) {
queue_flag_clear_unlocked(QUEUE_FLAG_STOPPED, ns->queue);
blk_mq_unfreeze_queue(ns->queue);
blk_mq_start_stopped_hw_queues(ns->queue, true);
blk_mq_kick_requeue_list(ns->queue);
}
}

static void nvme_dev_shutdown(struct nvme_dev *dev)
{
int i;
Expand All @@ -2413,8 +2446,10 @@ static void nvme_dev_shutdown(struct nvme_dev *dev)
dev->initialized = 0;
nvme_dev_list_remove(dev);

if (dev->bar)
if (dev->bar) {
nvme_freeze_queues(dev);
csts = readl(&dev->bar->csts);
}
if (csts & NVME_CSTS_CFS || !(csts & NVME_CSTS_RDY)) {
for (i = dev->queue_count - 1; i >= 0; i--) {
struct nvme_queue *nvmeq = dev->queues[i];
Expand Down Expand Up @@ -2670,6 +2705,9 @@ static int nvme_dev_resume(struct nvme_dev *dev)
dev->reset_workfn = nvme_remove_disks;
queue_work(nvme_workq, &dev->reset_work);
spin_unlock(&dev_list_lock);
} else {
nvme_unfreeze_queues(dev);
nvme_set_irq_hints(dev);
}
dev->initialized = 1;
return 0;
Expand Down Expand Up @@ -2807,8 +2845,8 @@ static void nvme_remove(struct pci_dev *pdev)
pci_set_drvdata(pdev, NULL);
flush_work(&dev->reset_work);
misc_deregister(&dev->miscdev);
nvme_dev_remove(dev);
nvme_dev_shutdown(dev);
nvme_dev_remove(dev);
nvme_dev_remove_admin(dev);
nvme_free_queues(dev, 0);
nvme_release_prp_pools(dev);
Expand Down

0 comments on commit c9d3bf8

Please sign in to comment.