[PATCH v2 1/2] iommu/arm-smmu-v3: Manage teardown with devm
Shameer Kolothum
skolothumtho at nvidia.com
Thu Jun 11 01:42:04 PDT 2026
arm_smmu_device_remove() manually frees the IOPF queue, destroys the
vmid_map and disables the device, while the IRQs and queues are devm
managed. devm unwinds only after remove() returns, so the cleanup runs
in the wrong order. The IOPF queue is freed before the event-queue IRQ
whose handler uses it.
Manage all of it with devm so the unwind order is correct. Free the IOPF
queue and vmid_map via devm actions, and disable the device from one
registered after arm_smmu_device_reset().
This is also a prerequisite for fixing a Tegra241 CMDQV CMD_SYNC
use-after-free in the subsequent patch.
Suggested-by: Jason Gunthorpe <jgg at ziepe.ca>
Signed-off-by: Shameer Kolothum <skolothumtho at nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 50 ++++++++++++++-------
1 file changed, 34 insertions(+), 16 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index e8d7dbe495f0..00261e77e7bc 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4464,6 +4464,16 @@ int arm_smmu_cmdq_init(struct arm_smmu_device *smmu,
return 0;
}
+static void arm_smmu_free_iopf_action(void *data)
+{
+ iopf_queue_free(data);
+}
+
+static void arm_smmu_destroy_vmid_map(void *data)
+{
+ ida_destroy(data);
+}
+
static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
{
int ret;
@@ -4491,6 +4501,11 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
smmu->evtq.iopf = iopf_queue_alloc(dev_name(smmu->dev));
if (!smmu->evtq.iopf)
return -ENOMEM;
+ ret = devm_add_action_or_reset(smmu->dev,
+ arm_smmu_free_iopf_action,
+ smmu->evtq.iopf);
+ if (ret)
+ return ret;
}
/* priq */
@@ -4569,7 +4584,8 @@ static int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
ida_init(&smmu->vmid_map);
- return 0;
+ return devm_add_action_or_reset(smmu->dev, arm_smmu_destroy_vmid_map,
+ &smmu->vmid_map);
}
static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
@@ -4782,6 +4798,11 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
return ret;
}
+static void arm_smmu_disable_action(void *data)
+{
+ arm_smmu_device_disable(data);
+}
+
static void arm_smmu_write_strtab(struct arm_smmu_device *smmu)
{
struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
@@ -5540,7 +5561,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
/* Initialise in-memory data structures */
ret = arm_smmu_init_structures(smmu);
if (ret)
- goto err_free_iopf;
+ return ret;
/* Record our private device structure */
platform_set_drvdata(pdev, smmu);
@@ -5550,30 +5571,30 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
/* Reset the device */
ret = arm_smmu_device_reset(smmu);
+ if (ret) {
+ arm_smmu_device_disable(smmu);
+ return ret;
+ }
+
+ /* Register last so it unwinds first, while the CMDQ is still up. */
+ ret = devm_add_action_or_reset(smmu->dev, arm_smmu_disable_action, smmu);
if (ret)
- goto err_disable;
+ return ret;
/* And we're up. Go go go! */
ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
"smmu3.%pa", &ioaddr);
if (ret)
- goto err_disable;
+ return ret;
ret = iommu_device_register(&smmu->iommu, &arm_smmu_ops, dev);
if (ret) {
dev_err(dev, "Failed to register iommu\n");
- goto err_free_sysfs;
+ iommu_device_sysfs_remove(&smmu->iommu);
+ return ret;
}
return 0;
-
-err_free_sysfs:
- iommu_device_sysfs_remove(&smmu->iommu);
-err_disable:
- arm_smmu_device_disable(smmu);
-err_free_iopf:
- iopf_queue_free(smmu->evtq.iopf);
- return ret;
}
static void arm_smmu_device_remove(struct platform_device *pdev)
@@ -5582,9 +5603,6 @@ static void arm_smmu_device_remove(struct platform_device *pdev)
iommu_device_unregister(&smmu->iommu);
iommu_device_sysfs_remove(&smmu->iommu);
- arm_smmu_device_disable(smmu);
- iopf_queue_free(smmu->evtq.iopf);
- ida_destroy(&smmu->vmid_map);
}
static void arm_smmu_device_shutdown(struct platform_device *pdev)
--
2.43.0
More information about the linux-arm-kernel
mailing list