diff options
author | Sagi Grimberg <sagi@grimberg.me> | 2024-05-21 23:20:28 +0300 |
---|---|---|
committer | Keith Busch <kbusch@kernel.org> | 2024-05-23 23:44:42 +0300 |
commit | f97914e35fd98b2b18fb8a092e0a0799f73afdfe (patch) | |
tree | 3f859afa9c933567c144e4ebb4ae90e406af687f /drivers/nvme/target/configfs.c | |
parent | a2e4c5f5f68dbd206f132bc709b98dea64afc3b8 (diff) | |
download | linux-f97914e35fd98b2b18fb8a092e0a0799f73afdfe.tar.xz |
nvmet: fix ns enable/disable possible hang
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>
Diffstat (limited to 'drivers/nvme/target/configfs.c')
-rw-r--r-- | drivers/nvme/target/configfs.c | 8 |
1 files changed, 8 insertions, 0 deletions
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index 7c43a0ad6877..bd87dfd173a4 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -676,10 +676,18 @@ static ssize_t nvmet_ns_enable_store(struct config_item *item, if (kstrtobool(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; } |