diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2009-07-01 14:21:24 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-07-01 14:21:24 -0400 |
commit | c85994e4771025ef2a66533eb1a4c6c2217b9cda (patch) | |
tree | 9f827d5587a5cac2c8a412317e2997b8add67b9a /drivers/pci | |
parent | 3238c0c4d68d9a9022b411a11a4b933fbdb53a14 (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.c | 37 |
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 | ||
223 | static inline u64 dma_pte_addr(struct dma_pte *pte) | 223 | static 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 | ||
228 | static inline void dma_set_pte_pfn(struct dma_pte *pte, unsigned long pfn) | 233 | static 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 != |