mirror of https://gitee.com/openkylin/linux.git
NVMe: Start and stop h/w queues on reset
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>
This commit is contained in:
parent
cef6a94827
commit
c9d3bf8810
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue