[PATCH v2 1/3] iommu/msm: track a context master per device and IOMMU
Dmitry Baryshkov
dmitry.baryshkov at oss.qualcomm.com
Thu Jul 30 09:04:54 PDT 2026
insert_iommu_master() decided whether to allocate a struct
msm_iommu_ctx_dev by testing whether the IOMMU's ctx_list was empty,
caching the result in dev_iommu_priv, then unconditionally dereferenced
the master:
master = dev_iommu_priv_get(dev);
if (list_empty(&(*iommu)->ctx_list)) {
master = kzalloc_obj(...);
...
}
for (sid = 0; sid < master->num_mids; sid++)
Neither key is right. A master describes one device on one IOMMU
instance: the attach and lookup paths find a device on an IOMMU by
walking that IOMMU's ctx_list for a master whose of_node matches. The
ctx_list emptiness test is per-IOMMU, and the dev_iommu_priv cache is
per-device, so the two disagree as soon as one device spans several
IOMMUs. On apq8064 the GPU uses &gfx3d and &gfx3d1 and the display
controller uses &mdp_port0 and &mdp_port1, each instance carrying the
same stream IDs.
The per-device cache also turns into a NULL dereference. When the first
of a device's IOMMUs registers, iommu_device_register() walks the bus and
drives ->of_xlate(), which links a master onto that IOMMU's ctx_list. The
device cannot finish probing because its second IOMMU is not registered
yet (-EPROBE_DEFER); that tears down dev->iommu and drops the cached
master, but the master stays linked on the first IOMMU's ctx_list. Once
the second IOMMU registers, its bus walk replays of_iommu_configure() for
all of the device's entries, so ->of_xlate() runs again for the first
IOMMU: dev_iommu_priv is NULL while ctx_list is not empty, allocation is
skipped and master->num_mids dereferences NULL:
qcom_iommu_of_xlate from of_iommu_xlate
of_iommu_xlate from of_iommu_configure
of_iommu_configure from platform_dma_configure
platform_dma_configure from __iommu_probe_device
__iommu_probe_device from probe_iommu_group
probe_iommu_group from bus_for_each_dev
bus_for_each_dev from iommu_device_register
iommu_device_register from msm_iommu_probe
The faulty guard has been there since the driver gained generic master
bindings, but it stayed dormant for years because a device's ->of_xlate()
only ran once all of its IOMMUs were present. It became reachable when
iommu_fwspec_init() started deferring per instance on the ->ready flag,
which is what lets a master be stranded on the first IOMMU and then have
->of_xlate() replayed with dev_iommu_priv already reset. Hence the two
Fixes below: the first introduced the guard, the second made it fatal.
Look the master up in the target IOMMU's ctx_list instead, allocating one
only when that IOMMU has none for the device yet. Each instance now keeps
its own master and gets programmed with the full set of stream IDs.
That replay also means a master outlives the attempt that built it, so
the stream IDs it already holds have to be dropped before the next
attempt collects them again, or every ID is seen twice and the whole set
is reported as repeated. dev_iommu_priv is now free, and it is allocated
and cleared together with dev->iommu, so use it to recognise the first
->of_xlate() of an attempt and reset the device's masters there.
Fixes: f78ebca8ff3d ("iommu/msm: Add support for generic master bindings")
Fixes: da33e87bd2bf ("iommu: Handle yet another race around registration")
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov at oss.qualcomm.com>
Assisted-by: Claude:claude-opus-5
---
drivers/iommu/msm_iommu.c | 49 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 39 insertions(+), 10 deletions(-)
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 0ad5ff431d5b..109688d69e50 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -597,24 +597,51 @@ static void print_ctx_regs(void __iomem *base, int ctx)
GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
}
+/*
+ * ->of_xlate() is replayed from scratch every time the client device's probe
+ * is retried, so the stream IDs gathered by the previous attempt have to be
+ * dropped before the new set is collected. dev_iommu_priv lives in dev->iommu,
+ * which the IOMMU core frees whenever an attempt fails, so a NULL value marks
+ * the first ->of_xlate() call of a pass. The device itself is stored as the
+ * marker, its value is never used.
+ */
+static void start_of_xlate_pass(struct device *dev)
+{
+ struct msm_iommu_dev *iommu;
+ struct msm_iommu_ctx_dev *master;
+
+ if (dev_iommu_priv_get(dev))
+ return;
+
+ list_for_each_entry(iommu, &qcom_iommu_devices, dev_node)
+ list_for_each_entry(master, &iommu->ctx_list, list)
+ if (master->of_node == dev->of_node)
+ master->num_mids = 0;
+
+ dev_iommu_priv_set(dev, dev);
+}
+
static int insert_iommu_master(struct device *dev,
struct msm_iommu_dev **iommu,
const struct of_phandle_args *spec)
{
- struct msm_iommu_ctx_dev *master = dev_iommu_priv_get(dev);
+ struct msm_iommu_ctx_dev *master;
int sid;
- if (list_empty(&(*iommu)->ctx_list)) {
- master = kzalloc_obj(*master, GFP_ATOMIC);
- if (!master) {
- dev_err(dev, "Failed to allocate iommu_master\n");
- return -ENOMEM;
- }
- master->of_node = dev->of_node;
- list_add(&master->list, &(*iommu)->ctx_list);
- dev_iommu_priv_set(dev, master);
+ /* A master describes one device on one IOMMU instance. */
+ list_for_each_entry(master, &(*iommu)->ctx_list, list)
+ if (master->of_node == dev->of_node)
+ goto add_sid;
+
+ master = kzalloc_obj(*master, GFP_ATOMIC);
+ if (!master) {
+ dev_err(dev, "Failed to allocate iommu_master\n");
+ return -ENOMEM;
}
+ master->of_node = dev->of_node;
+ list_add(&master->list, &(*iommu)->ctx_list);
+add_sid:
for (sid = 0; sid < master->num_mids; sid++)
if (master->mids[sid] == spec->args[0]) {
dev_warn(dev, "Stream ID 0x%x repeated; ignoring\n",
@@ -646,6 +673,8 @@ static int qcom_iommu_of_xlate(struct device *dev,
goto fail;
}
+ start_of_xlate_pass(dev);
+
ret = insert_iommu_master(dev, &iommu, spec);
fail:
spin_unlock_irqrestore(&msm_iommu_lock, flags);
--
2.47.3
More information about the Linux-rockchip
mailing list