Enable PASID for DMA API users
Subsystems (5)
v3 cover Enable PASID for DMA API users
Some modern accelerators such as Intel's Data Streaming Accelerator (DSA) require PASID in DMA requests to be operational. Specifically, the work submissions with ENQCMD on shared work queues require PASIDs. The use cases include both user DMA with shared virtual addressing (SVA) and in-kernel DMA similar to legacy DMA w/o PASID. Here we address the latter.
DMA mapping API is the de facto standard for in-kernel DMA. However, it operates on a per device or Requester ID(RID) basis which is not PASID-aware. To leverage DMA API for devices relies on PASIDs, this patchset introduces the following APIs
1. A driver facing API that enables DMA API PASID usage:iommu_enable_pasid_dma(struct device *dev, ioasid_t &pasid);
2. An IOMMU op that allows attaching device-domain-PASID generically (will be used beyond DMA API PASID support)
Once PASID DMA is enabled and attached to the appropriate IOMMU domain, device drivers can continue to use DMA APIs as-is. There is no difference in terms of mapping in dma_handle between without PASID and with PASID. The DMA mapping performed by IOMMU will be identical for both requests, let it be IOVA or PA in case of pass-through.
In addition, this set converts DSA driver in-kernel DMA with PASID from SVA lib to DMA API. There have been security and functional issues with the kernel SVA approach:
(https://lore.kernel.org/linux-iommu/[email protected]/)
The highlights are as the following:
- The lack of IOTLB synchronization upon kernel page table updates. (vmalloc, module/BPF loading, CONFIG_DEBUG_PAGEALLOC etc.)
- Other than slight more protection, using kernel virtual address (KVA)
has little advantage over physical address. There are also no use cases yet where DMA engines need kernel virtual addresses for in-kernel DMA.
Subsequently, cleanup is done around the usage of sva_bind_device() for in-kernel DMA. Removing special casing code in VT-d driver and tightening SVA lib API.
This work and idea behind it is a collaboration with many people, many thanks to Baolu Lu, Jason Gunthorpe, Dave Jiang, and others.
ChangeLog:
v3
- Rebased on "Baolu's SVA and IOPF refactoring" series v5.
(https://github.com/LuBaolu/intel-iommu/commits/iommu-sva-refactoring-v5)
This version is significantly simplified by leveraging IOMMU domain ops, attach_dev_pasid() op is implemented differently on a DMA domain than on a SVA domain.
We currently have no need to support multiple PASIDs per DMA domain.
(https://lore.kernel.org/lkml/[email protected]/).
Removed PASID-device list from V2, a PASID field is introduced to struct iommu_domain instead. It is intended for DMA requests with PASID by all devices attached to the domain.
v2
- Do not reserve a special PASID for DMA API usage. Use IOASID allocation instead.
- Introduced a generic device-pasid-domain attachment IOMMU op. Replaced the DMA API only IOMMU op.
- Removed supervisor SVA support in VT-d
- Removed unused sva_bind_device parameters
- Use IOMMU specific data instead of struct device to store PASID info
Jacob Pan (4):
iommu/vt-d: Implement domain ops for attach_dev_pasid
iommu: Add PASID support for DMA mapping API users
dmaengine: idxd: Use DMA API for in-kernel DMA with PASID iommu/vt-d: Delete unused SVM flag
drivers/dma/idxd/idxd.h | 1 - drivers/dma/idxd/init.c | 34 +++--------- drivers/dma/idxd/sysfs.c | 7 --- drivers/iommu/dma-iommu.c | 107 ++++++++++++++++++++++++++++++++++++ drivers/iommu/intel/iommu.c | 81 ++++++++++++++++++++++++++- drivers/iommu/intel/svm.c | 2 +- include/linux/dma-iommu.h | 3 + include/linux/intel-iommu.h | 1 + include/linux/intel-svm.h | 13 ----- include/linux/iommu.h | 2 + 10 files changed, 202 insertions(+), 49 deletions(-)
Signature · 1 line
2.25.1
PATCH v3 1/4 iommu/vt-d: Implement domain ops for attach_dev_pasid
On VT-d platforms with scalable mode enabled, devices issue DMA requests with PASID need to attach PASIDs to given IOMMU domains. The attach operation involves the following:
- Programming the PASID into the device's PASID table
- Tracking device domain and the PASID relationship
- Managing IOTLB and device TLB invalidations
This patch add attach_dev_pasid functions to the default domain ops which is used by DMA and identity domain types. It could be extended to support other domain types whenever necessary.
--- drivers/iommu/intel/iommu.c | 81 ++++++++++++++++++++++++++++++++++++- include/linux/intel-iommu.h | 1 + 2 files changed, 80 insertions(+), 2 deletions(-)
drivers/iommu/intel/iommu.c +79 -2
@@ -1562,6 +1562,10 @@ static void __iommu_flush_dev_iotlb(struct device_domain_info *info, sid = info->bus <<8 | info->devfn; qdep = info->ats_qdep; +if (info->pasid) { +qi_flush_dev_iotlb_pasid (info->iommu, sid, info->pfsid, + info->pasid, qdep, addr, mask); + }qi_flush_dev_iotlb (info->iommu, sid, info->pfsid, qdep, addr, mask); }
@@ -1591,6 +1595,7 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,unsigned int mask =ilog2 (aligned_pages);uint64_t addr = (uint64_t )pfn <<VTD_PAGE_SHIFT ;u16 did = domain->iommu_did[iommu->seq_id]; +struct iommu_domain *iommu_domain = &domain->domain;BUG_ON (pages ==0 );
@@ -1599,6 +1604,9 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,if (domain_use_first_level (domain)) {qi_flush_piotlb (iommu, did,PASID_RID2PASID , addr, pages, ih); +/* flush additional kernel DMA PASIDs attached */ +if (iommu_domain->pasid) +qi_flush_piotlb (iommu, did, iommu_domain->pasid, addr, pages, ih); }else {unsigned long bitmask = aligned_pages -1 ;
@@ -4265,10 +4273,13 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info) domain = info->domain;if (info->dev && !dev_is_real_dma_subdevice (info->dev)) { -if (dev_is_pci (info->dev) &&sm_supported (iommu)) +if (dev_is_pci (info->dev) &&sm_supported (iommu)) {intel_pasid_tear_down_entry (iommu, info->dev,PASID_RID2PASID , false); - +if (info->pasid) +intel_pasid_tear_down_entry (iommu, info->dev, + info->pasid, false); + }iommu_disable_dev_iotlb (info);domain_context_clear (info);intel_pasid_free_table (info->dev);
@@ -4912,6 +4923,70 @@ static void intel_iommu_iotlb_sync_map(struct iommu_domain *domain, } } +static int intel_iommu_attach_dev_pasid (struct iommu_domain *domain, +struct device *dev, +ioasid_t pasid) +{ +struct device_domain_info *info =dev_iommu_priv_get (dev); +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct intel_iommu *iommu = info->iommu; +unsigned long flags; +int ret =0 ; + +if (!sm_supported (iommu) || !info) +return -ENODEV ; + +spin_lock_irqsave (&device_domain_lock, flags); +/* +* If the same device already has a PASID attached, just return. +* DMA layer will return the PASID value to the caller. +*/ +if (pasid !=PASID_RID2PASID && info->pasid) { +if (info->pasid == pasid) + ret =0 ; +else { +dev_warn (dev,"Cannot attach PASID %u, %u already attached\n" , + pasid, info->pasid); + ret = -EBUSY ; + } + goto out_unlock_domain; + } + +spin_lock (&iommu->lock); +if (hw_pass_through &&domain_type_is_si (dmar_domain)) + ret =intel_pasid_setup_pass_through (iommu, dmar_domain, + dev, pasid); +else if (domain_use_first_level (dmar_domain)) + ret =domain_setup_first_level (iommu, dmar_domain, + dev, pasid); +else + ret =intel_pasid_setup_second_level (iommu, dmar_domain, + dev, pasid); + +spin_unlock (&iommu->lock); +out_unlock_domain: +spin_unlock_irqrestore (&device_domain_lock, flags); +if (!ret) + info->pasid = pasid; + +return ret; +} + +static void intel_iommu_detach_dev_pasid (struct iommu_domain *domain, +struct device *dev, +ioasid_t pasid) +{ +struct device_domain_info *info =dev_iommu_priv_get (dev); +struct intel_iommu *iommu = info->iommu; +unsigned long flags; + +WARN_ON (info->pasid != pasid); +spin_lock_irqsave (&iommu->lock, flags); +intel_pasid_tear_down_entry (iommu, dev, pasid, false); + info->pasid =0 ; +spin_unlock_irqrestore (&iommu->lock, flags); +} +const struct iommu_ops intel_iommu_ops = { .capable = intel_iommu_capable, .domain_alloc = intel_iommu_domain_alloc,
@@ -4940,6 +5015,8 @@ const struct iommu_ops intel_iommu_ops = { .iova_to_phys = intel_iommu_iova_to_phys, .free = intel_iommu_domain_free, .enforce_cache_coherency = intel_iommu_enforce_cache_coherency, + .attach_dev_pasid = intel_iommu_attach_dev_pasid, + .detach_dev_pasid = intel_iommu_detach_dev_pasid, } };
include/linux/intel-iommu.h +1 -0
@@ -627,6 +627,7 @@ struct device_domain_info {struct intel_iommu *iommu;/* IOMMU used by this device */ struct dmar_domain *domain;/* pointer to domain */ struct pasid_table *pasid_table;/* pasid table */ +ioasid_t pasid;/* DMA request with PASID */ }; static inline void __iommu_flush_cache(
Signature · 1 line
2.25.1
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Quoted 9 lines · Jacob Pan
drivers/iommu/intel/iommu.c +9
-0+static int intel_iommu_attach_dev_pasid (struct iommu_domain *domain, +struct device *dev, +ioasid_t pasid) +{ +struct device_domain_info *info =dev_iommu_priv_get (dev); +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct intel_iommu *iommu = info->iommu; +unsigned long flags; +int ret =0 ;
drivers/iommu/intel/iommu.c static void intel_iommu_iotlb_sync_map(struct iommu_domain *domain, · line 4944
Quoted 10 lines
drivers/iommu/intel/iommu.c +10
-0+ +if (!sm_supported (iommu) || !info) +return -ENODEV ; + +spin_lock_irqsave (&device_domain_lock, flags); +/* +* If the same device already has a PASID attached, just return. +* DMA layer will return the PASID value to the caller. +*/ +if (pasid !=PASID_RID2PASID && info->pasid) {
Why check for PASID == 0 like this? Shouldn't pasid == 0 be rejected as an invalid argument?
drivers/iommu/intel/iommu.c static void intel_iommu_iotlb_sync_map(struct iommu_domain *domain, · line 4946
Quoted 2 lines
drivers/iommu/intel/iommu.c +2
-0+if (info->pasid == pasid) + ret =0 ;
Doesn't this need to check that the current domain is the requested domain as well? How can this happen anyhow - isn't it an error to double attach?
include/linux/intel-iommu.h struct device_domain_info { · line 630
Quoted 8 lines
include/linux/intel-iommu.h +2
-0+++ b/include/linux/intel-iommu.h@@ -627,6 +627,7 @@ struct device_domain_info {struct intel_iommu *iommu;/* IOMMU used by this device */ struct dmar_domain *domain;/* pointer to domain */ struct pasid_table *pasid_table;/* pasid table */ +ioasid_t pasid;/* DMA request with PASID */
And this seems wrong - the DMA API is not the only user of attach_dev_pasid, so there should not be any global pasid for the device.
I suspect this should be a counter of # of pasid domains attached so that the special flush logic triggers
And rely on the core code to worry about assigning only one domain per pasid - this should really be a 'set' function.
Jason
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Hi Jason,
Quoted 16 lines · Jason Gunthorpe
Quoted 15 lines · Jacob Pan
drivers/iommu/intel/iommu.c +15
-0+static int intel_iommu_attach_dev_pasid (struct iommu_domain *domain, +struct device *dev, +ioasid_t pasid) +{ +struct device_domain_info *info =dev_iommu_priv_get (dev); +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct intel_iommu *iommu = info->iommu; +unsigned long flags; +int ret =0 ; + +if (!sm_supported (iommu) || !info) +return -ENODEV ; + +spin_lock_irqsave (&device_domain_lock, flags); +/*
Quoted 8 lines
drivers/iommu/intel/iommu.c static void intel_iommu_iotlb_sync_map(struct iommu_domain *domain, · line 4944
Quoted 5 lines
(quoted diff) +1
-0+ * If the same device already has a PASID attached, justreturn.
drivers/iommu/intel/iommu.c +3
-0+* DMA layer will return the PASID value to the caller. +*/ +if (pasid !=PASID_RID2PASID && info->pasid) {Why check for PASID == 0 like this? Shouldn't pasid == 0 be rejected as an invalid argument?
Right, I was planning on reuse the attach function for RIDPASID as clean up, but didn't include here. Will fix.
Quoted 8 lines
drivers/iommu/intel/iommu.c static void intel_iommu_iotlb_sync_map(struct iommu_domain *domain, · line 4946
Quoted 3 lines
drivers/iommu/intel/iommu.c +2
-0+if (info->pasid == pasid) + ret =0 ;Doesn't this need to check that the current domain is the requested domain as well? How can this happen anyhow - isn't it an error to double attach?
Quoted 13 lines
include/linux/intel-iommu.h struct device_domain_info { · line 630
Quoted 8 lines
include/linux/intel-iommu.h +2
-0+++ b/include/linux/intel-iommu.h@@ -627,6 +627,7 @@ struct device_domain_info {struct intel_iommu *iommu;/* IOMMU used by this device */ struct dmar_domain *domain;/* pointer to domain */ struct pasid_table *pasid_table;/* pasid table */ +ioasid_t pasid;/* DMA request with PASID */ And this seems wrong - the DMA API is not the only user of
attach_dev_pasid, so there should not be any global pasid for the device.
True but the attach_dev_pasid() op is domain type specific. i.e. DMA API has its own attach_dev_pasid which is different than sva domain attach_dev_pasid().device_domain_info is only used by DMA API.
Quoted 3 lines
I suspect this should be a counter of # of pasid domains attached so that the special flush logic triggers
This field is only used for devTLB, so it is per domain-device. struct device_domain_info is allocated per device-domain as well. Sorry, I might have totally missed your point.
Quoted 3 lines
And rely on the core code to worry about assigning only one domain per pasid - this should really be a 'set' function.
Yes, in this set the core code (in dma-iommu.c) only assign one PASID per DMA domain type.
Are you suggesting the dma-iommu API should be called iommu_set_dma_pasid instead of iommu_attach_dma_pasid?
Thanks a lot for the quick review!
Jacob
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Quoted 13 lines · Jacob Pan
Quoted 4 lines
Quoted 4 lines
include/linux/intel-iommu.h +1
-0+++ b/include/linux/intel-iommu.h@@ -627,6 +627,7 @@ struct device_domain_info {Quoted 9 lines
include/linux/intel-iommu.h struct device_domain_info { · line 630
Quoted 4 lines
include/linux/intel-iommu.h +1
-0struct intel_iommu *iommu;/* IOMMU used by this device */ struct dmar_domain *domain;/* pointer to domain */ struct pasid_table *pasid_table;/* pasid table */ +ioasid_t pasid;/* DMA request with PASID */ And this seems wrong - the DMA API is not the only user of
attach_dev_pasid, so there should not be any global pasid for the device.
Quoted 3 lines
True but the
attach_dev_pasid()op is domain type specific. i.e. DMA API has its ownattach_dev_pasidwhich is different than sva domainattach_dev_pasid().
Huh? The intel driver shares the same ops between UNMANAGED and DMA - and in general I do not think we should be putting special knowledge about the DMA domains in the drivers. Drivers should continue to treat them identically to UNMANAGED.
Quoted 1 line
device_domain_infois only used by DMA API.
Huh?
Quoted 6 lines
Quoted 3 lines
I suspect this should be a counter of # of pasid domains attached so that the special flush logic triggers
This field is only used for devTLB, so it is per domain-device. struct
device_domain_infois allocated per device-domain as well. Sorry, I might have totally missed your point.
You can't store a single pasid in the driver like this, since the only thing it does is trigger the flush logic just count how many pasids are used by the device-domain and trigger pasid flush if any pasids are attached
Quoted 8 lines
Quoted 2 lines
And rely on the core code to worry about assigning only one domain per pasid - this should really be a 'set' function.
Yes, in this set the core code (in
dma-iommu.c) only assign one PASID per DMA domain type.Are you suggesting the dma-iommu API should be called
iommu_set_dma_pasidinstead ofiommu_attach_dma_pasid?
No that API is Ok - the driver ops API should be 'set' not attach/detach
Jason
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Hi Jason,
Quoted 15 lines · Jason Gunthorpe
Quoted 13 lines · Jacob Pan
Quoted 13 lines
include/linux/intel-iommu.h struct device_domain_info { · line 630
Quoted 8 lines
(quoted diff) +0
-0
b/include/linux/intel-iommu.hindex5af24befc9f1..55845a8c4f4d100644 +++b/include/linux/intel-iommu.hinclude/linux/intel-iommu.h +1
-0@@ -627,6 +627,7 @@ struct device_domain_info {struct intel_iommu *iommu;/* IOMMU used by this device */ struct dmar_domain *domain;/* pointer to domain */ struct pasid_table *pasid_table;/* pasid table */ +ioasid_t pasid;/* DMA request with PASID */ And this seems wrong - the DMA API is not the only user of
attach_dev_pasid, so there should not be any global pasid for the device.
Quoted 9 lines
Quoted 3 lines
True but the
attach_dev_pasid()op is domain type specific. i.e. DMA API has its ownattach_dev_pasidwhich is different than sva domainattach_dev_pasid().Huh? The intel driver shares the same ops between UNMANAGED and DMA - and in general I do not think we should be putting special knowledge about the DMA domains in the drivers. Drivers should continue to treat them identically to UNMANAGED.
OK, other than SVA domain, the rest domain types share the same default ops. I agree that the default ops should be the same for UNMANAGED, IDENTITY, and DMA domain types. Minor detail is that we need to treat IDENTITY domain slightly different when it comes down to PASID entry programming.
If not global, perhaps we could have a list of pasids (e.g. xarray) attached to the device_domain_info. The TLB flush logic would just go through the list w/o caring what the PASIDs are for. Does it make sense to you?
Quoted 3 lines
Quoted 1 line
device_domain_infois only used by DMA API.Huh?
My mistake, i meant the device_domain_info.pasid is only used by DMA API
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
I suspect this should be a counter of # of pasid domains attached so that the special flush logic triggers
Quoted 9 lines
Quoted 3 lines
This field is only used for devTLB, so it is per domain-device. struct
device_domain_infois allocated per device-domain as well. Sorry, I might have totally missed your point.You can't store a single pasid in the driver like this, since the only thing it does is trigger the flush logic just count how many pasids are used by the device-domain and trigger pasid flush if any pasids are attached
Got it, will put the pasids in an xa as described above.
Quoted 11 lines
Quoted 8 lines
Quoted 2 lines
And rely on the core code to worry about assigning only one domain per pasid - this should really be a 'set' function.
Yes, in this set the core code (in
dma-iommu.c) only assign one PASID per DMA domain type.Are you suggesting the dma-iommu API should be called
iommu_set_dma_pasidinstead ofiommu_attach_dma_pasid?No that API is Ok - the driver ops API should be 'set' not attach/detach
Sounds good, this operation has little in common with domain_ops.dev_attach_pasid() used by SVA domain. So I will add a new domain_ops.dev_set_pasid()
Thanks,
Jacob
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Quoted 9 lines · Jacob Pan
Quoted 5 lines
Huh? The intel driver shares the same ops between UNMANAGED and DMA - and in general I do not think we should be putting special knowledge about the DMA domains in the drivers. Drivers should continue to treat them identically to UNMANAGED.
OK, other than SVA domain, the rest domain types share the same default ops. I agree that the default ops should be the same for UNMANAGED, IDENTITY, and DMA domain types. Minor detail is that we need to treat IDENTITY domain slightly different when it comes down to PASID entry programming.
I would be happy if IDENTITY had its own ops, if that makes sense
Quoted 3 lines
If not global, perhaps we could have a list of pasids (e.g. xarray) attached to the
device_domain_info. The TLB flush logic would just go through the list w/o caring what the PASIDs are for. Does it make sense to you?
Sort of, but we shouldn't duplicate xarrays - the group already has this xarray - need to find some way to allow access to it from the driver.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Are you suggesting the dma-iommu API should be called
iommu_set_dma_pasidinstead ofiommu_attach_dma_pasid?No that API is Ok - the driver ops API should be 'set' not attach/detach
Sounds good, this operation has little in common with
domain_ops.dev_attach_pasid()used by SVA domain. So I will add a newdomain_ops.dev_set_pasid()
What? No, their should only be one operation, 'dev_set_pasid' and it is exactly the same as the SVA operation. It configures things so that any existing translation on the PASID is removed and the PASID translates according to the given domain.
SVA given domain or UNMANAGED given domain doesn't matter to the higher level code. The driver should implement per-domain ops as required to get the different behaviors.
Jason
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Hi Jason,
Quoted 7 lines · Jason Gunthorpe
Quoted 5 lines · Jacob Pan
Quoted 5 lines
Huh? The intel driver shares the same ops between UNMANAGED and DMA - and in general I do not think we should be putting special knowledge about the DMA domains in the drivers. Drivers should continue to treat them identically to UNMANAGED.
Quoted 8 lines
Quoted 5 lines
OK, other than SVA domain, the rest domain types share the same default ops. I agree that the default ops should be the same for UNMANAGED, IDENTITY, and DMA domain types. Minor detail is that we need to treat IDENTITY domain slightly different when it comes down to PASID entry programming.
I would be happy if IDENTITY had its own ops, if that makes sense
I have tried to have its own ops but there are complications around checking if a domain has ops. It would be a logic thing to clean up next.
Quoted 9 lines
Quoted 4 lines
If not global, perhaps we could have a list of pasids (e.g. xarray) attached to the
device_domain_info. The TLB flush logic would just go through the list w/o caring what the PASIDs are for. Does it make sense to you?Sort of, but we shouldn't duplicate xarrays - the group already has this xarray - need to find some way to allow access to it from the driver.
I am not following, here are the PASIDs for devTLB flush which is per device. Why group?
We could retrieve PASIDs from the device PASID table but xa would be more efficient.
Quoted 9 lines
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Are you suggesting the dma-iommu API should be called
iommu_set_dma_pasidinstead ofiommu_attach_dma_pasid?No that API is Ok - the driver ops API should be 'set' not attach/detach
Sounds good, this operation has little in common with
domain_ops.dev_attach_pasid()used by SVA domain. So I will add a newdomain_ops.dev_set_pasid()
Quoted 8 lines
What? No, their should only be one operation, '
dev_set_pasid' and it is exactly the same as the SVA operation. It configures things so that any existing translation on the PASID is removed and the PASID translates according to the given domain.SVA given domain or UNMANAGED given domain doesn't matter to the higher level code. The driver should implement per-domain ops as required to get the different behaviors.
Perhaps some code to clarify, we have sva_domain_ops.dev_attach_pasid() = intel_svm_attach_dev_pasid; default_domain_ops.dev_attach_pasid() = intel_iommu_attach_dev_pasid;
Consolidate pasid programming into dev_set_pasid() then called by both intel_svm_attach_dev_pasid() and intel_iommu_attach_dev_pasid(), right?
Thanks,
Jacob
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Quoted 11 lines · Jacob Pan
Quoted 9 lines
Quoted 4 lines
If not global, perhaps we could have a list of pasids (e.g. xarray) attached to the
device_domain_info. The TLB flush logic would just go through the list w/o caring what the PASIDs are for. Does it make sense to you?Sort of, but we shouldn't duplicate xarrays - the group already has this xarray - need to find some way to allow access to it from the driver.
I am not following, here are the PASIDs for devTLB flush which is per device. Why group?
Because group is where the core code stores it.
Quoted 17 lines
We could retrieve PASIDs from the device PASID table but xa would be more efficient.
Quoted 14 lines
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Are you suggesting the dma-iommu API should be called
iommu_set_dma_pasidinstead ofiommu_attach_dma_pasid?No that API is Ok - the driver ops API should be 'set' not attach/detach
Sounds good, this operation has little in common with
domain_ops.dev_attach_pasid()used by SVA domain. So I will add a newdomain_ops.dev_set_pasid()What? No, their should only be one operation, '
dev_set_pasid' and it is exactly the same as the SVA operation. It configures things so that any existing translation on the PASID is removed and the PASID translates according to the given domain.
Quoted 6 lines
Quoted 3 lines
SVA given domain or UNMANAGED given domain doesn't matter to the higher level code. The driver should implement per-domain ops as required to get the different behaviors.
Perhaps some code to clarify, we have
sva_domain_ops.dev_attach_pasid()=intel_svm_attach_dev_pasid;default_domain_ops.dev_attach_pasid()=intel_iommu_attach_dev_pasid;
Yes, keep that structure
Quoted 2 lines
Consolidate pasid programming into
dev_set_pasid()then called by bothintel_svm_attach_dev_pasid()andintel_iommu_attach_dev_pasid(), right?
I was only suggesting that really dev_attach_pasid() op is misnamed, it should be called set_dev_pasid() and act like a set, not a paired attach/detach - same as the non-PASID ops.
Jason
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Hi Jason,
Quoted 6 lines · Jason Gunthorpe
Quoted 5 lines · Jacob Pan
Quoted 5 lines
Quoted 4 lines
If not global, perhaps we could have a list of pasids (e.g. xarray) attached to the
device_domain_info. The TLB flush logic would just go through the list w/o caring what the PASIDs are for. Does it make sense to you?
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Sort of, but we shouldn't duplicate xarrays - the group already has this xarray - need to find some way to allow access to it from the driver.
I am not following, here are the PASIDs for devTLB flush which is per device. Why group?
Because group is where the core code stores it.
I see, with singleton group. I guess I can let dma-iommu code call
iommu_attach_dma_pasid {
iommu_attach_device_pasid();Then the PASID will be stored in the group xa.
The flush code can retrieve PASIDs from device_domain_info.device -> group -> pasid_array.
Thanks for pointing it out, I missed the new pasid_array.
Quoted 27 lines
Quoted 18 lines
We could retrieve PASIDs from the device PASID table but xa would be more efficient.
Quoted 14 lines
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Are you suggesting the dma-iommu API should be called
iommu_set_dma_pasidinstead ofiommu_attach_dma_pasid?No that API is Ok - the driver ops API should be 'set' not attach/detach
Sounds good, this operation has little in common with
domain_ops.dev_attach_pasid()used by SVA domain. So I will add a newdomain_ops.dev_set_pasid()What? No, their should only be one operation, '
dev_set_pasid' and it is exactly the same as the SVA operation. It configures things so that any existing translation on the PASID is removed and the PASID translates according to the given domain.Quoted 6 lines
Quoted 3 lines
SVA given domain or UNMANAGED given domain doesn't matter to the higher level code. The driver should implement per-domain ops as required to get the different behaviors.
Perhaps some code to clarify, we have
sva_domain_ops.dev_attach_pasid()=intel_svm_attach_dev_pasid;default_domain_ops.dev_attach_pasid()=intel_iommu_attach_dev_pasid;Yes, keep that structure
Quoted 8 lines
Quoted 3 lines
Consolidate pasid programming into
dev_set_pasid()then called by bothintel_svm_attach_dev_pasid()andintel_iommu_attach_dev_pasid(), right?I was only suggesting that really
dev_attach_pasid()op is misnamed, it should be calledset_dev_pasid()and act like a set, not a paired attach/detach - same as the non-PASID ops.
Got it. Perhaps another patch to rename, Baolu?
Thanks,
Jacob
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Quoted 17 lines · Jacob Pan
Hi Jason,
Quoted 13 lines · Jason Gunthorpe
Quoted 11 lines · Jacob Pan
Quoted 9 lines
Quoted 4 lines
If not global, perhaps we could have a list of pasids (e.g. xarray) attached to the
device_domain_info. The TLB flush logic would just go through the list w/o caring what the PASIDs are for. Does it make sense to you?Sort of, but we shouldn't duplicate xarrays - the group already has this xarray - need to find some way to allow access to it from the driver.
I am not following, here are the PASIDs for devTLB flush which is per device. Why group?
Quoted 6 lines
Quoted 1 line
Because group is where the core code stores it.
I see, with singleton group. I guess I can let dma-iommu code call
iommu_attach_dma_pasid { iommu_attach_device_pasid();Then the PASID will be stored in the group xa.
Yes, again, the dma-iommu should not be any different from the normal unmanaged path. At this point there is no longer any difference, we should not invent new ones.
Quoted 3 lines
The flush code can retrieve PASIDs from device_domain_info.device -> group ->
pasid_array. Thanks for pointing it out, I missed the newpasid_array.
Yes.. It seems inefficient to iterate over that xarray multiple times on the flush hot path, but maybe there is little choice. Try to use use the xas iterators under the xa_lock spinlock..
The challenge will be accessing the group xa in the first place, but maybe the core code can gain a function call to return a pointer to that XA or something..
Jason
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Hi Jason,
Quoted 29 lines · Jason Gunthorpe
Quoted 17 lines · Jacob Pan
Hi Jason,
Quoted 13 lines · Jason Gunthorpe
Quoted 11 lines · Jacob Pan
Quoted 9 lines
Quoted 4 lines
If not global, perhaps we could have a list of pasids (e.g. xarray) attached to the
device_domain_info. The TLB flush logic would just go through the list w/o caring what the PASIDs are for. Does it make sense to you?Sort of, but we shouldn't duplicate xarrays - the group already has this xarray - need to find some way to allow access to it from the driver.
I am not following, here are the PASIDs for devTLB flush which is per device. Why group?
Quoted 6 lines
Quoted 1 line
Because group is where the core code stores it.
I see, with singleton group. I guess I can let dma-iommu code call
iommu_attach_dma_pasid { iommu_attach_device_pasid();Then the PASID will be stored in the group xa.
Yes, again, the dma-iommu should not be any different from the normal unmanaged path. At this point there is no longer any difference, we should not invent new ones.
Quoted 8 lines
Quoted 3 lines
The flush code can retrieve PASIDs from device_domain_info.device -> group ->
pasid_array. Thanks for pointing it out, I missed the newpasid_array.Yes.. It seems inefficient to iterate over that xarray multiple times on the flush hot path, but maybe there is little choice. Try to use use the xas iterators under the
xa_lockspinlock..
xas_for_each takes a max range, here we don't really have one. So I posted v4 w/o using the xas advanced API. Please let me know if you have suggestions.xa_for_each takes RCU read lock, it should be fast for tlb flush, right? The worst case maybe over flush when we have stale data but should be very rare.
Quoted 4 lines
The challenge will be accessing the group xa in the first place, but maybe the core code can gain a function call to return a pointer to that XA or something..
I added a helper function to find the matching DMA API PASID in v4.
Thanks,
Jacob
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Quoted 7 lines · Jacob Pan
Quoted 4 lines
Yes.. It seems inefficient to iterate over that xarray multiple times on the flush hot path, but maybe there is little choice. Try to use use the xas iterators under the
xa_lockspinlock..
xas_for_eachtakes a max range, here we don't really have one. So I posted v4 w/o using the xas advanced API. Please let me know if you have suggestions.
You are supposed to use ULONG_MAX in cases like that.
Quoted 2 lines
xa_for_eachtakes RCU read lock, it should be fast for tlb flush, right? The worst case maybe over flush when we have stale data but should be very rare.
Not really, xa_for_each walks the tree for every iteration, it is slower than a linked list walk in any cases where the xarray is multi-node. xas_for_each is able to retain a pointer where it is in the tree so each iteration is usually just a pointer increment.
The downside is you cannot sleep while doing xas_for_each
Quoted 5 lines
Quoted 3 lines
The challenge will be accessing the group xa in the first place, but maybe the core code can gain a function call to return a pointer to that XA or something..
I added a helper function to find the matching DMA API PASID in v4.
Again, why are we focused on DMA API? Nothing you build here should be DMA API beyond the fact that the iommu_domain being attached is the default domain.
Jason
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Hi Jason,
Quoted 6 lines · Jason Gunthorpe
Quoted 4 lines · Jacob Pan
Quoted 4 lines
Yes.. It seems inefficient to iterate over that xarray multiple times on the flush hot path, but maybe there is little choice. Try to use use the xas iterators under the
xa_lockspinlock..
Quoted 6 lines
Quoted 3 lines
xas_for_eachtakes a max range, here we don't really have one. So I posted v4 w/o using the xas advanced API. Please let me know if you have suggestions.You are supposed to use
ULONG_MAXin cases like that.
got it.
Quoted 9 lines
Quoted 3 lines
xa_for_eachtakes RCU read lock, it should be fast for tlb flush, right? The worst case maybe over flush when we have stale data but should be very rare.Not really,
xa_for_eachwalks the tree for every iteration, it is slower than a linked list walk in any cases where the xarray is multi-node.xas_for_eachis able to retain a pointer where it is in the tree so each iteration is usually just a pointer increment.
Thanks for explaining, yeah if we have to iterate multiple times xas_for_each() is better.
Quoted 2 lines
The downside is you cannot sleep while doing
xas_for_each
will do under RCU read lock
Quoted 9 lines
Quoted 5 lines
Quoted 3 lines
The challenge will be accessing the group xa in the first place, but maybe the core code can gain a function call to return a pointer to that XA or something..
I added a helper function to find the matching DMA API PASID in v4.
Again, why are we focused on DMA API? Nothing you build here should be DMA API beyond the fact that the
iommu_domainbeing attached is the default domain.
The helper is not DMA API specific. Just a domain-PASID look up. Sorry for the confusion.
Thanks,
Jacob
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Baolu Lu
maintainerRe: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
In reply to Jacob PanQuoted 57 lines · Jacob Pan
Hi Jason,
Quoted 6 lines · Jason Gunthorpe
Quoted 5 lines · Jacob Pan
Quoted 5 lines
Quoted 4 lines
If not global, perhaps we could have a list of pasids (e.g. xarray) attached to the
device_domain_info. The TLB flush logic would just go through the list w/o caring what the PASIDs are for. Does it make sense to you?Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Sort of, but we shouldn't duplicate xarrays - the group already has this xarray - need to find some way to allow access to it from the driver.
I am not following, here are the PASIDs for devTLB flush which is per device. Why group?
Because group is where the core code stores it.
I see, with singleton group. I guess I can let dma-iommu code call
iommu_attach_dma_pasid { iommu_attach_device_pasid();Then the PASID will be stored in the group xa.
The flush code can retrieve PASIDs from device_domain_info.device -> group ->pasid_array.
Thanks for pointing it out, I missed the newpasid_array.Quoted 31 lines
Quoted 18 lines
We could retrieve PASIDs from the device PASID table but xa would be more efficient.
Quoted 14 lines
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Are you suggesting the dma-iommu API should be called
iommu_set_dma_pasidinstead ofiommu_attach_dma_pasid?No that API is Ok - the driver ops API should be 'set' not attach/detach
Sounds good, this operation has little in common with
domain_ops.dev_attach_pasid()used by SVA domain. So I will add a newdomain_ops.dev_set_pasid()What? No, their should only be one operation, '
dev_set_pasid' and it is exactly the same as the SVA operation. It configures things so that any existing translation on the PASID is removed and the PASID translates according to the given domain.Quoted 6 lines
Quoted 3 lines
SVA given domain or UNMANAGED given domain doesn't matter to the higher level code. The driver should implement per-domain ops as required to get the different behaviors.
Perhaps some code to clarify, we have
sva_domain_ops.dev_attach_pasid()=intel_svm_attach_dev_pasid;default_domain_ops.dev_attach_pasid()=intel_iommu_attach_dev_pasid;Yes, keep that structure
Quoted 3 lines
Consolidate pasid programming into
dev_set_pasid()then called by bothintel_svm_attach_dev_pasid()andintel_iommu_attach_dev_pasid(), right?
Quoted 5 lines
Quoted 4 lines
I was only suggesting that really
dev_attach_pasid()op is misnamed, it should be calledset_dev_pasid()and act like a set, not a paired attach/detach - same as the non-PASID ops.Got it. Perhaps another patch to rename, Baolu?
Yes. I can rename it in my sva series if others are also happy with this naming.
Best regards,
baolu
Baolu Lu
maintainerRe: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
In reply to Jason GunthorpeHi Jason,
Quoted 5 lines · Jason Gunthorpe
Quoted 2 lines
Consolidate pasid programming into
dev_set_pasid()then called by bothintel_svm_attach_dev_pasid()andintel_iommu_attach_dev_pasid(), right?I was only suggesting that really
dev_attach_pasid()op is misnamed, it should be calledset_dev_pasid()and act like a set, not a paired attach/detach - same as the non-PASID ops.
So,
"set_dev_pasid(domain, device, pasid)" equals to dev_attach_pasid()
and
"set_dev_pasid(NULL, device, pasid)" equals to dev_detach_pasid()?
do I understand it right?
Best regards,
baolu
Re: [PATCH v3 1/4] iommu/vt-d: Implement domain ops for attach_dev_pasid
Quoted 11 lines · Baolu Lu
Hi Jason,
Quoted 5 lines · Jason Gunthorpe
Quoted 2 lines
Consolidate pasid programming into
dev_set_pasid()then called by bothintel_svm_attach_dev_pasid()andintel_iommu_attach_dev_pasid(), right?I was only suggesting that really
dev_attach_pasid()op is misnamed, it should be calledset_dev_pasid()and act like a set, not a paired attach/detach - same as the non-PASID ops.So,
Quoted 7 lines
"
set_dev_pasid(domain, device, pasid)" equals todev_attach_pasid()and
"
set_dev_pasid(NULL, device, pasid)" equals todev_detach_pasid()?do I understand it right?
blocking_domain should be passed, not null
Jason
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
PATCH v3 2/4 iommu: Add PASID support for DMA mapping API users
In reply to Jacob PanDMA mapping API is the de facto standard for in-kernel DMA. It operates on a per device/RID basis which is not PASID-aware.
Some modern devices such as Intel Data Streaming Accelerator, PASID is required for certain work submissions. To allow such devices use DMA mapping API, we need the following functionalities:
1. Provide device a way to retrieve a PASID for work submission within the kernel
2. Enable the kernel PASID on the IOMMU for the device
3. Attach the kernel PASID to the device's default DMA domain, let it be IOVA or physical address in case of pass-through.
This patch introduces a driver facing API that enables DMA API PASID usage. Once enabled, device drivers can continue to use DMA APIs as is. There is no difference in dma_handle between without PASID and with PASID.
--- drivers/iommu/dma-iommu.c | 107 ++++++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 3 ++ include/linux/iommu.h | 2 + 3 files changed, 112 insertions(+)
drivers/iommu/dma-iommu.c +107 -0
@@ -34,6 +34,8 @@ struct iommu_dma_msi_page {phys_addr_t phys; }; +static DECLARE_IOASID_SET (iommu_dma_pasid); +enum iommu_dma_cookie_type {IOMMU_DMA_IOVA_COOKIE ,IOMMU_DMA_MSI_COOKIE ,
@@ -370,6 +372,111 @@ void iommu_put_dma_cookie(struct iommu_domain *domain) domain->iova_cookie =NULL ; } +/** +* iommu_attach_dma_pasid --Attach a PASID for in-kernel DMA. Use the device's +* DMA domain. +* @dev: Device to be enabled +* @pasid: The returned kernel PASID to be used for DMA +* +* DMA request with PASID will be mapped the same way as the legacy DMA. +* If the device is in pass-through, PASID will also pass-through. If the +* device is in IOVA, the PASID will point to the same IOVA page table. +* +* @return err code or 0 on success +*/ +int iommu_attach_dma_pasid (struct device *dev,ioasid_t *pasid) +{ +struct iommu_domain *dom; +ioasid_t id, max; +int ret =0 ; + + dom =iommu_get_domain_for_dev (dev); +if (!dom || !dom->ops || !dom->ops->attach_dev_pasid) +return -ENODEV ; + +/* Only support domain types that DMA API can be used */ +if (dom->type ==IOMMU_DOMAIN_UNMANAGED || + dom->type ==IOMMU_DOMAIN_BLOCKED ) { +dev_warn (dev,"Invalid domain type %d" , dom->type); +return -EPERM ; + } + + id = dom->pasid; +if (!id) { +/* +* First device to use PASID in its DMA domain, allocate +* a single PASID per DMA domain is all we need, it is also +* good for performance when it comes down to IOTLB flush. +*/ + max =1U << dev->iommu->pasid_bits; +if (!max) +return -EINVAL ; + + id =ioasid_alloc (&iommu_dma_pasid,1 , max, dev); +if (id ==INVALID_IOASID ) +return -ENOMEM ; + + dom->pasid = id; +atomic_set (&dom->pasid_users,1 ); + } + + ret = dom->ops->attach_dev_pasid (dom, dev, id); +if (!ret) { + *pasid = id; +atomic_inc (&dom->pasid_users); +return 0 ; + } + +if (atomic_dec_and_test (&dom->pasid_users)) { +ioasid_free (id); + dom->pasid =0 ; + } + +return ret; +} +EXPORT_SYMBOL (iommu_attach_dma_pasid); + +/** +* iommu_detach_dma_pasid --Disable in-kernel DMA request with PASID +* @dev: Device's PASID DMA to be disabled +* +* It is the device driver's responsibility to ensure no more incoming DMA +* requests with the kernel PASID before calling this function. IOMMU driver +* ensures PASID cache, IOTLBs related to the kernel PASID are cleared and +* drained. +* +*/ +void iommu_detach_dma_pasid (struct device *dev) +{ +struct iommu_domain *dom; +ioasid_t pasid; + + dom =iommu_get_domain_for_dev (dev); +if (!dom || !dom->ops || !dom->ops->detach_dev_pasid) { +dev_warn (dev,"No ops for detaching PASID %u" , pasid); +return ; + } +/* Only support DMA API managed domain type */ +if (dom->type ==IOMMU_DOMAIN_UNMANAGED || + dom->type ==IOMMU_DOMAIN_BLOCKED ) { +dev_err (dev,"Invalid domain type %d to detach DMA PASID %u\n" , + dom->type, pasid); +return ; + } + + pasid = dom->pasid; +if (!pasid) { +dev_err (dev,"No DMA PASID attached\n" ); +return ; + } + dom->ops->detach_dev_pasid (dom, dev, pasid); +if (atomic_dec_and_test (&dom->pasid_users)) { +ioasid_free (pasid); + dom->pasid =0 ; + } +} +EXPORT_SYMBOL (iommu_detach_dma_pasid); +/** * iommu_dma_get_resv_regions - Reserved region driver helper * @dev: Device from iommu_get_resv_regions()
include/linux/dma-iommu.h +3 -0
@@ -18,6 +18,9 @@ int iommu_get_dma_cookie(struct iommu_domain *domain);int iommu_get_msi_cookie (struct iommu_domain *domain,dma_addr_t base);void iommu_put_dma_cookie (struct iommu_domain *domain); +int iommu_attach_dma_pasid (struct device *dev,ioasid_t *pasid); +void iommu_detach_dma_pasid (struct device *dev); +/* Setup call for arch DMA mapping code */ void iommu_setup_dma_ops (struct device *dev,u64 dma_base,u64 dma_limit);int iommu_dma_init_fq (struct iommu_domain *domain);
include/linux/iommu.h +2 -0
@@ -105,6 +105,8 @@ struct iommu_domain {enum iommu_page_response_code (*iopf_handler)(struct iommu_fault *fault,void *data);void *fault_data; +ioasid_t pasid;/* Used for DMA requests with PASID */ +atomic_t pasid_users; };static inline bool iommu_is_dma_domain (struct iommu_domain *domain)
Signature · 1 line
2.25.1
Re: [PATCH v3 2/4] iommu: Add PASID support for DMA mapping API users
Quoted 55 lines · Jacob Pan
DMA mapping API is the de facto standard for in-kernel DMA. It operates on a per device/RID basis which is not PASID-aware.
Some modern devices such as Intel Data Streaming Accelerator, PASID is required for certain work submissions. To allow such devices use DMA mapping API, we need the following functionalities:
1. Provide device a way to retrieve a PASID for work submission within the kernel
2. Enable the kernel PASID on the IOMMU for the device
3. Attach the kernel PASID to the device's default DMA domain, let it be IOVA or physical address in case of pass-through.This patch introduces a driver facing API that enables DMA API PASID usage. Once enabled, device drivers can continue to use DMA APIs as is. There is no difference in
dma_handlebetween without PASID and with PASID.Signed-off-by Jacob Pan <[email protected]>drivers/iommu/dma-iommu.c | 107 ++++++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 3 ++ include/linux/iommu.h | 2 + 3 files changed, 112 insertions(+)drivers/iommu/dma-iommu.c +19
-0+++ b/drivers/iommu/dma-iommu.c@@ -34,6 +34,8 @@ struct iommu_dma_msi_page {phys_addr_t phys; }; +static DECLARE_IOASID_SET (iommu_dma_pasid); +enum iommu_dma_cookie_type {IOMMU_DMA_IOVA_COOKIE ,IOMMU_DMA_MSI_COOKIE ,@@ -370,6 +372,111 @@ void iommu_put_dma_cookie(struct iommu_domain *domain) domain->iova_cookie =NULL ; } +/** +* iommu_attach_dma_pasid --Attach a PASID for in-kernel DMA. Use the device's +* DMA domain. +* @dev: Device to be enabled +* @pasid: The returned kernel PASID to be used for DMA +* +* DMA request with PASID will be mapped the same way as the legacy DMA. +* If the device is in pass-through, PASID will also pass-through. If the +* device is in IOVA, the PASID will point to the same IOVA page table. +* +* @return err code or 0 on success +*/ +int iommu_attach_dma_pasid (struct device *dev,ioasid_t *pasid) +{ +struct iommu_domain *dom; +ioasid_t id, max;
drivers/iommu/dma-iommu.c void iommu_put_dma_cookie(struct iommu_domain *domain) · line 400
Quoted 10 lines
drivers/iommu/dma-iommu.c +10
-0+int ret =0 ; + + dom =iommu_get_domain_for_dev (dev); +if (!dom || !dom->ops || !dom->ops->attach_dev_pasid) +return -ENODEV ; + +/* Only support domain types that DMA API can be used */ +if (dom->type ==IOMMU_DOMAIN_UNMANAGED || + dom->type ==IOMMU_DOMAIN_BLOCKED ) { +dev_warn (dev,"Invalid domain type %d" , dom->type);
This should be a WARN_ON
Quoted 10 lines
drivers/iommu/dma-iommu.c +10
-0+return -EPERM ; + } + + id = dom->pasid; +if (!id) { +/* +* First device to use PASID in its DMA domain, allocate +* a single PASID per DMA domain is all we need, it is also +* good for performance when it comes down to IOTLB flush. +*/
drivers/iommu/dma-iommu.c void iommu_put_dma_cookie(struct iommu_domain *domain) · line 420
Quoted 10 lines
drivers/iommu/dma-iommu.c +10
-0+ max =1U << dev->iommu->pasid_bits; +if (!max) +return -EINVAL ; + + id =ioasid_alloc (&iommu_dma_pasid,1 , max, dev); +if (id ==INVALID_IOASID ) +return -ENOMEM ; + + dom->pasid = id; +atomic_set (&dom->pasid_users,1 );
All of this needs proper locking.
Quoted 40 lines
drivers/iommu/dma-iommu.c +40
-0+ } + + ret = dom->ops->attach_dev_pasid (dom, dev, id); +if (!ret) { + *pasid = id; +atomic_inc (&dom->pasid_users); +return 0 ; + } + +if (atomic_dec_and_test (&dom->pasid_users)) { +ioasid_free (id); + dom->pasid =0 ; + } + +return ret; +} +EXPORT_SYMBOL (iommu_attach_dma_pasid); + +/** +* iommu_detach_dma_pasid --Disable in-kernel DMA request with PASID +* @dev: Device's PASID DMA to be disabled +* +* It is the device driver's responsibility to ensure no more incoming DMA +* requests with the kernel PASID before calling this function. IOMMU driver +* ensures PASID cache, IOTLBs related to the kernel PASID are cleared and +* drained. +* +*/ +void iommu_detach_dma_pasid (struct device *dev) +{ +struct iommu_domain *dom; +ioasid_t pasid; + + dom =iommu_get_domain_for_dev (dev); +if (!dom || !dom->ops || !dom->ops->detach_dev_pasid) { +dev_warn (dev,"No ops for detaching PASID %u" , pasid); +return ; + } +/* Only support DMA API managed domain type */ + if (dom->type == IOMMU_DOMAIN_UNMANAGED ||
drivers/iommu/dma-iommu.c void iommu_put_dma_cookie(struct iommu_domain *domain) · line 471
Quoted 10 lines
drivers/iommu/dma-iommu.c +10
-0+ dom->type == IOMMU_DOMAIN_BLOCKED) { +dev_err (dev,"Invalid domain type %d to detach DMA PASID %u\n" , + dom->type, pasid); +return ; + } + pasid = dom->pasid; +if (!pasid) { +dev_err (dev,"No DMA PASID attached\n" ); +return ; + }
All WARN_ON's too
Quoted 28 lines
drivers/iommu/dma-iommu.c +8
-0+ dom->ops->detach_dev_pasid (dom, dev, pasid); +if (atomic_dec_and_test (&dom->pasid_users)) { +ioasid_free (pasid); + dom->pasid =0 ; + } +} +EXPORT_SYMBOL (iommu_detach_dma_pasid); +/** * iommu_dma_get_resv_regions - Reserved region driver helper * @dev: Device from iommu_get_resv_regions() include/linux/dma-iommu.h +4
-0+++ b/include/linux/dma-iommu.h@@ -18,6 +18,9 @@ int iommu_get_dma_cookie(struct iommu_domain *domain);int iommu_get_msi_cookie (struct iommu_domain *domain,dma_addr_t base);void iommu_put_dma_cookie (struct iommu_domain *domain); +int iommu_attach_dma_pasid (struct device *dev,ioasid_t *pasid); +void iommu_detach_dma_pasid (struct device *dev); +/* Setup call for arch DMA mapping code */ void iommu_setup_dma_ops (struct device *dev,u64 dma_base,u64 dma_limit);int iommu_dma_init_fq (struct iommu_domain *domain);include/linux/iommu.h +1
-0+++ b/include/linux/iommu.h
include/linux/iommu.h struct iommu_domain { · line 109
Quoted 5 lines
include/linux/iommu.h +2
-0@@ -105,6 +105,8 @@ struct iommu_domain {enum iommu_page_response_code (*iopf_handler)(struct iommu_fault *fault,void *data);void *fault_data; +ioasid_t pasid;/* Used for DMA requests with PASID */ +atomic_t pasid_users;
These are poorly named, this is really the DMA API global PASID and shouldn't be used for other things.
Jason
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v3 2/4] iommu: Add PASID support for DMA mapping API users
Hi Jason,
Quoted 63 lines · Jason Gunthorpe
Quoted 62 lines · Jacob Pan
DMA mapping API is the de facto standard for in-kernel DMA. It operates on a per device/RID basis which is not PASID-aware.
Some modern devices such as Intel Data Streaming Accelerator, PASID is required for certain work submissions. To allow such devices use DMA mapping API, we need the following functionalities:
1. Provide device a way to retrieve a PASID for work submission within the kernel
2. Enable the kernel PASID on the IOMMU for the device
3. Attach the kernel PASID to the device's default DMA domain, let it be IOVA or physical address in case of pass-through.This patch introduces a driver facing API that enables DMA API PASID usage. Once enabled, device drivers can continue to use DMA APIs as is. There is no difference in
dma_handlebetween without PASID and with PASID.Signed-off-by Jacob Pan <[email protected]>drivers/iommu/dma-iommu.c | 107 ++++++++++++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 3 ++ include/linux/iommu.h | 2 + 3 files changed, 112 insertions(+)drivers/iommu/dma-iommu.c +5
-0+++ b/drivers/iommu/dma-iommu.c@@ -34,6 +34,8 @@ struct iommu_dma_msi_page {phys_addr_t phys; }; +static DECLARE_IOASID_SET (iommu_dma_pasid); +enum iommu_dma_cookie_type {IOMMU_DMA_IOVA_COOKIE ,IOMMU_DMA_MSI_COOKIE ,@@ -370,6 +372,111 @@ void iommu_put_dma_cookie(struct iommu_domain *domain) domain->iova_cookie = NULL; } +/** +* iommu_attach_dma_pasid --Attach a PASID for in-kernel DMA. Use the device's
+ * DMA domain.
+ * @dev: Device to be enabled
+ * @pasid: The returned kernel PASID to be used for DMA
+ *
+ * DMA request with PASID will be mapped the same way as the legacy DMA.
+ * If the device is in pass-through, PASID will also pass-through. If the
+ * device is in IOVA, the PASID will point to the same IOVA page table.
+ *
+ * @return err code or 0 on success
+ */drivers/iommu/dma-iommu.c +8
-0+int iommu_attach_dma_pasid (struct device *dev,ioasid_t *pasid) +{ +struct iommu_domain *dom; +ioasid_t id, max; +int ret =0 ; + + dom =iommu_get_domain_for_dev (dev); +if (!dom || !dom->ops || !dom->ops->attach_dev_pasid)
Quoted 9 lines
drivers/iommu/dma-iommu.c void iommu_put_dma_cookie(struct iommu_domain *domain) · line 400
Quoted 6 lines
drivers/iommu/dma-iommu.c +6
-0+return -ENODEV ; + +/* Only support domain types that DMA API can be used */ +if (dom->type ==IOMMU_DOMAIN_UNMANAGED || + dom->type ==IOMMU_DOMAIN_BLOCKED ) { +dev_warn (dev,"Invalid domain type %d" , dom->type);This should be a
WARN_ON
will do, thanks
Quoted 17 lines
Quoted 17 lines
drivers/iommu/dma-iommu.c +7
-0+return -EPERM ; + } + + id = dom->pasid; +if (!id) { +/* +* First device to use PASID in its DMA domain, allocate
drivers/iommu/dma-iommu.c +1
-0+* a single PASID per DMA domain is all we need, it is also
drivers/iommu/dma-iommu.c +1
-0+* good for performance when it comes down to IOTLB flush.
drivers/iommu/dma-iommu.c +5
-0+*/ + max =1U << dev->iommu->pasid_bits; +if (!max) +return -EINVAL ; +
Quoted 9 lines
drivers/iommu/dma-iommu.c void iommu_put_dma_cookie(struct iommu_domain *domain) · line 420
Quoted 6 lines
drivers/iommu/dma-iommu.c +6
-0+ id =ioasid_alloc (&iommu_dma_pasid,1 , max, dev); +if (id ==INVALID_IOASID ) +return -ENOMEM ; + + dom->pasid = id; +atomic_set (&dom->pasid_users,1 );All of this needs proper locking.
good catch, will add a mutex for domain updates, detach as well.
Quoted 48 lines
Quoted 48 lines
drivers/iommu/dma-iommu.c +23
-0+ } + + ret = dom->ops->attach_dev_pasid (dom, dev, id); +if (!ret) { + *pasid = id; +atomic_inc (&dom->pasid_users); +return 0 ; + } + +if (atomic_dec_and_test (&dom->pasid_users)) { +ioasid_free (id); + dom->pasid =0 ; + } + +return ret; +} +EXPORT_SYMBOL (iommu_attach_dma_pasid); + +/** +* iommu_detach_dma_pasid --Disable in-kernel DMA request with PASID +* @dev: Device's PASID DMA to be disabled +* +* It is the device driver's responsibility to ensure no more incoming DMA
+ * requests with the kernel PASID before calling this function. IOMMU driver
+ * ensures PASID cache, IOTLBs related to the kernel PASID are cleared and
+ * drained.
+ *
+ */drivers/iommu/dma-iommu.c +14
-0+void iommu_detach_dma_pasid (struct device *dev) +{ +struct iommu_domain *dom; +ioasid_t pasid; + + dom =iommu_get_domain_for_dev (dev); +if (!dom || !dom->ops || !dom->ops->detach_dev_pasid) { +dev_warn (dev,"No ops for detaching PASID %u" , pasid); +return ; + } +/* Only support DMA API managed domain type */ +if (dom->type ==IOMMU_DOMAIN_UNMANAGED || + dom->type ==IOMMU_DOMAIN_BLOCKED ) { + dev_err(dev, "Invalid domain type %d to detach DMAPASID %u\n",
drivers/iommu/dma-iommu.c +2
-0+ dom->type, pasid); +return ;
Quoted 9 lines
drivers/iommu/dma-iommu.c void iommu_put_dma_cookie(struct iommu_domain *domain) · line 471
Quoted 6 lines
drivers/iommu/dma-iommu.c +6
-0+ } + pasid = dom->pasid; +if (!pasid) { +dev_err (dev,"No DMA PASID attached\n" ); +return ; + }All WARN_ON's too
will do.
Quoted 29 lines
Quoted 29 lines
drivers/iommu/dma-iommu.c +8
-0+ dom->ops->detach_dev_pasid (dom, dev, pasid); +if (atomic_dec_and_test (&dom->pasid_users)) { +ioasid_free (pasid); + dom->pasid =0 ; + } +} +EXPORT_SYMBOL (iommu_detach_dma_pasid); +/** * iommu_dma_get_resv_regions - Reserved region driver helper * @dev: Device from iommu_get_resv_regions() include/linux/dma-iommu.h +4
-0+++ b/include/linux/dma-iommu.h@@ -18,6 +18,9 @@ int iommu_get_dma_cookie(struct iommu_domain *domain);int iommu_get_msi_cookie (struct iommu_domain *domain,dma_addr_t base);void iommu_put_dma_cookie (struct iommu_domain *domain); +int iommu_attach_dma_pasid (struct device *dev,ioasid_t *pasid); +void iommu_detach_dma_pasid (struct device *dev); +/* Setup call for arch DMA mapping code */ void iommu_setup_dma_ops (struct device *dev,u64 dma_base,u64 dma_limit);int iommu_dma_init_fq (struct iommu_domain *domain);include/linux/iommu.h +1
-0+++ b/include/linux/iommu.h@@ -105,6 +105,8 @@ struct iommu_domain { enum iommu_page_response_code (*iopf_handler)(struct
Quoted 9 lines
include/linux/iommu.h struct iommu_domain { · line 109
Quoted 5 lines
include/linux/iommu.h +1
-0iommu_fault *fault, void *data);void *fault_data; +ioasid_t pasid;/* Used for DMA requests with PASID */
include/linux/iommu.h +1
-0+atomic_t pasid_users;These are poorly named, this is really the DMA API global PASID and shouldn't be used for other things.
I was hoping it can be generic since sva_cookie also has a pasid field but it looks like sva uses mm->pasid now.
Shall we call it dma_api_pasid, dma_pasid, or something else?
Quoted 1 line
Jason
Thanks,
Jacob
PATCH v3 3/4 dmaengine: idxd: Use DMA API for in-kernel DMA with PASID
In reply to Jacob PanThe current in-kernel supervisor PASID support is based on the SVM/SVA machinery in SVA lib. The binding between a kernel PASID and kernel mapping has many flaws. See discussions in the link below.
This patch enables in-kernel DMA by switching from SVA lib to the standard DMA mapping APIs. Since both DMA requests with and without PASIDs are mapped identically, there is no change to how DMA APIs are used after the kernel PASID is enabled.
--- drivers/dma/idxd/idxd.h | 1 - drivers/dma/idxd/init.c | 34 +++++++++------------------------- drivers/dma/idxd/sysfs.c | 7 ------- 3 files changed, 9 insertions(+), 33 deletions(-)
drivers/dma/idxd/idxd.h +0 -1
@@ -277,7 +277,6 @@ struct idxd_device {struct idxd_wq **wqs;struct idxd_engine **engines; -struct iommu_sva *sva;unsigned int pasid;int num_groups;
drivers/dma/idxd/init.c +9 -25
@@ -16,6 +16,7 @@#include <linux/idr.h> #include <linux/intel-svm.h> #include <linux/iommu.h> +#include <linux/dma-iommu.h> #include <uapi/linux/idxd.h> #include <linux/dmaengine.h> #include "../dmaengine.h"
@@ -466,36 +467,22 @@ static struct idxd_device *idxd_alloc(struct pci_dev *pdev, struct idxd_driver_dstatic int idxd_enable_system_pasid (struct idxd_device *idxd) { -int flags; -unsigned int pasid; -struct iommu_sva *sva; +u32 pasid; +int ret; - flags =SVM_FLAG_SUPERVISOR_MODE ; - - sva =iommu_sva_bind_device (&idxd->pdev->dev,NULL , &flags); -if (IS_ERR (sva)) { -dev_warn (&idxd->pdev->dev, -"iommu sva bind failed: %ld\n" ,PTR_ERR (sva)); -return PTR_ERR (sva); - } - - pasid =iommu_sva_get_pasid (sva); -if (pasid ==IOMMU_PASID_INVALID ) { -iommu_sva_unbind_device (sva); -return -ENODEV ; + ret =iommu_attach_dma_pasid (&idxd->pdev->dev, &pasid); +if (ret) { +dev_err (&idxd->pdev->dev,"No DMA PASID %d\n" , ret); +return ret; } - - idxd->sva = sva; idxd->pasid = pasid; -dev_dbg (&idxd->pdev->dev,"system pasid: %u\n" , pasid); +return 0 ; }static void idxd_disable_system_pasid (struct idxd_device *idxd) { - -iommu_sva_unbind_device (idxd->sva); - idxd->sva =NULL ; +iommu_detach_dma_pasid (&idxd->pdev->dev); }static int idxd_probe (struct idxd_device *idxd)
@@ -527,10 +514,7 @@ static int idxd_probe(struct idxd_device *idxd)else set_bit (IDXD_FLAG_PASID_ENABLED , &idxd->flags); } - }else if (!sva) { -dev_warn (dev,"User forced SVA off via module param.\n" ); } -idxd_read_caps (idxd);idxd_read_table_offsets (idxd);
drivers/dma/idxd/sysfs.c +0 -7
@@ -839,13 +839,6 @@ static ssize_t wq_name_store(struct device *dev,if (strlen (buf) >WQ_NAME_SIZE ||strlen (buf) ==0 )return -EINVAL ; -/* -* This is temporarily placed here until we have SVM support for -* dmaengine. -*/ -if (wq->type ==IDXD_WQT_KERNEL &&device_pasid_enabled (wq->idxd)) -return -EOPNOTSUPP ; -memset (wq->name,0 ,WQ_NAME_SIZE +1 );strncpy (wq->name, buf,WQ_NAME_SIZE );strreplace (wq->name,'\n' ,'\0' );
Signature · 1 line
2.25.1
PATCH v3 4/4 iommu/vt-d: Delete unused SVM flag
In reply to Jacob PanSupervisor PASID for SVA/SVM is no longer supported, delete the unused flag.
--- drivers/iommu/intel/svm.c | 2 +- include/linux/intel-svm.h | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-)
drivers/iommu/intel/svm.c +1 -1
@@ -750,7 +750,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)* to unbind the mm while any page faults are outstanding. */ svm =pasid_private_find (req->pasid); -if (IS_ERR_OR_NULL (svm) || (svm->flags &SVM_FLAG_SUPERVISOR_MODE )) +if (IS_ERR_OR_NULL (svm)) goto bad_req; }
include/linux/intel-svm.h +0 -13
@@ -13,17 +13,4 @@#define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x20)#define PRQ_DEPTH ((0x1000 << PRQ_ORDER) >> 5) -/* -* The SVM_FLAG_SUPERVISOR_MODE flag requests a PASID which can be used only -* for access to kernel addresses. No IOTLB flushes are automatically done -* for kernel mappings; it is valid only for access to the kernel's static -* 1:1 mapping of physical memory — not to vmalloc or even module mappings. -* A future API addition may permit the use of such ranges, by means of an -* explicit IOTLB flush call (akin to the DMA API's unmap method). -* -* It is unlikely that we will ever hook into flush_tlb_kernel_range() to -* do such IOTLB flushes automatically. -*/ -#define SVM_FLAG_SUPERVISOR_MODE BIT(0) -#endif /* __INTEL_SVM_H__ */
Signature · 1 line
2.25.1