Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jason Gunthorpe and committed by
Joerg Roedel
101a2854 d373449d

+22 -19
+6 -2
drivers/iommu/intel/iommu.c
··· 1300 1300 if (domain->force_snooping) 1301 1301 flags |= PASID_FLAG_PAGE_SNOOP; 1302 1302 1303 + if (!(domain->fspt.x86_64_pt.common.features & 1304 + BIT(PT_FEAT_DMA_INCOHERENT))) 1305 + flags |= PASID_FLAG_PWSNP; 1306 + 1303 1307 return __domain_setup_first_level(iommu, dev, pasid, 1304 1308 domain_id_iommu(domain, iommu), 1305 1309 pt_info.gcr3_pt, flags, old); ··· 2994 2990 if (!sm_supported(iommu) || !ecap_flts(iommu->ecap)) 2995 2991 return -EINVAL; 2996 2992 2997 - if (!!ecap_smpwc(iommu->ecap) != 2993 + if (!ecap_smpwc(iommu->ecap) && 2998 2994 !(dmar_domain->fspt.x86_64_pt.common.features & 2999 2995 BIT(PT_FEAT_DMA_INCOHERENT))) 3000 2996 return -EINVAL; ··· 3035 3031 if (sm_supported(iommu) && !ecap_slts(iommu->ecap)) 3036 3032 return -EINVAL; 3037 3033 3038 - if (iommu_paging_structure_coherency(iommu) != 3034 + if (!iommu_paging_structure_coherency(iommu) && 3039 3035 !(dmar_domain->sspt.vtdss_pt.common.features & 3040 3036 BIT(PT_FEAT_DMA_INCOHERENT))) 3041 3037 return -EINVAL;
+14 -17
drivers/iommu/intel/pasid.c
··· 366 366 367 367 pasid_set_domain_id(pte, did); 368 368 pasid_set_address_width(pte, iommu->agaw); 369 - pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap)); 369 + pasid_set_page_snoop(pte, flags & PASID_FLAG_PWSNP); 370 370 371 371 /* Setup Present and PASID Granular Transfer Type: */ 372 372 pasid_set_translation_type(pte, PASID_ENTRY_PGTT_FL_ONLY); ··· 461 461 */ 462 462 static void pasid_pte_config_second_level(struct intel_iommu *iommu, 463 463 struct pasid_entry *pte, 464 - u64 pgd_val, int agaw, u16 did, 465 - bool dirty_tracking) 464 + struct dmar_domain *domain, u16 did) 466 465 { 466 + struct pt_iommu_vtdss_hw_info pt_info; 467 + 467 468 lockdep_assert_held(&iommu->lock); 468 469 470 + pt_iommu_vtdss_hw_info(&domain->sspt, &pt_info); 469 471 pasid_clear_entry(pte); 470 472 pasid_set_domain_id(pte, did); 471 - pasid_set_slptr(pte, pgd_val); 472 - pasid_set_address_width(pte, agaw); 473 + pasid_set_slptr(pte, pt_info.ssptptr); 474 + pasid_set_address_width(pte, pt_info.aw); 473 475 pasid_set_translation_type(pte, PASID_ENTRY_PGTT_SL_ONLY); 474 476 pasid_set_fault_enable(pte); 475 - pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap)); 476 - if (dirty_tracking) 477 + pasid_set_page_snoop(pte, !(domain->sspt.vtdss_pt.common.features & 478 + BIT(PT_FEAT_DMA_INCOHERENT))); 479 + if (domain->dirty_tracking) 477 480 pasid_set_ssade(pte); 478 481 479 482 pasid_set_present(pte); ··· 486 483 struct dmar_domain *domain, 487 484 struct device *dev, u32 pasid) 488 485 { 489 - struct pt_iommu_vtdss_hw_info pt_info; 490 486 struct pasid_entry *pte; 491 487 u16 did; 492 488 493 - pt_iommu_vtdss_hw_info(&domain->sspt, &pt_info); 494 489 495 490 /* 496 491 * If hardware advertises no support for second level ··· 514 513 return -EBUSY; 515 514 } 516 515 517 - pasid_pte_config_second_level(iommu, pte, pt_info.ssptptr, pt_info.aw, 518 - did, domain->dirty_tracking); 516 + pasid_pte_config_second_level(iommu, pte, domain, did); 519 517 spin_unlock(&iommu->lock); 520 518 521 519 pasid_flush_caches(iommu, pte, pasid, did); ··· 527 527 struct device *dev, u16 old_did, 528 528 u32 pasid) 529 529 { 530 - struct pt_iommu_vtdss_hw_info pt_info; 531 530 struct pasid_entry *pte, new_pte; 532 531 u16 did; 533 - 534 - pt_iommu_vtdss_hw_info(&domain->sspt, &pt_info); 535 532 536 533 /* 537 534 * If hardware advertises no support for second level ··· 542 545 543 546 did = domain_id_iommu(domain, iommu); 544 547 545 - pasid_pte_config_second_level(iommu, &new_pte, pt_info.ssptptr, 546 - pt_info.aw, did, domain->dirty_tracking); 548 + pasid_pte_config_second_level(iommu, &new_pte, domain, did); 547 549 548 550 spin_lock(&iommu->lock); 549 551 pte = intel_pasid_get_entry(dev, pasid); ··· 769 773 pasid_set_fault_enable(pte); 770 774 pasid_set_domain_id(pte, did); 771 775 pasid_set_address_width(pte, pt_info.aw); 772 - pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap)); 776 + pasid_set_page_snoop(pte, !(s2_domain->sspt.vtdss_pt.common.features & 777 + BIT(PT_FEAT_DMA_INCOHERENT))); 773 778 if (s2_domain->dirty_tracking) 774 779 pasid_set_ssade(pte); 775 780 pasid_set_translation_type(pte, PASID_ENTRY_PGTT_NESTED);
+1
drivers/iommu/intel/pasid.h
··· 24 24 25 25 #define PASID_FLAG_NESTED BIT(1) 26 26 #define PASID_FLAG_PAGE_SNOOP BIT(2) 27 + #define PASID_FLAG_PWSNP BIT(2) 27 28 28 29 /* 29 30 * The PASID_FLAG_FL5LP flag Indicates using 5-level paging for first-
+1
drivers/iommu/intel/svm.c
··· 170 170 171 171 /* Setup the pasid table: */ 172 172 sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0; 173 + sflags |= PASID_FLAG_PWSNP; 173 174 ret = __domain_setup_first_level(iommu, dev, pasid, 174 175 FLPT_DEFAULT_DID, __pa(mm->pgd), 175 176 sflags, old);