aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorDavid Woodhouse <David.Woodhouse@intel.com>2009-07-01 14:21:24 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-07-01 14:21:24 -0400
commitc85994e4771025ef2a66533eb1a4c6c2217b9cda (patch)
tree9f827d5587a5cac2c8a412317e2997b8add67b9a /drivers/pci
parent3238c0c4d68d9a9022b411a11a4b933fbdb53a14 (diff)
intel-iommu: Ensure that PTE writes are 64-bit atomic, even on i386
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/intel-iommu.c37
1 files changed, 23 insertions, 14 deletions
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index ec7e032d5ab5..eea1006c860a 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -222,7 +222,12 @@ static inline void dma_set_pte_prot(struct dma_pte *pte, unsigned long prot)
222 222
223static inline u64 dma_pte_addr(struct dma_pte *pte) 223static inline u64 dma_pte_addr(struct dma_pte *pte)
224{ 224{
225 return (pte->val & VTD_PAGE_MASK); 225#ifdef CONFIG_64BIT
226 return pte->val & VTD_PAGE_MASK;
227#else
228 /* Must have a full atomic 64-bit read */
229 return __cmpxchg64(pte, 0ULL, 0ULL) & VTD_PAGE_MASK;
230#endif
226} 231}
227 232
228static inline void dma_set_pte_pfn(struct dma_pte *pte, unsigned long pfn) 233static inline void dma_set_pte_pfn(struct dma_pte *pte, unsigned long pfn)
@@ -712,6 +717,8 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
712 break; 717 break;
713 718
714 if (!dma_pte_present(pte)) { 719 if (!dma_pte_present(pte)) {
720 uint64_t pteval;
721
715 tmp_page = alloc_pgtable_page(); 722 tmp_page = alloc_pgtable_page();
716 723
717 if (!tmp_page) { 724 if (!tmp_page) {
@@ -719,15 +726,15 @@ static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
719 flags); 726 flags);
720 return NULL; 727 return NULL;
721 } 728 }
722 domain_flush_cache(domain, tmp_page, PAGE_SIZE); 729 domain_flush_cache(domain, tmp_page, VTD_PAGE_SIZE);
723 dma_set_pte_pfn(pte, virt_to_dma_pfn(tmp_page)); 730 pteval = (virt_to_dma_pfn(tmp_page) << VTD_PAGE_SHIFT) | DMA_PTE_READ | DMA_PTE_WRITE;
724 /* 731 if (cmpxchg64(&pte->val, 0ULL, pteval)) {
725 * high level table always sets r/w, last level page 732 /* Someone else set it while we were thinking; use theirs. */
726 * table control read/write 733 free_pgtable_page(tmp_page);
727 */ 734 } else {
728 dma_set_pte_readable(pte); 735 dma_pte_addr(pte);
729 dma_set_pte_writable(pte); 736 domain_flush_cache(domain, pte, sizeof(*pte));
730 domain_flush_cache(domain, pte, sizeof(*pte)); 737 }
731 } 738 }
732 parent = phys_to_virt(dma_pte_addr(pte)); 739 parent = phys_to_virt(dma_pte_addr(pte));
733 level--; 740 level--;
@@ -1666,6 +1673,8 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
1666 } 1673 }
1667 1674
1668 while (nr_pages--) { 1675 while (nr_pages--) {
1676 uint64_t tmp;
1677
1669 if (!sg_res) { 1678 if (!sg_res) {
1670 sg_res = (sg->offset + sg->length + VTD_PAGE_SIZE - 1) >> VTD_PAGE_SHIFT; 1679 sg_res = (sg->offset + sg->length + VTD_PAGE_SIZE - 1) >> VTD_PAGE_SHIFT;
1671 sg->dma_address = ((dma_addr_t)iov_pfn << VTD_PAGE_SHIFT) + sg->offset; 1680 sg->dma_address = ((dma_addr_t)iov_pfn << VTD_PAGE_SHIFT) + sg->offset;
@@ -1680,17 +1689,17 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
1680 /* We don't need lock here, nobody else 1689 /* We don't need lock here, nobody else
1681 * touches the iova range 1690 * touches the iova range
1682 */ 1691 */
1683 if (unlikely(dma_pte_addr(pte))) { 1692 tmp = cmpxchg64(&pte->val, 0ULL, pteval);
1693 if (tmp) {
1684 static int dumps = 5; 1694 static int dumps = 5;
1685 printk(KERN_CRIT "ERROR: DMA PTE for vPFN 0x%lx already set (to %llx)\n", 1695 printk(KERN_CRIT "ERROR: DMA PTE for vPFN 0x%lx already set (to %llx not %llx)\n",
1686 iov_pfn, pte->val); 1696 iov_pfn, tmp, (unsigned long long)pteval);
1687 if (dumps) { 1697 if (dumps) {
1688 dumps--; 1698 dumps--;
1689 debug_dma_dump_mappings(NULL); 1699 debug_dma_dump_mappings(NULL);
1690 } 1700 }
1691 WARN_ON(1); 1701 WARN_ON(1);
1692 } 1702 }
1693 pte->val = pteval;
1694 pte++; 1703 pte++;
1695 if (!nr_pages || 1704 if (!nr_pages ||
1696 (unsigned long)pte >> VTD_PAGE_SHIFT != 1705 (unsigned long)pte >> VTD_PAGE_SHIFT !=