Commit 0fb59cbc authored by Keith Busch's avatar Keith Busch Committed by Jens Axboe

NVMe: Admin queue removal handling

This protects admin queue access on shutdown. When the controller is
disabled, the queue is frozen to prevent new entry, and unfrozen on
resume, and fixes cq_vector signedness to not suspend a queue twice.

Since unfreezing the queue makes it available for commands, it requires
the queue be initialized, so this moves this part after that.

Special handling is done when the device is unresponsive during
shutdown. This can be optimized to not require subsequent commands to
timeout, but saving that fix for later.

This patch also removes the kill signals in this path that were left-over
artifacts from the blk-mq conversion and no longer necessary.
Signed-off-by: default avatarKeith Busch <keith.busch@intel.com>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent ea191d2f
...@@ -1183,6 +1183,8 @@ static void nvme_disable_queue(struct nvme_dev *dev, int qid) ...@@ -1183,6 +1183,8 @@ static void nvme_disable_queue(struct nvme_dev *dev, int qid)
adapter_delete_sq(dev, qid); adapter_delete_sq(dev, qid);
adapter_delete_cq(dev, qid); adapter_delete_cq(dev, qid);
} }
if (!qid && dev->admin_q)
blk_mq_freeze_queue_start(dev->admin_q);
nvme_clear_queue(nvmeq); nvme_clear_queue(nvmeq);
} }
...@@ -1400,7 +1402,8 @@ static int nvme_alloc_admin_tags(struct nvme_dev *dev) ...@@ -1400,7 +1402,8 @@ static int nvme_alloc_admin_tags(struct nvme_dev *dev)
nvme_dev_remove_admin(dev); nvme_dev_remove_admin(dev);
return -ENODEV; return -ENODEV;
} }
} } else
blk_mq_unfreeze_queue(dev->admin_q);
return 0; return 0;
} }
...@@ -1459,19 +1462,13 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev) ...@@ -1459,19 +1462,13 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
if (result) if (result)
goto free_nvmeq; goto free_nvmeq;
result = nvme_alloc_admin_tags(dev);
if (result)
goto free_nvmeq;
nvmeq->cq_vector = 0; nvmeq->cq_vector = 0;
result = queue_request_irq(dev, nvmeq, nvmeq->irqname); result = queue_request_irq(dev, nvmeq, nvmeq->irqname);
if (result) if (result)
goto free_tags; goto free_nvmeq;
return result; return result;
free_tags:
nvme_dev_remove_admin(dev);
free_nvmeq: free_nvmeq:
nvme_free_queues(dev, 0); nvme_free_queues(dev, 0);
return result; return result;
...@@ -2256,13 +2253,18 @@ static void nvme_wait_dq(struct nvme_delq_ctx *dq, struct nvme_dev *dev) ...@@ -2256,13 +2253,18 @@ static void nvme_wait_dq(struct nvme_delq_ctx *dq, struct nvme_dev *dev)
break; break;
if (!schedule_timeout(ADMIN_TIMEOUT) || if (!schedule_timeout(ADMIN_TIMEOUT) ||
fatal_signal_pending(current)) { fatal_signal_pending(current)) {
/*
* Disable the controller first since we can't trust it
* at this point, but leave the admin queue enabled
* until all queue deletion requests are flushed.
* FIXME: This may take a while if there are more h/w
* queues than admin tags.
*/
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
nvme_disable_ctrl(dev, readq(&dev->bar->cap)); nvme_disable_ctrl(dev, readq(&dev->bar->cap));
nvme_disable_queue(dev, 0); nvme_clear_queue(dev->queues[0]);
send_sig(SIGKILL, dq->worker->task, 1);
flush_kthread_worker(dq->worker); flush_kthread_worker(dq->worker);
nvme_disable_queue(dev, 0);
return; return;
} }
} }
...@@ -2339,7 +2341,6 @@ static void nvme_del_queue_start(struct kthread_work *work) ...@@ -2339,7 +2341,6 @@ static void nvme_del_queue_start(struct kthread_work *work)
{ {
struct nvme_queue *nvmeq = container_of(work, struct nvme_queue, struct nvme_queue *nvmeq = container_of(work, struct nvme_queue,
cmdinfo.work); cmdinfo.work);
allow_signal(SIGKILL);
if (nvme_delete_sq(nvmeq)) if (nvme_delete_sq(nvmeq))
nvme_del_queue_end(nvmeq); nvme_del_queue_end(nvmeq);
} }
...@@ -2607,15 +2608,20 @@ static int nvme_dev_start(struct nvme_dev *dev) ...@@ -2607,15 +2608,20 @@ static int nvme_dev_start(struct nvme_dev *dev)
} }
nvme_init_queue(dev->queues[0], 0); nvme_init_queue(dev->queues[0], 0);
result = nvme_alloc_admin_tags(dev);
if (result)
goto disable;
result = nvme_setup_io_queues(dev); result = nvme_setup_io_queues(dev);
if (result) if (result)
goto disable; goto free_tags;
nvme_set_irq_hints(dev); nvme_set_irq_hints(dev);
return result; return result;
free_tags:
nvme_dev_remove_admin(dev);
disable: disable:
nvme_disable_queue(dev, 0); nvme_disable_queue(dev, 0);
nvme_dev_list_remove(dev); nvme_dev_list_remove(dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment