mbox series

[v2,0/6] Make set_dev_pasid op supporting domain replacement

Message ID 20240912130427.10119-1-yi.l.liu@intel.com
Headers show
Series Make set_dev_pasid op supporting domain replacement | expand

Message

Liu, Yi L Sept. 12, 2024, 1:04 p.m. UTC
This splits the preparation works of the iommu and the Intel iommu driver
out from the iommufd pasid attach/replace series. [1]

To support domain replacement, the definition of the set_dev_pasid op
needs to be enhanced. Meanwhile, the existing set_dev_pasid callbacks
should be extended as well to suit the new definition.

This series first prepares the Intel iommu set_dev_pasid op for the new
definition, adds the missing set_dev_pasid support for nested domain, makes
ARM SMMUv3 set_dev_pasid op to suit the new definition, and in the end
enhances the definition of set_dev_pasid op. The AMD set_dev_pasid callback
is extended to fail if the caller tries to do domain replacement to meet the
new definition of set_dev_pasid op. AMD iommu driver would support it later
per Vasant [2].

[1] https://lore.kernel.org/linux-iommu/20240412081516.31168-1-yi.l.liu@intel.com/
[2] https://lore.kernel.org/linux-iommu/fa9c4fc3-9365-465e-8926-b4d2d6361b9c@amd.com/

v2:
 - Make ARM SMMUv3 set_dev_pasid op support domain replacement (Jason)
 - Drop patch 03 of v1 (Kevin)
 - Multiple tweaks in VT-d driver (Kevin)

v1: https://lore.kernel.org/linux-iommu/20240628085538.47049-1-yi.l.liu@intel.com/

Regards,
	Yi Liu

Jason Gunthorpe (1):
  iommu/arm-smmu-v3: Make smmuv3 set_dev_pasid() op support replace

Lu Baolu (1):
  iommu/vt-d: Add set_dev_pasid callback for nested domain

Yi Liu (4):
  iommu: Pass old domain to set_dev_pasid op
  iommu/vt-d: Move intel_drain_pasid_prq() into
    intel_pasid_tear_down_entry()
  iommu/vt-d: Make intel_iommu_set_dev_pasid() to handle domain
    replacement
  iommu: Make set_dev_pasid op support domain replacement

 drivers/iommu/amd/amd_iommu.h                 |   3 +-
 drivers/iommu/amd/pasid.c                     |   6 +-
 .../iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c   |   5 +-
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c   |   8 +-
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h   |   2 +-
 drivers/iommu/intel/iommu.c                   | 122 ++++++++++++------
 drivers/iommu/intel/iommu.h                   |   3 +
 drivers/iommu/intel/nested.c                  |   1 +
 drivers/iommu/intel/pasid.c                   |  13 +-
 drivers/iommu/intel/pasid.h                   |   8 +-
 drivers/iommu/intel/svm.c                     |   6 +-
 drivers/iommu/iommu.c                         |   3 +-
 include/linux/iommu.h                         |   5 +-
 13 files changed, 129 insertions(+), 56 deletions(-)

Comments

Baolu Lu Sept. 13, 2024, 1:42 a.m. UTC | #1
On 9/12/24 9:04 PM, Yi Liu wrote:
> @@ -4325,24 +4363,18 @@ static int intel_iommu_set_dev_pasid(struct iommu_domain *domain,
>   		ret = intel_pasid_setup_second_level(iommu, dmar_domain,
>   						     dev, pasid);
>   	if (ret)
> -		goto out_unassign_tag;
> +		goto out_undo_dev_pasid;
>   
> -	dev_pasid->dev = dev;
> -	dev_pasid->pasid = pasid;
> -	spin_lock_irqsave(&dmar_domain->lock, flags);
> -	list_add(&dev_pasid->link_domain, &dmar_domain->dev_pasids);
> -	spin_unlock_irqrestore(&dmar_domain->lock, flags);
> +	if (old)
> +		domain_remove_dev_pasid(old, dev, pasid);
>   
>   	if (domain->type & __IOMMU_DOMAIN_PAGING)
>   		intel_iommu_debugfs_create_dev_pasid(dev_pasid);
>   
>   	return 0;
> -out_unassign_tag:
> -	cache_tag_unassign_domain(dmar_domain, dev, pasid);
> -out_detach_iommu:
> -	domain_detach_iommu(dmar_domain, iommu);
> -out_free:
> -	kfree(dev_pasid);
> +
> +out_undo_dev_pasid:
> +	domain_remove_dev_pasid(domain, dev, pasid);
>   	return ret;
>   }

Do you need to re-install the old domain to the pasid entry in the
failure path?

Thanks,
baolu
Baolu Lu Sept. 13, 2024, 2:17 a.m. UTC | #2
On 9/13/24 9:35 AM, Baolu Lu wrote:
> On 9/12/24 9:04 PM, Yi Liu wrote:
>> set_dev_pasid op is going to support domain replacement and keep the old
>> hardware config if it fails. Make the Intel iommu driver be prepared for
>> it.
>>
>> Signed-off-by: Yi Liu <yi.l.liu@intel.com>
>> ---
>>   drivers/iommu/intel/iommu.c | 98 ++++++++++++++++++++++++-------------
>>   1 file changed, 65 insertions(+), 33 deletions(-)
>>
>> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
>> index 80b587de226d..6f5a8e549f3f 100644
>> --- a/drivers/iommu/intel/iommu.c
>> +++ b/drivers/iommu/intel/iommu.c
>> @@ -4248,8 +4248,8 @@ static int intel_iommu_iotlb_sync_map(struct 
>> iommu_domain *domain,
>>       return 0;
>>   }
>> -static void intel_iommu_remove_dev_pasid(struct device *dev, ioasid_t 
>> pasid,
>> -                     struct iommu_domain *domain)
>> +static void domain_remove_dev_pasid(struct iommu_domain *domain,
>> +                    struct device *dev, ioasid_t pasid)
>>   {
>>       struct device_domain_info *info = dev_iommu_priv_get(dev);
>>       struct dev_pasid_info *curr, *dev_pasid = NULL;
>> @@ -4257,11 +4257,6 @@ static void intel_iommu_remove_dev_pasid(struct 
>> device *dev, ioasid_t pasid,
>>       struct dmar_domain *dmar_domain;
>>       unsigned long flags;
>> -    if (domain->type == IOMMU_DOMAIN_IDENTITY) {
>> -        intel_pasid_tear_down_entry(iommu, dev, pasid, 0);
>> -        return;
>> -    }
>> -
>>       dmar_domain = to_dmar_domain(domain);
>>       spin_lock_irqsave(&dmar_domain->lock, flags);
>>       list_for_each_entry(curr, &dmar_domain->dev_pasids, link_domain) {
>> @@ -4278,13 +4273,24 @@ static void 
>> intel_iommu_remove_dev_pasid(struct device *dev, ioasid_t pasid,
>>       domain_detach_iommu(dmar_domain, iommu);
>>       intel_iommu_debugfs_remove_dev_pasid(dev_pasid);
>>       kfree(dev_pasid);
>> +}
>> +
>> +static void intel_iommu_remove_dev_pasid(struct device *dev, ioasid_t 
>> pasid,
>> +                     struct iommu_domain *domain)
>> +{
>> +    struct device_domain_info *info = dev_iommu_priv_get(dev);
>> +    struct intel_iommu *iommu = info->iommu;
>> +
>>       intel_pasid_tear_down_entry(iommu, dev, pasid,
>>                       INTEL_PASID_TEARDOWN_DRAIN_PRQ);
>> +    if (domain->type == IOMMU_DOMAIN_IDENTITY)
>> +        return;
> 
> The static identity domain is not capable of handling page requests.
> Therefore there is no need to drain PRQ for an identity domain removal.
> 
> So it probably should be something like this:
> 
>      if (domain->type == IOMMU_DOMAIN_IDENTITY) {
>          intel_pasid_tear_down_entry(iommu, dev, pasid, 0);
>          return;
>      }
> 
>      intel_pasid_tear_down_entry(iommu, dev, pasid,
>                                      INTEL_PASID_TEARDOWN_DRAIN_PRQ);

Just revisited this. It seems that we just need to drain PRQ if the
attached domain is iopf-capable. Therefore, how about making it like
this?

	unsigned int flags = 0;

	if (domain->iopf_handler)
		flags |= INTEL_PASID_TEARDOWN_DRAIN_PRQ;

	intel_pasid_tear_down_entry(iommu, dev, pasid, flags);

	/* Identity domain has no meta data for pasid. */
	if (domain->type == IOMMU_DOMAIN_IDENTITY)
		return;

Thanks,
baolu
Liu, Yi L Sept. 13, 2024, 12:18 p.m. UTC | #3
On 2024/9/13 10:17, Baolu Lu wrote:
> On 9/13/24 9:35 AM, Baolu Lu wrote:
>> On 9/12/24 9:04 PM, Yi Liu wrote:
>>> set_dev_pasid op is going to support domain replacement and keep the old
>>> hardware config if it fails. Make the Intel iommu driver be prepared for
>>> it.
>>>
>>> Signed-off-by: Yi Liu <yi.l.liu@intel.com>
>>> ---
>>>   drivers/iommu/intel/iommu.c | 98 ++++++++++++++++++++++++-------------
>>>   1 file changed, 65 insertions(+), 33 deletions(-)
>>>
>>> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
>>> index 80b587de226d..6f5a8e549f3f 100644
>>> --- a/drivers/iommu/intel/iommu.c
>>> +++ b/drivers/iommu/intel/iommu.c
>>> @@ -4248,8 +4248,8 @@ static int intel_iommu_iotlb_sync_map(struct 
>>> iommu_domain *domain,
>>>       return 0;
>>>   }
>>> -static void intel_iommu_remove_dev_pasid(struct device *dev, ioasid_t 
>>> pasid,
>>> -                     struct iommu_domain *domain)
>>> +static void domain_remove_dev_pasid(struct iommu_domain *domain,
>>> +                    struct device *dev, ioasid_t pasid)
>>>   {
>>>       struct device_domain_info *info = dev_iommu_priv_get(dev);
>>>       struct dev_pasid_info *curr, *dev_pasid = NULL;
>>> @@ -4257,11 +4257,6 @@ static void intel_iommu_remove_dev_pasid(struct 
>>> device *dev, ioasid_t pasid,
>>>       struct dmar_domain *dmar_domain;
>>>       unsigned long flags;
>>> -    if (domain->type == IOMMU_DOMAIN_IDENTITY) {
>>> -        intel_pasid_tear_down_entry(iommu, dev, pasid, 0);
>>> -        return;
>>> -    }
>>> -
>>>       dmar_domain = to_dmar_domain(domain);
>>>       spin_lock_irqsave(&dmar_domain->lock, flags);
>>>       list_for_each_entry(curr, &dmar_domain->dev_pasids, link_domain) {
>>> @@ -4278,13 +4273,24 @@ static void intel_iommu_remove_dev_pasid(struct 
>>> device *dev, ioasid_t pasid,
>>>       domain_detach_iommu(dmar_domain, iommu);
>>>       intel_iommu_debugfs_remove_dev_pasid(dev_pasid);
>>>       kfree(dev_pasid);
>>> +}
>>> +
>>> +static void intel_iommu_remove_dev_pasid(struct device *dev, ioasid_t 
>>> pasid,
>>> +                     struct iommu_domain *domain)
>>> +{
>>> +    struct device_domain_info *info = dev_iommu_priv_get(dev);
>>> +    struct intel_iommu *iommu = info->iommu;
>>> +
>>>       intel_pasid_tear_down_entry(iommu, dev, pasid,
>>>                       INTEL_PASID_TEARDOWN_DRAIN_PRQ);
>>> +    if (domain->type == IOMMU_DOMAIN_IDENTITY)
>>> +        return;
>>
>> The static identity domain is not capable of handling page requests.
>> Therefore there is no need to drain PRQ for an identity domain removal.
>>
>> So it probably should be something like this:
>>
>>      if (domain->type == IOMMU_DOMAIN_IDENTITY) {
>>          intel_pasid_tear_down_entry(iommu, dev, pasid, 0);
>>          return;
>>      }
>>
>>      intel_pasid_tear_down_entry(iommu, dev, pasid,
>>                                      INTEL_PASID_TEARDOWN_DRAIN_PRQ);
> 
> Just revisited this. It seems that we just need to drain PRQ if the
> attached domain is iopf-capable. Therefore, how about making it like
> this?
> 
>      unsigned int flags = 0;
> 
>      if (domain->iopf_handler)
>          flags |= INTEL_PASID_TEARDOWN_DRAIN_PRQ;
> 
>      intel_pasid_tear_down_entry(iommu, dev, pasid, flags);
> 
>      /* Identity domain has no meta data for pasid. */
>      if (domain->type == IOMMU_DOMAIN_IDENTITY)
>          return;
> 
got it.
Liu, Yi L Sept. 13, 2024, 12:21 p.m. UTC | #4
On 2024/9/13 09:42, Baolu Lu wrote:
> On 9/12/24 9:04 PM, Yi Liu wrote:
>> @@ -4325,24 +4363,18 @@ static int intel_iommu_set_dev_pasid(struct 
>> iommu_domain *domain,
>>           ret = intel_pasid_setup_second_level(iommu, dmar_domain,
>>                                dev, pasid);
>>       if (ret)
>> -        goto out_unassign_tag;
>> +        goto out_undo_dev_pasid;
>> -    dev_pasid->dev = dev;
>> -    dev_pasid->pasid = pasid;
>> -    spin_lock_irqsave(&dmar_domain->lock, flags);
>> -    list_add(&dev_pasid->link_domain, &dmar_domain->dev_pasids);
>> -    spin_unlock_irqrestore(&dmar_domain->lock, flags);
>> +    if (old)
>> +        domain_remove_dev_pasid(old, dev, pasid);
>>       if (domain->type & __IOMMU_DOMAIN_PAGING)
>>           intel_iommu_debugfs_create_dev_pasid(dev_pasid);
>>       return 0;
>> -out_unassign_tag:
>> -    cache_tag_unassign_domain(dmar_domain, dev, pasid);
>> -out_detach_iommu:
>> -    domain_detach_iommu(dmar_domain, iommu);
>> -out_free:
>> -    kfree(dev_pasid);
>> +
>> +out_undo_dev_pasid:
>> +    domain_remove_dev_pasid(domain, dev, pasid);
>>       return ret;
>>   }
> 
> Do you need to re-install the old domain to the pasid entry in the
> failure path?

yes, but no. The old domain is still installed in the pasid entry
when the failure happened. :)
Baolu Lu Sept. 14, 2024, 1:03 a.m. UTC | #5
On 9/13/24 8:21 PM, Yi Liu wrote:
> On 2024/9/13 09:42, Baolu Lu wrote:
>> On 9/12/24 9:04 PM, Yi Liu wrote:
>>> @@ -4325,24 +4363,18 @@ static int intel_iommu_set_dev_pasid(struct 
>>> iommu_domain *domain,
>>>           ret = intel_pasid_setup_second_level(iommu, dmar_domain,
>>>                                dev, pasid);
>>>       if (ret)
>>> -        goto out_unassign_tag;
>>> +        goto out_undo_dev_pasid;
>>> -    dev_pasid->dev = dev;
>>> -    dev_pasid->pasid = pasid;
>>> -    spin_lock_irqsave(&dmar_domain->lock, flags);
>>> -    list_add(&dev_pasid->link_domain, &dmar_domain->dev_pasids);
>>> -    spin_unlock_irqrestore(&dmar_domain->lock, flags);
>>> +    if (old)
>>> +        domain_remove_dev_pasid(old, dev, pasid);
>>>       if (domain->type & __IOMMU_DOMAIN_PAGING)
>>>           intel_iommu_debugfs_create_dev_pasid(dev_pasid);
>>>       return 0;
>>> -out_unassign_tag:
>>> -    cache_tag_unassign_domain(dmar_domain, dev, pasid);
>>> -out_detach_iommu:
>>> -    domain_detach_iommu(dmar_domain, iommu);
>>> -out_free:
>>> -    kfree(dev_pasid);
>>> +
>>> +out_undo_dev_pasid:
>>> +    domain_remove_dev_pasid(domain, dev, pasid);
>>>       return ret;
>>>   }
>>
>> Do you need to re-install the old domain to the pasid entry in the
>> failure path?
> 
> yes, but no. The old domain is still installed in the pasid entry
> when the failure happened. :)

I am afraid not. The old domain has already been cleaned up by
intel_pasid_tear_down_entry(). Or not?

Thanks,
baolu
Liu, Yi L Sept. 14, 2024, 3:03 a.m. UTC | #6
> On Sep 14, 2024, at 09:08, Baolu Lu <baolu.lu@linux.intel.com> wrote:
> 
> On 9/13/24 8:21 PM, Yi Liu wrote:
>>> On 2024/9/13 09:42, Baolu Lu wrote:
>>> On 9/12/24 9:04 PM, Yi Liu wrote:
>>>> @@ -4325,24 +4363,18 @@ static int intel_iommu_set_dev_pasid(struct iommu_domain *domain,
>>>>           ret = intel_pasid_setup_second_level(iommu, dmar_domain,
>>>>                                dev, pasid);
>>>>       if (ret)
>>>> -        goto out_unassign_tag;
>>>> +        goto out_undo_dev_pasid;
>>>> -    dev_pasid->dev = dev;
>>>> -    dev_pasid->pasid = pasid;
>>>> -    spin_lock_irqsave(&dmar_domain->lock, flags);
>>>> -    list_add(&dev_pasid->link_domain, &dmar_domain->dev_pasids);
>>>> -    spin_unlock_irqrestore(&dmar_domain->lock, flags);
>>>> +    if (old)
>>>> +        domain_remove_dev_pasid(old, dev, pasid);
>>>>       if (domain->type & __IOMMU_DOMAIN_PAGING)
>>>>           intel_iommu_debugfs_create_dev_pasid(dev_pasid);
>>>>       return 0;
>>>> -out_unassign_tag:
>>>> -    cache_tag_unassign_domain(dmar_domain, dev, pasid);
>>>> -out_detach_iommu:
>>>> -    domain_detach_iommu(dmar_domain, iommu);
>>>> -out_free:
>>>> -    kfree(dev_pasid);
>>>> +
>>>> +out_undo_dev_pasid:
>>>> +    domain_remove_dev_pasid(domain, dev, pasid);
>>>>       return ret;
>>>>   }
>>> 
>>> Do you need to re-install the old domain to the pasid entry in the
>>> failure path?
>> yes, but no. The old domain is still installed in the pasid entry
>> when the failure happened. :)
> 
> I am afraid not. The old domain has already been cleaned up by
> intel_pasid_tear_down_entry(). Or not?

oops, yes. The tear down was lifted to this function now instead of in the setup helpers. I may move them back to the setup helpers just like v1 does.

Good catch!

Regards,
Yi Liu