[PATCH] nvme: apple: don't unbind the driver on reset failure
Fan Wu
fanwu01 at zju.edu.cn
Tue Sep 22 19:22:46 PDT 2026
When apple_nvme_reset_work() fails it queues anv->remove_work, whose
callback apple_nvme_remove_dead_ctrl_work() unbinds the driver through
device_release_driver(). The work is embedded in the devm-allocated
apple_nvme, but apple_nvme_remove() only flushes ctrl.reset_work, so an
external unbind while the work is pending frees anv through
devres_release_all() before the callback runs, and it operates on freed
memory.
Fix this by not unbinding from a work item at all, mirroring commit
c7c16c5b1967 ("nvme-pci: don't unbind the driver on reset failure"):
on reset failure, disable the controller, mark the namespaces dead and
leave the controller in the dead state instead of queueing the deferred
unbind. The device stays bound and can be recovered by unbinding and
rebinding the driver. apple_nvme_disable() is now passed shutdown=true,
as in the final disable in apple_nvme_remove(), so entered requests are
completed.
Draining the work from apple_nvme_remove() is not an option: that path
holds the device lock the callback blocks on, so waiting would deadlock.
This issue was found by an in-house static analysis tool.
Fixes: 5bd2927aceba ("nvme-apple: Add initial Apple SoC NVMe driver")
Cc: stable at vger.kernel.org
Co-developed-by: Song Li <songl at zju.edu.cn>
Signed-off-by: Song Li <songl at zju.edu.cn>
Signed-off-by: Fan Wu <fanwu01 at zju.edu.cn>
---
drivers/nvme/host/apple.c | 2 insertions(+), 15 deletions(-)
--- a/drivers/nvme/host/apple.c
+++ b/drivers/nvme/host/apple.c
@@ -193,7 +193,6 @@
mempool_t *iod_mempool;
struct nvme_ctrl ctrl;
- struct work_struct remove_work;
struct apple_nvme_queue adminq;
struct apple_nvme_queue ioq;
@@ -1224,20 +1223,9 @@
out:
dev_warn(anv->ctrl.device, "Reset failure status: %d\n", ret);
nvme_change_ctrl_state(&anv->ctrl, NVME_CTRL_DELETING);
- nvme_get_ctrl(&anv->ctrl);
- apple_nvme_disable(anv, false);
+ apple_nvme_disable(anv, true);
nvme_mark_namespaces_dead(&anv->ctrl);
- if (!queue_work(nvme_wq, &anv->remove_work))
- nvme_put_ctrl(&anv->ctrl);
-}
-
-static void apple_nvme_remove_dead_ctrl_work(struct work_struct *work)
-{
- struct apple_nvme *anv =
- container_of(work, struct apple_nvme, remove_work);
-
- nvme_put_ctrl(&anv->ctrl);
- device_release_driver(anv->dev);
+ nvme_change_ctrl_state(&anv->ctrl, NVME_CTRL_DEAD);
}
static int apple_nvme_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val)
@@ -1531,7 +1519,6 @@
}
INIT_WORK(&anv->ctrl.reset_work, apple_nvme_reset_work);
- INIT_WORK(&anv->remove_work, apple_nvme_remove_dead_ctrl_work);
spin_lock_init(&anv->lock);
ret = apple_nvme_queue_alloc(anv, &anv->adminq);
--
2.39.5
More information about the linux-arm-kernel
mailing list