iommu: Fix the longstanding probe issues
Subsystems (9)
Robin Murphy
reviewercover iommu: Fix the longstanding probe issues
Hi all,
Finally, it's that thing I've been mentioning on and off for the last 5 years or so! After far too much thinking about "clean" ways to refactor everything towards an eventual goal of working properly, I finally gave up and decided to see how bad it would be to just get it working, then worry about cleaning up afterwards. Frankly I still can't quiet believe how small the patch turned out to be...
Patch #1 is a fix for what I think is the only "expected" race in the core API design - we knew we had that window where a default domain is NULL, but the potential consequences weren't clear until Charan figured it out the hard way. That should be good to go already, and it's not strictly a dependency for patch #2, but it seemed worth posting them together on the common theme.
Patch #2 is then the first big bite out of the problem I feel compelled to fix because it's my name on d7b0558230e4 ("iommu/of: Prepare for deferred IOMMU configuration") which, in hindsight, started it. I hope this will be the only patch which has to touch absolutely everything all at once. I've not got very far with the follow-up patches yet, but I do foresee being able to tackle the driver core, firmware subsystems and bus drivers more independently. I don't expect anyone to love it, but I hope you can at least see where I'm going and give a bit of leeway for now :)
I also do rather expect that WARN_ON to be hit, but the intent is that I'd like to get the attention of any of_dma_configure() abusers so they can help me figure out what the heck their code *should* be doing...
Thanks,
Robin.
Robin Murphy (2):
iommu: Handle race with default domain setup
iommu: Get DT/ACPI parsing into the proper probe path
drivers/acpi/arm64/dma.c | 5 ++++ drivers/acpi/scan.c | 10 +++---- drivers/amba/bus.c | 2 +- drivers/base/platform.c | 2 +- drivers/bus/fsl-mc/fsl-mc-bus.c | 2 +- drivers/cdx/cdx.c | 2 +- drivers/iommu/iommu.c | 48 +++++++++++++++++++++++++-------- drivers/iommu/of_iommu.c | 10 ++++++- drivers/of/device.c | 7 ++++- drivers/pci/pci-driver.c | 2 +- 10 files changed, 65 insertions(+), 25 deletions(-)
Signature · 1 line
2.39.2.101.g768bb238c484.dirty
Robin Murphy
reviewerPATCH 1/2 iommu: Handle race with default domain setup
It turns out that deferred default domain creation leaves a subtle race window during iommu_device_register() wherein a client driver may asynchronously probe in parallel and get as far as performing DMA API operations with dma-direct, only to be switched to iommu-dma underfoot once the default domain attachment finally happens, with obviously disastrous consequences. Even the wonky of_iommu_configure() path is at risk, since iommu_fwspec_init() will no longer defer client probe as the instance ops are (necessarily) already registered, and the "replay" iommu_probe_device() call can see dev->iommu_group already set and so think there's nothing to do either.
Fortunately we already have the right tool in the right place in the form of iommu_device_use_default_domain(), which just needs to ensure that said default domain is actually ready to *be* used. Deferring the client probe shouldn't have too much impact, given that this only happens while the IOMMU driver is probing, and thus due to kick the deferred probe list again once it finishes.
---
Note this fixes tag is rather nuanced - historically there was a more general issue before deac0b3bed26 ("iommu: Split off default domain allocation from group assignment") set the basis for the current conditions; 1ea2a07a532b ("iommu: Add DMA ownership management interfaces") is then the point at which it becomes logical to fix the current race this way; however only from 98ac73f99bc4 can we rely on all drivers supporting default domains and so avoid false negatives, thus even though this might apply to older kernels without conflict it would not be functionally correct. LTS-wise, prior to 6.6 and commit f188056352bc ("iommu: Avoid locking/unlocking for iommu_probe_device()") the impact of this race is merely the historical issue again, but since deac0b3bed26 that would raise a visible warning if it did lead to a default domain mismatch, which nobody has ever reported seeing. Thus we should only need a backport for 6.6, which is probably just this with an additional IS_ENABLED(CONFIG_IOMMU_DMA) check. Phew!
--- drivers/iommu/iommu.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/iommu/iommu.c +5 -0
@@ -3097,6 +3097,11 @@ int iommu_device_use_default_domain(struct device *dev)return 0 ;mutex_lock (&group->mutex); +/* We may race against bus_iommu_probe() finalising groups here */ +if (!group->default_domain) { + ret = -EPROBE_DEFER ; + goto unlock_out; + }if (group->owner_cnt) {if (group->domain != group->default_domain || group->owner || !xa_empty (&group->pasid_array)) {
Signature · 1 line
2.39.2.101.g768bb238c484.dirty
Re: [PATCH 1/2] iommu: Handle race with default domain setup
Thanks a lot for posting these patches, Robin.
Quoted 8 lines · Robin Murphy
drivers/iommu/iommu.c | 5 +++++ 1 file changed, 5 insertions(+)drivers/iommu/iommu.c +0
-0
drivers/iommu/iommu.c int iommu_device_use_default_domain(struct device *dev) · line 3104
Quoted 8 lines
drivers/iommu/iommu.c +5
-0@@ -3097,6 +3097,11 @@ int iommu_device_use_default_domain(struct device *dev)return 0 ;mutex_lock (&group->mutex); +/* We may race against bus_iommu_probe() finalising groups here */ +if (!group->default_domain) { + ret = -EPROBE_DEFER ; + goto unlock_out; + }
We just hit the issue again even after picking up this patch, though very hard to reproduce, on 6.6 LTS.
After code inspection, it seems the issue is that - default domain is setup in the bus_iommu_probe() before hitting of this replay.
A:async client probe in platform_dma_configure(), B:bus_iommu_probe() :-
1) A: sets up iommu_fwspec under iommu_probe_device_lock.
2) B: Sets the dev->iommu_group under iommu_probe_device_lock. Domain setup is deferred.
3) A: Returns with out allocating the default domain, as dev->iommu_group is set, whose checks are also made under the same 'iommu_probe_device_lock'. __This miss setting of the valid dev->dma_ops__.
4) B: Sets up the group->default_domain under group->mutex.
5) A: iommu_device_use_default_domain(): Relies on this group->default_domain, under the same mutex, to decide if need to go for replay, which is skipped. This is skipping the setting up of valid dma_ops and that's an issue.
But I don't think that the same issue exists on 6.13 because of your patch, b67483b3c44e ("iommu/dma: Centralise iommu_setup_dma_ops()"). bus_iommu_probe():
list_for_each_entry_safe(group, next, &group_list, entry) {
mutex_lock(&group->mutex);
for_each_group_device(group, gdev)
iommu_setup_dma_ops(gdev->dev);
mutex_unlock(&group->mutex);
}This makes the step4 above force to use the valid dma_iommu api, thus I see no issue when there is no probe deferral.
So, I think we are good with this patch on 6.13.
Now coming back to 6.6 LTS, any ideas you have here, please?
drivers/iommu/iommu.c int iommu_device_use_default_domain(struct device *dev) · line 3107
Quoted 3 lines
drivers/iommu/iommu.c +0
-0if (group->owner_cnt) {if (group->domain != group->default_domain || group->owner || !xa_empty (&group->pasid_array)) {
Thanks,
Charan
Robin Murphy
reviewerRe: [PATCH 1/2] iommu: Handle race with default domain setup
Quoted 54 lines · Charan Teja Kalla
Thanks a lot for posting these patches, Robin.
Quoted 8 lines · Robin Murphy
drivers/iommu/iommu.c | 5 +++++ 1 file changed, 5 insertions(+)drivers/iommu/iommu.c +0
-0drivers/iommu/iommu.c int iommu_device_use_default_domain(struct device *dev) · line 3104
Quoted 8 lines
drivers/iommu/iommu.c +5
-0@@ -3097,6 +3097,11 @@ int iommu_device_use_default_domain(struct device *dev)return 0 ;mutex_lock (&group->mutex); +/* We may race against bus_iommu_probe() finalising groups here */ +if (!group->default_domain) { + ret = -EPROBE_DEFER ; + goto unlock_out; + }We just hit the issue again even after picking up this patch, though very hard to reproduce, on 6.6 LTS.
After code inspection, it seems the issue is that - default domain is setup in the
bus_iommu_probe()before hitting of this replay.A:async client probe in
platform_dma_configure(), B:bus_iommu_probe() :-1) A: sets up
iommu_fwspecunderiommu_probe_device_lock.2) B: Sets the
dev->iommu_groupunderiommu_probe_device_lock. Domain setup is deferred.3) A: Returns with out allocating the default domain, as
dev->iommu_groupis set, whose checks are also made under the same 'iommu_probe_device_lock'.__Thismiss setting of the validdev->dma_ops__.4) B: Sets up the
group->default_domainundergroup->mutex.5) A:
iommu_device_use_default_domain(): Relies on thisgroup->default_domain, under the same mutex, to decide if need to go for replay, which is skipped. This is skipping the setting up of validdma_opsand that's an issue.But I don't think that the same issue exists on 6.13 because of your patch,
b67483b3c44e("iommu/dma: Centraliseiommu_setup_dma_ops()").bus_iommu_probe():list_for_each_entry_safe(group, next, &group_list, entry) { mutex_lock(&group->mutex); for_each_group_device(group, gdev) iommu_setup_dma_ops(gdev->dev); mutex_unlock(&group->mutex); }
Quoted 6 lines
This makes the step4 above force to use the valid
dma_iommuapi, thus I see no issue when there is no probe deferral.So, I think we are good with this patch on 6.13.
Now coming back to 6.6 LTS, any ideas you have here, please?
Thanks for the analysis once again! I've not looked closely at 6.6 yet, but if we're all happy this looks like the right fix for mainline then I'll start having a look at the backport as soon as I can (so much for hoping it would be simple...)
Cheers,
Robin.
Quoted 8 lines
drivers/iommu/iommu.c int iommu_device_use_default_domain(struct device *dev) · line 3107
Quoted 4 lines
drivers/iommu/iommu.c +0
-0if (group->owner_cnt) {if (group->domain != group->default_domain || group->owner || !xa_empty (&group->pasid_array)) {Thanks,
Charan
Re: [PATCH 1/2] iommu: Handle race with default domain setup
In reply to Robin MurphyQuoted 18 lines · Robin Murphy
It turns out that deferred default domain creation leaves a subtle race window during
iommu_device_register()wherein a client driver may asynchronously probe in parallel and get as far as performing DMA API operations with dma-direct, only to be switched to iommu-dma underfoot once the default domain attachment finally happens, with obviously disastrous consequences. Even the wonkyof_iommu_configure()path is at risk, sinceiommu_fwspec_init()will no longer defer client probe as the instance ops are (necessarily) already registered, and the "replay"iommu_probe_device()call can seedev->iommu_groupalready set and so think there's nothing to do either.Fortunately we already have the right tool in the right place in the form of
iommu_device_use_default_domain(), which just needs to ensure that said default domain is actually ready to *be* used. Deferring the client probe shouldn't have too much impact, given that this only happens while the IOMMU driver is probing, and thus due to kick the deferred probe list again once it finishes.
Quoted 6 lines
Reported-by Charan Teja Kalla <[email protected]>Fixes 98ac73f99bc4 ("iommu: Require a default_domain for all iommu drivers")Signed-off-by Robin Murphy <[email protected]>--- drivers/iommu/iommu.c | 5 +++++ 1 file changed, 5 insertions(+)
Jason
Robin Murphy
reviewerPATCH 2/2 iommu: Get DT/ACPI parsing into the proper probe path
In reply to Robin MurphyIn hindsight, there were some crucial subtleties overlooked when moving {of,acpi}_dma_configure() to driver probe time to allow waiting for IOMMU drivers with -EPROBE_DEFER, and these have become an ever-increasing source of problems. The IOMMU API has some fundamental assumptions that iommu_probe_device() is called for every device added to the system, in the order in which they are added. Calling it in a random order or not at all dependent on driver binding leads to malformed groups, a potential lack of isolation for devices with no driver, and all manner of unexpected concurrency and race conditions. We've attempted to mitigate the latter with point-fix bodges like iommu_probe_device_lock, but it's a losing battle and the time has come to bite the bullet and address the true source of the problem instead.
The crux of the matter is that the firmware parsing actually serves two distinct purposes; one is identifying the IOMMU instance associated with a device so we can check its availability, the second is actually telling that instance about the relevant firmware-provided data for the device. However the latter also depends on the former, and at the time there was no good place to defer and retry that separately from the availability check we also wanted for client driver probe.
Nowadays, though, we have a proper notion of multiple IOMMU instances in the core API itself, and each one gets a chance to probe its own devices upon registration, so we can finally make that work as intended for DT/IORT/VIOT platforms too. All we need is for iommu_probe_device() to be able to run the iommu_fwspec machinery currently buried deep in the wrong end of {of,acpi}_dma_configure(). Luckily it turns out to be surprisingly straightforward to bootstrap this transformation by pretty much just calling the same path twice. At client driver probe time, dev->driver is obviously set; conversely at device_add(), or a subsequent bus_iommu_probe(), any device waiting for an IOMMU really should *not* have a driver already, so we can use that as a condition to disambiguate the two cases, and avoid recursing back into the IOMMU core at the wrong times.
Obviously this isn't the nicest thing, but for now it gives us a functional baseline to then unpick the layers in between without many more awkward cross-subsystem patches. There are some minor side-effects like dma_range_map potentially being created earlier, and some debug prints being repeated, but these aren't significantly detrimental. Let's make things work first, then deal with making them nice.
With the basic flow finally in the right order again, the next step is probably turning the bus->dma_configure paths inside-out, since all we really need from bus code is its notion of which device and input ID(s) to parse the common firmware properties with...
--- drivers/acpi/arm64/dma.c | 5 ++++ drivers/acpi/scan.c | 10 +++----- drivers/amba/bus.c | 2 +- drivers/base/platform.c | 2 +- drivers/bus/fsl-mc/fsl-mc-bus.c | 2 +- drivers/cdx/cdx.c | 2 +- drivers/iommu/iommu.c | 43 ++++++++++++++++++++++++--------- drivers/iommu/of_iommu.c | 10 +++++++- drivers/of/device.c | 7 +++++- drivers/pci/pci-driver.c | 2 +- 10 files changed, 60 insertions(+), 25 deletions(-)
drivers/acpi/arm64/dma.c +5 -0
@@ -26,6 +26,11 @@ void acpi_arch_dma_setup(struct device *dev)else end = (1ULL <<32 ) -1 ; +if (dev->dma_range_map) { +dev_dbg (dev,"dma_range_map already set\n" ); +return ; + } + ret =acpi_dma_get_range (dev, &map);if (!ret && map) { end =dma_range_map_max (map);
drivers/acpi/scan.c +3 -7
@@ -1619,6 +1619,9 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) {int err; +if (device_iommu_mapped (dev)) +return 0 ; +/* Serialise to make dev->iommu stable under our potential fwspec */ mutex_lock (&iommu_probe_device_lock);/* If we already translated the fwspec there is nothing left to do */
@@ -1632,13 +1635,6 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) err =viot_iommu_configure (dev);mutex_unlock (&iommu_probe_device_lock); -/* -* If we have reason to believe the IOMMU driver missed the initial -* iommu_probe_device() call for dev, replay it to get things in order. -*/ -if (!err && dev->bus) - err =iommu_probe_device (dev); -return err; }
drivers/amba/bus.c +1 -1
@@ -364,7 +364,7 @@ static int amba_dma_configure(struct device *dev) ret =acpi_dma_configure (dev, attr); } -if (!ret && !drv->driver_managed_dma) { +if (dev->driver && !ret && !drv->driver_managed_dma) { ret =iommu_device_use_default_domain (dev);if (ret)arch_teardown_dma_ops (dev);
drivers/base/platform.c +1 -1
@@ -1451,7 +1451,7 @@ static int platform_dma_configure(struct device *dev) attr =acpi_get_dma_attr (to_acpi_device_node (fwnode)); ret =acpi_dma_configure (dev, attr); } -if (ret || drv->driver_managed_dma) +if (!dev->driver || ret || drv->driver_managed_dma)return ret; ret =iommu_device_use_default_domain (dev);
drivers/bus/fsl-mc/fsl-mc-bus.c +1 -1
@@ -153,7 +153,7 @@ static int fsl_mc_dma_configure(struct device *dev)else ret =acpi_dma_configure_id (dev,DEV_DMA_COHERENT , &input_id); -if (!ret && !mc_drv->driver_managed_dma) { +if (dev->driver && !ret && !mc_drv->driver_managed_dma) { ret =iommu_device_use_default_domain (dev);if (ret)arch_teardown_dma_ops (dev);
drivers/cdx/cdx.c +1 -1
@@ -360,7 +360,7 @@ static int cdx_dma_configure(struct device *dev)return ret; } -if (!ret && !cdx_drv->driver_managed_dma) { +if (dev->driver && !ret && !cdx_drv->driver_managed_dma) { ret =iommu_device_use_default_domain (dev);if (ret)arch_teardown_dma_ops (dev);
drivers/iommu/iommu.c +32 -11
58 lines not on this page · raw message
drivers/iommu/of_iommu.c +9 -1
@@ -121,6 +121,9 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,if (!master_np)return -ENODEV ; +if (device_iommu_mapped (dev)) +return 0 ; +/* Serialise to make dev->iommu stable under our potential fwspec */ mutex_lock (&iommu_probe_device_lock);if (dev_iommu_fwspec_get (dev)) {
@@ -151,7 +154,12 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,iommu_fwspec_free (dev);mutex_unlock (&iommu_probe_device_lock); -if (!err && dev->bus) +/* +* If we have reason to believe the IOMMU driver missed the initial +* iommu_probe_device() call for dev, try to fix it up. This should +* no longer happen unless of_dma_configure() is being misused. +*/ +if (!err && dev->driver) err =iommu_probe_device (dev);if (err && err != -EPROBE_DEFER )
drivers/of/device.c +6 -1
@@ -99,6 +99,11 @@ int of_dma_configure_id(struct device *dev, struct device_node *np,bool coherent, set_map = false;int ret; +if (dev->dma_range_map) { +dev_dbg (dev,"dma_range_map already set\n" ); + goto skip_map; + } +if (np == dev->of_node) bus_np =__of_get_dma_parent (np);else
@@ -119,7 +124,7 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, end =dma_range_map_max (map); set_map = true; } - +skip_map:/* * If @dev is expected to be DMA-capable then the bus code that created * it should have initialised its dma_mask pointer by this point. For
drivers/pci/pci-driver.c +1 -1
@@ -1653,7 +1653,7 @@ static int pci_dma_configure(struct device *dev)pci_put_host_bridge_device (bridge); -if (!ret && !driver->driver_managed_dma) { +if (dev->driver && !ret && !driver->driver_managed_dma) { ret =iommu_device_use_default_domain (dev);if (ret)arch_teardown_dma_ops (dev);
Signature · 1 line
2.39.2.101.g768bb238c484.dirty
Re: [PATCH 2/2] iommu: Get DT/ACPI parsing into the proper probe path
Quoted 48 lines · Robin Murphy
In hindsight, there were some crucial subtleties overlooked when moving {of,acpi}_dma_configure() to driver probe time to allow waiting for IOMMU drivers with -EPROBE_DEFER, and these have become an ever-increasing source of problems. The IOMMU API has some fundamental assumptions that
iommu_probe_device()is called for every device added to the system, in the order in which they are added. Calling it in a random order or not at all dependent on driver binding leads to malformed groups, a potential lack of isolation for devices with no driver, and all manner of unexpected concurrency and race conditions. We've attempted to mitigate the latter with point-fix bodges likeiommu_probe_device_lock, but it's a losing battle and the time has come to bite the bullet and address the true source of the problem instead.The crux of the matter is that the firmware parsing actually serves two distinct purposes; one is identifying the IOMMU instance associated with a device so we can check its availability, the second is actually telling that instance about the relevant firmware-provided data for the device. However the latter also depends on the former, and at the time there was no good place to defer and retry that separately from the availability check we also wanted for client driver probe.
Nowadays, though, we have a proper notion of multiple IOMMU instances in the core API itself, and each one gets a chance to probe its own devices upon registration, so we can finally make that work as intended for DT/IORT/VIOT platforms too. All we need is for
iommu_probe_device()to be able to run theiommu_fwspecmachinery currently buried deep in the wrong end of {of,acpi}_dma_configure(). Luckily it turns out to be surprisingly straightforward to bootstrap this transformation by pretty much just calling the same path twice. At client driver probe time,dev->driveris obviously set; conversely atdevice_add(), or a subsequentbus_iommu_probe(), any device waiting for an IOMMU really should *not* have a driver already, so we can use that as a condition to disambiguate the two cases, and avoid recursing back into the IOMMU core at the wrong times.Obviously this isn't the nicest thing, but for now it gives us a functional baseline to then unpick the layers in between without many more awkward cross-subsystem patches. There are some minor side-effects like
dma_range_mappotentially being created earlier, and some debug prints being repeated, but these aren't significantly detrimental. Let's make things work first, then deal with making them nice.With the basic flow finally in the right order again, the next step is probably turning the
bus->dma_configurepaths inside-out, since all we really need from bus code is its notion of which device and input ID(s) to parse the common firmware properties with...Signed-off-by Robin Murphy <[email protected]>
Quoted 11 lines
--- drivers/acpi/arm64/dma.c | 5 ++++ drivers/acpi/scan.c | 10 +++----- drivers/amba/bus.c | 2 +- drivers/base/platform.c | 2 +- drivers/bus/fsl-mc/fsl-mc-bus.c | 2 +- drivers/cdx/cdx.c | 2 +- drivers/iommu/iommu.c | 43 ++++++++++++++++++++++++--------- drivers/iommu/of_iommu.c | 10 +++++++- drivers/of/device.c | 7 +++++- drivers/pci/pci-driver.c | 2 +-
I assume this will be merged via the IOMMU tree.
Quoted 231 lines
10 files changed, 60 insertions(+), 25 deletions(-)drivers/acpi/arm64/dma.c +5
-0@@ -26,6 +26,11 @@ void acpi_arch_dma_setup(struct device *dev)else end = (1ULL <<32 ) -1 ; +if (dev->dma_range_map) { +dev_dbg (dev,"dma_range_map already set\n" ); +return ; + } + ret =acpi_dma_get_range (dev, &map);if (!ret && map) { end =dma_range_map_max (map);drivers/acpi/scan.c +3
-7@@ -1619,6 +1619,9 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) {int err; +if (device_iommu_mapped (dev)) +return 0 ; +/* Serialise to make dev->iommu stable under our potential fwspec */ mutex_lock (&iommu_probe_device_lock);/* If we already translated the fwspec there is nothing left to do */ @@ -1632,13 +1635,6 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) err =viot_iommu_configure (dev);mutex_unlock (&iommu_probe_device_lock); -/* -* If we have reason to believe the IOMMU driver missed the initial -* iommu_probe_device() call for dev, replay it to get things in order. -*/ -if (!err && dev->bus) - err =iommu_probe_device (dev); -return err; }drivers/amba/bus.c +1
-1@@ -364,7 +364,7 @@ static int amba_dma_configure(struct device *dev) ret =acpi_dma_configure (dev, attr); } -if (!ret && !drv->driver_managed_dma) { +if (dev->driver && !ret && !drv->driver_managed_dma) { ret =iommu_device_use_default_domain (dev);if (ret)arch_teardown_dma_ops (dev);drivers/base/platform.c +1
-1@@ -1451,7 +1451,7 @@ static int platform_dma_configure(struct device *dev) attr =acpi_get_dma_attr (to_acpi_device_node (fwnode)); ret =acpi_dma_configure (dev, attr); } -if (ret || drv->driver_managed_dma) +if (!dev->driver || ret || drv->driver_managed_dma)return ret; ret =iommu_device_use_default_domain (dev);drivers/bus/fsl-mc/fsl-mc-bus.c +1
-1@@ -153,7 +153,7 @@ static int fsl_mc_dma_configure(struct device *dev)else ret =acpi_dma_configure_id (dev,DEV_DMA_COHERENT , &input_id); -if (!ret && !mc_drv->driver_managed_dma) { +if (dev->driver && !ret && !mc_drv->driver_managed_dma) { ret =iommu_device_use_default_domain (dev);if (ret)arch_teardown_dma_ops (dev);drivers/cdx/cdx.c +1
-1@@ -360,7 +360,7 @@ static int cdx_dma_configure(struct device *dev)return ret; } -if (!ret && !cdx_drv->driver_managed_dma) { +if (dev->driver && !ret && !cdx_drv->driver_managed_dma) { ret =iommu_device_use_default_domain (dev);if (ret)arch_teardown_dma_ops (dev);drivers/iommu/iommu.c +32
-11@@ -519,17 +519,6 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_liststruct group_device *gdev;int ret; -/* -* For FDT-based systems and ACPI IORT/VIOT, drivers register IOMMU -* instances with non-NULL fwnodes, and client devices should have been -* identified with a fwspec by this point. Otherwise, we can currently -* assume that only one of Intel, AMD, s390, PAMU or legacy SMMUv2 can -* be present, and that any of their registered instances has suitable -* ops for probing, and thus cheekily co-opt the same mechanism. -*/ - ops =iommu_fwspec_ops (dev_iommu_fwspec_get (dev)); -if (!ops) -return -ENODEV ;/* * Serialise to avoid races between IOMMU drivers registering in * parallel and/or the "replay" calls from ACPI/OF code via client @@ -539,9 +528,41 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list*/ lockdep_assert_held (&iommu_probe_device_lock); +/* +* For FDT-based systems and ACPI IORT/VIOT, the common firmware parsing +* is buried in the bus dma_configure path. Properly unpicking that is +* still a fairly big job, so for now just invoke the whole thing. Our +* bus_iommu_probe() walk may see devices with drivers already bound, +* but that must mean they're already configured - either probed by +* another IOMMU, or there was no IOMMU for iommu_fwspec_init() to wait +* for - so either way we can safely skip this and avoid worrying about +* those recursing back here thinking they need a replay call. +*/ +if (!dev->driver && dev->bus->dma_configure) { +mutex_unlock (&iommu_probe_device_lock); + dev->bus->dma_configure (dev); +mutex_lock (&iommu_probe_device_lock); + } + +/* +* At this point, either valid devices now have a fwspec, or we can +* assume that only one of Intel, AMD, s390, PAMU or legacy SMMUv2 can +* be present, and that any of their registered instances has suitable +* ops for probing, and thus cheekily co-opt the same mechanism. +*/ + ops =iommu_fwspec_ops (dev_iommu_fwspec_get (dev)); +if (!ops) +return -ENODEV ; +/* Device is probed already if in a group */ if (dev->iommu_group)return 0 ; +/* +* And if we do now see any replay calls, they would indicate someone +* misusing the dma_configure path outside bus code. +*/ +if (dev_iommu_fwspec_get (dev) && dev->driver) +dev_WARN (dev,"late IOMMU probe at driver bind, something fishy here!\n" ); ret =iommu_init_device (dev, ops);if (ret)drivers/iommu/of_iommu.c +9
-1@@ -121,6 +121,9 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,if (!master_np)return -ENODEV ; +if (device_iommu_mapped (dev)) +return 0 ; +/* Serialise to make dev->iommu stable under our potential fwspec */ mutex_lock (&iommu_probe_device_lock);if (dev_iommu_fwspec_get (dev)) {@@ -151,7 +154,12 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,iommu_fwspec_free (dev);mutex_unlock (&iommu_probe_device_lock); -if (!err && dev->bus) +/* +* If we have reason to believe the IOMMU driver missed the initial +* iommu_probe_device() call for dev, try to fix it up. This should +* no longer happen unless of_dma_configure() is being misused. +*/ +if (!err && dev->driver) err =iommu_probe_device (dev);if (err && err != -EPROBE_DEFER )drivers/of/device.c +6
-1@@ -99,6 +99,11 @@ int of_dma_configure_id(struct device *dev, struct device_node *np,bool coherent, set_map = false;int ret; +if (dev->dma_range_map) { +dev_dbg (dev,"dma_range_map already set\n" ); + goto skip_map; + } +if (np == dev->of_node) bus_np =__of_get_dma_parent (np);else @@ -119,7 +124,7 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, end =dma_range_map_max (map); set_map = true; } - +skip_map:/* * If @dev is expected to be DMA-capable then the bus code that created * it should have initialised its dma_mask pointer by this point. For drivers/pci/pci-driver.c +1
-1@@ -1653,7 +1653,7 @@ static int pci_dma_configure(struct device *dev)pci_put_host_bridge_device (bridge); -if (!ret && !driver->driver_managed_dma) { +if (dev->driver && !ret && !driver->driver_managed_dma) { ret =iommu_device_use_default_domain (dev);if (ret)arch_teardown_dma_ops (dev);Signature · 1 line
2.39.2.101.g768bb238c484.dirty
Re: [PATCH 2/2] iommu: Get DT/ACPI parsing into the proper probe path
In reply to Robin MurphyQuoted 3 lines · Robin Murphy
much just calling the same path twice. At client driver probe time,
dev->driveris obviously set; conversely atdevice_add(), or a subsequentbus_iommu_probe(), any device waiting for an IOMMU really
Could you put the dev->driver test into iommu_device_use_default_domain()?
It looks like many of the cases are just guarding that call.
Quoted 3 lines
should *not* have a driver already, so we can use that as a condition to disambiguate the two cases, and avoid recursing back into the IOMMU core at the wrong times.
Which sounds like this:
drivers/iommu/iommu.c static int __iommu_probe_device(struct device *dev, struct list_head *group_list · line 545
Quoted 4 lines
drivers/iommu/iommu.c +4
-0+mutex_unlock (&iommu_probe_device_lock); + dev->bus->dma_configure (dev); +mutex_lock (&iommu_probe_device_lock); + }
Shouldn't call iommu_device_use_default_domain() ?
But... I couldn't guess what the problem with calling it is?
In the not-probed case it will see dev->iommu_group is NULL and succeed.
The probed case could be prevented by checking dev->iommu_group sooner in __iommu_probe_device()?
Anyhow, the approach seems OK
drivers/acpi/scan.c static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) · line 1623
Quoted 10 lines
drivers/acpi/scan.c +2
-0@@ -1619,6 +1619,9 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) {int err; +if (device_iommu_mapped (dev)) +return 0 ;
This is unlocked and outside a driver context, it should have a comment explaining why races with probe can't happen?
Quoted 19 lines
drivers/acpi/scan.c +19
-0+/* +* For FDT-based systems and ACPI IORT/VIOT, the common firmware parsing +* is buried in the bus dma_configure path. Properly unpicking that is +* still a fairly big job, so for now just invoke the whole thing. Our +* bus_iommu_probe() walk may see devices with drivers already bound, +* but that must mean they're already configured - either probed by +* another IOMMU, or there was no IOMMU for iommu_fwspec_init() to wait +* for - so either way we can safely skip this and avoid worrying about +* those recursing back here thinking they need a replay call. +*/ +if (!dev->driver && dev->bus->dma_configure) { +mutex_unlock (&iommu_probe_device_lock); + dev->bus->dma_configure (dev); +mutex_lock (&iommu_probe_device_lock); + } + +/* +* At this point, either valid devices now have a fwspec, or we can +* assume that only one of Intel, AMD, s390, PAMU or legacy SMMUv2 can
drivers/acpi/scan.c static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
Quoted 10 lines
drivers/acpi/scan.c +7
-0+* be present, and that any of their registered instances has suitable +* ops for probing, and thus cheekily co-opt the same mechanism. +*/ + ops =iommu_fwspec_ops (dev_iommu_fwspec_get (dev)); +if (!ops) +return -ENODEV ; +/* Device is probed already if in a group */ if (dev->iommu_group)return 0 ;
This is the test I mean, if iommu_group is set then dev->iommu->iommu_dev->ops is supposed to be valid too. It seems like it should be done earlier..
drivers/acpi/scan.c
Quoted 6 lines
drivers/acpi/scan.c +6
-0+/* +* And if we do now see any replay calls, they would indicate someone +* misusing the dma_configure path outside bus code. +*/ +if (dev_iommu_fwspec_get (dev) && dev->driver) +dev_WARN (dev,"late IOMMU probe at driver bind, something fishy here!\n" );
WARN_ON_ONCE or dump_stack() to get the stack trace out?
drivers/acpi/scan.c int of_iommu_configure(struct device *dev, struct device_node *master_np, · line 125
Quoted 6 lines
drivers/acpi/scan.c +2
-0@@ -121,6 +121,9 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,if (!master_np)return -ENODEV ; +if (device_iommu_mapped (dev)) +return 0 ;
Same note
drivers/acpi/scan.c int of_iommu_configure(struct device *dev, struct device_node *master_np, · line 163
Quoted 12 lines
drivers/acpi/scan.c +6
-1@@ -151,7 +154,12 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,iommu_fwspec_free (dev);mutex_unlock (&iommu_probe_device_lock); -if (!err && dev->bus) +/* +* If we have reason to believe the IOMMU driver missed the initial +* iommu_probe_device() call for dev, try to fix it up. This should +* no longer happen unless of_dma_configure() is being misused. +*/ +if (!err && dev->driver) err =iommu_probe_device (dev);
This is being conservative? After some time of nobody complaining it can be removed?
Jason
Robin Murphy
reviewerRe: [PATCH 2/2] iommu: Get DT/ACPI parsing into the proper probe path
Quoted 14 lines · Jason Gunthorpe
Quoted 3 lines · Robin Murphy
much just calling the same path twice. At client driver probe time,
dev->driveris obviously set; conversely atdevice_add(), or a subsequentbus_iommu_probe(), any device waiting for an IOMMU reallyCould you put the
dev->drivertest intoiommu_device_use_default_domain()?It looks like many of the cases are just guarding that call.
Quoted 3 lines
should *not* have a driver already, so we can use that as a condition to disambiguate the two cases, and avoid recursing back into the IOMMU core at the wrong times.
Quoted 8 lines
Which sounds like this:
drivers/iommu/iommu.c static int __iommu_probe_device(struct device *dev, struct list_head *group_list · line 545
Quoted 4 lines
drivers/iommu/iommu.c +4
-0+mutex_unlock (&iommu_probe_device_lock); + dev->bus->dma_configure (dev); +mutex_lock (&iommu_probe_device_lock); + }Shouldn't call
iommu_device_use_default_domain()?
Semantically it shouldn't really be called at this stage, but it won't be anyway since "to_<x>_driver(NULL)->driver_managed_dma" is not false - trouble is it's also not true ;)
Quoted 17 lines
But... I couldn't guess what the problem with calling it is?
In the not-probed case it will see
dev->iommu_groupis NULL and succeed.The probed case could be prevented by checking
dev->iommu_groupsooner in__iommu_probe_device()?Anyhow, the approach seems OK
Quoted 8 lines
drivers/acpi/scan.c +0
-0@@ -1619,6 +1619,9 @@ static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) {int err;
Quoted 5 lines
drivers/acpi/scan.c static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in) · line 1623
Quoted 2 lines
drivers/acpi/scan.c +2
-0+if (device_iommu_mapped (dev)) +return 0 ;This is unlocked and outside a driver context, it should have a comment explaining why races with probe can't happen?
Sure, for now this is more just an opportunistic thing - since we'll now expect to come through this path twice, if we see a group assigned then we know for sure that someone else already set up the fwspec for that to happen, so there's definitely nothing to do here and we can skip potentially waiting for iommu_probe_device_lock.
Quoted 25 lines
Quoted 25 lines
drivers/acpi/scan.c +25
-0+/* +* For FDT-based systems and ACPI IORT/VIOT, the common firmware parsing +* is buried in the bus dma_configure path. Properly unpicking that is +* still a fairly big job, so for now just invoke the whole thing. Our +* bus_iommu_probe() walk may see devices with drivers already bound, +* but that must mean they're already configured - either probed by +* another IOMMU, or there was no IOMMU for iommu_fwspec_init() to wait +* for - so either way we can safely skip this and avoid worrying about +* those recursing back here thinking they need a replay call. +*/ +if (!dev->driver && dev->bus->dma_configure) { +mutex_unlock (&iommu_probe_device_lock); + dev->bus->dma_configure (dev); +mutex_lock (&iommu_probe_device_lock); + } + +/* +* At this point, either valid devices now have a fwspec, or we can +* assume that only one of Intel, AMD, s390, PAMU or legacy SMMUv2 can +* be present, and that any of their registered instances has suitable +* ops for probing, and thus cheekily co-opt the same mechanism. +*/ + ops =iommu_fwspec_ops (dev_iommu_fwspec_get (dev)); +if (!ops) +return -ENODEV ;
Quoted 8 lines
Quoted 4 lines
+
/* Device is probed already if in a group */ if (dev->iommu_group)
return 0;This is the test I mean, if
iommu_groupis set thendev->iommu->iommu_dev->opsis supposed to be valid too. It seems like it should be done earlier..
Yeah, looking at it now I'm really not sure why this ended up in this order - I guess I was effectively adding the dma_configure() call to the front of the existing iommu_fwspec_ops() check, and then I moved the lockdep_assert() up to make more sense. But then the ops check probably should have been after the group check to begin with, for much the same reasoning as above. I'll sort that out for v2.
Quoted 8 lines
drivers/acpi/scan.c
Quoted 6 lines
drivers/acpi/scan.c +6
-0+/* +* And if we do now see any replay calls, they would indicate someone +* misusing the dma_configure path outside bus code. +*/ +if (dev_iommu_fwspec_get (dev) && dev->driver) +dev_WARN (dev,"late IOMMU probe at driver bind, something fishy here!\n" );
WARN_ON_ONCEordump_stack()to get the stack trace out?
Indeed, hence dev_WARN() (!= dev_warn())
Quoted 8 lines
drivers/acpi/scan.c int of_iommu_configure(struct device *dev, struct device_node *master_np, · line 125
Quoted 6 lines
drivers/acpi/scan.c +2
-0@@ -121,6 +121,9 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,if (!master_np)return -ENODEV ; +if (device_iommu_mapped (dev)) +return 0 ;Same note
Ack.
Quoted 4 lines
Quoted 4 lines
drivers/acpi/scan.c +0
-0@@ -151,7 +154,12 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,iommu_fwspec_free (dev);mutex_unlock (&iommu_probe_device_lock);
Quoted 11 lines
drivers/acpi/scan.c int of_iommu_configure(struct device *dev, struct device_node *master_np, · line 163
Quoted 8 lines
drivers/acpi/scan.c +6
-1-if (!err && dev->bus) +/* +* If we have reason to believe the IOMMU driver missed the initial +* iommu_probe_device() call for dev, try to fix it up. This should +* no longer happen unless of_dma_configure() is being misused. +*/ +if (!err && dev->driver) err =iommu_probe_device (dev);This is being conservative? After some time of nobody complaining it can be removed?
Indeed I feel sufficiently confident about the ACPI path (which at the moment is effectively arm64-only) to remove it from there already, but less so about all the assorted DT platforms. That said, I guess adding a new dependency on dev->driver here might still represent a change of behaviour for the sketchy direct calls of of_dma_configure() outside bus code, since in a lot of those the target device doesn't actually have its own driver either. Maybe I need to think about this a bit more...
Thanks,
Robin.
Re: [PATCH 2/2] iommu: Get DT/ACPI parsing into the proper probe path
Quoted 24 lines · Robin Murphy
Quoted 14 lines · Jason Gunthorpe
Quoted 3 lines · Robin Murphy
much just calling the same path twice. At client driver probe time,
dev->driveris obviously set; conversely atdevice_add(), or a subsequentbus_iommu_probe(), any device waiting for an IOMMU reallyCould you put the
dev->drivertest intoiommu_device_use_default_domain()?It looks like many of the cases are just guarding that call.
Quoted 3 lines
should *not* have a driver already, so we can use that as a condition to disambiguate the two cases, and avoid recursing back into the IOMMU core at the wrong times.
Quoted 8 lines
Which sounds like this:
drivers/iommu/iommu.c static int __iommu_probe_device(struct device *dev, struct list_head *group_list · line 545
Quoted 4 lines
drivers/iommu/iommu.c +4
-0+mutex_unlock (&iommu_probe_device_lock); + dev->bus->dma_configure (dev); +mutex_lock (&iommu_probe_device_lock); + }Shouldn't call
iommu_device_use_default_domain()?
Quoted 3 lines
Semantically it shouldn't really be called at this stage, but it won't be anyway since "to_<x>_driver(NULL)->driver_managed_dma" is not false - trouble is it's also not true ;)
That case in PCI I understood, but the other cases seemed like they would be OK, especially if group is NULL
Quoted 10 lines
Quoted 3 lines
This is the test I mean, if
iommu_groupis set thendev->iommu->iommu_dev->opsis supposed to be valid too. It seems like it should be done earlier..Yeah, looking at it now I'm really not sure why this ended up in this order
- I guess I was effectively adding thedma_configure()call to the front of the existingiommu_fwspec_ops()check, and then I moved thelockdep_assert()up to make more sense. But then the ops check probably should have been after the group check to begin with, for much the same reasoning as above. I'll sort that out for v2.
I guess check it at the top and then check it again after re-locking.
Quoted 9 lines
Quoted 7 lines
drivers/iommu/iommu.c static int __iommu_probe_device(struct device *dev, struct list_head *group_list · line 565
Quoted 5 lines
drivers/iommu/iommu.c +5
-0+* And if we do now see any replay calls, they would indicate someone +* misusing the dma_configure path outside bus code. +*/ +if (dev_iommu_fwspec_get (dev) && dev->driver) +dev_WARN (dev,"late IOMMU probe at driver bind, something fishy here!\n" );
WARN_ON_ONCEordump_stack()to get the stack trace out?Indeed, hence
dev_WARN()(!=dev_warn())
Oh, I've never seen that variation before!
Jason
Re: [PATCH 2/2] iommu: Get DT/ACPI parsing into the proper probe path
In reply to Robin MurphyQuoted 49 lines · Robin Murphy
In hindsight, there were some crucial subtleties overlooked when moving {of,acpi}_dma_configure() to driver probe time to allow waiting for IOMMU drivers with -EPROBE_DEFER, and these have become an ever-increasing source of problems. The IOMMU API has some fundamental assumptions that
iommu_probe_device()is called for every device added to the system, in the order in which they are added. Calling it in a random order or not at all dependent on driver binding leads to malformed groups, a potential lack of isolation for devices with no driver, and all manner of unexpected concurrency and race conditions. We've attempted to mitigate the latter with point-fix bodges likeiommu_probe_device_lock, but it's a losing battle and the time has come to bite the bullet and address the true source of the problem instead.The crux of the matter is that the firmware parsing actually serves two distinct purposes; one is identifying the IOMMU instance associated with a device so we can check its availability, the second is actually telling that instance about the relevant firmware-provided data for the device. However the latter also depends on the former, and at the time there was no good place to defer and retry that separately from the availability check we also wanted for client driver probe.
Nowadays, though, we have a proper notion of multiple IOMMU instances in the core API itself, and each one gets a chance to probe its own devices upon registration, so we can finally make that work as intended for DT/IORT/VIOT platforms too. All we need is for
iommu_probe_device()to be able to run theiommu_fwspecmachinery currently buried deep in the wrong end of {of,acpi}_dma_configure(). Luckily it turns out to be surprisingly straightforward to bootstrap this transformation by pretty much just calling the same path twice. At client driver probe time,dev->driveris obviously set; conversely atdevice_add(), or a subsequentbus_iommu_probe(), any device waiting for an IOMMU really should *not* have a driver already, so we can use that as a condition to disambiguate the two cases, and avoid recursing back into the IOMMU core at the wrong times.Obviously this isn't the nicest thing, but for now it gives us a functional baseline to then unpick the layers in between without many more awkward cross-subsystem patches. There are some minor side-effects like
dma_range_mappotentially being created earlier, and some debug prints being repeated, but these aren't significantly detrimental. Let's make things work first, then deal with making them nice.With the basic flow finally in the right order again, the next step is probably turning the
bus->dma_configurepaths inside-out, since all we really need from bus code is its notion of which device and input ID(s) to parse the common firmware properties with...Signed-off-by Robin Murphy <[email protected]>
Quoted 10 lines
--- drivers/acpi/arm64/dma.c | 5 ++++ drivers/acpi/scan.c | 10 +++----- drivers/amba/bus.c | 2 +- drivers/base/platform.c | 2 +- drivers/bus/fsl-mc/fsl-mc-bus.c | 2 +- drivers/cdx/cdx.c | 2 +- drivers/iommu/iommu.c | 43 ++++++++++++++++++++++++--------- drivers/iommu/of_iommu.c | 10 +++++++- drivers/of/device.c | 7 +++++-
Quoted 2 lines
drivers/pci/pci-driver.c | 2 +- 10 files changed, 60 insertions(+), 25 deletions(-)