All of lore.kernel.org
 help / color / mirror / Atom feed
From: Qian Cai <cai@lca.pw>
To: Joerg Roedel <jroedel@suse.de>
Cc: don.brace@microsemi.com, iommu@lists.linux-foundation.org,
	esc.storagedev@microsemi.com, hch@lst.de,
	linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH] iommu/amd: fix a race in increase_address_space()
Date: Thu, 05 Sep 2019 16:57:21 -0400	[thread overview]
Message-ID: <1567717041.5576.102.camel@lca.pw> (raw)
In-Reply-To: <20190905114339.GC5457@suse.de>

On Thu, 2019-09-05 at 13:43 +0200, Joerg Roedel wrote:
> Hi Qian,
> 
> On Wed, Sep 04, 2019 at 05:24:22PM -0400, Qian Cai wrote:
> > 	if (domain->mode == PAGE_MODE_6_LEVEL)
> > 		/* address space already 64 bit large */
> > 		return false;
> > 
> > This gives a clue that there must be a race between multiple concurrent
> > threads in increase_address_space().
> 
> Thanks for tracking this down, there is a race indeed.
> 
> > +	mutex_lock(&domain->api_lock);
> >  	*dma_addr = __map_single(dev, dma_dom, page_to_phys(page),
> >  				 size, DMA_BIDIRECTIONAL, dma_mask);
> > +	mutex_unlock(&domain->api_lock);
> >  
> >  	if (*dma_addr == DMA_MAPPING_ERROR)
> >  		goto out_free;
> > @@ -2696,7 +2698,9 @@ static void free_coherent(struct device *dev, size_t size,
> >  
> >  	dma_dom = to_dma_ops_domain(domain);
> >  
> > +	mutex_lock(&domain->api_lock);
> >  	__unmap_single(dma_dom, dma_addr, size, DMA_BIDIRECTIONAL);
> > +	mutex_unlock(&domain->api_lock);
> 
> But I think the right fix is to lock the operation in
> increase_address_space() directly, and not the calls around it, like in
> the diff below. It is untested, so can you please try it and report back
> if it fixes your issue?

Yes, it works great so far.

> 
> diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
> index b607a92791d3..1ff705f16239 100644
> --- a/drivers/iommu/amd_iommu.c
> +++ b/drivers/iommu/amd_iommu.c
> @@ -1424,18 +1424,21 @@ static void free_pagetable(struct protection_domain *domain)
>   * another level increases the size of the address space by 9 bits to a size up
>   * to 64 bits.
>   */
> -static bool increase_address_space(struct protection_domain *domain,
> +static void increase_address_space(struct protection_domain *domain,
>  				   gfp_t gfp)
>  {
> +	unsigned long flags;
>  	u64 *pte;
>  
> -	if (domain->mode == PAGE_MODE_6_LEVEL)
> +	spin_lock_irqsave(&domain->lock, flags);
> +
> +	if (WARN_ON_ONCE(domain->mode == PAGE_MODE_6_LEVEL))
>  		/* address space already 64 bit large */
> -		return false;
> +		goto out;
>  
>  	pte = (void *)get_zeroed_page(gfp);
>  	if (!pte)
> -		return false;
> +		goto out;
>  
>  	*pte             = PM_LEVEL_PDE(domain->mode,
>  					iommu_virt_to_phys(domain->pt_root));
> @@ -1443,7 +1446,10 @@ static bool increase_address_space(struct protection_domain *domain,
>  	domain->mode    += 1;
>  	domain->updated  = true;
>  
> -	return true;
> +out:
> +	spin_unlock_irqrestore(&domain->lock, flags);
> +
> +	return;
>  }
>  
>  static u64 *alloc_pte(struct protection_domain *domain,
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

WARNING: multiple messages have this Message-ID (diff)
From: Qian Cai <cai@lca.pw>
To: Joerg Roedel <jroedel@suse.de>
Cc: hch@lst.de, iommu@lists.linux-foundation.org,
	don.brace@microsemi.com, esc.storagedev@microsemi.com,
	linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH] iommu/amd: fix a race in increase_address_space()
Date: Thu, 05 Sep 2019 16:57:21 -0400	[thread overview]
Message-ID: <1567717041.5576.102.camel@lca.pw> (raw)
In-Reply-To: <20190905114339.GC5457@suse.de>

On Thu, 2019-09-05 at 13:43 +0200, Joerg Roedel wrote:
> Hi Qian,
> 
> On Wed, Sep 04, 2019 at 05:24:22PM -0400, Qian Cai wrote:
> > 	if (domain->mode == PAGE_MODE_6_LEVEL)
> > 		/* address space already 64 bit large */
> > 		return false;
> > 
> > This gives a clue that there must be a race between multiple concurrent
> > threads in increase_address_space().
> 
> Thanks for tracking this down, there is a race indeed.
> 
> > +	mutex_lock(&domain->api_lock);
> >  	*dma_addr = __map_single(dev, dma_dom, page_to_phys(page),
> >  				 size, DMA_BIDIRECTIONAL, dma_mask);
> > +	mutex_unlock(&domain->api_lock);
> >  
> >  	if (*dma_addr == DMA_MAPPING_ERROR)
> >  		goto out_free;
> > @@ -2696,7 +2698,9 @@ static void free_coherent(struct device *dev, size_t size,
> >  
> >  	dma_dom = to_dma_ops_domain(domain);
> >  
> > +	mutex_lock(&domain->api_lock);
> >  	__unmap_single(dma_dom, dma_addr, size, DMA_BIDIRECTIONAL);
> > +	mutex_unlock(&domain->api_lock);
> 
> But I think the right fix is to lock the operation in
> increase_address_space() directly, and not the calls around it, like in
> the diff below. It is untested, so can you please try it and report back
> if it fixes your issue?

Yes, it works great so far.

> 
> diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
> index b607a92791d3..1ff705f16239 100644
> --- a/drivers/iommu/amd_iommu.c
> +++ b/drivers/iommu/amd_iommu.c
> @@ -1424,18 +1424,21 @@ static void free_pagetable(struct protection_domain *domain)
>   * another level increases the size of the address space by 9 bits to a size up
>   * to 64 bits.
>   */
> -static bool increase_address_space(struct protection_domain *domain,
> +static void increase_address_space(struct protection_domain *domain,
>  				   gfp_t gfp)
>  {
> +	unsigned long flags;
>  	u64 *pte;
>  
> -	if (domain->mode == PAGE_MODE_6_LEVEL)
> +	spin_lock_irqsave(&domain->lock, flags);
> +
> +	if (WARN_ON_ONCE(domain->mode == PAGE_MODE_6_LEVEL))
>  		/* address space already 64 bit large */
> -		return false;
> +		goto out;
>  
>  	pte = (void *)get_zeroed_page(gfp);
>  	if (!pte)
> -		return false;
> +		goto out;
>  
>  	*pte             = PM_LEVEL_PDE(domain->mode,
>  					iommu_virt_to_phys(domain->pt_root));
> @@ -1443,7 +1446,10 @@ static bool increase_address_space(struct protection_domain *domain,
>  	domain->mode    += 1;
>  	domain->updated  = true;
>  
> -	return true;
> +out:
> +	spin_unlock_irqrestore(&domain->lock, flags);
> +
> +	return;
>  }
>  
>  static u64 *alloc_pte(struct protection_domain *domain,

  reply	other threads:[~2019-09-05 20:57 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-09-04 21:24 [RFC PATCH] iommu/amd: fix a race in increase_address_space() Qian Cai
2019-09-04 21:24 ` Qian Cai
2019-09-05 11:43 ` Joerg Roedel
2019-09-05 11:43   ` Joerg Roedel
2019-09-05 20:57   ` Qian Cai [this message]
2019-09-05 20:57     ` Qian Cai
2019-09-06  8:56     ` [PATCH] iommu/amd: Fix " Joerg Roedel
2019-09-06  8:56       ` Joerg Roedel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1567717041.5576.102.camel@lca.pw \
    --to=cai@lca.pw \
    --cc=don.brace@microsemi.com \
    --cc=esc.storagedev@microsemi.com \
    --cc=hch@lst.de \
    --cc=iommu@lists.linux-foundation.org \
    --cc=jroedel@suse.de \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.