Re: [PATCH v5] driver core: enforce device_lock for driver_match_device()

From: Robin Murphy

Date: Wed Jan 21 2026 - 08:31:38 EST


On 2026-01-21 11:02 am, Danilo Krummrich wrote:
On Wed Jan 21, 2026 at 11:40 AM CET, Danilo Krummrich wrote:
So, the problem is that in the callstack of the arm-smmu driver's (a platform
driver) probe() function, the QCOM specific code (through arm_smmu_impl_init())
registers another platform driver. Since we are still in probe() of arm-smmu the
call to platform_driver_register() happens with the device lock of the arm-smmu
platform device held.

platform_driver_register() eventually results in driver_attach() which iterates
over all the devices of a bus. Since the device we are probing and the driver we
are registering are for the same bus (i.e. the platform bus) it can now happen
that by chance that we also match the exact same device that is currently probed
again. And since we take the device lock for matching now, we actually take the
same lock twice.

Now, we could avoid this by not matching bound devices, but we check this
through dev->driver while holding the device lock, so that doesn't help.

But on the other hand, I don't see any reason why a driver would call
platform_driver_register() from probe() in the first place. I think drivers
should not do that and instead just register the driver through a normal
initcall.

(If, however, it turns out that registering drivers from probe() is something we
really need for some reason, it is probably best to drop the patch and don't
make any guarantees about whether match() is called with the device lock held or
not.

Consequently, driver_override must be protected with a separate lock (which
would be the cleaner solution in any case).)

I assume that this should resolve the problem (unless there are more drivers
that register drivers in probe()):

diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
index 573085349df3..9bb793efc35f 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
@@ -774,10 +774,6 @@ struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu)
{
const struct device_node *np = smmu->dev->of_node;
const struct of_device_id *match;
- static u8 tbu_registered;
-
- if (!tbu_registered++)
- platform_driver_register(&qcom_smmu_tbu_driver);

#ifdef CONFIG_ACPI
if (np == NULL) {
@@ -802,3 +798,5 @@ struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu)

return smmu;
}
+
+builtin_platform_driver(qcom_smmu_tbu_driver);

@qcom maintainers: I'm aware of commit 0b4eeee2876f ("iommu/arm-smmu-qcom:
Register the TBU driver in qcom_smmu_impl_init"), but I think the above patch
should work fine as it is still *not only* registered when
CONFIG_ARM_SMMU_QCOM_DEBUG?

In principle there should be nothing wrong with registering the driver unconditionally - that existing tbu_registered logic looks racy in the face of async_probe anyway - however I don't think the *_platform_driver macros will work here, as this all gets combined into arm_smmu.ko wherein ending up with multiple module_init declarations breaks the build.

(Please do double-check all the build permutations of ARM_SMMU, ARM_SMMU_QCOM and ARM_SMMU_QCOM_DEBUG)

Thanks,
Robin.