summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolin Chen <nicolinc@nvidia.com>2026-07-14 13:55:06 -0700
committerWill Deacon <will@kernel.org>2026-07-28 09:51:48 +0000
commitf40f3144477314b489e4bc209c06cb51679fe82b (patch)
treedcc743c51e3263ead4f960a1511b7905cc833b26
parentfb292bfc9be936dade7eef7ec5762de1201983d8 (diff)
downloadlinux-f40f3144477314b489e4bc209c06cb51679fe82b.tar.gz
linux-f40f3144477314b489e4bc209c06cb51679fe82b.zip
iommu/tegra241-cmdqv: Fix VINTF0 leak on the init-failure path
tegra241_cmdqv_init_structures() allocates VINTF0 with kzalloc_obj(), inits it, and preallocates its logical VCMDQs. Two of its error paths leak. When tegra241_cmdqv_init_vintf() fails it returns before VINTF0 reaches the cmdqv->vintfs[] array, so the devres unwind on probe failure cannot reach it; free it directly there. A later VCMDQ preallocation failure instead leaves VINTF0 published, and so this time the unwind does reach tegra241_cmdqv_remove_vintf(), which then frees it from vintf->hyp_own. But tegra241_vintf_hw_init() sets that flag only afterward, from a HW read-back, so the still-uninited VINTF0 reads as guest-owned and leaks, with mutex_destroy() and ida_destroy() run on fields it never set up. Decide ownership from vintf->idx instead, the index assigned when its id is allocated: idx 0 is the kernel-owned VINTF0, while idx >= 1 marks a guest VINTF. So the in-kernel free decision in tegra241_cmdqv_remove_vintf() and tegra241_vintf_free_lvcmdq() now keys on idx too, and hyp_own stays a pure HW-readback state. Fixes: 918eb5c856f6 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Will Deacon <will@kernel.org>
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
index 269a06fcca27..8b97db10ecde 100644
--- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
+++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
@@ -716,7 +716,7 @@ static void tegra241_vintf_free_lvcmdq(struct tegra241_vintf *vintf, u16 lidx)
dev_dbg(vintf->cmdqv->dev,
"%sdeallocated\n", lvcmdq_error_header(vcmdq, header, 64));
/* Guest-owned VCMDQ is free-ed with hw_queue by iommufd core */
- if (vcmdq->vintf->hyp_own)
+ if (!vcmdq->vintf->idx)
kfree(vcmdq);
}
@@ -814,7 +814,7 @@ static void tegra241_cmdqv_remove_vintf(struct tegra241_cmdqv *cmdqv, u16 idx)
dev_dbg(cmdqv->dev, "VINTF%u: deallocated\n", vintf->idx);
tegra241_cmdqv_deinit_vintf(cmdqv, idx);
- if (!vintf->hyp_own) {
+ if (vintf->idx) {
mutex_destroy(&vintf->lvcmdq_mutex);
ida_destroy(&vintf->sids);
/* Guest-owned VINTF is free-ed with viommu by iommufd core */
@@ -931,6 +931,12 @@ static int tegra241_cmdqv_init_structures(struct arm_smmu_device *smmu)
ret = tegra241_cmdqv_init_vintf(cmdqv, 0, vintf);
if (ret) {
dev_err(cmdqv->dev, "failed to init vintf0: %d\n", ret);
+ /*
+ * tegra241_cmdqv_init_vintf() failed to publish the vintf0 to
+ * cmdqv->vintfs[], so the probe unwind path that goes through
+ * cmdqv->vintfs[] would miss it. Free it here.
+ */
+ kfree(vintf);
return ret;
}