1
0
mirror of https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2026-01-12 01:20:14 +00:00

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 <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>
This commit is contained in:
Jason Gunthorpe 2025-10-23 15:22:37 -03:00 committed by Joerg Roedel
parent d373449d8e
commit 101a285411
4 changed files with 22 additions and 19 deletions

View 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;

View 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);

View 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-

View 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);