Skip to contentSkip to conversation

intel-iommu: iova_to_phys: fill in bits from iova when large pte

PATCH Marcelo Tosatti 2 participants 3 messages Nov 2, 2013 – Nov 4, 2013 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

intel_iommu_iova_to_phys returns incorrect physical address when iova is translated by large pte.

Fill in bits from iova when creating the physical address.

drivers/iommu/intel-iommu.c +22 -7
index 15e9b57..f8f2988 100644
@@ -774,7 +774,8 @@ out:
 }
 
 static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
-				      unsigned long pfn, int target_level)
+				      unsigned long pfn, int target_level,
+				      int *large_page)
 {
 	int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
 	struct dma_pte *parent, *pte = NULL;
@@ -790,8 +791,15 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
 
 		offset = pfn_level_offset(pfn, level);
 		pte = &parent[offset];
-		if (!target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
-			break;
+		if (!target_level) {
+			if (!dma_pte_present(pte))
+				break;
+			if (dma_pte_superpage(pte)) {
+				if (large_page)
+					*large_page = level;
+				break;
+			}
+		}
 		if (level == target_level)
 			break;
 
@@ -1824,7 +1832,8 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
 		if (!pte) {
 			largepage_lvl = hardware_largepage_caps(domain, iov_pfn, phys_pfn, sg_res);
 
-			first_pte = pte = pfn_to_dma_pte(domain, iov_pfn, largepage_lvl);
+			first_pte = pte = pfn_to_dma_pte(domain, iov_pfn,
+							 largepage_lvl, NULL);
 			if (!pte)
 				return -ENOMEM;
 			/* It is large page*/
@@ -4129,11 +4138,17 @@ static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
 {
 	struct dmar_domain *dmar_domain = domain->priv;
 	struct dma_pte *pte;
-	u64 phys = 0;
+	u64 phys = 0, iova_mask;
+	int large_page = 1;
 
-	pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, 0);
-	if (pte)
+	pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, 0,
+			     &large_page);
+	if (pte) {
 		phys = dma_pte_addr(pte);
+		large_page--;
+		iova_mask = (1ULL << (VTD_PAGE_SHIFT+(large_page*9)))-1;
+		phys |= iova & iova_mask;
+	}
 
 	return phys;
 }

A message that is not in the archive (sent off-list or not yet received): [email protected]

#2 lore raw
Quoted 5 lines · Marcelo Tosatti

intel_iommu_iova_to_phys returns incorrect physical address when iova is translated by large pte.

Fill in bits from iova when creating the physical address.

Marcelo, for what it's worth, this patch alone didn't fix the BUG when using KVM PCI assignment with huge pages. I still needed the hva alignment patch as well.

Greg

#3 lore raw
Quoted 5 lines · Greg Edwards
Quoted 4 lines · Marcelo Tosatti

intel_iommu_iova_to_phys returns incorrect physical address when iova is translated by large pte.

Quoted 7 lines
Quoted 1 line

Fill in bits from iova when creating the physical address.

Marcelo, for what it's worth, this patch alone didn't fix the BUG when using KVM PCI assignment with huge pages. I still needed the hva alignment patch as well.

Greg

Yep, still a bugfix though.

Good catch, Greg.