All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] xen/passthrough: Support a single iommu_domain(context bank) per xen domain per SMMU
@ 2015-03-19 18:25 Robbie VanVossen
  2015-03-20 12:39 ` Julien Grall
  0 siblings, 1 reply; 4+ messages in thread
From: Robbie VanVossen @ 2015-03-19 18:25 UTC (permalink / raw)
  To: xen-devel
  Cc: julien.grall, edgar.iglesias, Josh.Whitehead, Ian.Campbell,
	Stefano.Stabellini

If multiple devices are being passed through to the same domain and they
share a single SMMU, then they only require a single iommu_domain.

In arm_smmu_assign_dev, before a new iommu_domain is created, the
xen_domain->contexts is checked for any iommu_domains that are already
assigned to device that uses the same SMMU as the current device. If one
is found, attach the device to that iommu_domain. If a new one isn't
found, create a new iommu_domain just like before.

The arm_smmu_deassign_dev function assumes that there is a single
device per iommu_domain. This meant that when the first device was
deassigned, the iommu_domain was freed and when another device was
deassigned a crash occured in xen.

To fix this, a reference counter was added to the iommu_domain struct.
When an arm_smmu_xen_device references an iommu_domain, the
iommu_domains ref is incremented. When that reference is removed, the
iommu_domains ref is decremented. The iommu_domain will only be freed
when the ref is 0.

Signed-off-by: Robbie VanVossen <robert.vanvossen@dornerworks.com>
---
 xen/drivers/passthrough/arm/smmu.c |  113 ++++++++++++++++++++++++++++--------
 1 file changed, 88 insertions(+), 25 deletions(-)

diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
index a7a7da9..9b46054 100644
--- a/xen/drivers/passthrough/arm/smmu.c
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -223,6 +223,7 @@ struct iommu_domain
 	/* Runtime SMMU configuration for this iommu_domain */
 	struct arm_smmu_domain		*priv;
 
+	atomic_t ref;
 	/* Used to link iommu_domain contexts for a same domain.
 	 * There is at least one per-SMMU to used by the domain.
 	 * */
@@ -315,6 +316,26 @@ static struct iommu_group *iommu_group_get(struct device *dev)
 
 #define iommu_group_get_iommudata(group) (group)->cfg
 
+static int iommu_domain_add_device(struct iommu_domain *domain,
+				  struct device *dev)
+{
+	dev_iommu_domain(dev) = domain;
+
+	atomic_inc(&domain->ref);
+
+	return 0;
+}
+
+static int iommu_domain_remove_device(struct iommu_domain *domain,
+				  struct device *dev)
+{
+	dev_iommu_domain(dev) = NULL;
+
+	atomic_dec(&domain->ref);
+
+	return 0;
+}
+
 /***** Start of Linux SMMU code *****/
 
 /* Maximum number of stream IDs assigned to a single device */
@@ -1583,7 +1604,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
 
 	if (!ret)
-		dev_iommu_domain(dev) = domain;
+		ret = iommu_domain_add_device(domain, dev);
 	return ret;
 }
 
@@ -1596,7 +1617,7 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
 	if (!cfg)
 		return;
 
-	dev_iommu_domain(dev) = NULL;
+	iommu_domain_remove_device(domain, dev);
 	arm_smmu_domain_remove_master(smmu_domain, cfg);
 }
 
@@ -2569,7 +2590,9 @@ static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
 {
 	struct iommu_domain *domain;
 	struct arm_smmu_xen_domain *xen_domain;
+	struct arm_smmu_device *smmu;
 	int ret;
+	int existing_ctxt_fnd = 0;
 
 	xen_domain = domain_hvm_iommu(d)->arch.priv;
 
@@ -2585,29 +2608,65 @@ static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
 			return ret;
 	}
 
-	/*
-	 * TODO: Share the context bank (i.e iommu_domain) when the device is
-	 * under the same SMMU as another device assigned to this domain.
-	 * Would it useful for PCI
+	/* 
+	 * Check to see if a context bank (iommu_domain) already exists for this xen domain
+	 * under the same SMMU 
 	 */
-	domain = xzalloc(struct iommu_domain);
-	if (!domain)
-		return -ENOMEM;
+	if (!list_empty(&xen_domain->contexts)) {
+		smmu = find_smmu_for_device(dev);
+		if (!smmu) {
+			dev_err(dev, "cannot find SMMU\n");
+			return -ENXIO;
+		}
 
-	ret = arm_smmu_domain_init(domain);
-	if (ret)
-		goto err_dom_init;
+		/* Loop through the &xen_domain->contexts to locate a context assigned to this SMMU */
+		spin_lock(&xen_domain->lock);
+		list_for_each_entry(domain, &xen_domain->contexts, list) {
+			if(domain->priv->smmu == smmu)
+			{
+				/* We have found a context already associated with the same xen domain and SMMU */
+				ret = arm_smmu_attach_dev(domain, dev);
+				if (ret) {
+					/* 
+					 * TODO: If arm_smmu_attach_dev fails, should we perform arm_smmu_domain_destroy,
+					 * eventhough another smmu_master is configured correctly? If Not, what error 
+					 * code should we use
+					 */
+					dev_err(dev, "cannot attach device to already existing iommu_domain\n");
+					return -ENXIO;
+				}
+
+				existing_ctxt_fnd = 1;
+				break;
+			}
+			
+		}
+		spin_unlock(&xen_domain->lock);
+	}
+	
+	if(!existing_ctxt_fnd){
 
-	domain->priv->cfg.domain = d;
+		domain = xzalloc(struct iommu_domain);
+		if (!domain)
+			return -ENOMEM;
 
-	ret = arm_smmu_attach_dev(domain, dev);
-	if (ret)
-		goto err_attach_dev;
 
-	spin_lock(&xen_domain->lock);
-	/* Chain the new context to the domain */
-	list_add(&domain->list, &xen_domain->contexts);
-	spin_unlock(&xen_domain->lock);
+		ret = arm_smmu_domain_init(domain);
+		if (ret)
+			goto err_dom_init;
+
+		domain->priv->cfg.domain = d;
+
+		ret = arm_smmu_attach_dev(domain, dev);
+		if (ret)
+			goto err_attach_dev;
+
+		spin_lock(&xen_domain->lock);
+		/* Chain the new context to the domain */
+		list_add(&domain->list, &xen_domain->contexts);
+		spin_unlock(&xen_domain->lock);
+		
+	}
 
 	return 0;
 
@@ -2633,12 +2692,16 @@ static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
 
 	arm_smmu_detach_dev(domain, dev);
 
-	spin_lock(&xen_domain->lock);
-	list_del(&domain->list);
-	spin_unlock(&xen_domain->lock);
+	if (domain->ref.counter == 0)
+	{
 
-	arm_smmu_domain_destroy(domain);
-	xfree(domain);
+		spin_lock(&xen_domain->lock);
+		list_del(&domain->list);
+		spin_unlock(&xen_domain->lock);
+
+		arm_smmu_domain_destroy(domain);
+		xfree(domain);
+	}
 
 	return 0;
 }
-- 
1.7.9.5

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

* Re: [PATCH] xen/passthrough: Support a single iommu_domain(context bank) per xen domain per SMMU
  2015-03-19 18:25 [PATCH] xen/passthrough: Support a single iommu_domain(context bank) per xen domain per SMMU Robbie VanVossen
@ 2015-03-20 12:39 ` Julien Grall
  2015-03-20 13:56   ` Robert VanVossen
  0 siblings, 1 reply; 4+ messages in thread
From: Julien Grall @ 2015-03-20 12:39 UTC (permalink / raw)
  To: Robbie VanVossen, xen-devel
  Cc: julien.grall, edgar.iglesias, Josh.Whitehead, Ian.Campbell,
	Stefano.Stabellini

Hello Robert,

Thank you for the patch.

The commit title is too long (over 80 characters).

On 19/03/2015 18:25, Robbie VanVossen wrote:
> If multiple devices are being passed through to the same domain and they
> share a single SMMU, then they only require a single iommu_domain.
>
> In arm_smmu_assign_dev, before a new iommu_domain is created, the
> xen_domain->contexts is checked for any iommu_domains that are already
> assigned to device that uses the same SMMU as the current device. If one
> is found, attach the device to that iommu_domain. If a new one isn't
> found, create a new iommu_domain just like before.
>
> The arm_smmu_deassign_dev function assumes that there is a single
> device per iommu_domain. This meant that when the first device was
> deassigned, the iommu_domain was freed and when another device was
> deassigned a crash occured in xen.
>
> To fix this, a reference counter was added to the iommu_domain struct.
> When an arm_smmu_xen_device references an iommu_domain, the
> iommu_domains ref is incremented. When that reference is removed, the
> iommu_domains ref is decremented. The iommu_domain will only be freed
> when the ref is 0.
>
> Signed-off-by: Robbie VanVossen <robert.vanvossen@dornerworks.com>
> ---
>   xen/drivers/passthrough/arm/smmu.c |  113 ++++++++++++++++++++++++++++--------
>   1 file changed, 88 insertions(+), 25 deletions(-)
>
> diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
> index a7a7da9..9b46054 100644

[..]

> @@ -1596,7 +1617,7 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
>   	if (!cfg)
>   		return;
>
> -	dev_iommu_domain(dev) = NULL;
> +	iommu_domain_remove_device(domain, dev);
>   	arm_smmu_domain_remove_master(smmu_domain, cfg);
>   }

I'd like to avoid modifying arm_smmu_attach_dev and arm_smmu_detach_dev 
as it's part of the Linux code.

I think you can increment the reference counter in arm_smmu_assign_dev 
and arm_smmu_deassign_dev. That would avoid some possible race condition 
(see below).

> @@ -2569,7 +2590,9 @@ static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
>   {
>   	struct iommu_domain *domain;
>   	struct arm_smmu_xen_domain *xen_domain;
> +	struct arm_smmu_device *smmu;
>   	int ret;
> +	int existing_ctxt_fnd = 0;
>
>   	xen_domain = domain_hvm_iommu(d)->arch.priv;
>
> @@ -2585,29 +2608,65 @@ static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
>   			return ret;
>   	}
>
> -	/*
> -	 * TODO: Share the context bank (i.e iommu_domain) when the device is
> -	 * under the same SMMU as another device assigned to this domain.
> -	 * Would it useful for PCI
> +	/*
> +	 * Check to see if a context bank (iommu_domain) already exists for this xen domain
> +	 * under the same SMMU

A general comment for coding style within this file. A line should not 
be more than 80 characters long.

>   	 */
> -	domain = xzalloc(struct iommu_domain);
> -	if (!domain)
> -		return -ENOMEM;
> +	if (!list_empty(&xen_domain->contexts)) {
> +		smmu = find_smmu_for_device(dev);
> +		if (!smmu) {
> +			dev_err(dev, "cannot find SMMU\n");
> +			return -ENXIO;
> +		}
>
> -	ret = arm_smmu_domain_init(domain);
> -	if (ret)
> -		goto err_dom_init;
> +		/* Loop through the &xen_domain->contexts to locate a context assigned to this SMMU */
> +		spin_lock(&xen_domain->lock);
> +		list_for_each_entry(domain, &xen_domain->contexts, list) {

The insertion of a new iommu_domain is done after the device is 
attached, which is not protected by xen_domain->lock.

Therefore it may be possible to end up with 2 iommu_domain on the same SMMU.

> +			if(domain->priv->smmu == smmu)
> +			{
> +				/* We have found a context already associated with the same xen domain and SMMU */
> +				ret = arm_smmu_attach_dev(domain, dev);
> +				if (ret) {
> +					/*
> +					 * TODO: If arm_smmu_attach_dev fails, should we perform arm_smmu_domain_destroy,
> +					 * eventhough another smmu_master is configured correctly? If Not, what error
> +					 * code should we use
> +					 */

The failure may be because we don't have any stream register available. 
So I don't think we should detach all the other devices within the same 
context.

> +					dev_err(dev, "cannot attach device to already existing iommu_domain\n");
> +					return -ENXIO;
> +				}
> +
> +				existing_ctxt_fnd = 1;
> +				break;
> +			}
> +			
> +		}
> +		spin_unlock(&xen_domain->lock);
> +	}
> +	
> +	if(!existing_ctxt_fnd){

if (!existing_ctx_fnd) {

[..]

> @@ -2633,12 +2692,16 @@ static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
>
>   	arm_smmu_detach_dev(domain, dev);
>
> -	spin_lock(&xen_domain->lock);
> -	list_del(&domain->list);
> -	spin_unlock(&xen_domain->lock);
> +	if (domain->ref.counter == 0)
> +	{

There is a possible race with the previous function. arm_smmu_assign_dev 
still have time to increment domain->ref and we will free a domain with 
1 device assigned.

Overall, I think the 2 functions should be completely protected by the 
xen_domain->lock.

Regards,

-- 
Julien Grall

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

* Re: [PATCH] xen/passthrough: Support a single iommu_domain(context bank) per xen domain per SMMU
  2015-03-20 12:39 ` Julien Grall
@ 2015-03-20 13:56   ` Robert VanVossen
  2015-03-20 15:29     ` Julien Grall
  0 siblings, 1 reply; 4+ messages in thread
From: Robert VanVossen @ 2015-03-20 13:56 UTC (permalink / raw)
  To: Julien Grall, xen-devel
  Cc: julien.grall, edgar.iglesias, Josh.Whitehead, Ian.Campbell,
	Stefano.Stabellini



On 3/20/2015 8:39 AM, Julien Grall wrote:
> Hello Robert,
> 
> Thank you for the patch.
> 

No problem. :)

>>   xen/drivers/passthrough/arm/smmu.c |  113 ++++++++++++++++++++++++++++--------
>>   1 file changed, 88 insertions(+), 25 deletions(-)
>>
>> diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
>> index a7a7da9..9b46054 100644
> 
> [..]
> 
>> @@ -1596,7 +1617,7 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
>>   	if (!cfg)
>>   		return;
>>
>> -	dev_iommu_domain(dev) = NULL;
>> +	iommu_domain_remove_device(domain, dev);
>>   	arm_smmu_domain_remove_master(smmu_domain, cfg);
>>   }
> 
> I'd like to avoid modifying arm_smmu_attach_dev and arm_smmu_detach_dev 
> as it's part of the Linux code.
> 
> I think you can increment the reference counter in arm_smmu_assign_dev 
> and arm_smmu_deassign_dev. That would avoid some possible race condition 
> (see below).
> 

I can definitely increment the reference counter in arm_smmu_assign_dev, but
arm_smmu_detach_dev doesn't return any results, so there isn't a guarantee that
the iommu_domain has actually been dereferenced for the device.

>> +			if(domain->priv->smmu == smmu)
>> +			{
>> +				/* We have found a context already associated with the same xen domain and SMMU */
>> +				ret = arm_smmu_attach_dev(domain, dev);
>> +				if (ret) {
>> +					/*
>> +					 * TODO: If arm_smmu_attach_dev fails, should we perform arm_smmu_domain_destroy,
>> +					 * eventhough another smmu_master is configured correctly? If Not, what error
>> +					 * code should we use
>> +					 */
> 
> The failure may be because we don't have any stream register available. 
> So I don't think we should detach all the other devices within the same 
> context.
> 

Agreed.

>> +					dev_err(dev, "cannot attach device to already existing iommu_domain\n");
>> +					return -ENXIO;
>> +				}

Is this an appropriate return error?

>> +
>> +				existing_ctxt_fnd = 1;
>> +				break;
>> +			}
>> +			
>> +		}
>> +		spin_unlock(&xen_domain->lock);
>> +	}
>> +	
>> +	if(!existing_ctxt_fnd){
> 
> if (!existing_ctx_fnd) {
> 
> [..]
> 
>> @@ -2633,12 +2692,16 @@ static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
>>
>>   	arm_smmu_detach_dev(domain, dev);
>>
>> -	spin_lock(&xen_domain->lock);
>> -	list_del(&domain->list);
>> -	spin_unlock(&xen_domain->lock);
>> +	if (domain->ref.counter == 0)
>> +	{
> 
> There is a possible race with the previous function. arm_smmu_assign_dev 
> still have time to increment domain->ref and we will free a domain with 
> 1 device assigned.
> 
> Overall, I think the 2 functions should be completely protected by the 
> xen_domain->lock.

Agreed. I will move the locks around.

Thanks,
Robbie VanVossen

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

* Re: [PATCH] xen/passthrough: Support a single iommu_domain(context bank) per xen domain per SMMU
  2015-03-20 13:56   ` Robert VanVossen
@ 2015-03-20 15:29     ` Julien Grall
  0 siblings, 0 replies; 4+ messages in thread
From: Julien Grall @ 2015-03-20 15:29 UTC (permalink / raw)
  To: Robert VanVossen, xen-devel
  Cc: julien.grall, edgar.iglesias, Josh.Whitehead, Ian.Campbell,
	Stefano.Stabellini



On 20/03/2015 13:56, Robert VanVossen wrote:

>>>    xen/drivers/passthrough/arm/smmu.c |  113 ++++++++++++++++++++++++++++--------
>>>    1 file changed, 88 insertions(+), 25 deletions(-)
>>>
>>> diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
>>> index a7a7da9..9b46054 100644
>>
>> [..]
>>
>>> @@ -1596,7 +1617,7 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
>>>    	if (!cfg)
>>>    		return;
>>>
>>> -	dev_iommu_domain(dev) = NULL;
>>> +	iommu_domain_remove_device(domain, dev);
>>>    	arm_smmu_domain_remove_master(smmu_domain, cfg);
>>>    }
>>
>> I'd like to avoid modifying arm_smmu_attach_dev and arm_smmu_detach_dev
>> as it's part of the Linux code.
>>
>> I think you can increment the reference counter in arm_smmu_assign_dev
>> and arm_smmu_deassign_dev. That would avoid some possible race condition
>> (see below).
>>
>
> I can definitely increment the reference counter in arm_smmu_assign_dev, but
> arm_smmu_detach_dev doesn't return any results, so there isn't a guarantee that
> the iommu_domain has actually been dereferenced for the device.

AFAICT arm_smmu_detach_dev will only fail it's not able to find an 
iommu_group (i.e the list of Stream IDs) for a device.

On Xen case, the check in arm_smmu_deassign_dev guaranty us that the 
device is used by the SMMU and therefore an iommu_group exits for it.

So I think we can safely move the call in arm_smmu_detach_dev.

[..]

>>> +					dev_err(dev, "cannot attach device to already existing iommu_domain\n");
>>> +					return -ENXIO;
>>> +				}
>
> Is this an appropriate return error?

I would return the result of arm_smmu_attach_dev (i.e ret).

[..]

>>> @@ -2633,12 +2692,16 @@ static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
>>>
>>>    	arm_smmu_detach_dev(domain, dev);
>>>
>>> -	spin_lock(&xen_domain->lock);
>>> -	list_del(&domain->list);
>>> -	spin_unlock(&xen_domain->lock);
>>> +	if (domain->ref.counter == 0)
>>> +	{
>>
>> There is a possible race with the previous function. arm_smmu_assign_dev
>> still have time to increment domain->ref and we will free a domain with
>> 1 device assigned.
>>
>> Overall, I think the 2 functions should be completely protected by the
>> xen_domain->lock.
>
> Agreed. I will move the locks around.

Thanks.

Regards,

-- 
Julien Grall

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

end of thread, other threads:[~2015-03-20 15:29 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-03-19 18:25 [PATCH] xen/passthrough: Support a single iommu_domain(context bank) per xen domain per SMMU Robbie VanVossen
2015-03-20 12:39 ` Julien Grall
2015-03-20 13:56   ` Robert VanVossen
2015-03-20 15:29     ` Julien Grall

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.