Re-enable IDXD kernel workqueue under DMA API
Subsystems (4)
v5 cover Re-enable IDXD kernel workqueue under DMA API
Hi all,
IDXD kernel work queues were disabled due to the flawed use of kernel VA and SVA API.
The solution is to enable it under DMA API where IDXD shared workqueue users can use ENQCMDS to submit work on buffers mapped by DMA API.
This patchset adds support for attaching PASID to the device's default domain and the ability to allocate global PASIDs from IOMMU APIs. We can then re-enable the kernel work queues and use them under DMA API.
This depends on the IOASID removal series. (merged)
https://lore.kernel.org/all/[email protected]/
Thanks,
Jacob
---
Changelog:
v5:
- exclude two patches related to supervisor mode, taken by VT-d maintainer Baolu.
- move PASID range check into allocation API so that device drivers only need to pass in struct device*. (Kevin)
- factor out helper functions in device-domain attach (Baolu)
- make explicit use of RID_PASID across architectures
v4:
- move dummy functions outside ifdef CONFIG_IOMMU_SVA (Baolu)
- dropped domain type check while disabling idxd system PASID (Baolu)
v3:
- moved global PASID allocation API from SVA to IOMMU (Kevin)
- remove #ifdef around global PASID reservation during boot (Baolu)
- remove restriction on PASID 0 allocation (Baolu)
- fix a bug in sysfs domain change when attaching devices
- clear idxd user interrupt enable bit after disabling device( Fenghua)
v2:
- refactored device PASID attach domain ops based on Baolu's early patch
- addressed TLB flush gap
- explicitly reserve RID_PASID from SVA PASID number space
- get dma domain directly, avoid checking domain types
Jacob Pan (6):
iommu: Generalize default PCIe requester ID PASID iommu/sva: Explicitly exclude RID_PASID from SVA
iommu: Move global PASID allocation from SVA to core iommu/vt-d: Prepare PASID attachment beyond RID_PASID iommu/vt-d: Implement set_dev_pasid domain op dmaengine/idxd: Re-enable kernel workqueue under DMA API
Lu Baolu (1):
iommu/vt-d: Factoring out PASID set up helper function
drivers/dma/idxd/device.c | 30 +---- drivers/dma/idxd/init.c | 60 ++++++++- drivers/dma/idxd/sysfs.c | 7 -- drivers/iommu/intel/iommu.c | 240 +++++++++++++++++++++++++++++------- drivers/iommu/intel/iommu.h | 8 ++ drivers/iommu/intel/pasid.c | 2 +- drivers/iommu/intel/pasid.h | 1 - drivers/iommu/iommu-sva.c | 25 +--- drivers/iommu/iommu.c | 24 ++++ include/linux/iommu.h | 10 ++ 10 files changed, 305 insertions(+), 102 deletions(-)
Signature · 1 line
2.25.1
PATCH v5 1/7 iommu: Generalize default PCIe requester ID PASID
PCIe Process address space ID (PASID) is used to tag DMA traffic, it provides finer grained isolation than requester ID (RID).
For each RID, 0 is as a special PASID for the legacy DMA (without PASID), thus RID_PASID. This is universal across all architectures, therefore warranted to be declared in the common header. Noting that VT-d could support none-zero RID_PASID, but currently not used.
By having a common RID_PASID, we can avoid conflicts between different use cases in the generic code. e.g. SVA and DMA API with PASIDs.
--- drivers/iommu/intel/iommu.c | 24 ++++++++++++------------ drivers/iommu/intel/pasid.c | 2 +- drivers/iommu/intel/pasid.h | 1 - include/linux/iommu.h | 1 + 4 files changed, 14 insertions(+), 14 deletions(-)
drivers/iommu/intel/iommu.c +12 -12
@@ -877,7 +877,7 @@ void dmar_fault_dump_ptes(struct intel_iommu *iommu, u16 source_id, }/* For request-without-pasid, get the pasid from context entry */ if (intel_iommu_sm && pasid ==IOMMU_PASID_INVALID ) - pasid =PASID_RID2PASID ; + pasid =IOMMU_DEF_RID_PASID ; dir_index = pasid >>PASID_PDE_SHIFT ; pde = &dir[dir_index];
@@ -1480,7 +1480,7 @@ static void __iommu_flush_dev_iotlb(struct device_domain_info *info, qdep = info->ats_qdep;qi_flush_dev_iotlb (info->iommu, sid, info->pfsid, qdep, addr, mask); -quirk_extra_dev_tlb_flush (info, addr, mask,PASID_RID2PASID , qdep); +quirk_extra_dev_tlb_flush (info, addr, mask,IOMMU_DEF_RID_PASID , qdep); } static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
@@ -1514,7 +1514,7 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, ih =1 <<6 ;if (domain->use_first_level) { -qi_flush_piotlb (iommu, did,PASID_RID2PASID , addr, pages, ih); +qi_flush_piotlb (iommu, did,IOMMU_DEF_RID_PASID , addr, pages, ih); }else {unsigned long bitmask = aligned_pages -1 ;
@@ -1584,7 +1584,7 @@ static void intel_flush_iotlb_all(struct iommu_domain *domain)u16 did =domain_id_iommu (dmar_domain, iommu);if (dmar_domain->use_first_level) -qi_flush_piotlb (iommu, did,PASID_RID2PASID ,0 ,-1 ,0 ); +qi_flush_piotlb (iommu, did,IOMMU_DEF_RID_PASID ,0 ,-1 ,0 );else iommu->flush.flush_iotlb (iommu, did,0 ,0 ,DMA_TLB_DSI_FLUSH );
@@ -1976,7 +1976,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain,context_pdts (pds);/* Setup the RID_PASID field: */ -context_set_sm_rid2pasid (context,PASID_RID2PASID ); +context_set_sm_rid2pasid (context,IOMMU_DEF_RID_PASID );/* * Setup the Device-TLB enable bit and Page request
@@ -2455,13 +2455,13 @@ static int dmar_domain_attach_device(struct dmar_domain *domain,/* Setup the PASID entry for requests without PASID: */ if (hw_pass_through &&domain_type_is_si (domain)) ret =intel_pasid_setup_pass_through (iommu, domain, - dev,PASID_RID2PASID ); + dev,IOMMU_DEF_RID_PASID );else if (domain->use_first_level) ret =domain_setup_first_level (iommu, domain, dev, -PASID_RID2PASID ); +IOMMU_DEF_RID_PASID );else ret =intel_pasid_setup_second_level (iommu, domain, - dev,PASID_RID2PASID ); + dev,IOMMU_DEF_RID_PASID );if (ret) {dev_err (dev,"Setup RID2PASID failed\n" );device_block_translation (dev);
@@ -3997,7 +3997,7 @@ static void dmar_remove_one_dev_info(struct device *dev)if (!dev_is_real_dma_subdevice (info->dev)) {if (dev_is_pci (info->dev) &&sm_supported (iommu))intel_pasid_tear_down_entry (iommu, info->dev, -PASID_RID2PASID , false); +IOMMU_DEF_RID_PASID , false);iommu_disable_pci_caps (info);domain_context_clear (info);
@@ -4026,7 +4026,7 @@ static void device_block_translation(struct device *dev)if (!dev_is_real_dma_subdevice (dev)) {if (sm_supported (iommu))intel_pasid_tear_down_entry (iommu, dev, -PASID_RID2PASID , false); +IOMMU_DEF_RID_PASID , false);else domain_context_clear (info); }
@@ -4360,7 +4360,7 @@ static void domain_set_force_snooping(struct dmar_domain *domain)list_for_each_entry (info, &domain->devices, link)intel_pasid_setup_page_snoop_control (info->iommu, info->dev, -PASID_RID2PASID ); +IOMMU_DEF_RID_PASID ); }static bool intel_iommu_enforce_cache_coherency (struct iommu_domain *domain)
@@ -4948,7 +4948,7 @@ void quirk_extra_dev_tlb_flush(struct device_domain_info *info,return ; sid =PCI_DEVID (info->bus, info->devfn); -if (pasid ==PASID_RID2PASID ) { +if (pasid ==IOMMU_DEF_RID_PASID ) {qi_flush_dev_iotlb (info->iommu, sid, info->pfsid, qdep, address, mask); }else {
drivers/iommu/intel/pasid.c +1 -1
@@ -438,7 +438,7 @@ devtlb_invalidation_with_pasid(struct intel_iommu *iommu,* SVA usage, device could do DMA with multiple PASIDs. It is more * efficient to flush devTLB specific to the PASID. */ -if (pasid ==PASID_RID2PASID ) +if (pasid ==IOMMU_DEF_RID_PASID )qi_flush_dev_iotlb (iommu, sid, pfsid, qdep,0 ,64 -VTD_PAGE_SHIFT );else qi_flush_dev_iotlb_pasid (iommu, sid, pfsid, pasid, qdep,0 ,64 -VTD_PAGE_SHIFT );
drivers/iommu/intel/pasid.h +0 -1
@@ -10,7 +10,6 @@#ifndef __INTEL_PASID_H#define __INTEL_PASID_H -#define PASID_RID2PASID 0x0#define PASID_MIN 0x1#define PASID_MAX 0x100000#define PASID_PTE_MASK 0x3F
include/linux/iommu.h +1 -0
@@ -190,6 +190,7 @@ enum iommu_dev_features { IOMMU_DEV_FEAT_IOPF, }; +#define IOMMU_DEF_RID_PASID (0U)/* Reserved for DMA w/o PASID */ #define IOMMU_PASID_INVALID (-1U)typedef unsigned int ioasid_t ;
Signature · 1 line
2.25.1
RE: [PATCH v5 1/7] iommu: Generalize default PCIe requester ID PASID
Quoted 6 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AMPCIe Process address space ID (PASID) is used to tag DMA traffic, it provides finer grained isolation than requester ID (RID).
Quoted 8 lines
For each RID, 0 is as a special PASID for the legacy DMA (without PASID), thus
RID_PASID. This is universal across all architectures, therefore warranted to be declared in the common header. Noting that VT-d could support none-zeroRID_PASID, but currently not used.By having a common
RID_PASID, we can avoid conflicts between different use cases in the generic code. e.g. SVA and DMA API with PASIDs.
You intend it to be generic but in the end only vt-d driver is changed to use it in this series...
include/linux/iommu.h enum iommu_dev_features { · line 194
Quoted 6 lines
include/linux/iommu.h +1
-0@@ -190,6 +190,7 @@ enum iommu_dev_features { IOMMU_DEV_FEAT_IOPF, }; +#define IOMMU_DEF_RID_PASID (0U)/* Reserved for DMA w/o PASID */
Is RID a general team on other platform?
Would IOMMU_DEF_NO_PASID serve the purpose better?
Re: [PATCH v5 1/7] iommu: Generalize default PCIe requester ID PASID
Hi Kevin,
Quoted 12 lines · Kevin
Quoted 12 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AMPCIe Process address space ID (PASID) is used to tag DMA traffic, it provides finer grained isolation than requester ID (RID).
For each RID, 0 is as a special PASID for the legacy DMA (without PASID), thus
RID_PASID. This is universal across all architectures, therefore warranted to be declared in the common header. Noting that VT-d could support none-zeroRID_PASID, but currently not used.
Quoted 5 lines
Quoted 2 lines
By having a common
RID_PASID, we can avoid conflicts between different use cases in the generic code. e.g. SVA and DMA API with PASIDs.You intend it to be generic but in the end only vt-d driver is changed to use it in this series...
change for SVA is in the patch.
Quoted 8 lines
include/linux/iommu.h enum iommu_dev_features { · line 194
Quoted 6 lines
include/linux/iommu.h +1
-0@@ -190,6 +190,7 @@ enum iommu_dev_features { IOMMU_DEV_FEAT_IOPF, }; +#define IOMMU_DEF_RID_PASID (0U)/* Reserved for DMA w/o PASID */ Is RID a general team on other platform?
RID, aka requester id is a PCI term. so I this it is common though on SMMU might be better called stream ID PASID?
Quoted 1 line
Would
IOMMU_DEF_NO_PASIDserve the purpose better?
not sure, it is still a PASID. For completeness, might be called IOMMU_DEF_PASID_FOR_DMA_NO_PASID :)
other suggestions?
Thanks,
Jacob
RE: [PATCH v5 1/7] iommu: Generalize default PCIe requester ID PASID
Quoted 20 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 11:56 PMHi Kevin,
Quoted 12 lines · Kevin
Quoted 12 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AMPCIe Process address space ID (PASID) is used to tag DMA traffic, it provides finer grained isolation than requester ID (RID).
For each RID, 0 is as a special PASID for the legacy DMA (without PASID), thus
RID_PASID. This is universal across all architectures, therefore warranted to be declared in the common header. Noting that VT-d could support none-zeroRID_PASID, but currently not used.
Quoted 6 lines
Quoted 5 lines
Quoted 2 lines
By having a common
RID_PASID, we can avoid conflicts between different use cases in the generic code. e.g. SVA and DMA API with PASIDs.You intend it to be generic but in the end only vt-d driver is changed to use it in this series...
change for SVA is in the patch.
My point was that since it is common why there is no change in arm-smmu driver to use the common macro?
Quoted 9 lines
Quoted 9 lines
include/linux/iommu.h enum iommu_dev_features { · line 194
Quoted 7 lines
include/linux/iommu.h +1
-0@@ -190,6 +190,7 @@ enum iommu_dev_features { IOMMU_DEV_FEAT_IOPF, }; +#define IOMMU_DEF_RID_PASID (0U)/* Reserved for DMA w/o PASID */ Is RID a general team on other platform?
Quoted 9 lines
RID, aka requester id is a PCI term. so I this it is common though on SMMU might be better called stream ID PASID?
Quoted 1 line
Would
IOMMU_DEF_NO_PASIDserve the purpose better?not sure, it is still a PASID. For completeness, might be called
IOMMU_DEF_PASID_FOR_DMA_NO_PASID:)other suggestions?
that is even worse. Let's keep the original one and see whether others have better suggestion.
Re: [PATCH v5 1/7] iommu: Generalize default PCIe requester ID PASID
Hi Kevin,
Quoted 11 lines · Kevin
Quoted 8 lines
Quoted 7 lines
Quoted 4 lines
By having a common
RID_PASID, we can avoid conflicts between different use cases in the generic code. e.g. SVA and DMA API with PASIDs.You intend it to be generic but in the end only vt-d driver is changed to use it in this series...
change for SVA is in the patch.
My point was that since it is common why there is no change in arm-smmu driver to use the common macro?
Got it, I will include changes to SSID 0 in smmu code.
Thanks,
Jacob
PATCH v5 2/7 iommu/sva: Explicitly exclude RID_PASID from SVA
In reply to Jacob PanSVA PASID allocation is hardcoded to start from 1 because 0 is used for RID_PASID, let's make it explicit to avoid the potential conflicts.
--- drivers/iommu/iommu-sva.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/iommu-sva.c +1 -1
@@ -66,7 +66,7 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mmreturn ERR_PTR (-EOPNOTSUPP );/* Allocate mm->pasid if necessary. */ - ret =iommu_sva_alloc_pasid (mm,1 , max_pasids -1 ); + ret =iommu_sva_alloc_pasid (mm,IOMMU_DEF_RID_PASID +1 , max_pasids -1 );if (ret)return ERR_PTR (ret);
Signature · 1 line
2.25.1
RE: [PATCH v5 2/7] iommu/sva: Explicitly exclude RID_PASID from SVA
Quoted 22 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AMSVA PASID allocation is hardcoded to start from 1 because 0 is used for
RID_PASID, let's make it explicit to avoid the potential conflicts.Signed-off-by Jacob Pan <[email protected]>--- drivers/iommu/iommu-sva.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/iommu/iommu-sva.c +0
-0@@ -66,7 +66,7 @@ struct iommu_sva *iommu_sva_bind_device(structdevice *dev, struct
mm_struct*mmdrivers/iommu/iommu-sva.c +1
-1return ERR_PTR (-EOPNOTSUPP );/* Allocate mm->pasid if necessary. */ - ret =iommu_sva_alloc_pasid (mm,1 , max_pasids -1 ); + ret = iommu_sva_alloc_pasid(mm, IOMMU_DEF_RID_PASID + 1,
Quoted 1 line
max_pasids- 1);
To be future proof it's probably cleaner to define a IOMMU_MAX_RSVD_PASID in case there may be more reserved pasids in future usages?
Re: [PATCH v5 2/7] iommu/sva: Explicitly exclude RID_PASID from SVA
Hi Kevin,
Quoted 19 lines · Kevin
Quoted 19 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AMSVA PASID allocation is hardcoded to start from 1 because 0 is used for
RID_PASID, let's make it explicit to avoid the potential conflicts.Signed-off-by Jacob Pan <[email protected]>--- drivers/iommu/iommu-sva.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/iommu/iommu-sva.c +0
-0@@ -66,7 +66,7 @@ struct iommu_sva *iommu_sva_bind_device(structdevice *dev, struct
mm_struct*mm
returnERR_PTR(-EOPNOTSUPP);
Quoted 8 lines
Quoted 4 lines
drivers/iommu/iommu-sva.c +1
-1/* Allocate mm->pasid if necessary. */ - ret =iommu_sva_alloc_pasid (mm,1 , max_pasids -1 ); + ret = iommu_sva_alloc_pasid(mm, IOMMU_DEF_RID_PASID + 1,
max_pasids- 1);To be future proof it's probably cleaner to define a
IOMMU_MAX_RSVD_PASIDin case there may be more reserved pasids in future usages?
much better, will do.
Thanks,
Jacob
PATCH v5 3/7 iommu: Move global PASID allocation from SVA to core
In reply to Jacob PanDevices that use Intel ENQCMD to submit work must use global PASIDs in that the PASID are stored in a per CPU MSR. When such device need to submit work for in-kernel DMA with PASID, it must allocate PASIDs from the same global number space to avoid conflict.
This patch moves global PASID allocation APIs from SVA to IOMMU APIs. It is expected that device drivers will use the allocated PASIDs to attach to appropriate IOMMU domains for use.
---
v5: move PASID range check inside API so that device drivers only pass
in struct device* (Kevin)
v4: move dummy functions outside ifdef CONFIG_IOMMU_SVA (Baolu)
--- drivers/iommu/iommu-sva.c | 25 ++++++------------------- drivers/iommu/iommu.c | 24 ++++++++++++++++++++++++ include/linux/iommu.h | 9 +++++++++ 3 files changed, 39 insertions(+), 19 deletions(-)
drivers/iommu/iommu-sva.c +6 -19
@@ -9,27 +9,19 @@#include "iommu-sva.h" static DEFINE_MUTEX (iommu_sva_lock); -static DEFINE_IDA (iommu_global_pasid_ida);/* Allocate a PASID for the mm within range (inclusive) */ -static int iommu_sva_alloc_pasid (struct mm_struct *mm,ioasid_t min,ioasid_t max) +static int iommu_sva_alloc_pasid (struct mm_struct *mm,struct device *dev) {int ret =0 ; -if (!pasid_valid (min) || !pasid_valid (max) || - min ==0 || max < min) -return -EINVAL ; -mutex_lock (&iommu_sva_lock);/* Is a PASID already associated with this mm? */ -if (pasid_valid (mm->pasid)) { -if (mm->pasid < min || mm->pasid > max) - ret = -EOVERFLOW ; +if (pasid_valid (mm->pasid)) goto out; - } - ret =ida_alloc_range (&iommu_global_pasid_ida, min, max,GFP_KERNEL ); -if (ret < min) + ret =iommu_alloc_global_pasid_dev (dev); +if (!pasid_valid (ret)) goto out; mm->pasid = ret; ret =0 ;
@@ -58,15 +50,10 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm {struct iommu_domain *domain;struct iommu_sva *handle; -ioasid_t max_pasids;int ret; - max_pasids = dev->iommu->max_pasids; -if (!max_pasids) -return ERR_PTR (-EOPNOTSUPP ); -/* Allocate mm->pasid if necessary. */ - ret =iommu_sva_alloc_pasid (mm,IOMMU_DEF_RID_PASID +1 , max_pasids -1 ); + ret =iommu_sva_alloc_pasid (mm, dev);if (ret)return ERR_PTR (ret);
@@ -211,5 +198,5 @@ void mm_pasid_drop(struct mm_struct *mm)if (likely (!pasid_valid (mm->pasid)))return ; -ida_free (&iommu_global_pasid_ida, mm->pasid); +iommu_free_global_pasid (mm->pasid); }
drivers/iommu/iommu.c +24 -0
@@ -38,6 +38,7 @@static struct kset *iommu_group_kset;static DEFINE_IDA (iommu_group_ida); +static DEFINE_IDA (iommu_global_pasid_ida);static unsigned int iommu_def_domain_type__read_mostly ;static bool iommu_dma_strict__read_mostly =IS_ENABLED (CONFIG_IOMMU_DEFAULT_DMA_STRICT );
@@ -3450,3 +3451,26 @@ struct iommu_domain *iommu_sva_domain_alloc(struct device *dev,return domain; } + +ioasid_t iommu_alloc_global_pasid_dev (struct device *dev) +{ +int ret; +ioasid_t max; + + max =dev_iommu_get_max_pasids (dev); + ret =ida_alloc_range (&iommu_global_pasid_ida,IOMMU_DEF_RID_PASID +1 , max,GFP_KERNEL ); +if (ret <0 ) +return IOMMU_PASID_INVALID ; + +return ret; +} +EXPORT_SYMBOL_GPL (iommu_alloc_global_pasid_dev); + +void iommu_free_global_pasid (ioasid_t pasid) +{ +if (WARN_ON (!pasid_valid (pasid))) +return ; + +ida_free (&iommu_global_pasid_ida, pasid); +} +EXPORT_SYMBOL_GPL (iommu_free_global_pasid);
include/linux/iommu.h +9 -0
@@ -724,6 +724,8 @@ void iommu_detach_device_pasid(struct iommu_domain *domain,struct iommu_domain *iommu_get_domain_for_dev_pasid (struct device *dev,ioasid_t pasid,unsigned int type); +ioasid_t iommu_alloc_global_pasid_dev (struct device *dev); +void iommu_free_global_pasid (ioasid_t pasid);#else /* CONFIG_IOMMU_API */ struct iommu_ops {};
@@ -1090,6 +1092,13 @@ iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid, {return NULL ; } + +static inline ioasid_t iommu_alloc_global_pasid_dev (struct device *dev) +{ +return IOMMU_PASID_INVALID ; +} + +static inline void iommu_free_global_pasid (ioasid_t pasid) {}#endif /* CONFIG_IOMMU_API */ /**
Signature · 1 line
2.25.1
RE: [PATCH v5 3/7] iommu: Move global PASID allocation from SVA to core
Quoted 7 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AMDevices that use Intel ENQCMD to submit work must use global PASIDs in that the PASID are stored in a per CPU MSR. When such device need to submit work for in-kernel DMA with PASID, it must allocate PASIDs from the same global number space to avoid conflict.
well the device itself cannot submit work to itself. It's software to submit work to the device. 😊
Quoted 8 lines
/* Allocate a PASID for the mm within range (inclusive) */
-static intiommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max)drivers/iommu/iommu-sva.c +1
-1+static int iommu_sva_alloc_pasid (struct mm_struct *mm,struct device *dev) {int ret =0 ; - if (!pasid_valid(min) || !pasid_valid(max) ||
drivers/iommu/iommu-sva.c line 21
Quoted 10 lines
drivers/iommu/iommu-sva.c +1
-6- min == 0 || max < min) -return -EINVAL ; -mutex_lock (&iommu_sva_lock);/* Is a PASID already associated with this mm? */ -if (pasid_valid (mm->pasid)) { -if (mm->pasid < min || mm->pasid > max) - ret = -EOVERFLOW ; +if (pasid_valid (mm->pasid)) goto out;
emmm here you still want to check whether mm->pasid exceeds the max pasid width of the bound device.
Re: [PATCH v5 3/7] iommu: Move global PASID allocation from SVA to core
Hi Kevin,
Quoted 10 lines · Kevin
Quoted 7 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AMDevices that use Intel ENQCMD to submit work must use global PASIDs in that the PASID are stored in a per CPU MSR. When such device need to submit work for in-kernel DMA with PASID, it must allocate PASIDs from the same global number space to avoid conflict.
well the device itself cannot submit work to itself. It's software to submit work to the device. 😊
will rephrase, how about:
Intel ENQCMD work submission requires the use of global PASIDs in ...
Quoted 9 lines
Quoted 9 lines
that the PASID are stored in a per CPU MSR
/* Allocate a PASID for the mm within range (inclusive) */
-static intiommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max)
+static intiommu_sva_alloc_pasid(struct mm_struct *mm, struct device *dev){
int ret = 0;
Quoted 14 lines
drivers/iommu/iommu-sva.c line 21
Quoted 11 lines
drivers/iommu/iommu-sva.c +1
-7-if (!pasid_valid (min) || !pasid_valid (max) || - min ==0 || max < min) -return -EINVAL ; -mutex_lock (&iommu_sva_lock);/* Is a PASID already associated with this mm? */ -if (pasid_valid (mm->pasid)) { -if (mm->pasid < min || mm->pasid > max) - ret = -EOVERFLOW ; +if (pasid_valid (mm->pasid)) goto out;emmm here you still want to check whether
mm->pasidexceeds the max pasid width of the bound device.
good point, existing mm->pasid could be from another device that has a larger pasid range.
Thanks,
Jacob
Baolu Lu
maintainerRe: [PATCH v5 3/7] iommu: Move global PASID allocation from SVA to core
In reply to Jacob Pandrivers/iommu/iommu.c struct iommu_domain *iommu_sva_domain_alloc(struct device *dev, · line 3460
Quoted 6 lines · Jacob Pan
drivers/iommu/iommu.c +6
-0+ioasid_t iommu_alloc_global_pasid_dev (struct device *dev) +{ +int ret; +ioasid_t max; + + max =dev_iommu_get_max_pasids (dev);
Perhaps you can use dev->iommu->max_pasids. It's static, so no need to recalculate it.
drivers/iommu/iommu.c struct iommu_domain *iommu_sva_domain_alloc(struct device *dev, · line 3467
Quoted 7 lines
drivers/iommu/iommu.c +7
-0+ ret =ida_alloc_range (&iommu_global_pasid_ida,IOMMU_DEF_RID_PASID +1 , max,GFP_KERNEL ); +if (ret <0 ) +return IOMMU_PASID_INVALID ; + +return ret; +} +EXPORT_SYMBOL_GPL (iommu_alloc_global_pasid_dev);
Best regards,
baolu
Re: [PATCH v5 3/7] iommu: Move global PASID allocation from SVA to core
Hi Baolu,
Quoted 11 lines · Baolu Lu
drivers/iommu/iommu.c struct iommu_domain *iommu_sva_domain_alloc(struct device *dev, · line 3460
Quoted 6 lines · Jacob Pan
drivers/iommu/iommu.c +6
-0+ioasid_t iommu_alloc_global_pasid_dev (struct device *dev) +{ +int ret; +ioasid_t max; + + max =dev_iommu_get_max_pasids (dev);Perhaps you can use
dev->iommu->max_pasids. It's static, so no need to recalculate it.
sounds good!
Thanks,
Jacob
PATCH v5 4/7 iommu/vt-d: Factoring out PASID set up helper function
In reply to Jacob PanFrom: Lu Baolu <[email protected]>
To prepare non-RID_PASID attachment, factoring out common code to be reused. PASID entry set up is common for all DMA API PASIDs.
--- drivers/iommu/intel/iommu.c | 42 ++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-)
drivers/iommu/intel/iommu.c +25 -17
@@ -2429,6 +2429,26 @@ static int __init si_domain_init(int hw)return 0 ; } +static int dmar_domain_attach_device_pasid (struct dmar_domain *domain, +struct intel_iommu *iommu, +struct device *dev,ioasid_t pasid) +{ +int ret; + +/* PASID table is mandatory for a PCI device in scalable mode. */ +if (!sm_supported (iommu) &&dev_is_real_dma_subdevice (dev)) +return -EOPNOTSUPP ; + +if (hw_pass_through &&domain_type_is_si (domain)) + ret =intel_pasid_setup_pass_through (iommu, domain, dev, pasid); +else if (domain->use_first_level) + ret =domain_setup_first_level (iommu, domain, dev, pasid); +else + ret =intel_pasid_setup_second_level (iommu, domain, dev, pasid); + +return 0 ; +} +static int dmar_domain_attach_device (struct dmar_domain *domain,struct device *dev) {
@@ -2450,23 +2470,11 @@ static int dmar_domain_attach_device(struct dmar_domain *domain,list_add (&info->link, &domain->devices);spin_unlock_irqrestore (&domain->lock, flags); -/* PASID table is mandatory for a PCI device in scalable mode. */ -if (sm_supported (iommu) && !dev_is_real_dma_subdevice (dev)) { -/* Setup the PASID entry for requests without PASID: */ -if (hw_pass_through &&domain_type_is_si (domain)) - ret =intel_pasid_setup_pass_through (iommu, domain, - dev,IOMMU_DEF_RID_PASID ); -else if (domain->use_first_level) - ret =domain_setup_first_level (iommu, domain, dev, -IOMMU_DEF_RID_PASID ); -else - ret =intel_pasid_setup_second_level (iommu, domain, - dev,IOMMU_DEF_RID_PASID ); -if (ret) { -dev_err (dev,"Setup RID2PASID failed\n" ); -device_block_translation (dev); -return ret; - } + ret =dmar_domain_attach_device_pasid (domain, iommu, dev, +IOMMU_DEF_RID_PASID ); +if (ret) { +dev_err (dev,"Setup RID2PASID failed\n" ); +device_block_translation (dev); } ret =domain_context_mapping (domain, dev);
Signature · 1 line
2.25.1
RE: [PATCH v5 4/7] iommu/vt-d: Factoring out PASID set up helper function
drivers/iommu/intel/iommu.c static int __init si_domain_init(int hw) · line 2440
Quoted 13 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AMdrivers/iommu/intel/iommu.c +9
-0+static int dmar_domain_attach_device_pasid (struct dmar_domain *domain, +struct intel_iommu *iommu, +struct device *dev,ioasid_t pasid) +{ +int ret; + +/* PASID table is mandatory for a PCI device in scalable mode. */ +if (!sm_supported (iommu) &&dev_is_real_dma_subdevice (dev)) +return -EOPNOTSUPP ;
"&&" should be "||"
Baolu Lu
maintainerRe: [PATCH v5 4/7] iommu/vt-d: Factoring out PASID set up helper function
Quoted 4 lines · Kevin
Quoted 4 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AM
Quoted 12 lines
drivers/iommu/intel/iommu.c static int __init si_domain_init(int hw) · line 2440
Quoted 9 lines
drivers/iommu/intel/iommu.c +9
-0+static int dmar_domain_attach_device_pasid (struct dmar_domain *domain, +struct intel_iommu *iommu, +struct device *dev,ioasid_t pasid) +{ +int ret; + +/* PASID table is mandatory for a PCI device in scalable mode. */ +if (!sm_supported (iommu) &&dev_is_real_dma_subdevice (dev)) +return -EOPNOTSUPP ;"&&" should be "||"
Also should return success instead if this is a RID case. Perhaps,
if (!sm_supported(iommu) || dev_is_real_dma_subdevice(dev)) return pasid == RID2PASID ? 0 : -EOPNOTSUPP;
Best regards,
baolu
Re: [PATCH v5 4/7] iommu/vt-d: Factoring out PASID set up helper function
Hi Baolu,
Quoted 19 lines · Baolu Lu
Quoted 4 lines · Kevin
Quoted 4 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AMQuoted 13 lines
drivers/iommu/intel/iommu.c static int __init si_domain_init(int hw) · line 2440
Quoted 10 lines
drivers/iommu/intel/iommu.c +3
-0+static int dmar_domain_attach_device_pasid(struct dmar_domain *domain, + struct intel_iommu *iommu, + struct device *dev,
ioasid_tpasid) +{drivers/iommu/intel/iommu.c +3
-0+int ret; + +/* PASID table is mandatory for a PCI device in scalable mode. */
drivers/iommu/intel/iommu.c +2
-0+if (!sm_supported (iommu) &&dev_is_real_dma_subdevice (dev)) +return -EOPNOTSUPP ;"&&" should be "||"
Quoted 5 lines
Also should return success instead if this is a RID case. Perhaps,
if (!sm_supported(iommu) || dev_is_real_dma_subdevice(dev)) return pasid == RID2PASID ? 0 : -EOPNOTSUPP;
Yeah, I think this is better. will do.
I was hoping not to treat RIDPASID special here. The caller of this function does the check if that is RIDPASID but code is duplicated.
Thanks,
Jacob
Re: [PATCH v5 4/7] iommu/vt-d: Factoring out PASID set up helper function
In reply to Tian, KevinHi Kevin,
Quoted 4 lines · Kevin
Quoted 4 lines
From: Jacob Pan <[email protected]>
Sent: Friday, April 28, 2023 1:50 AM
Quoted 13 lines
drivers/iommu/intel/iommu.c static int __init si_domain_init(int hw) · line 2440
Quoted 10 lines
drivers/iommu/intel/iommu.c +3
-0+static int dmar_domain_attach_device_pasid(struct dmar_domain *domain, + struct intel_iommu *iommu, + struct device *dev,
ioasid_tpasid) +{drivers/iommu/intel/iommu.c +3
-0+int ret; + +/* PASID table is mandatory for a PCI device in scalable mode. */
drivers/iommu/intel/iommu.c +2
-0+if (!sm_supported (iommu) &&dev_is_real_dma_subdevice (dev)) +return -EOPNOTSUPP ;"&&" should be "||"
good catch,
Thanks,
Jacob
PATCH v5 5/7 iommu/vt-d: Prepare PASID attachment beyond RID_PASID
In reply to Jacob PanCurrently, device and default domain attaching process includes RID_PASID setup whenever PASID is supported.
To prepare for non-RID_PASID usage such as ENQCMDS, we can factor out helper functions such that they can be reused in any order between PASID and device attachment. i.e. non-RID_PASID attachment via device_set_pasid() can happen prior to device_attach().
It was agreed that upper level APIs should not make assumptions about ordering.
--- drivers/iommu/intel/iommu.c | 106 ++++++++++++++++++++++++++++++++---- drivers/iommu/intel/iommu.h | 8 +++ 2 files changed, 102 insertions(+), 12 deletions(-)
drivers/iommu/intel/iommu.c +94 -12
@@ -1365,6 +1365,7 @@ domain_lookup_dev_info(struct dmar_domain *domain,static void domain_update_iotlb (struct dmar_domain *domain) { +struct device_pasid_info *dev_pasid;struct device_domain_info *info;bool has_iotlb_device = false;unsigned long flags;
@@ -1376,6 +1377,14 @@ static void domain_update_iotlb(struct dmar_domain *domain)break ; } } + +list_for_each_entry (dev_pasid, &domain->dev_pasids, link_domain) { + info =dev_iommu_priv_get (dev_pasid->dev); +if (info->ats_enabled) { + has_iotlb_device = true; +break ; + } + } domain->has_iotlb_device = has_iotlb_device;spin_unlock_irqrestore (&domain->lock, flags); }
@@ -1486,6 +1495,7 @@ static void __iommu_flush_dev_iotlb(struct device_domain_info *info,static void iommu_flush_dev_iotlb (struct dmar_domain *domain,u64 addr,unsigned mask) { +struct device_pasid_info *dev_pasid;struct device_domain_info *info;unsigned long flags;
@@ -1495,6 +1505,39 @@ static void iommu_flush_dev_iotlb(struct dmar_domain *domain,spin_lock_irqsave (&domain->lock, flags);list_for_each_entry (info, &domain->devices, link)__iommu_flush_dev_iotlb (info, addr, mask); + +list_for_each_entry (dev_pasid, &domain->dev_pasids, link_domain) { +/* device TLB is not aware of the use of RID PASID is for DMA w/o PASID */ +if (dev_pasid->pasid ==IOMMU_DEF_RID_PASID ) +continue ; + + info =dev_iommu_priv_get (dev_pasid->dev); +qi_flush_dev_iotlb_pasid (info->iommu, +PCI_DEVID (info->bus, info->devfn), + info->pfsid, dev_pasid->pasid, + info->ats_qdep, addr, + mask); + } +spin_unlock_irqrestore (&domain->lock, flags); +} + +/* +* The VT-d spec requires to use PASID-based-IOTLB Invalidation to +* invalidate IOTLB and the paging-structure-caches for a first-stage +* page table. +*/ +static void domain_flush_pasid_iotlb (struct intel_iommu *iommu, +struct dmar_domain *domain,u64 addr, +unsigned long npages,bool ih) +{ +u16 did =domain_id_iommu (domain, iommu); +struct device_pasid_info *dev_pasid; +unsigned long flags; + +spin_lock_irqsave (&domain->lock, flags); +list_for_each_entry (dev_pasid, &domain->dev_pasids, link_domain) +qi_flush_piotlb (iommu, did, dev_pasid->pasid, addr, npages, ih); +spin_unlock_irqrestore (&domain->lock, flags); }
@@ -1514,7 +1557,7 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, ih =1 <<6 ;if (domain->use_first_level) { -qi_flush_piotlb (iommu, did,IOMMU_DEF_RID_PASID , addr, pages, ih); +domain_flush_pasid_iotlb (iommu, domain, addr, pages, ih); }else {unsigned long bitmask = aligned_pages -1 ;
@@ -1584,7 +1627,7 @@ static void intel_flush_iotlb_all(struct iommu_domain *domain)u16 did =domain_id_iommu (dmar_domain, iommu);if (dmar_domain->use_first_level) -qi_flush_piotlb (iommu, did,IOMMU_DEF_RID_PASID ,0 ,-1 ,0 ); +domain_flush_pasid_iotlb (iommu, dmar_domain,0 ,-1 ,0 );else iommu->flush.flush_iotlb (iommu, did,0 ,0 ,DMA_TLB_DSI_FLUSH );
@@ -1756,6 +1799,7 @@ static struct dmar_domain *alloc_domain(unsigned int type) domain->use_first_level = true; domain->has_iotlb_device = false;INIT_LIST_HEAD (&domain->devices); +INIT_LIST_HEAD (&domain->dev_pasids);spin_lock_init (&domain->lock);xa_init (&domain->iommu_array);
@@ -2433,12 +2477,17 @@ static int dmar_domain_attach_device_pasid(struct dmar_domain *domain,struct intel_iommu *iommu, struct device *dev, ioasid_t pasid) { + struct device_pasid_info *dev_pasid; +unsigned long flags;int ret; -/* PASID table is mandatory for a PCI device in scalable mode. */ if (!sm_supported (iommu) &&dev_is_real_dma_subdevice (dev))return -EOPNOTSUPP ; + dev_pasid =kzalloc (sizeof (*dev_pasid),GFP_KERNEL ); +if (!dev_pasid) +return -ENOMEM ; +if (hw_pass_through &&domain_type_is_si (domain)) ret =intel_pasid_setup_pass_through (iommu, domain, dev, pasid);else if (domain->use_first_level)
@@ -2446,6 +2495,17 @@ static int dmar_domain_attach_device_pasid(struct dmar_domain *domain,else ret =intel_pasid_setup_second_level (iommu, domain, dev, pasid); +if (ret) { +kfree (dev_pasid); +return ret; + } + + dev_pasid->pasid = pasid; + dev_pasid->dev = dev; +spin_lock_irqsave (&domain->lock, flags); +list_add (&dev_pasid->link_domain, &domain->dev_pasids); +spin_unlock_irqrestore (&domain->lock, flags); +return 0 ; }
@@ -2467,16 +2527,13 @@ static int dmar_domain_attach_device(struct dmar_domain *domain,return ret; info->domain = domain;spin_lock_irqsave (&domain->lock, flags); +if (info->dev_attached) { +spin_unlock_irqrestore (&domain->lock, flags); +return 0 ; + }list_add (&info->link, &domain->devices);spin_unlock_irqrestore (&domain->lock, flags); - ret =dmar_domain_attach_device_pasid (domain, iommu, dev, -IOMMU_DEF_RID_PASID ); -if (ret) { -dev_err (dev,"Setup RID2PASID failed\n" ); -device_block_translation (dev); - } - ret =domain_context_mapping (domain, dev);if (ret) {dev_err (dev,"Domain context map failed\n" );
@@ -2485,8 +2542,9 @@ static int dmar_domain_attach_device(struct dmar_domain *domain, }iommu_enable_pci_caps (info); + info->dev_attached =1 ; -return 0 ; +return ret; }static bool device_has_rmrr (struct device *dev)
@@ -4044,6 +4102,7 @@ static void device_block_translation(struct device *dev)spin_lock_irqsave (&info->domain->lock, flags);list_del (&info->link); + info->dev_attached =0 ;spin_unlock_irqrestore (&info->domain->lock, flags);domain_detach_iommu (info->domain, iommu);
@@ -4175,8 +4234,15 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, struct device *dev) {struct device_domain_info *info =dev_iommu_priv_get (dev); +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct intel_iommu *iommu; +u8 bus, devfn;int ret; + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; +if (domain->type ==IOMMU_DOMAIN_UNMANAGED &&device_is_rmrr_locked (dev)) {dev_warn (dev,"Device is ineligible for IOMMU domain attach due to platform RMRR requirement. Contact your platform vendor.\n" );
@@ -4190,7 +4256,23 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,if (ret)return ret; -return dmar_domain_attach_device (to_dmar_domain (domain), dev); + ret =dmar_domain_attach_device (to_dmar_domain (domain), dev); +if (ret) { +dev_err (dev,"Attach device failed\n" ); +return ret; + } + +/* PASID table is mandatory for a PCI device in scalable mode. */ +if (sm_supported (iommu) && !dev_is_real_dma_subdevice (dev)) { +/* Setup the PASID entry for requests without PASID: */ + ret =dmar_domain_attach_device_pasid (dmar_domain, iommu, dev, +IOMMU_DEF_RID_PASID ); +if (ret) { +dev_err (dev,"Setup RID2PASID failed\n" ); +device_block_translation (dev); + } + } +return ret; } static int intel_iommu_map(struct iommu_domain *domain,
drivers/iommu/intel/iommu.h +8 -0
@@ -595,6 +595,7 @@ struct dmar_domain {spinlock_t lock;/* Protect device tracking lists */ struct list_head devices;/* all devices' list */ +struct list_head dev_pasids;/* all attached pasids */ struct dma_pte *pgd;/* virtual address */ int gaw;/* max guest address width */
@@ -708,6 +709,7 @@ struct device_domain_info { u8 ats_supported:1; u8 ats_enabled:1; u8 dtlb_extra_inval:1; /* Quirk for devices need extra flush */ + u8 dev_attached:1; /* Device context activated */u8 ats_qdep;struct device *dev;/* it's NULL for PCIe-to-PCI bridge */ struct intel_iommu *iommu;/* IOMMU used by this device */
@@ -715,6 +717,12 @@ struct device_domain_info {struct pasid_table *pasid_table;/* pasid table */ }; +struct device_pasid_info { +struct list_head link_domain;/* link to domain siblings */ +struct device *dev;/* physical device derived from */ +ioasid_t pasid;/* PASID on physical device */ +}; +static inline void __iommu_flush_cache (struct intel_iommu *iommu,void *addr,int size) {
Signature · 1 line
2.25.1
Baolu Lu
maintainerRe: [PATCH v5 5/7] iommu/vt-d: Prepare PASID attachment beyond RID_PASID
Quoted 108 lines · Jacob Pan
drivers/iommu/intel/iommu.c +41
-10@@ -2433,12 +2477,17 @@ static int dmar_domain_attach_device_pasid(struct dmar_domain *domain,struct intel_iommu *iommu, struct device *dev, ioasid_t pasid) { + struct device_pasid_info *dev_pasid; +unsigned long flags;int ret; -/* PASID table is mandatory for a PCI device in scalable mode. */ if (!sm_supported (iommu) &&dev_is_real_dma_subdevice (dev))return -EOPNOTSUPP ; + dev_pasid =kzalloc (sizeof (*dev_pasid),GFP_KERNEL ); +if (!dev_pasid) +return -ENOMEM ; +if (hw_pass_through &&domain_type_is_si (domain)) ret =intel_pasid_setup_pass_through (iommu, domain, dev, pasid);else if (domain->use_first_level)@@ -2446,6 +2495,17 @@ static int dmar_domain_attach_device_pasid(struct dmar_domain *domain,else ret =intel_pasid_setup_second_level (iommu, domain, dev, pasid); +if (ret) { +kfree (dev_pasid); +return ret; + } + + dev_pasid->pasid = pasid; + dev_pasid->dev = dev; +spin_lock_irqsave (&domain->lock, flags); +list_add (&dev_pasid->link_domain, &domain->dev_pasids); +spin_unlock_irqrestore (&domain->lock, flags); +return 0 ; }@@ -2467,16 +2527,13 @@ static int dmar_domain_attach_device(struct dmar_domain *domain,return ret; info->domain = domain;spin_lock_irqsave (&domain->lock, flags); +if (info->dev_attached) { +spin_unlock_irqrestore (&domain->lock, flags); +return 0 ; + }list_add (&info->link, &domain->devices);spin_unlock_irqrestore (&domain->lock, flags); - ret =dmar_domain_attach_device_pasid (domain, iommu, dev, -IOMMU_DEF_RID_PASID ); -if (ret) { -dev_err (dev,"Setup RID2PASID failed\n" ); -device_block_translation (dev); - } - ret =domain_context_mapping (domain, dev);if (ret) {dev_err (dev,"Domain context map failed\n" );@@ -2485,8 +2542,9 @@ static int dmar_domain_attach_device(struct dmar_domain *domain, }iommu_enable_pci_caps (info); + info->dev_attached =1 ; -return 0 ; +return ret; }static bool device_has_rmrr (struct device *dev)@@ -4044,6 +4102,7 @@ static void device_block_translation(struct device *dev)spin_lock_irqsave (&info->domain->lock, flags);list_del (&info->link); + info->dev_attached =0 ;spin_unlock_irqrestore (&info->domain->lock, flags);domain_detach_iommu (info->domain, iommu);@@ -4175,8 +4234,15 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, struct device *dev) {struct device_domain_info *info =dev_iommu_priv_get (dev); +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct intel_iommu *iommu; +u8 bus, devfn;int ret; + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; +if (domain->type ==IOMMU_DOMAIN_UNMANAGED &&device_is_rmrr_locked (dev)) {dev_warn (dev,"Device is ineligible for IOMMU domain attach due to platform RMRR requirement. Contact your platform vendor.\n" );@@ -4190,7 +4256,23 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,if (ret)return ret; -return dmar_domain_attach_device (to_dmar_domain (domain), dev); + ret =dmar_domain_attach_device (to_dmar_domain (domain), dev); +if (ret) { +dev_err (dev,"Attach device failed\n" ); +return ret; + } + +/* PASID table is mandatory for a PCI device in scalable mode. */ +if (sm_supported (iommu) && !dev_is_real_dma_subdevice (dev)) { +/* Setup the PASID entry for requests without PASID: */ + ret = dmar_domain_attach_device_pasid(dmar_domain, iommu, dev,
drivers/iommu/intel/iommu.c static int intel_iommu_attach_device(struct iommu_domain *domain, · line 4278
Quoted 10 lines
drivers/iommu/intel/iommu.c +7
-0+ IOMMU_DEF_RID_PASID); +if (ret) { +dev_err (dev,"Setup RID2PASID failed\n" ); +device_block_translation (dev); + } + } +return ret; } static int intel_iommu_map(struct iommu_domain *domain,
I am not following why do you need to change the attach_device path in this patch. Perhaps you want to make sure that context entry for the device is configured before attach_device_pasid?
Best regards,
baolu
Re: [PATCH v5 5/7] iommu/vt-d: Prepare PASID attachment beyond RID_PASID
Hi Baolu,
Quoted 121 lines · Baolu Lu
Quoted 120 lines · Jacob Pan
(quoted diff) +0
-0@@ -2433,12 +2477,17 @@ static int
dmar_domain_attach_device_pasid(struct dmar_domain *domain, struct intel_iommu *iommu, struct device *dev, ioasid_t pasid)
{drivers/iommu/intel/iommu.c +2
-1+struct device_pasid_info *dev_pasid; +unsigned long flags;int ret; -/* PASID table is mandatory for a PCI device in scalable mode. */ if (!sm_supported(iommu) &&
dev_is_real_dma_subdevice(dev))
return-EOPNOTSUPP;drivers/iommu/intel/iommu.c +4
-0+ dev_pasid =kzalloc (sizeof (*dev_pasid),GFP_KERNEL ); +if (!dev_pasid) +return -ENOMEM ; +if (hw_pass_through &&domain_type_is_si (domain)) ret = intel_pasid_setup_pass_through(iommu, domain,dev, pasid); else if (
domain->use_first_level)(quoted diff) +0
-0@@ -2446,6 +2495,17 @@ static intdmar_domain_attach_device_pasid(struct
dmar_domain*domain, else
ret = intel_pasid_setup_second_level(iommu, domain,
dev, pasid);drivers/iommu/intel/iommu.c +25
-8+if (ret) { +kfree (dev_pasid); +return ret; + } + + dev_pasid->pasid = pasid; + dev_pasid->dev = dev; +spin_lock_irqsave (&domain->lock, flags); +list_add (&dev_pasid->link_domain, &domain->dev_pasids); +spin_unlock_irqrestore (&domain->lock, flags); +return 0 ; }@@ -2467,16 +2527,13 @@ static int dmar_domain_attach_device(struct dmar_domain *domain, return ret; info->domain = domain;spin_lock_irqsave (&domain->lock, flags); +if (info->dev_attached) { +spin_unlock_irqrestore (&domain->lock, flags); +return 0 ; + }list_add (&info->link, &domain->devices);spin_unlock_irqrestore (&domain->lock, flags); - ret =dmar_domain_attach_device_pasid (domain, iommu, dev, -IOMMU_DEF_RID_PASID ); -if (ret) { -dev_err (dev,"Setup RID2PASID failed\n" ); -device_block_translation (dev); - } - ret =domain_context_mapping (domain, dev);if (ret) {dev_err (dev,"Domain context map failed\n" );@@ -2485,8 +2542,9 @@ static int dmar_domain_attach_device(struct dmar_domain *domain, }iommu_enable_pci_caps (info ); + info->dev_attached =1 ; -return 0 ; +return ret; }static bool device_has_rmrr (struct device *dev)@@ -4044,6 +4102,7 @@ static void device_block_translation(struct device *dev) spin_lock_irqsave(&info->domain->lock, flags);list_del (&info->link); + info->dev_attached =0 ;spin_unlock_irqrestore (&info->domain->lock, flags);domain_detach_iommu (info->domain, iommu);@@ -4175,8 +4234,15 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, struct device *dev) { struct device_domain_info *info = dev_iommu_priv_get(dev); +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct intel_iommu *iommu; +u8 bus, devfn;int ret; + iommu =device_to_iommu (dev, &bus, &devfn); +if (!iommu) +return -ENODEV ; +if (domain->type ==IOMMU_DOMAIN_UNMANAGED &&device_is_rmrr_locked (dev)) { dev_warn(dev, "Device is ineligible for IOMMU domainattach due to platform RMRR requirement. Contact your platform vendor.\n"); @@ -4190,7 +4256,23 @@ static int intel_iommu_attach_device(struct
iommu_domain*domain, if (ret) return ret;drivers/iommu/intel/iommu.c +7
-1-return dmar_domain_attach_device (to_dmar_domain (domain), dev); + ret =dmar_domain_attach_device (to_dmar_domain (domain), dev); +if (ret) { +dev_err (dev,"Attach device failed\n" ); +return ret; + } + +/* PASID table is mandatory for a PCI device in scalable mode. */
drivers/iommu/intel/iommu.c +3
-0+if (sm_supported (iommu) && !dev_is_real_dma_subdevice (dev)) { +/* Setup the PASID entry for requests without PASID: */ + ret = dmar_domain_attach_device_pasid(dmar_domain,iommu, dev,
+IOMMU_DEF_RID_PASID);drivers/iommu/intel/iommu.c +6
-0+if (ret) { +dev_err (dev,"Setup RID2PASID failed\n" ); +device_block_translation (dev); + } + } +return ret; }
Quoted 5 lines
Quoted 1 line
static int intel_iommu_map(struct
iommu_domain*domain,I am not following why do you need to change the
attach_devicepath in this patch. Perhaps you want to make sure that context entry for the device is configured beforeattach_device_pasid?
This is just refactoring, with this patch attach_device is broken down into
1. prepare_domain_attach_device()
2. dmar_domain_attach_device()
3. dmar_domain_attach_device_pasid()
the change is due to factoring out dmar_domain_attach_device_pasid(). device context set up in #2, already ensured before PASID attachment.
perhaps I miss your point?
Thanks,
Jacob
PATCH v5 6/7 iommu/vt-d: Implement set_dev_pasid domain op
In reply to Jacob PanDevices that use ENQCMDS to submit work on buffers mapped by DMA API must attach a PASID to the default domain of the device. In preparation for this use case, this patch implements set_dev_pasid() for the default_domain_ops.
If the device context has not been set up prior to this call, this will set up the device context in addition to PASID attachment.
--- drivers/iommu/intel/iommu.c | 92 ++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 16 deletions(-)
drivers/iommu/intel/iommu.c +76 -16
@@ -278,6 +278,8 @@ static LIST_HEAD(dmar_satc_units);list_for_each_entry (rmrr, &dmar_rmrr_units, list) static void device_block_translation(struct device *dev); +static void intel_iommu_detach_device_pasid (struct iommu_domain *domain, +struct device *dev,ioasid_t pasid);static void intel_iommu_domain_free (struct iommu_domain *domain);int dmar_disabled = !IS_ENABLED (CONFIG_INTEL_IOMMU_DEFAULT_ON );
@@ -4091,8 +4093,7 @@ static void device_block_translation(struct device *dev)iommu_disable_pci_caps (info);if (!dev_is_real_dma_subdevice (dev)) {if (sm_supported (iommu)) -intel_pasid_tear_down_entry (iommu, dev, -IOMMU_DEF_RID_PASID , false); +intel_iommu_detach_device_pasid (&info->domain->domain, dev,IOMMU_DEF_RID_PASID );else domain_context_clear (info); }
@@ -4761,28 +4762,86 @@ static void intel_iommu_iotlb_sync_map(struct iommu_domain *domain,__mapping_notify_one (info->iommu, dmar_domain, pfn, pages); } -static void intel_iommu_remove_dev_pasid (struct device *dev,ioasid_t pasid) +static void intel_iommu_detach_device_pasid (struct iommu_domain *domain, +struct device *dev,ioasid_t pasid) { -struct intel_iommu *iommu =device_to_iommu (dev,NULL ,NULL ); -struct iommu_domain *domain; +struct device_domain_info *info =dev_iommu_priv_get (dev); +struct dmar_domain *dmar_domain =to_dmar_domain (domain); +struct device_pasid_info *i, *dev_pasid =NULL ; +struct intel_iommu *iommu = info->iommu; +unsigned long flags; -/* Domain type specific cleanup: */ - domain =iommu_get_domain_for_dev_pasid (dev, pasid,0 ); -if (domain) { -switch (domain->type) { -case IOMMU_DOMAIN_SVA : -intel_svm_remove_dev_pasid (dev, pasid); -break ; -default : -/* should never reach here */ -WARN_ON (1 ); +spin_lock_irqsave (&dmar_domain->lock, flags); +list_for_each_entry (i, &dmar_domain->dev_pasids, link_domain) { +if (i->dev == dev && i->pasid == pasid) { +list_del (&i->link_domain); + dev_pasid = i;break ; } } +spin_unlock_irqrestore (&dmar_domain->lock, flags); +if (WARN_ON (!dev_pasid)) +return ; + +/* PASID entry already cleared during SVA unbind */ +if (domain->type !=IOMMU_DOMAIN_SVA ) +intel_pasid_tear_down_entry (iommu, dev, pasid, false); + +kfree (dev_pasid); +} + +static void intel_iommu_remove_dev_pasid (struct device *dev,ioasid_t pasid) +{ +struct device_domain_info *info =dev_iommu_priv_get (dev); +struct dmar_domain *dmar_domain; +struct iommu_domain *domain; + + domain =iommu_get_domain_for_dev_pasid (dev, pasid,0 ); + dmar_domain =to_dmar_domain (domain); + +/* +* SVA Domain type specific cleanup: Not ideal but not until we have +* IOPF capable domain specific ops, we need this special case. +*/ +if (domain->type ==IOMMU_DOMAIN_SVA ) +return intel_svm_remove_dev_pasid (dev, pasid); -intel_pasid_tear_down_entry (iommu, dev, pasid, false); +intel_iommu_detach_device_pasid (domain, dev, pasid); +domain_detach_iommu (dmar_domain, info->iommu); } +static int intel_iommu_attach_device_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; +int ret; + +if (!pasid_supported (iommu)) +return -ENODEV ; + + ret =prepare_domain_attach_device (domain, dev); +if (ret) +return ret; + +/* +* Most likely the device context has already been set up, will only +* take a domain ID reference. Otherwise, device context will be set +* up here. +* The upper layer APIs make no assumption about the ordering between +* device attachment and the PASID attachment. +*/ + ret =dmar_domain_attach_device (to_dmar_domain (domain), dev); +if (ret) { +dev_err (dev,"Attach device failed\n" ); +return ret; + } +return dmar_domain_attach_device_pasid (dmar_domain, iommu, dev, pasid); +} + + +const struct iommu_ops intel_iommu_ops = { .capable = intel_iommu_capable, .domain_alloc = intel_iommu_domain_alloc,
@@ -4802,6 +4861,7 @@ const struct iommu_ops intel_iommu_ops = {#endif .default_domain_ops = &(const struct iommu_domain_ops) { .attach_dev = intel_iommu_attach_device, + .set_dev_pasid = intel_iommu_attach_device_pasid, .map_pages = intel_iommu_map_pages, .unmap_pages = intel_iommu_unmap_pages, .iotlb_sync_map = intel_iommu_iotlb_sync_map,
Signature · 1 line
2.25.1
Baolu Lu
maintainerRe: [PATCH v5 6/7] iommu/vt-d: Implement set_dev_pasid domain op
Quoted 27 lines · Jacob Pan
Devices that use ENQCMDS to submit work on buffers mapped by DMA API must attach a PASID to the default domain of the device. In preparation for this use case, this patch implements
set_dev_pasid()for thedefault_domain_ops.If the device context has not been set up prior to this call, this will set up the device context in addition to PASID attachment.
Signed-off-by Jacob Pan <[email protected]>--- drivers/iommu/intel/iommu.c | 92 ++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 16 deletions(-)drivers/iommu/intel/iommu.c +2
-0@@ -278,6 +278,8 @@ static LIST_HEAD(dmar_satc_units);list_for_each_entry (rmrr, &dmar_rmrr_units, list) static void device_block_translation(struct device *dev); +static void intel_iommu_detach_device_pasid (struct iommu_domain *domain, +struct device *dev,ioasid_t pasid);static void intel_iommu_domain_free (struct iommu_domain *domain);int dmar_disabled = !IS_ENABLED (CONFIG_INTEL_IOMMU_DEFAULT_ON );
drivers/iommu/intel/iommu.c static void device_block_translation(struct device *dev) · line 4096
Quoted 6 lines
drivers/iommu/intel/iommu.c +1
-2@@ -4091,8 +4093,7 @@ static void device_block_translation(struct device *dev)iommu_disable_pci_caps (info);if (!dev_is_real_dma_subdevice (dev)) {if (sm_supported (iommu)) -intel_pasid_tear_down_entry (iommu, dev, -IOMMU_DEF_RID_PASID , false); +intel_iommu_detach_device_pasid (&info->domain->domain, dev,IOMMU_DEF_RID_PASID );
device_block_translation() is called when switch RID's domain or release the device. I assume that we don't need to touch this path when we add the attach_dev_pasid support.
Blocking DMA translation through RID/PASID should be done in remove_dev_pasid path.
Or, I overlooked anything?
[...]
Quoted 10 lines
drivers/iommu/intel/iommu.c +9
-0+static int intel_iommu_attach_device_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; +int ret; + +if (!pasid_supported (iommu))
drivers/iommu/intel/iommu.c static void device_block_translation(struct device *dev)
Quoted 10 lines
drivers/iommu/intel/iommu.c +10
-0+return -ENODEV ; + + ret =prepare_domain_attach_device (domain, dev); +if (ret) +return ret; + +/* +* Most likely the device context has already been set up, will only +* take a domain ID reference. Otherwise, device context will be set +* up here.
The "otherwise" case is only default domain deferred attaching case, right?
When the device driver starts to call attach_dev_pasid api, it means that the bus and device DMA configuration have been done. We could do the deferred default domain attaching now. So, perhaps we should add below code in the core:
drivers/iommu/iommu.c +6 -0
@@ -3296,6 +3296,12 @@ int iommu_attach_device_pasid(struct iommu_domain *domain,if (!group)return -ENODEV ; + ret =iommu_deferred_attach (dev, group->default_domain); +if (ret) { +iommu_group_put (group); +return ret; + } +mutex_lock (&group->mutex); curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, domain,
GFP_KERNEL);
if (curr) {Perhaps need to call iommu_deferred_attach() inside the group->mutex critical region?
drivers/iommu/intel/iommu.c
Quoted 4 lines
drivers/iommu/intel/iommu.c +4
-0+* The upper layer APIs make no assumption about the ordering between +* device attachment and the PASID attachment. +*/ + ret =dmar_domain_attach_device (to_dmar_domain (domain), dev);
Calling attach_device on the attach_dev_pasid path is not right.
Quoted 13 lines
drivers/iommu/intel/iommu.c +9
-0+if (ret) { +dev_err (dev,"Attach device failed\n" ); +return ret; + } +return dmar_domain_attach_device_pasid (dmar_domain, iommu, dev, pasid); +} + + +const struct iommu_ops intel_iommu_ops = { .capable = intel_iommu_capable, .domain_alloc = intel_iommu_domain_alloc,
drivers/iommu/intel/iommu.c const struct iommu_ops intel_iommu_ops = { · line 4867
Quoted 7 lines
drivers/iommu/intel/iommu.c +1
-0@@ -4802,6 +4861,7 @@ const struct iommu_ops intel_iommu_ops = {#endif .default_domain_ops = &(const struct iommu_domain_ops) { .attach_dev = intel_iommu_attach_device, + .set_dev_pasid = intel_iommu_attach_device_pasid, .map_pages = intel_iommu_map_pages, .unmap_pages = intel_iommu_unmap_pages, .iotlb_sync_map = intel_iommu_iotlb_sync_map,
Best regards,
baolu
Re: [PATCH v5 6/7] iommu/vt-d: Implement set_dev_pasid domain op
Hi Baolu,
Quoted 67 lines · Baolu Lu
Quoted 28 lines · Jacob Pan
Devices that use ENQCMDS to submit work on buffers mapped by DMA API must attach a PASID to the default domain of the device. In preparation for this use case, this patch implements
set_dev_pasid()for thedefault_domain_ops.If the device context has not been set up prior to this call, this will set up the device context in addition to PASID attachment.
Signed-off-by Jacob Pan <[email protected]>--- drivers/iommu/intel/iommu.c | 92 ++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 16 deletions(-)drivers/iommu/intel/iommu.c +2
-0@@ -278,6 +278,8 @@ static LIST_HEAD(dmar_satc_units);list_for_each_entry (rmrr, &dmar_rmrr_units, list) static void device_block_translation(struct device *dev); +static void intel_iommu_detach_device_pasid(struct iommu_domain *domain, + struct device *dev,
ioasid_tpasid); static voidintel_iommu_domain_free(struct iommu_domain *domain);
intdmar_disabled= !IS_ENABLED(CONFIG_INTEL_IOMMU_DEFAULT_ON);drivers/iommu/intel/iommu.c static void device_block_translation(struct · line 4099
Quoted 9 lines
drivers/iommu/intel/iommu.c +1
-2@@ -4091,8 +4093,7 @@ static void device_block_translation(struct device *dev) iommu_disable_pci_caps(info);if (!dev_is_real_dma_subdevice (dev)) {if (sm_supported (iommu)) -intel_pasid_tear_down_entry (iommu, dev, - IOMMU_DEF_RID_PASID, false); +intel_iommu_detach_device_pasid (&info->domain->domain, dev,IOMMU_DEF_RID_PASID );
device_block_translation()is called when switch RID's domain or release the device. I assume that we don't need to touch this path when we add theattach_dev_pasidsupport.Blocking DMA translation through RID/PASID should be done in
remove_dev_pasidpath.Or, I overlooked anything?
[...]
Quoted 17 lines
drivers/iommu/intel/iommu.c +2
-0+static int intel_iommu_attach_device_pasid(struct iommu_domain *domain, + struct device *dev,
ioasid_tpasid) +{drivers/iommu/intel/iommu.c +13
-0+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; +int ret; + +if (!pasid_supported (iommu)) +return -ENODEV ; + + ret =prepare_domain_attach_device (domain, dev); +if (ret) +return ret; + +/*
Quoted 8 lines
drivers/iommu/intel/iommu.c
Quoted 5 lines
drivers/iommu/intel/iommu.c +1
-0+* Most likely the device context has already been set up, will only
drivers/iommu/intel/iommu.c +1
-0+* take a domain ID reference. Otherwise, device context will be set
drivers/iommu/intel/iommu.c +1
-0+* up here. The "otherwise" case is only default domain deferred attaching case, right?
it might be the only case so far, but my intention is to be general. i.e. no ordering requirements. I believe it is more future proof in case device_attach_pasid called before device_attach.
Quoted 10 lines
When the device driver starts to call
attach_dev_pasidapi, it means that the bus and device DMA configuration have been done. We could do the deferred default domain attaching now. So, perhaps we should add below code in the core:drivers/iommu/iommu.c +0
-0
drivers/iommu/iommu.c int iommu_attach_device_pasid(struct iommu_domain · line 3304
Quoted 9 lines
drivers/iommu/iommu.c +5
-0@@ -3296,6 +3296,12 @@ int iommu_attach_device_pasid(struct iommu_domain *domain, if (!group) return -ENODEV; + ret =iommu_deferred_attach (dev, group->default_domain); +if (ret) { +iommu_group_put (group); +return ret; + }
it will cover the device_attach, but adding a special case.
Quoted 7 lines
drivers/iommu/iommu.c +0
-0mutex_lock (&group->mutex); curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, domain,GFP_KERNEL); if (curr) {Perhaps need to call
iommu_deferred_attach()inside thegroup->mutexcritical region?
i agree, attach RID_PASID should also be on the group's pasid_array.
Quoted 7 lines
drivers/iommu/intel/iommu.c
Quoted 5 lines
drivers/iommu/intel/iommu.c +1
-0+* The upper layer APIs make no assumption about the ordering between
drivers/iommu/intel/iommu.c +3
-0+* device attachment and the PASID attachment. +*/ + ret =dmar_domain_attach_device (to_dmar_domain (domain), dev);Calling
attach_deviceon theattach_dev_pasidpath is not right.
I think it comes down to the philosophical differences in terms of who is responsible for ensuring device ctx is set up prior to device pasid attach:
1. vt-d driver
2. upper layer API
Quoted 17 lines
Quoted 17 lines
drivers/iommu/intel/iommu.c +5
-0+if (ret) { +dev_err (dev,"Attach device failed\n" ); +return ret; + } + return dmar_domain_attach_device_pasid(dmar_domain, iommu,dev, pasid); +}
drivers/iommu/intel/iommu.c +3
-0+ + +const struct iommu_ops intel_iommu_ops = { .capable = intel_iommu_capable, .domain_alloc = intel_iommu_domain_alloc,@@ -4802,6 +4861,7 @@ const struct iommu_ops intel_iommu_ops = {#endif .default_domain_ops = &(const struct iommu_domain_ops) { .attach_dev = intel_iommu_attach_device,
Quoted 8 lines
Quoted 5 lines
drivers/iommu/intel/iommu.c +1
-0+ .set_dev_pasid =
intel_iommu_attach_device_pasid, .map_pages =intel_iommu_map_pages, .unmap_pages =intel_iommu_unmap_pages, .iotlb_sync_map =intel_iommu_iotlb_sync_map,Best regards,
baolu
Thanks,
Jacob
Baolu Lu
maintainerRe: [PATCH v5 6/7] iommu/vt-d: Implement set_dev_pasid domain op
Quoted 23 lines · Jacob Pan
Quoted 15 lines
Quoted 15 lines
(quoted diff) +2
-0+static int intel_iommu_attach_device_pasid(struct iommu_domain *domain, + struct device *dev,
ioasid_tpasid) +{(quoted diff) +12
-0+ 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; + int ret; + + if (!pasid_supported(iommu)) + return -ENODEV; + + ret = prepare_domain_attach_device(domain, dev); + if (ret) + return ret; +Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +2
-0+ /* + * Most likely the device context has already been set up,will only
(quoted diff) +1
-0+ * take a domain ID reference. Otherwise, device context willbe set
(quoted diff) +1
-0+ * up here.The "otherwise" case is only default domain deferred attaching case, right?
Quoted 3 lines
it might be the only case so far, but my intention is to be general. i.e. no ordering requirements. I believe it is more future proof in case
device_attach_pasidcalled beforedevice_attach.
Let's put aside deferred attach and talk about it later.
The device's context entry is configured when the default domain is being attached to the device. And, the default domain attaching is in the iommu probe_device path. It always happens before set_device_pasid which is designed to be called by the device driver. So the real situation is that when the device driver calls set_device_pasid, the context entry should already have been configured.
Then let's pick up the deferred attach case. It is a workaround for kdump (Documentation/admin-guide/kdump/kdump.rst). I don't think PASID feature is functionally required by any kdump capture kernel as its main purpose is to dump the memory of a panic kernel.
In summary, it seems to be reasonable for the vt-d driver to return -EBUSY when a device's context was copied. The idxd driver should continue to work without kernel DMA with PASID support.
if (context_copied(iommu, bus, devfn)) return -EBUSY;
Make things general is always good, but this doesn't mean that we need to make the code complex to support cases that do not exist or are not used. Thoughts?
Best regards,
baolu
Re: [PATCH v5 6/7] iommu/vt-d: Implement set_dev_pasid domain op
Hi Baolu,
Quoted 47 lines · Baolu Lu
Quoted 24 lines · Jacob Pan
Quoted 16 lines
Quoted 16 lines
+static int intel_iommu_attach_device_pasid(struct
iommu_domain*domain,(quoted diff) +1
-0+ struct device *dev,
ioasid_tpasid) +{(quoted diff) +12
-0+ 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; + int ret; + + if (!pasid_supported(iommu)) + return -ENODEV; + + ret = prepare_domain_attach_device(domain, dev); + if (ret) + return ret; +Quoted 8 lines
Review
Quoted 6 lines
(quoted diff) +2
-0+ /* + * Most likely the device context has already been set up,will only
(quoted diff) +1
-0+ * take a domain ID reference. Otherwise, device context willbe set
(quoted diff) +1
-0+ * up here.The "otherwise" case is only default domain deferred attaching case, right?
Quoted 3 lines
it might be the only case so far, but my intention is to be general. i.e. no ordering requirements. I believe it is more future proof in case
device_attach_pasidcalled beforedevice_attach.Let's put aside deferred attach and talk about it later.
The device's context entry is configured when the default domain is being attached to the device. And, the default domain attaching is in the iommu
probe_devicepath. It always happens beforeset_device_pasidwhich is designed to be called by the device driver. So the real situation is that when the device driver callsset_device_pasid, the context entry should already have been configured.Then let's pick up the deferred attach case. It is a workaround for kdump (
Documentation/admin-guide/kdump/kdump.rst). I don't think PASID feature is functionally required by any kdump capture kernel as its main purpose is to dump the memory of a panic kernel.In summary, it seems to be reasonable for the vt-d driver to return
-EBUSYwhen a device's context was copied. The idxd driver should continue to work without kernel DMA with PASID support.
Quoted 7 lines
if (context_copied(iommu, bus, devfn)) return -EBUSY;Make things general is always good, but this doesn't mean that we need to make the code complex to support cases that do not exist or are not used. Thoughts?
Good point, it is better not put dead code in. Let me also document this behavior for future change that may affect the ordering requirement.
Thanks,
Jacob
PATCH v5 7/7 dmaengine/idxd: Re-enable kernel workqueue under DMA API
In reply to Jacob PanKernel workqueues were disabled due to flawed use of kernel VA and SVA API. Now that we have the support for attaching PASID to the device's default domain and the ability to reserve global PASIDs from SVA APIs, we can re-enable the kernel work queues and use them under DMA API.
We also use non-privileged access for in-kernel DMA to be consistent with the IOMMU settings. Consequently, interrupt for user privilege is enabled for work completion IRQs.
--- drivers/dma/idxd/device.c | 30 ++++---------------- drivers/dma/idxd/init.c | 60 ++++++++++++++++++++++++++++++++++++--- drivers/dma/idxd/sysfs.c | 7 ----- 3 files changed, 61 insertions(+), 36 deletions(-)
drivers/dma/idxd/device.c +5 -25
@@ -299,21 +299,6 @@ void idxd_wqs_unmap_portal(struct idxd_device *idxd) } } -static void __idxd_wq_set_priv_locked (struct idxd_wq *wq,int priv) -{ -struct idxd_device *idxd = wq->idxd; -union wqcfg wqcfg; -unsigned int offset; - - offset =WQCFG_OFFSET (idxd, wq->id,WQCFG_PRIVL_IDX ); -spin_lock (&idxd->dev_lock); - wqcfg.bits[WQCFG_PRIVL_IDX ] =ioread32 (idxd->reg_base + offset); - wqcfg.priv = priv; - wq->wqcfg->bits[WQCFG_PRIVL_IDX ] = wqcfg.bits[WQCFG_PRIVL_IDX ]; -iowrite32 (wqcfg.bits[WQCFG_PRIVL_IDX ], idxd->reg_base + offset); -spin_unlock (&idxd->dev_lock); -} -static void __idxd_wq_set_pasid_locked (struct idxd_wq *wq,int pasid) {struct idxd_device *idxd = wq->idxd;
@@ -1324,15 +1309,14 @@ int drv_enable_wq(struct idxd_wq *wq) }/* -* In the event that the WQ is configurable for pasid and priv bits. -* For kernel wq, the driver should setup the pasid, pasid_en, and priv bit. -* However, for non-kernel wq, the driver should only set the pasid_en bit for -* shared wq. A dedicated wq that is not 'kernel' type will configure pasid and +* In the event that the WQ is configurable for pasid, the driver +* should setup the pasid, pasid_en bit. This is true for both kernel +* and user shared workqueues. There is no need to setup priv bit in +* that in-kernel DMA will also do user privileged requests. +* A dedicated wq that is not 'kernel' type will configure pasid and * pasid_en later on so there is no need to setup. */ if (test_bit (IDXD_FLAG_CONFIGURABLE , &idxd->flags)) { -int priv =0 ; -if (wq_pasid_enabled (wq)) {if (is_idxd_wq_kernel (wq) ||wq_shared (wq)) {u32 pasid =wq_dedicated (wq) ? idxd->pasid :0 ;
@@ -1340,10 +1324,6 @@ int drv_enable_wq(struct idxd_wq *wq)__idxd_wq_set_pasid_locked (wq, pasid); } } - -if (is_idxd_wq_kernel (wq)) - priv =1 ; -__idxd_wq_set_priv_locked (wq, priv); } rc =0 ;
drivers/dma/idxd/init.c +56 -4
78 lines not on this page · raw message
drivers/dma/idxd/sysfs.c +0 -7
@@ -944,13 +944,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 ; - input =kstrndup (buf, count,GFP_KERNEL );if (!input)return -ENOMEM ;
Signature · 1 line
2.25.1