]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iommu/vt-d: Follow PT_FEAT_DMA_INCOHERENT into the PASID entry
authorJason Gunthorpe <jgg@nvidia.com>
Thu, 23 Oct 2025 18:22:37 +0000 (15:22 -0300)
committerJoerg Roedel <joerg.roedel@amd.com>
Wed, 5 Nov 2025 08:50:20 +0000 (09:50 +0100)
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 <baolu.lu@linux.intel.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
drivers/iommu/intel/iommu.c
drivers/iommu/intel/pasid.c
drivers/iommu/intel/pasid.h
drivers/iommu/intel/svm.c

index 2d35867729df20aac53456338186b41b30c4a051..2d2f64ce2bc6357513f31582dde4230c9a97ecba 100644 (file)
@@ -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;
index b03da83583ac3a3f508b1983c889d28cf998d06e..3e2255057079c5e7d893ea818bdb4ded80f1a53a 100644 (file)
@@ -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);
index a771a77d4239c479a381d1979f33d9bc0baf64df..b4c85242dc7962543c777df2da9d43bfa506a117 100644 (file)
@@ -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-
index e147f71f91b72297248bdb2f57f513911b46ac77..71de7947971f823c24935ec707ea40a5f10c2757 100644 (file)
@@ -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);