Re: [PATCH v2 11/12] x86/mmu: Allocate/free PASID
Hi Fenghua, On 6/13/20 8:41 AM, Fenghua Yu wrote: A PASID is allocated for an "mm" the first time any thread attaches to an SVM capable device. Later device attachments (whether to the same device or another SVM device) will re-use the same PASID. The PASID is freed when the process exits (so no need to keep reference counts on how many SVM devices are sharing the PASID). Signed-off-by: Fenghua Yu Reviewed-by: Tony Luck --- v2: - Define a helper free_bind() to simplify error exit code in bind_mm() (Thomas) - Fix a ret error code in bind_mm() (Thomas) - Change pasid's type from "int" to "unsigned int" to have consistent pasid type in iommu (Thomas) - Simplify alloc_pasid() a bit. arch/x86/include/asm/iommu.h | 2 + arch/x86/include/asm/mmu_context.h | 14 drivers/iommu/intel/svm.c | 101 + 3 files changed, 105 insertions(+), 12 deletions(-) diff --git a/arch/x86/include/asm/iommu.h b/arch/x86/include/asm/iommu.h index bf1ed2ddc74b..ed41259fe7ac 100644 --- a/arch/x86/include/asm/iommu.h +++ b/arch/x86/include/asm/iommu.h @@ -26,4 +26,6 @@ arch_rmrr_sanity_check(struct acpi_dmar_reserved_memory *rmrr) return -EINVAL; } +void __free_pasid(struct mm_struct *mm); + #endif /* _ASM_X86_IOMMU_H */ diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 47562147e70b..f8c91ce8c451 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -13,6 +13,7 @@ #include #include #include +#include extern atomic64_t last_mm_ctx_id; @@ -117,9 +118,22 @@ static inline int init_new_context(struct task_struct *tsk, init_new_context_ldt(mm); return 0; } + +static inline void free_pasid(struct mm_struct *mm) +{ + if (!IS_ENABLED(CONFIG_INTEL_IOMMU_SVM)) + return; + + if (!cpu_feature_enabled(X86_FEATURE_ENQCMD)) + return; + + __free_pasid(mm); +} + static inline void destroy_context(struct mm_struct *mm) { destroy_context_ldt(mm); + free_pasid(mm); } extern void switch_mm(struct mm_struct *prev, struct mm_struct *next, diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index 4e775e12ae52..27dc866b8461 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -425,6 +425,53 @@ int intel_svm_unbind_gpasid(struct device *dev, unsigned int pasid) return ret; } +static void free_bind(struct intel_svm *svm, struct intel_svm_dev *sdev, + bool new_pasid) +{ + if (new_pasid) + ioasid_free(svm->pasid); + kfree(svm); + kfree(sdev); +} + +/* + * If this mm already has a PASID, use it. Otherwise allocate a new one. + * Let the caller know if a new PASID is allocated via 'new_pasid'. + */ +static int alloc_pasid(struct intel_svm *svm, struct mm_struct *mm, + unsigned int pasid_max, bool *new_pasid, + unsigned int flags) +{ + unsigned int pasid; + + *new_pasid = false; + + /* +* Reuse the PASID if the mm already has a PASID and not a private +* PASID is requested. +*/ + if (mm && mm->pasid && !(flags & SVM_FLAG_PRIVATE_PASID)) { + /* +* Once a PASID is allocated for this mm, the PASID +* stays with the mm until the mm is dropped. Reuse +* the PASID which has been already allocated for the +* mm instead of allocating a new one. +*/ + ioasid_set_data(mm->pasid, svm); How about adding some sanity checks here? For example, void *p = ioasid_find(NULL, mm->pasid, NULL); if (!p) ioasid_set_data(mm->pasid, svm); else if (IS_ERR(p) || p != svm) return INVALID_IOSASID; Best regards, baolu
Re: [PATCH v2 11/12] x86/mmu: Allocate/free PASID
Hi Fenghua, On 2020/6/13 8:41, Fenghua Yu wrote: A PASID is allocated for an "mm" the first time any thread attaches to an SVM capable device. Later device attachments (whether to the same device or another SVM device) will re-use the same PASID. The PASID is freed when the process exits (so no need to keep reference counts on how many SVM devices are sharing the PASID). FYI. Jean-Philippe Brucker has a patch for mm->pasid management in the vendor agnostic manner. https://www.spinics.net/lists/iommu/msg44459.html Best regards, baolu Signed-off-by: Fenghua Yu Reviewed-by: Tony Luck --- v2: - Define a helper free_bind() to simplify error exit code in bind_mm() (Thomas) - Fix a ret error code in bind_mm() (Thomas) - Change pasid's type from "int" to "unsigned int" to have consistent pasid type in iommu (Thomas) - Simplify alloc_pasid() a bit. arch/x86/include/asm/iommu.h | 2 + arch/x86/include/asm/mmu_context.h | 14 drivers/iommu/intel/svm.c | 101 + 3 files changed, 105 insertions(+), 12 deletions(-) diff --git a/arch/x86/include/asm/iommu.h b/arch/x86/include/asm/iommu.h index bf1ed2ddc74b..ed41259fe7ac 100644 --- a/arch/x86/include/asm/iommu.h +++ b/arch/x86/include/asm/iommu.h @@ -26,4 +26,6 @@ arch_rmrr_sanity_check(struct acpi_dmar_reserved_memory *rmrr) return -EINVAL; } +void __free_pasid(struct mm_struct *mm); + #endif /* _ASM_X86_IOMMU_H */ diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 47562147e70b..f8c91ce8c451 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -13,6 +13,7 @@ #include #include #include +#include extern atomic64_t last_mm_ctx_id; @@ -117,9 +118,22 @@ static inline int init_new_context(struct task_struct *tsk, init_new_context_ldt(mm); return 0; } + +static inline void free_pasid(struct mm_struct *mm) +{ + if (!IS_ENABLED(CONFIG_INTEL_IOMMU_SVM)) + return; + + if (!cpu_feature_enabled(X86_FEATURE_ENQCMD)) + return; + + __free_pasid(mm); +} + static inline void destroy_context(struct mm_struct *mm) { destroy_context_ldt(mm); + free_pasid(mm); } extern void switch_mm(struct mm_struct *prev, struct mm_struct *next, diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index 4e775e12ae52..27dc866b8461 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -425,6 +425,53 @@ int intel_svm_unbind_gpasid(struct device *dev, unsigned int pasid) return ret; } +static void free_bind(struct intel_svm *svm, struct intel_svm_dev *sdev, + bool new_pasid) +{ + if (new_pasid) + ioasid_free(svm->pasid); + kfree(svm); + kfree(sdev); +} + +/* + * If this mm already has a PASID, use it. Otherwise allocate a new one. + * Let the caller know if a new PASID is allocated via 'new_pasid'. + */ +static int alloc_pasid(struct intel_svm *svm, struct mm_struct *mm, + unsigned int pasid_max, bool *new_pasid, + unsigned int flags) +{ + unsigned int pasid; + + *new_pasid = false; + + /* +* Reuse the PASID if the mm already has a PASID and not a private +* PASID is requested. +*/ + if (mm && mm->pasid && !(flags & SVM_FLAG_PRIVATE_PASID)) { + /* +* Once a PASID is allocated for this mm, the PASID +* stays with the mm until the mm is dropped. Reuse +* the PASID which has been already allocated for the +* mm instead of allocating a new one. +*/ + ioasid_set_data(mm->pasid, svm); + + return mm->pasid; + } + + /* Allocate a new pasid. Do not use PASID 0, reserved for init PASID. */ + pasid = ioasid_alloc(NULL, PASID_MIN, pasid_max - 1, svm); + if (pasid != INVALID_IOASID) { + /* A new pasid is allocated. */ + *new_pasid = true; + } + + return pasid; +} + /* Caller must hold pasid_mutex, mm reference */ static int intel_svm_bind_mm(struct device *dev, unsigned int flags, @@ -518,6 +565,8 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags, init_rcu_head(>rcu); if (!svm) { + bool new_pasid; + svm = kzalloc(sizeof(*svm), GFP_KERNEL); if (!svm) { ret = -ENOMEM; @@ -529,12 +578,9 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags, if (pasid_max > intel_pasid_max_id) pasid_max = intel_pasid_max_id; - /* Do not use PASID 0, reserved for RID to PASID */ - svm->pasid = ioasid_alloc(NULL, PASID_MIN, - pasid_max - 1, svm); + svm->pasid = alloc_pasid(svm, mm,
[PATCH v2 11/12] x86/mmu: Allocate/free PASID
A PASID is allocated for an "mm" the first time any thread attaches to an SVM capable device. Later device attachments (whether to the same device or another SVM device) will re-use the same PASID. The PASID is freed when the process exits (so no need to keep reference counts on how many SVM devices are sharing the PASID). Signed-off-by: Fenghua Yu Reviewed-by: Tony Luck --- v2: - Define a helper free_bind() to simplify error exit code in bind_mm() (Thomas) - Fix a ret error code in bind_mm() (Thomas) - Change pasid's type from "int" to "unsigned int" to have consistent pasid type in iommu (Thomas) - Simplify alloc_pasid() a bit. arch/x86/include/asm/iommu.h | 2 + arch/x86/include/asm/mmu_context.h | 14 drivers/iommu/intel/svm.c | 101 + 3 files changed, 105 insertions(+), 12 deletions(-) diff --git a/arch/x86/include/asm/iommu.h b/arch/x86/include/asm/iommu.h index bf1ed2ddc74b..ed41259fe7ac 100644 --- a/arch/x86/include/asm/iommu.h +++ b/arch/x86/include/asm/iommu.h @@ -26,4 +26,6 @@ arch_rmrr_sanity_check(struct acpi_dmar_reserved_memory *rmrr) return -EINVAL; } +void __free_pasid(struct mm_struct *mm); + #endif /* _ASM_X86_IOMMU_H */ diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 47562147e70b..f8c91ce8c451 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -13,6 +13,7 @@ #include #include #include +#include extern atomic64_t last_mm_ctx_id; @@ -117,9 +118,22 @@ static inline int init_new_context(struct task_struct *tsk, init_new_context_ldt(mm); return 0; } + +static inline void free_pasid(struct mm_struct *mm) +{ + if (!IS_ENABLED(CONFIG_INTEL_IOMMU_SVM)) + return; + + if (!cpu_feature_enabled(X86_FEATURE_ENQCMD)) + return; + + __free_pasid(mm); +} + static inline void destroy_context(struct mm_struct *mm) { destroy_context_ldt(mm); + free_pasid(mm); } extern void switch_mm(struct mm_struct *prev, struct mm_struct *next, diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index 4e775e12ae52..27dc866b8461 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -425,6 +425,53 @@ int intel_svm_unbind_gpasid(struct device *dev, unsigned int pasid) return ret; } +static void free_bind(struct intel_svm *svm, struct intel_svm_dev *sdev, + bool new_pasid) +{ + if (new_pasid) + ioasid_free(svm->pasid); + kfree(svm); + kfree(sdev); +} + +/* + * If this mm already has a PASID, use it. Otherwise allocate a new one. + * Let the caller know if a new PASID is allocated via 'new_pasid'. + */ +static int alloc_pasid(struct intel_svm *svm, struct mm_struct *mm, + unsigned int pasid_max, bool *new_pasid, + unsigned int flags) +{ + unsigned int pasid; + + *new_pasid = false; + + /* +* Reuse the PASID if the mm already has a PASID and not a private +* PASID is requested. +*/ + if (mm && mm->pasid && !(flags & SVM_FLAG_PRIVATE_PASID)) { + /* +* Once a PASID is allocated for this mm, the PASID +* stays with the mm until the mm is dropped. Reuse +* the PASID which has been already allocated for the +* mm instead of allocating a new one. +*/ + ioasid_set_data(mm->pasid, svm); + + return mm->pasid; + } + + /* Allocate a new pasid. Do not use PASID 0, reserved for init PASID. */ + pasid = ioasid_alloc(NULL, PASID_MIN, pasid_max - 1, svm); + if (pasid != INVALID_IOASID) { + /* A new pasid is allocated. */ + *new_pasid = true; + } + + return pasid; +} + /* Caller must hold pasid_mutex, mm reference */ static int intel_svm_bind_mm(struct device *dev, unsigned int flags, @@ -518,6 +565,8 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags, init_rcu_head(>rcu); if (!svm) { + bool new_pasid; + svm = kzalloc(sizeof(*svm), GFP_KERNEL); if (!svm) { ret = -ENOMEM; @@ -529,12 +578,9 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags, if (pasid_max > intel_pasid_max_id) pasid_max = intel_pasid_max_id; - /* Do not use PASID 0, reserved for RID to PASID */ - svm->pasid = ioasid_alloc(NULL, PASID_MIN, - pasid_max - 1, svm); + svm->pasid = alloc_pasid(svm, mm, pasid_max, _pasid, flags); if (svm->pasid == INVALID_IOASID) { - kfree(svm); - kfree(sdev); + free_bind(svm, sdev, new_pasid); ret =