summaryrefslogtreecommitdiff
path: root/drivers/nvme
diff options
context:
space:
mode:
authorSagi Grimberg <sagi@grimberg.me>2024-05-21 23:20:28 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-06-12 12:03:54 +0300
commitca7ad37b604a66d42aa2c8905c87d336e3754df5 (patch)
tree29abb397a93d77fec6788347be11c82871079c9c /drivers/nvme
parent8e1ba9df9a35e8dc64f657a64e523c79ba01e464 (diff)
downloadlinux-ca7ad37b604a66d42aa2c8905c87d336e3754df5.tar.xz
nvmet: fix ns enable/disable possible hang
[ Upstream commit f97914e35fd98b2b18fb8a092e0a0799f73afdfe ] When disabling an nvmet namespace, there is a period where the subsys->lock is released, as the ns disable waits for backend IO to complete, and the ns percpu ref to be properly killed. The original intent was to avoid taking the subsystem lock for a prolong period as other processes may need to acquire it (for example new incoming connections). However, it opens up a window where another process may come in and enable the ns, (re)intiailizing the ns percpu_ref, causing the disable sequence to hang. Solve this by taking the global nvmet_config_sem over the entire configfs enable/disable sequence. Fixes: a07b4970f464 ("nvmet: add a generic NVMe target") Signed-off-by: Sagi Grimberg <sagi@grimberg.me> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com> Signed-off-by: Keith Busch <kbusch@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'drivers/nvme')
-rw-r--r--drivers/nvme/target/configfs.c8
1 files changed, 8 insertions, 0 deletions
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 40c1c3db5d7c..2e87718aa194 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -537,10 +537,18 @@ static ssize_t nvmet_ns_enable_store(struct config_item *item,
if (strtobool(page, &enable))
return -EINVAL;
+ /*
+ * take a global nvmet_config_sem because the disable routine has a
+ * window where it releases the subsys-lock, giving a chance to
+ * a parallel enable to concurrently execute causing the disable to
+ * have a misaccounting of the ns percpu_ref.
+ */
+ down_write(&nvmet_config_sem);
if (enable)
ret = nvmet_ns_enable(ns);
else
nvmet_ns_disable(ns);
+ up_write(&nvmet_config_sem);
return ret ? ret : count;
}