iommu.lists.linux-foundation.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1] iommu/tegra-smmu: Add missing locks around mapping operations
@ 2020-05-24 18:37 Dmitry Osipenko
  2020-05-25  8:35 ` Thierry Reding
  0 siblings, 1 reply; 5+ messages in thread
From: Dmitry Osipenko @ 2020-05-24 18:37 UTC (permalink / raw)
  To: Thierry Reding, Joerg Roedel, Jonathan Hunter
  Cc: linux-tegra, iommu, linux-kernel

The mapping operations of the Tegra SMMU driver are subjected to a race
condition issues because SMMU Address Space isn't allocated and freed
atomically, while it should be. This patch makes the mapping operations
atomic, it fixes an accidentally released Host1x Address Space problem
which happens while running multiple graphics tests in parallel on
Tegra30, i.e. by having multiple threads racing with each other in the
Host1x's submission and completion code paths, performing IOVA mappings
and unmappings in parallel.

Cc: <stable@vger.kernel.org>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/iommu/tegra-smmu.c | 43 +++++++++++++++++++++++++++++++++-----
 1 file changed, 38 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 7426b7666e2b..4f956a797838 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -12,6 +12,7 @@
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
 #include <linux/dma-mapping.h>
 
 #include <soc/tegra/ahb.h>
@@ -49,6 +50,7 @@ struct tegra_smmu_as {
 	struct iommu_domain domain;
 	struct tegra_smmu *smmu;
 	unsigned int use_count;
+	spinlock_t lock;
 	u32 *count;
 	struct page **pts;
 	struct page *pd;
@@ -308,6 +310,8 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)
 		return NULL;
 	}
 
+	spin_lock_init(&as->lock);
+
 	/* setup aperture */
 	as->domain.geometry.aperture_start = 0;
 	as->domain.geometry.aperture_end = 0xffffffff;
@@ -578,7 +582,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,
 		struct page *page;
 		dma_addr_t dma;
 
-		page = alloc_page(GFP_KERNEL | __GFP_DMA | __GFP_ZERO);
+		page = alloc_page(GFP_ATOMIC | __GFP_DMA | __GFP_ZERO);
 		if (!page)
 			return NULL;
 
@@ -655,8 +659,9 @@ static void tegra_smmu_set_pte(struct tegra_smmu_as *as, unsigned long iova,
 	smmu_flush(smmu);
 }
 
-static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
-			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+static int
+tegra_smmu_map_locked(struct iommu_domain *domain, unsigned long iova,
+		      phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
 	struct tegra_smmu_as *as = to_smmu_as(domain);
 	dma_addr_t pte_dma;
@@ -685,8 +690,9 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
 	return 0;
 }
 
-static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
-			       size_t size, struct iommu_iotlb_gather *gather)
+static size_t
+tegra_smmu_unmap_locked(struct iommu_domain *domain, unsigned long iova,
+			size_t size, struct iommu_iotlb_gather *gather)
 {
 	struct tegra_smmu_as *as = to_smmu_as(domain);
 	dma_addr_t pte_dma;
@@ -702,6 +708,33 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
 	return size;
 }
 
+static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
+			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+{
+	struct tegra_smmu_as *as = to_smmu_as(domain);
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&as->lock, flags);
+	ret = tegra_smmu_map_locked(domain, iova, paddr, size, prot, gfp);
+	spin_unlock_irqrestore(&as->lock, flags);
+
+	return ret;
+}
+
+static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
+			       size_t size, struct iommu_iotlb_gather *gather)
+{
+	struct tegra_smmu_as *as = to_smmu_as(domain);
+	unsigned long flags;
+
+	spin_lock_irqsave(&as->lock, flags);
+	size = tegra_smmu_unmap_locked(domain, iova, size, gather);
+	spin_unlock_irqrestore(&as->lock, flags);
+
+	return size;
+}
+
 static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain,
 					   dma_addr_t iova)
 {
-- 
2.26.0

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v1] iommu/tegra-smmu: Add missing locks around mapping operations
  2020-05-24 18:37 [PATCH v1] iommu/tegra-smmu: Add missing locks around mapping operations Dmitry Osipenko
@ 2020-05-25  8:35 ` Thierry Reding
  2020-05-25 10:51   ` Dmitry Osipenko
  0 siblings, 1 reply; 5+ messages in thread
From: Thierry Reding @ 2020-05-25  8:35 UTC (permalink / raw)
  To: Dmitry Osipenko; +Cc: linux-tegra, linux-kernel, iommu, Jonathan Hunter


[-- Attachment #1.1: Type: text/plain, Size: 4640 bytes --]

On Sun, May 24, 2020 at 09:37:55PM +0300, Dmitry Osipenko wrote:
> The mapping operations of the Tegra SMMU driver are subjected to a race
> condition issues because SMMU Address Space isn't allocated and freed
> atomically, while it should be. This patch makes the mapping operations
> atomic, it fixes an accidentally released Host1x Address Space problem
> which happens while running multiple graphics tests in parallel on
> Tegra30, i.e. by having multiple threads racing with each other in the
> Host1x's submission and completion code paths, performing IOVA mappings
> and unmappings in parallel.
> 
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  drivers/iommu/tegra-smmu.c | 43 +++++++++++++++++++++++++++++++++-----
>  1 file changed, 38 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
> index 7426b7666e2b..4f956a797838 100644
> --- a/drivers/iommu/tegra-smmu.c
> +++ b/drivers/iommu/tegra-smmu.c
> @@ -12,6 +12,7 @@
>  #include <linux/of_device.h>
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
> +#include <linux/spinlock.h>
>  #include <linux/dma-mapping.h>
>  
>  #include <soc/tegra/ahb.h>
> @@ -49,6 +50,7 @@ struct tegra_smmu_as {
>  	struct iommu_domain domain;
>  	struct tegra_smmu *smmu;
>  	unsigned int use_count;
> +	spinlock_t lock;
>  	u32 *count;
>  	struct page **pts;
>  	struct page *pd;
> @@ -308,6 +310,8 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)
>  		return NULL;
>  	}
>  
> +	spin_lock_init(&as->lock);
> +
>  	/* setup aperture */
>  	as->domain.geometry.aperture_start = 0;
>  	as->domain.geometry.aperture_end = 0xffffffff;
> @@ -578,7 +582,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,
>  		struct page *page;
>  		dma_addr_t dma;
>  
> -		page = alloc_page(GFP_KERNEL | __GFP_DMA | __GFP_ZERO);
> +		page = alloc_page(GFP_ATOMIC | __GFP_DMA | __GFP_ZERO);

I'm not sure this is a good idea. My recollection is that GFP_ATOMIC
will allocate from a special reserved region of memory, which may be
easily exhausted.

Is there any reason why we need the spinlock? Can't we use a mutex
instead?

>  		if (!page)
>  			return NULL;
>  
> @@ -655,8 +659,9 @@ static void tegra_smmu_set_pte(struct tegra_smmu_as *as, unsigned long iova,
>  	smmu_flush(smmu);
>  }
>  
> -static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
> -			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
> +static int
> +tegra_smmu_map_locked(struct iommu_domain *domain, unsigned long iova,
> +		      phys_addr_t paddr, size_t size, int prot, gfp_t gfp)

I think it's more typical to use the _unlocked suffix for functions that
don't take a lock themselves.

>  {
>  	struct tegra_smmu_as *as = to_smmu_as(domain);
>  	dma_addr_t pte_dma;
> @@ -685,8 +690,9 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
>  	return 0;
>  }
>  
> -static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
> -			       size_t size, struct iommu_iotlb_gather *gather)
> +static size_t
> +tegra_smmu_unmap_locked(struct iommu_domain *domain, unsigned long iova,
> +			size_t size, struct iommu_iotlb_gather *gather)
>  {
>  	struct tegra_smmu_as *as = to_smmu_as(domain);
>  	dma_addr_t pte_dma;
> @@ -702,6 +708,33 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
>  	return size;
>  }
>  
> +static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
> +			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
> +{
> +	struct tegra_smmu_as *as = to_smmu_as(domain);
> +	unsigned long flags;
> +	int ret;
> +
> +	spin_lock_irqsave(&as->lock, flags);
> +	ret = tegra_smmu_map_locked(domain, iova, paddr, size, prot, gfp);
> +	spin_unlock_irqrestore(&as->lock, flags);
> +
> +	return ret;
> +}
> +
> +static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
> +			       size_t size, struct iommu_iotlb_gather *gather)
> +{
> +	struct tegra_smmu_as *as = to_smmu_as(domain);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&as->lock, flags);
> +	size = tegra_smmu_unmap_locked(domain, iova, size, gather);
> +	spin_unlock_irqrestore(&as->lock, flags);
> +
> +	return size;
> +}

Why the extra functions here? We never call locked vs. unlocked variants
in the driver and the IOMMU framework only has a single callback, so I
think the locking can just move into the main implementation.

Thierry

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 156 bytes --]

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v1] iommu/tegra-smmu: Add missing locks around mapping operations
  2020-05-25  8:35 ` Thierry Reding
@ 2020-05-25 10:51   ` Dmitry Osipenko
  2020-05-25 12:20     ` Thierry Reding
  0 siblings, 1 reply; 5+ messages in thread
From: Dmitry Osipenko @ 2020-05-25 10:51 UTC (permalink / raw)
  To: Thierry Reding; +Cc: linux-tegra, linux-kernel, iommu, Jonathan Hunter

25.05.2020 11:35, Thierry Reding пишет:
> On Sun, May 24, 2020 at 09:37:55PM +0300, Dmitry Osipenko wrote:
>> The mapping operations of the Tegra SMMU driver are subjected to a race
>> condition issues because SMMU Address Space isn't allocated and freed
>> atomically, while it should be. This patch makes the mapping operations
>> atomic, it fixes an accidentally released Host1x Address Space problem
>> which happens while running multiple graphics tests in parallel on
>> Tegra30, i.e. by having multiple threads racing with each other in the
>> Host1x's submission and completion code paths, performing IOVA mappings
>> and unmappings in parallel.
>>
>> Cc: <stable@vger.kernel.org>
>> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
>> ---
>>  drivers/iommu/tegra-smmu.c | 43 +++++++++++++++++++++++++++++++++-----
>>  1 file changed, 38 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
>> index 7426b7666e2b..4f956a797838 100644
>> --- a/drivers/iommu/tegra-smmu.c
>> +++ b/drivers/iommu/tegra-smmu.c
>> @@ -12,6 +12,7 @@
>>  #include <linux/of_device.h>
>>  #include <linux/platform_device.h>
>>  #include <linux/slab.h>
>> +#include <linux/spinlock.h>
>>  #include <linux/dma-mapping.h>
>>  
>>  #include <soc/tegra/ahb.h>
>> @@ -49,6 +50,7 @@ struct tegra_smmu_as {
>>  	struct iommu_domain domain;
>>  	struct tegra_smmu *smmu;
>>  	unsigned int use_count;
>> +	spinlock_t lock;
>>  	u32 *count;
>>  	struct page **pts;
>>  	struct page *pd;
>> @@ -308,6 +310,8 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)
>>  		return NULL;
>>  	}
>>  
>> +	spin_lock_init(&as->lock);
>> +
>>  	/* setup aperture */
>>  	as->domain.geometry.aperture_start = 0;
>>  	as->domain.geometry.aperture_end = 0xffffffff;
>> @@ -578,7 +582,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,
>>  		struct page *page;
>>  		dma_addr_t dma;
>>  
>> -		page = alloc_page(GFP_KERNEL | __GFP_DMA | __GFP_ZERO);
>> +		page = alloc_page(GFP_ATOMIC | __GFP_DMA | __GFP_ZERO);
> 
> I'm not sure this is a good idea. My recollection is that GFP_ATOMIC
> will allocate from a special reserved region of memory, which may be
> easily exhausted.

So far I haven't noticed any problems. Will be great if you could
provide more details about the pool size and how this exhaustion problem
could be reproduced in practice.

> Is there any reason why we need the spinlock? Can't we use a mutex
> instead?

This is what other IOMMU drivers do. I guess mutex might be too
expensive, it may create a noticeable contention which you don't want to
have in a case of a GPU submission code path.

I also suspect that drivers of other platforms are using IOMMU API in
interrupt context, although today this is not needed for Tegra.

>>  		if (!page)
>>  			return NULL;
>>  
>> @@ -655,8 +659,9 @@ static void tegra_smmu_set_pte(struct tegra_smmu_as *as, unsigned long iova,
>>  	smmu_flush(smmu);
>>  }
>>  
>> -static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
>> -			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
>> +static int
>> +tegra_smmu_map_locked(struct iommu_domain *domain, unsigned long iova,
>> +		      phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
> 
> I think it's more typical to use the _unlocked suffix for functions that
> don't take a lock themselves.

Personally I can't feel the difference. Both variants are good to me. I
can replace the literal postfix with a __tegra_smmu prefix, similarly to
what we have in the GART driver, to avoid bikeshedding.

>>  {
>>  	struct tegra_smmu_as *as = to_smmu_as(domain);
>>  	dma_addr_t pte_dma;
>> @@ -685,8 +690,9 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
>>  	return 0;
>>  }
>>  
>> -static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
>> -			       size_t size, struct iommu_iotlb_gather *gather)
>> +static size_t
>> +tegra_smmu_unmap_locked(struct iommu_domain *domain, unsigned long iova,
>> +			size_t size, struct iommu_iotlb_gather *gather)
>>  {
>>  	struct tegra_smmu_as *as = to_smmu_as(domain);
>>  	dma_addr_t pte_dma;
>> @@ -702,6 +708,33 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
>>  	return size;
>>  }
>>  
>> +static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
>> +			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
>> +{
>> +	struct tegra_smmu_as *as = to_smmu_as(domain);
>> +	unsigned long flags;
>> +	int ret;
>> +
>> +	spin_lock_irqsave(&as->lock, flags);
>> +	ret = tegra_smmu_map_locked(domain, iova, paddr, size, prot, gfp);
>> +	spin_unlock_irqrestore(&as->lock, flags);
>> +
>> +	return ret;
>> +}
>> +
>> +static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
>> +			       size_t size, struct iommu_iotlb_gather *gather)
>> +{
>> +	struct tegra_smmu_as *as = to_smmu_as(domain);
>> +	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&as->lock, flags);
>> +	size = tegra_smmu_unmap_locked(domain, iova, size, gather);
>> +	spin_unlock_irqrestore(&as->lock, flags);
>> +
>> +	return size;
>> +}
> 
> Why the extra functions here? We never call locked vs. unlocked variants
> in the driver and the IOMMU framework only has a single callback, so I
> think the locking can just move into the main implementation.

Because this makes code cleaner, easier to read and follow. You don't
need to care about handling error code paths. This is the same what we
do in the GART driver. Pretty much every other IOMMU driver use this
code pattern as well.
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v1] iommu/tegra-smmu: Add missing locks around mapping operations
  2020-05-25 10:51   ` Dmitry Osipenko
@ 2020-05-25 12:20     ` Thierry Reding
  2020-05-25 19:34       ` Dmitry Osipenko
  0 siblings, 1 reply; 5+ messages in thread
From: Thierry Reding @ 2020-05-25 12:20 UTC (permalink / raw)
  To: Dmitry Osipenko; +Cc: linux-tegra, linux-kernel, iommu, Jonathan Hunter


[-- Attachment #1.1: Type: text/plain, Size: 7962 bytes --]

On Mon, May 25, 2020 at 01:51:50PM +0300, Dmitry Osipenko wrote:
> 25.05.2020 11:35, Thierry Reding пишет:
> > On Sun, May 24, 2020 at 09:37:55PM +0300, Dmitry Osipenko wrote:
> >> The mapping operations of the Tegra SMMU driver are subjected to a race
> >> condition issues because SMMU Address Space isn't allocated and freed
> >> atomically, while it should be. This patch makes the mapping operations
> >> atomic, it fixes an accidentally released Host1x Address Space problem
> >> which happens while running multiple graphics tests in parallel on
> >> Tegra30, i.e. by having multiple threads racing with each other in the
> >> Host1x's submission and completion code paths, performing IOVA mappings
> >> and unmappings in parallel.
> >>
> >> Cc: <stable@vger.kernel.org>
> >> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> >> ---
> >>  drivers/iommu/tegra-smmu.c | 43 +++++++++++++++++++++++++++++++++-----
> >>  1 file changed, 38 insertions(+), 5 deletions(-)
> >>
> >> diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
> >> index 7426b7666e2b..4f956a797838 100644
> >> --- a/drivers/iommu/tegra-smmu.c
> >> +++ b/drivers/iommu/tegra-smmu.c
> >> @@ -12,6 +12,7 @@
> >>  #include <linux/of_device.h>
> >>  #include <linux/platform_device.h>
> >>  #include <linux/slab.h>
> >> +#include <linux/spinlock.h>
> >>  #include <linux/dma-mapping.h>
> >>  
> >>  #include <soc/tegra/ahb.h>
> >> @@ -49,6 +50,7 @@ struct tegra_smmu_as {
> >>  	struct iommu_domain domain;
> >>  	struct tegra_smmu *smmu;
> >>  	unsigned int use_count;
> >> +	spinlock_t lock;
> >>  	u32 *count;
> >>  	struct page **pts;
> >>  	struct page *pd;
> >> @@ -308,6 +310,8 @@ static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type)
> >>  		return NULL;
> >>  	}
> >>  
> >> +	spin_lock_init(&as->lock);
> >> +
> >>  	/* setup aperture */
> >>  	as->domain.geometry.aperture_start = 0;
> >>  	as->domain.geometry.aperture_end = 0xffffffff;
> >> @@ -578,7 +582,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova,
> >>  		struct page *page;
> >>  		dma_addr_t dma;
> >>  
> >> -		page = alloc_page(GFP_KERNEL | __GFP_DMA | __GFP_ZERO);
> >> +		page = alloc_page(GFP_ATOMIC | __GFP_DMA | __GFP_ZERO);
> > 
> > I'm not sure this is a good idea. My recollection is that GFP_ATOMIC
> > will allocate from a special reserved region of memory, which may be
> > easily exhausted.
> 
> So far I haven't noticed any problems. Will be great if you could
> provide more details about the pool size and how this exhaustion problem
> could be reproduced in practice.

I can't exactly pinpoint where that pool is created nor what its size
is, but just from looking at:

	Documentation/core-api/memory-allocation.rst

and searching for GFP_ATOMIC it says:

  * If you think that accessing memory reserves is justified and the kernel
    will be stressed unless allocation succeeds, you may use ``GFP_ATOMIC``.

That doesn't sound like the case that we're running into here. It sounds
to me like GFP_ATOMIC should really only be used in cases where
interrupts are being processed or where allocations really need to be
successful to ensure the system keeps operational (i.e. during handling
of severe errors, ...). Failure to allocate a page directory is hardly
very critical.

> > Is there any reason why we need the spinlock? Can't we use a mutex
> > instead?
> 
> This is what other IOMMU drivers do. I guess mutex might be too
> expensive, it may create a noticeable contention which you don't want to
> have in a case of a GPU submission code path.

I see indeed that many other drivers seem to heavily make use of
GFP_ATOMIC. But that doesn't necessarily mean that it's the right thing
to do on Tegra (or for the other drivers for that matter).

Do we have a good way to find out how bad exactly the contention would
be when using a mutex?

> I also suspect that drivers of other platforms are using IOMMU API in
> interrupt context, although today this is not needed for Tegra.
> 
> >>  		if (!page)
> >>  			return NULL;
> >>  
> >> @@ -655,8 +659,9 @@ static void tegra_smmu_set_pte(struct tegra_smmu_as *as, unsigned long iova,
> >>  	smmu_flush(smmu);
> >>  }
> >>  
> >> -static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
> >> -			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
> >> +static int
> >> +tegra_smmu_map_locked(struct iommu_domain *domain, unsigned long iova,
> >> +		      phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
> > 
> > I think it's more typical to use the _unlocked suffix for functions that
> > don't take a lock themselves.
> 
> Personally I can't feel the difference. Both variants are good to me. I
> can replace the literal postfix with a __tegra_smmu prefix, similarly to
> what we have in the GART driver, to avoid bikeshedding.

In my opinion a function name is most useful when it describes what the
function does. To me, an _unlocked suffix indicates that the function
itself doesn't lock, and therefore it needs to be called with some
specific lock already held.

Conversely, a _locked suffix indicates that the function will itself
take that specific lock and hence it can be used without taking any
extra care about locking.

With that interpretation the code below (taking a lock and then calling
a "_locked" function) seems like a bug on the surface.

Admittedly that's somewhat subjective. The __ prefix seems like a good
way to avoid some of the ambiguity, since it's probably an even more
common  theme.

> 
> >>  {
> >>  	struct tegra_smmu_as *as = to_smmu_as(domain);
> >>  	dma_addr_t pte_dma;
> >> @@ -685,8 +690,9 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
> >>  	return 0;
> >>  }
> >>  
> >> -static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
> >> -			       size_t size, struct iommu_iotlb_gather *gather)
> >> +static size_t
> >> +tegra_smmu_unmap_locked(struct iommu_domain *domain, unsigned long iova,
> >> +			size_t size, struct iommu_iotlb_gather *gather)
> >>  {
> >>  	struct tegra_smmu_as *as = to_smmu_as(domain);
> >>  	dma_addr_t pte_dma;
> >> @@ -702,6 +708,33 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
> >>  	return size;
> >>  }
> >>  
> >> +static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
> >> +			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
> >> +{
> >> +	struct tegra_smmu_as *as = to_smmu_as(domain);
> >> +	unsigned long flags;
> >> +	int ret;
> >> +
> >> +	spin_lock_irqsave(&as->lock, flags);
> >> +	ret = tegra_smmu_map_locked(domain, iova, paddr, size, prot, gfp);
> >> +	spin_unlock_irqrestore(&as->lock, flags);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
> >> +			       size_t size, struct iommu_iotlb_gather *gather)
> >> +{
> >> +	struct tegra_smmu_as *as = to_smmu_as(domain);
> >> +	unsigned long flags;
> >> +
> >> +	spin_lock_irqsave(&as->lock, flags);
> >> +	size = tegra_smmu_unmap_locked(domain, iova, size, gather);
> >> +	spin_unlock_irqrestore(&as->lock, flags);
> >> +
> >> +	return size;
> >> +}
> > 
> > Why the extra functions here? We never call locked vs. unlocked variants
> > in the driver and the IOMMU framework only has a single callback, so I
> > think the locking can just move into the main implementation.
> 
> Because this makes code cleaner, easier to read and follow. You don't
> need to care about handling error code paths. This is the same what we
> do in the GART driver. Pretty much every other IOMMU driver use this
> code pattern as well.

Alright, I don't mind very much. It just seemed odd to have that extra
function that doesn't really do anything.

Thierry

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 156 bytes --]

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v1] iommu/tegra-smmu: Add missing locks around mapping operations
  2020-05-25 12:20     ` Thierry Reding
@ 2020-05-25 19:34       ` Dmitry Osipenko
  0 siblings, 0 replies; 5+ messages in thread
From: Dmitry Osipenko @ 2020-05-25 19:34 UTC (permalink / raw)
  To: Thierry Reding; +Cc: linux-tegra, linux-kernel, iommu, Jonathan Hunter

25.05.2020 15:20, Thierry Reding пишет:
...
> Do we have a good way to find out how bad exactly the contention would
> be when using a mutex?

I'm now having a second thought about it. We don't need to care about
that scenario at all because it's a software-design defect of the
upstream Host1x driver that it maps gathers dynamically. The defect can
be fixed and then the potential problem won't exist at all.

Neither of the drivers that are using Tegra SMMU need the IOMMU mapping
operations to be performed under spinlock, so mutex will be good a
variant. I'll make a v2 with a mutex, thank you for the suggestion.
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-05-25 19:34 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-24 18:37 [PATCH v1] iommu/tegra-smmu: Add missing locks around mapping operations Dmitry Osipenko
2020-05-25  8:35 ` Thierry Reding
2020-05-25 10:51   ` Dmitry Osipenko
2020-05-25 12:20     ` Thierry Reding
2020-05-25 19:34       ` Dmitry Osipenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).