diff options
Diffstat (limited to 'drivers/nvme/host/pci.c')
-rw-r--r-- | drivers/nvme/host/pci.c | 25 |
1 files changed, 16 insertions, 9 deletions
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index febcef5ae0aa..6082f2775581 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -81,6 +81,7 @@ static int nvme_reset(struct nvme_dev *dev); static void nvme_process_cq(struct nvme_queue *nvmeq); static void nvme_unmap_data(struct nvme_dev *dev, struct nvme_iod *iod); static void nvme_dead_ctrl(struct nvme_dev *dev); +static void nvme_dev_shutdown(struct nvme_dev *dev); struct async_cmd_info { struct kthread_work work; @@ -1087,17 +1088,23 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req, bool reserved) struct nvme_command cmd; /* - * Schedule controller reset if the command was already aborted once - * before and still hasn't been returned to the driver, or if this is - * the admin queue. + * Shutdown the controller immediately and schedule a reset if the + * command was already aborted once before and still hasn't been + * returned to the driver, or if this is the admin queue. */ if (!nvmeq->qid || cmd_rq->aborted) { - if (queue_work(nvme_workq, &dev->reset_work)) { - dev_warn(dev->dev, - "I/O %d QID %d timeout, reset controller\n", - req->tag, nvmeq->qid); - } - return BLK_EH_RESET_TIMER; + dev_warn(dev->dev, + "I/O %d QID %d timeout, reset controller\n", + req->tag, nvmeq->qid); + nvme_dev_shutdown(dev); + queue_work(nvme_workq, &dev->reset_work); + + /* + * Mark the request as handled, since the inline shutdown + * forces all outstanding requests to complete. + */ + req->errors = NVME_SC_CANCELLED; + return BLK_EH_HANDLED; } if (!dev->ctrl.abort_limit) |