[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <08e0126f8075711e7bd59ca4110c2817c905d5a9.camel@linaro.org>
Date: Mon, 20 Nov 2023 11:26:39 +0000
From: André Draszik <andre.draszik@...aro.org>
To: Robin Murphy <robin.murphy@....com>, joro@...tes.org,
will@...nel.org
Cc: iommu@...ts.linux.dev, linux-kernel@...r.kernel.org,
linux-acpi@...r.kernel.org, rafael@...nel.org, lenb@...nel.org,
lpieralisi@...nel.org, quic_zhenhuah@...cinc.com, jgg@...dia.com
Subject: Re: [PATCH] iommu: Avoid more races around device probe
Hi Robin,
On Wed, 2023-11-15 at 18:25 +0000, Robin Murphy wrote:
> It turns out there are more subtle races beyond just the main part of
> __iommu_probe_device() itself running in parallel - the
> dev_iommu_free()
> on the way out of an unsuccessful probe can still manage to trip up
> concurrent accesses to a device's fwspec. Thus, extend the scope of
> iommu_probe_device_lock() to also serialise fwspec creation and
> initial
> retrieval.
>
> Reported-by: Zhenhua Huang <quic_zhenhuah@...cinc.com>
> Link:
> https://lore.kernel.org/linux-iommu/e2e20e1c-6450-4ac5-9804-b0000acdf7de@quicinc.com/
> Fixes: 01657bc14a39 ("iommu: Avoid races around device probe")
> Signed-off-by: Robin Murphy <robin.murphy@....com>
Reviewed-by: André Draszik <andre.draszik@...aro.org>
Tested-by: André Draszik <andre.draszik@...aro.org> (using continuous
boot test loop)
I like that this is easily back-portable to 6.1, thanks for this patch
:-)
Cheers,
André
> ---
>
> This is my idea of a viable fix, since it does not need a 700-line
> diffstat to make the code do what it was already *trying* to do
> anyway.
> This stuff should fundamentally not be hanging off driver probe in
> the
> first place, so I'd rather get on with removing the underlying
> brokenness than waste time and effort polishing it any further.
>
> drivers/acpi/scan.c | 7 ++++++-
> drivers/iommu/iommu.c | 20 ++++++++++----------
> drivers/iommu/of_iommu.c | 12 +++++++++---
> include/linux/iommu.h | 1 +
> 4 files changed, 26 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index fa5dd71a80fa..02bb2cce423f 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -1568,17 +1568,22 @@ static const struct iommu_ops
> *acpi_iommu_configure_id(struct device *dev,
> int err;
> const struct iommu_ops *ops;
>
> + /* 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,
> * return the iommu_ops.
> */
> ops = acpi_iommu_fwspec_ops(dev);
> - if (ops)
> + if (ops) {
> + mutex_unlock(&iommu_probe_device_lock);
> return ops;
> + }
>
> err = iort_iommu_configure_id(dev, id_in);
> if (err && err != -EPROBE_DEFER)
> err = viot_iommu_configure(dev);
> + mutex_unlock(&iommu_probe_device_lock);
>
> /*
> * If we have reason to believe the IOMMU driver missed the
> initial
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index f17a1113f3d6..e0c962648dde 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -485,11 +485,12 @@ static void iommu_deinit_device(struct device
> *dev)
> dev_iommu_free(dev);
> }
>
> +DEFINE_MUTEX(iommu_probe_device_lock);
> +
> static int __iommu_probe_device(struct device *dev, struct list_head
> *group_list)
> {
> const struct iommu_ops *ops = dev->bus->iommu_ops;
> struct iommu_group *group;
> - static DEFINE_MUTEX(iommu_probe_device_lock);
> struct group_device *gdev;
> int ret;
>
> @@ -502,17 +503,15 @@ static int __iommu_probe_device(struct device
> *dev, struct list_head *group_list
> * probably be able to use device_lock() here to minimise
> the scope,
> * but for now enforcing a simple global ordering is fine.
> */
> - mutex_lock(&iommu_probe_device_lock);
> + lockdep_assert_held(&iommu_probe_device_lock);
>
> /* 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);
> @@ -548,7 +547,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);
> - mutex_unlock(&iommu_probe_device_lock);
>
> if (dev_is_pci(dev))
> iommu_dma_set_pci_32bit_workaround(dev);
> @@ -562,8 +560,6 @@ 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:
> - mutex_unlock(&iommu_probe_device_lock);
>
> return ret;
> }
> @@ -573,7 +569,9 @@ int iommu_probe_device(struct device *dev)
> const struct iommu_ops *ops;
> int ret;
>
> + mutex_lock(&iommu_probe_device_lock);
> ret = __iommu_probe_device(dev, NULL);
> + mutex_unlock(&iommu_probe_device_lock);
> if (ret)
> return ret;
>
> @@ -1822,7 +1820,9 @@ static int probe_iommu_group(struct device
> *dev, void *data)
> struct list_head *group_list = data;
> int ret;
>
> + mutex_lock(&iommu_probe_device_lock);
> ret = __iommu_probe_device(dev, group_list);
> + mutex_unlock(&iommu_probe_device_lock);
> if (ret == -ENODEV)
> ret = 0;
>
> diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
> index 157b286e36bf..c25b4ae6aeee 100644
> --- a/drivers/iommu/of_iommu.c
> +++ b/drivers/iommu/of_iommu.c
> @@ -112,16 +112,20 @@ const struct iommu_ops
> *of_iommu_configure(struct device *dev,
> const u32 *id)
> {
> const struct iommu_ops *ops = NULL;
> - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
> + struct iommu_fwspec *fwspec;
> int err = NO_IOMMU;
>
> if (!master_np)
> return NULL;
>
> + /* Serialise to make dev->iommu stable under our potential
> fwspec */
> + mutex_lock(&iommu_probe_device_lock);
> + fwspec = dev_iommu_fwspec_get(dev);
> if (fwspec) {
> - if (fwspec->ops)
> + if (fwspec->ops) {
> + mutex_unlock(&iommu_probe_device_lock);
> return fwspec->ops;
> -
> + }
> /* In the deferred case, start again from scratch */
> iommu_fwspec_free(dev);
> }
> @@ -155,6 +159,8 @@ const struct iommu_ops *of_iommu_configure(struct
> device *dev,
> fwspec = dev_iommu_fwspec_get(dev);
> ops = fwspec->ops;
> }
> + mutex_unlock(&iommu_probe_device_lock);
> +
> /*
> * If we have reason to believe the IOMMU driver missed the
> initial
> * probe for dev, replay it to get things in order.
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index ec289c1016f5..6291aa7b079b 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -845,6 +845,7 @@ static inline void dev_iommu_priv_set(struct
> device *dev, void *priv)
> dev->iommu->priv = priv;
> }
>
> +extern struct mutex iommu_probe_device_lock;
> int iommu_probe_device(struct device *dev);
>
> int iommu_dev_enable_feature(struct device *dev, enum
> iommu_dev_features f);
Powered by blists - more mailing lists