From 69d3b8ac15a5eb938e6a01909f6cc8ae4b5d3a17 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Dec 2015 15:27:00 +0100 Subject: nvme: synchronize access to ctrl->namespaces Currently traversal and modification of ctrl->namespaces happens completely unsynchronized, which can be fixed by the addition of a simple mutex. Note: nvme_dev_ioctl will be handled in the next patch. Signed-off-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Acked-by: Keith Busch Signed-off-by: Jens Axboe --- drivers/nvme/host/core.c | 17 +++++++++++++++++ drivers/nvme/host/nvme.h | 1 + 2 files changed, 18 insertions(+) (limited to 'drivers/nvme') diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 27130056136b..a928ad5aabaa 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1034,6 +1034,8 @@ static struct nvme_ns *nvme_find_ns(struct nvme_ctrl *ctrl, unsigned nsid) { struct nvme_ns *ns; + lockdep_assert_held(&ctrl->namespaces_mutex); + list_for_each_entry(ns, &ctrl->namespaces, list) { if (ns->ns_id == nsid) return ns; @@ -1049,6 +1051,8 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid) struct gendisk *disk; int node = dev_to_node(ctrl->dev); + lockdep_assert_held(&ctrl->namespaces_mutex); + ns = kzalloc_node(sizeof(*ns), GFP_KERNEL, node); if (!ns) return; @@ -1118,6 +1122,8 @@ static void nvme_ns_remove(struct nvme_ns *ns) bool kill = nvme_io_incapable(ns->ctrl) && !blk_queue_dying(ns->queue); + lockdep_assert_held(&ns->ctrl->namespaces_mutex); + if (kill) blk_set_queue_dying(ns->queue); if (ns->disk->flags & GENHD_FL_UP) { @@ -1188,6 +1194,8 @@ static void __nvme_scan_namespaces(struct nvme_ctrl *ctrl, unsigned nn) struct nvme_ns *ns, *next; unsigned i; + lockdep_assert_held(&ctrl->namespaces_mutex); + for (i = 1; i <= nn; i++) nvme_validate_ns(ctrl, i); @@ -1205,6 +1213,7 @@ void nvme_scan_namespaces(struct nvme_ctrl *ctrl) if (nvme_identify_ctrl(ctrl, &id)) return; + mutex_lock(&ctrl->namespaces_mutex); nn = le32_to_cpu(id->nn); if (ctrl->vs >= NVME_VS(1, 1) && !(ctrl->quirks & NVME_QUIRK_IDENTIFY_CNS)) { @@ -1214,6 +1223,7 @@ void nvme_scan_namespaces(struct nvme_ctrl *ctrl) __nvme_scan_namespaces(ctrl, le32_to_cpup(&id->nn)); done: list_sort(NULL, &ctrl->namespaces, ns_cmp); + mutex_unlock(&ctrl->namespaces_mutex); kfree(id); } @@ -1221,8 +1231,10 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl) { struct nvme_ns *ns, *next; + mutex_lock(&ctrl->namespaces_mutex); list_for_each_entry_safe(ns, next, &ctrl->namespaces, list) nvme_ns_remove(ns); + mutex_unlock(&ctrl->namespaces_mutex); } static DEFINE_IDA(nvme_instance_ida); @@ -1290,6 +1302,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, int ret; INIT_LIST_HEAD(&ctrl->namespaces); + mutex_init(&ctrl->namespaces_mutex); kref_init(&ctrl->kref); ctrl->dev = dev; ctrl->ops = ops; @@ -1332,6 +1345,7 @@ void nvme_freeze_queues(struct nvme_ctrl *ctrl) { struct nvme_ns *ns; + mutex_lock(&ctrl->namespaces_mutex); list_for_each_entry(ns, &ctrl->namespaces, list) { blk_mq_freeze_queue_start(ns->queue); @@ -1342,18 +1356,21 @@ void nvme_freeze_queues(struct nvme_ctrl *ctrl) blk_mq_cancel_requeue_work(ns->queue); blk_mq_stop_hw_queues(ns->queue); } + mutex_unlock(&ctrl->namespaces_mutex); } void nvme_unfreeze_queues(struct nvme_ctrl *ctrl) { struct nvme_ns *ns; + mutex_lock(&ctrl->namespaces_mutex); list_for_each_entry(ns, &ctrl->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); } + mutex_unlock(&ctrl->namespaces_mutex); } int __init nvme_core_init(void) diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 0da67474ce6d..44375923bd6a 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -69,6 +69,7 @@ struct nvme_ctrl { int instance; struct blk_mq_tag_set *tagset; struct list_head namespaces; + struct mutex namespaces_mutex; struct device *device; /* char device */ struct list_head node; -- cgit