Thread (34 messages) 34 messages, 7 authors, 2023-08-09

Re: [PATCH v2 00/10] Refine the locking for dev->iommu_group

From: Chen-Yu Tsai <wenst@chromium.org>
Date: 2023-08-09 06:24:22
Also in: linux-iommu, linux-rockchip, linux-sunxi

On Tue, Aug 8, 2023 at 10:30 PM Jason Gunthorpe [off-list ref] wrote:
On Tue, Aug 08, 2023 at 04:02:40PM +0200, Marek Szyprowski wrote:
quoted
Hi Jason,

On 08.08.2023 15:25, Jason Gunthorpe wrote:
quoted
On Tue, Aug 08, 2023 at 03:08:30PM +0200, Marek Szyprowski wrote:
quoted
quoted
Any of the drivers that use platform device as the iommu_device will
have a problem, please try:

https://lore.kernel.org/linux-iommu/ZNIz%2FNVLb6WqqvQx@nvidia.com/ (local)
I've checked and it doesn't help in my case. I will soon check why.
Oh, I botched it. Forgot that the iommu_device->dev is the sysfs
handle not the HW device. Maybe this:
This fixed the early lockup, but then system hangs again a bit later. It
looks that this device lock in __iommu_probe_device() is really
problematic,
Yes, I expected we'd hit something like this - I checked alot of call
paths but missed these two. The self-probe is sneaky, but here the
device_lock is held way up the call chain, I just missed it.

The fix is to just annotate that we already hold the lock when calling
iommu_probe_device(), since we know in those cases that we must be
holding it:
This fixed things for me, so

Tested-by: Chen-Yu Tsai <wenst@chromium.org>
quoted hunk ↗ jump to hunk
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index daa64dd687524b..3fc5e12f2f1c09 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1582,7 +1582,7 @@ static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev,
         * iommu_probe_device() call for dev, replay it to get things in order.
         */
        if (!err && dev->bus)
-               err = iommu_probe_device(dev);
+               err = iommu_probe_device_locked(dev);

        /* Ignore all other errors apart from EPROBE_DEFER */
        if (err == -EPROBE_DEFER) {
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 7dbbcffac21930..b867d7f22954e9 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -264,6 +264,7 @@ int iommu_device_register(struct iommu_device *iommu,
                return -EBUSY;

        iommu->ops = ops;
+       iommu->hwdev = hwdev;
        if (hwdev)
                iommu->fwnode = dev_fwnode(hwdev);
@@ -273,7 +274,7 @@ int iommu_device_register(struct iommu_device *iommu,

        for (int i = 0; i < ARRAY_SIZE(iommu_buses) && !err; i++) {
                iommu_buses[i]->iommu_ops = ops;
-               err = bus_iommu_probe(iommu_buses[i]);
+               err = bus_iommu_probe(iommu_buses[i], iommu);
        }
        if (err)
                iommu_device_unregister(iommu);
@@ -452,24 +453,23 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
        struct group_device *gdev;
        int ret;

-       if (!ops)
-               return -ENODEV;
        /*
         * Allow __iommu_probe_device() to be safely called in parallel,
         * both dev->iommu_group and the initial setup of dev->iommu are
         * protected this way.
         */
-       device_lock(dev);
+       device_lock_assert(dev);
+
+       if (!ops)
+               return -ENODEV;

        /* Device is probed already if in a group */
-       if (dev->iommu_group) {
-               ret = 0;
-               goto out_unlock;
-       }
+       if (dev->iommu_group)
+               return 0;

        ret = iommu_init_device(dev, ops);
        if (ret)
-               goto out_unlock;
+               return ret;

        group = dev->iommu_group;
        gdev = iommu_group_alloc_device(group, dev);
@@ -505,7 +505,6 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
                        list_add_tail(&group->entry, group_list);
        }
        mutex_unlock(&group->mutex);
-       device_unlock(dev);

        if (dev_is_pci(dev))
                iommu_dma_set_pci_32bit_workaround(dev);
@@ -519,12 +518,10 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
        iommu_deinit_device(dev);
        mutex_unlock(&group->mutex);
        iommu_group_put(group);
-out_unlock:
-       device_unlock(dev);
        return ret;
 }

-int iommu_probe_device(struct device *dev)
+int iommu_probe_device_locked(struct device *dev)
 {
        const struct iommu_ops *ops;
        int ret;
@@ -540,6 +537,16 @@ int iommu_probe_device(struct device *dev)
        return 0;
 }

+int iommu_probe_device(struct device *dev)
+{
+       int ret;
+
+       device_lock(dev);
+       ret = iommu_probe_device_locked(dev);
+       device_unlock(dev);
+       return ret;
+}
+
 static void __iommu_group_free_device(struct iommu_group *group,
                                      struct group_device *grp_dev)
 {
@@ -1784,12 +1791,26 @@ struct iommu_domain *iommu_group_default_domain(struct iommu_group *group)
        return group->default_domain;
 }

+struct probe_iommu_args {
+       struct list_head *group_list;
+       struct iommu_device *iommu;
+};
+
 static int probe_iommu_group(struct device *dev, void *data)
 {
-       struct list_head *group_list = data;
+       struct probe_iommu_args *args = data;
+       bool need_lock;
        int ret;

-       ret = __iommu_probe_device(dev, group_list);
+       /* Probing the iommu itself is always done under the device_lock */
+       need_lock = !args->iommu || args->iommu->hwdev != dev;
+
+       if (need_lock)
+               device_lock(dev);
+       ret = __iommu_probe_device(dev, args->group_list);
+       if (need_lock)
+               device_unlock(dev);
+
        if (ret == -ENODEV)
                ret = 0;
@@ -1858,13 +1879,16 @@ static void iommu_group_do_probe_finalize(struct device *dev)
                ops->probe_finalize(dev);
 }

-int bus_iommu_probe(const struct bus_type *bus)
+int bus_iommu_probe(const struct bus_type *bus, struct iommu_device *iommu)
 {
        struct iommu_group *group, *next;
+       struct probe_iommu_args args = {};
        LIST_HEAD(group_list);
        int ret;

-       ret = bus_for_each_dev(bus, NULL, &group_list, probe_iommu_group);
+       args.group_list = &group_list;
+       args.iommu = iommu;
+       ret = bus_for_each_dev(bus, NULL, &args, probe_iommu_group);
        if (ret)
                return ret;
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 157b286e36bf3a..b5b7d4bd2cefb9 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -160,7 +160,7 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
         * probe for dev, replay it to get things in order.
         */
        if (!err && dev->bus)
-               err = iommu_probe_device(dev);
+               err = iommu_probe_device_locked(dev);

        /* Ignore all other errors apart from EPROBE_DEFER */
        if (err == -EPROBE_DEFER) {
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index 97c45f50bf4332..828679abef7503 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -1234,6 +1234,10 @@ static int omap_iommu_probe(struct platform_device *pdev)
                if (err)
                        goto out_sysfs;
                obj->has_iommu_driver = true;
+       } else {
+               /* Re-probe bus to probe device attached to this IOMMU */
+               obj->iommu.hwdev = &pdev->dev;
+               bus_iommu_probe(&platform_bus_type, &obj->iommu);
        }

        pm_runtime_enable(obj->dev);
@@ -1242,9 +1246,6 @@ static int omap_iommu_probe(struct platform_device *pdev)

        dev_info(&pdev->dev, "%s registered\n", obj->name);

-       /* Re-probe bus to probe device attached to this IOMMU */
-       bus_iommu_probe(&platform_bus_type);
-
        return 0;

 out_sysfs:
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index f1e18e81fca78b..96782bfb384462 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -361,6 +361,7 @@ struct iommu_domain_ops {
  * @list: Used by the iommu-core to keep a list of registered iommus
  * @ops: iommu-ops for talking to this iommu
  * @dev: struct device for sysfs handling
+ * @hwdev: The device HW that controls the iommu
  * @singleton_group: Used internally for drivers that have only one group
  * @max_pasids: number of supported PASIDs
  */
@@ -369,6 +370,7 @@ struct iommu_device {
        const struct iommu_ops *ops;
        struct fwnode_handle *fwnode;
        struct device *dev;
+       struct device *hwdev;
        struct iommu_group *singleton_group;
        u32 max_pasids;
 };
@@ -465,7 +467,8 @@ static inline const struct iommu_ops *dev_iommu_ops(struct device *dev)
        return dev->iommu->iommu_dev->ops;
 }

-extern int bus_iommu_probe(const struct bus_type *bus);
+extern int bus_iommu_probe(const struct bus_type *bus,
+                          struct iommu_device *iommu);
 extern bool iommu_present(const struct bus_type *bus);
 extern bool device_iommu_capable(struct device *dev, enum iommu_cap cap);
 extern bool iommu_group_has_isolated_msi(struct iommu_group *group);
@@ -709,6 +712,7 @@ static inline void dev_iommu_priv_set(struct device *dev, void *priv)
 }

 int iommu_probe_device(struct device *dev);
+int iommu_probe_device_locked(struct device *dev);

 int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features f);
 int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features f);
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help