From: Jason Gunthorpe Date: Thu, 23 Oct 2025 18:22:37 +0000 (-0300) Subject: iommu/vt-d: Follow PT_FEAT_DMA_INCOHERENT into the PASID entry X-Git-Tag: v6.19-rc1~133^2^8~32 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=101a2854110fa8787226dae1202892071ff2c369;p=thirdparty%2Fkernel%2Flinux.git iommu/vt-d: Follow PT_FEAT_DMA_INCOHERENT into the PASID entry Currently a incoherent walk domain cannot be attached to a coherent capable iommu. Kevin says HW probably doesn't exist with such a mixture, but making the driver support it makes logical sense anyhow. When building the PASID entry the PWSNP (Page Walk Snoop) bit tells the HW if it should issue snoops. If the page table is cache flushed because of PT_FEAT_DMA_INCOHERENT then it is fine to set this bit to 0 even if the HW supports 1. Weaken the compatible check to permit a coherent instance to accept an incoherent table and fix the PASID table construction to set PWSNP from PT_FEAT_DMA_INCOHERENT. SVA always sets PWSNP. Reviewed-by: Lu Baolu Signed-off-by: Jason Gunthorpe Reviewed-by: Kevin Tian Signed-off-by: Joerg Roedel --- diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 2d35867729df2..2d2f64ce2bc63 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -1300,6 +1300,10 @@ static int domain_setup_first_level(struct intel_iommu *iommu, if (domain->force_snooping) flags |= PASID_FLAG_PAGE_SNOOP; + if (!(domain->fspt.x86_64_pt.common.features & + BIT(PT_FEAT_DMA_INCOHERENT))) + flags |= PASID_FLAG_PWSNP; + return __domain_setup_first_level(iommu, dev, pasid, domain_id_iommu(domain, iommu), pt_info.gcr3_pt, flags, old); @@ -2990,7 +2994,7 @@ static int paging_domain_compatible_first_stage(struct dmar_domain *dmar_domain, if (!sm_supported(iommu) || !ecap_flts(iommu->ecap)) return -EINVAL; - if (!!ecap_smpwc(iommu->ecap) != + if (!ecap_smpwc(iommu->ecap) && !(dmar_domain->fspt.x86_64_pt.common.features & BIT(PT_FEAT_DMA_INCOHERENT))) return -EINVAL; @@ -3031,7 +3035,7 @@ paging_domain_compatible_second_stage(struct dmar_domain *dmar_domain, if (sm_supported(iommu) && !ecap_slts(iommu->ecap)) return -EINVAL; - if (iommu_paging_structure_coherency(iommu) != + if (!iommu_paging_structure_coherency(iommu) && !(dmar_domain->sspt.vtdss_pt.common.features & BIT(PT_FEAT_DMA_INCOHERENT))) return -EINVAL; diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c index b03da83583ac3..3e2255057079c 100644 --- a/drivers/iommu/intel/pasid.c +++ b/drivers/iommu/intel/pasid.c @@ -366,7 +366,7 @@ static void pasid_pte_config_first_level(struct intel_iommu *iommu, pasid_set_domain_id(pte, did); pasid_set_address_width(pte, iommu->agaw); - pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap)); + pasid_set_page_snoop(pte, flags & PASID_FLAG_PWSNP); /* Setup Present and PASID Granular Transfer Type: */ pasid_set_translation_type(pte, PASID_ENTRY_PGTT_FL_ONLY); @@ -461,19 +461,22 @@ int intel_pasid_replace_first_level(struct intel_iommu *iommu, */ static void pasid_pte_config_second_level(struct intel_iommu *iommu, struct pasid_entry *pte, - u64 pgd_val, int agaw, u16 did, - bool dirty_tracking) + struct dmar_domain *domain, u16 did) { + struct pt_iommu_vtdss_hw_info pt_info; + lockdep_assert_held(&iommu->lock); + pt_iommu_vtdss_hw_info(&domain->sspt, &pt_info); pasid_clear_entry(pte); pasid_set_domain_id(pte, did); - pasid_set_slptr(pte, pgd_val); - pasid_set_address_width(pte, agaw); + pasid_set_slptr(pte, pt_info.ssptptr); + pasid_set_address_width(pte, pt_info.aw); pasid_set_translation_type(pte, PASID_ENTRY_PGTT_SL_ONLY); pasid_set_fault_enable(pte); - pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap)); - if (dirty_tracking) + pasid_set_page_snoop(pte, !(domain->sspt.vtdss_pt.common.features & + BIT(PT_FEAT_DMA_INCOHERENT))); + if (domain->dirty_tracking) pasid_set_ssade(pte); pasid_set_present(pte); @@ -483,11 +486,9 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu, struct dmar_domain *domain, struct device *dev, u32 pasid) { - struct pt_iommu_vtdss_hw_info pt_info; struct pasid_entry *pte; u16 did; - pt_iommu_vtdss_hw_info(&domain->sspt, &pt_info); /* * If hardware advertises no support for second level @@ -513,8 +514,7 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu, return -EBUSY; } - pasid_pte_config_second_level(iommu, pte, pt_info.ssptptr, pt_info.aw, - did, domain->dirty_tracking); + pasid_pte_config_second_level(iommu, pte, domain, did); spin_unlock(&iommu->lock); pasid_flush_caches(iommu, pte, pasid, did); @@ -527,12 +527,9 @@ int intel_pasid_replace_second_level(struct intel_iommu *iommu, struct device *dev, u16 old_did, u32 pasid) { - struct pt_iommu_vtdss_hw_info pt_info; struct pasid_entry *pte, new_pte; u16 did; - pt_iommu_vtdss_hw_info(&domain->sspt, &pt_info); - /* * If hardware advertises no support for second level * translation, return directly. @@ -545,8 +542,7 @@ int intel_pasid_replace_second_level(struct intel_iommu *iommu, did = domain_id_iommu(domain, iommu); - pasid_pte_config_second_level(iommu, &new_pte, pt_info.ssptptr, - pt_info.aw, did, domain->dirty_tracking); + pasid_pte_config_second_level(iommu, &new_pte, domain, did); spin_lock(&iommu->lock); pte = intel_pasid_get_entry(dev, pasid); @@ -773,7 +769,8 @@ static void pasid_pte_config_nestd(struct intel_iommu *iommu, pasid_set_fault_enable(pte); pasid_set_domain_id(pte, did); pasid_set_address_width(pte, pt_info.aw); - pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap)); + pasid_set_page_snoop(pte, !(s2_domain->sspt.vtdss_pt.common.features & + BIT(PT_FEAT_DMA_INCOHERENT))); if (s2_domain->dirty_tracking) pasid_set_ssade(pte); pasid_set_translation_type(pte, PASID_ENTRY_PGTT_NESTED); diff --git a/drivers/iommu/intel/pasid.h b/drivers/iommu/intel/pasid.h index a771a77d4239c..b4c85242dc796 100644 --- a/drivers/iommu/intel/pasid.h +++ b/drivers/iommu/intel/pasid.h @@ -24,6 +24,7 @@ #define PASID_FLAG_NESTED BIT(1) #define PASID_FLAG_PAGE_SNOOP BIT(2) +#define PASID_FLAG_PWSNP BIT(2) /* * The PASID_FLAG_FL5LP flag Indicates using 5-level paging for first- diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index e147f71f91b72..71de7947971f8 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -170,6 +170,7 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain, /* Setup the pasid table: */ sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0; + sflags |= PASID_FLAG_PWSNP; ret = __domain_setup_first_level(iommu, dev, pasid, FLPT_DEFAULT_DID, __pa(mm->pgd), sflags, old);