diff options
Diffstat (limited to 'drivers/iommu/amd_iommu.c')
-rw-r--r-- | drivers/iommu/amd_iommu.c | 166 |
1 files changed, 77 insertions, 89 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index e8c412d..e43d489 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -33,6 +33,7 @@ #include <linux/export.h> #include <linux/irq.h> #include <linux/msi.h> +#include <linux/dma-contiguous.h> #include <asm/irq_remapping.h> #include <asm/io_apic.h> #include <asm/apic.h> @@ -1326,7 +1327,9 @@ static u64 *alloc_pte(struct protection_domain *domain, * This function checks if there is a PTE for a given dma address. If * there is one, it returns the pointer to it. */ -static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) +static u64 *fetch_pte(struct protection_domain *domain, + unsigned long address, + unsigned long *page_size) { int level; u64 *pte; @@ -1334,8 +1337,9 @@ static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) if (address > PM_LEVEL_SIZE(domain->mode)) return NULL; - level = domain->mode - 1; - pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; + level = domain->mode - 1; + pte = &domain->pt_root[PM_LEVEL_INDEX(level, address)]; + *page_size = PTE_LEVEL_PAGE_SIZE(level); while (level > 0) { @@ -1344,19 +1348,9 @@ static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) return NULL; /* Large PTE */ - if (PM_PTE_LEVEL(*pte) == 0x07) { - unsigned long pte_mask, __pte; - - /* - * If we have a series of large PTEs, make - * sure to return a pointer to the first one. - */ - pte_mask = PTE_PAGE_SIZE(*pte); - pte_mask = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1); - __pte = ((unsigned long)pte) & pte_mask; - - return (u64 *)__pte; - } + if (PM_PTE_LEVEL(*pte) == 7 || + PM_PTE_LEVEL(*pte) == 0) + break; /* No level skipping support yet */ if (PM_PTE_LEVEL(*pte) != level) @@ -1365,8 +1359,21 @@ static u64 *fetch_pte(struct protection_domain *domain, unsigned long address) level -= 1; /* Walk to the next level */ - pte = IOMMU_PTE_PAGE(*pte); - pte = &pte[PM_LEVEL_INDEX(level, address)]; + pte = IOMMU_PTE_PAGE(*pte); + pte = &pte[PM_LEVEL_INDEX(level, address)]; + *page_size = PTE_LEVEL_PAGE_SIZE(level); + } + + if (PM_PTE_LEVEL(*pte) == 0x07) { + unsigned long pte_mask; + + /* + * If we have a series of large PTEs, make + * sure to return a pointer to the first one. + */ + *page_size = pte_mask = PTE_PAGE_SIZE(*pte); + pte_mask = ~((PAGE_SIZE_PTE_COUNT(pte_mask) << 3) - 1); + pte = (u64 *)(((unsigned long)pte) & pte_mask); } return pte; @@ -1388,13 +1395,14 @@ static int iommu_map_page(struct protection_domain *dom, u64 __pte, *pte; int i, count; + BUG_ON(!IS_ALIGNED(bus_addr, page_size)); + BUG_ON(!IS_ALIGNED(phys_addr, page_size)); + if (!(prot & IOMMU_PROT_MASK)) return -EINVAL; - bus_addr = PAGE_ALIGN(bus_addr); - phys_addr = PAGE_ALIGN(phys_addr); - count = PAGE_SIZE_PTE_COUNT(page_size); - pte = alloc_pte(dom, bus_addr, page_size, NULL, GFP_KERNEL); + count = PAGE_SIZE_PTE_COUNT(page_size); + pte = alloc_pte(dom, bus_addr, page_size, NULL, GFP_KERNEL); if (!pte) return -ENOMEM; @@ -1403,7 +1411,7 @@ static int iommu_map_page(struct protection_domain *dom, if (IOMMU_PTE_PRESENT(pte[i])) return -EBUSY; - if (page_size > PAGE_SIZE) { + if (count > 1) { __pte = PAGE_SIZE_PTE(phys_addr, page_size); __pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_P | IOMMU_PTE_FC; } else @@ -1426,7 +1434,8 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom, unsigned long bus_addr, unsigned long page_size) { - unsigned long long unmap_size, unmapped; + unsigned long long unmapped; + unsigned long unmap_size; u64 *pte; BUG_ON(!is_power_of_2(page_size)); @@ -1435,28 +1444,12 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom, while (unmapped < page_size) { - pte = fetch_pte(dom, bus_addr); - - if (!pte) { - /* - * No PTE for this address - * move forward in 4kb steps - */ - unmap_size = PAGE_SIZE; - } else if (PM_PTE_LEVEL(*pte) == 0) { - /* 4kb PTE found for this address */ - unmap_size = PAGE_SIZE; - *pte = 0ULL; - } else { - int count, i; - - /* Large PTE found which maps this address */ - unmap_size = PTE_PAGE_SIZE(*pte); - - /* Only unmap from the first pte in the page */ - if ((unmap_size - 1) & bus_addr) - break; - count = PAGE_SIZE_PTE_COUNT(unmap_size); + pte = fetch_pte(dom, bus_addr, &unmap_size); + + if (pte) { + int i, count; + + count = PAGE_SIZE_PTE_COUNT(unmap_size); for (i = 0; i < count; i++) pte[i] = 0ULL; } @@ -1604,7 +1597,7 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom, { int index = dma_dom->aperture_size >> APERTURE_RANGE_SHIFT; struct amd_iommu *iommu; - unsigned long i, old_size; + unsigned long i, old_size, pte_pgsize; #ifdef CONFIG_IOMMU_STRESS populate = false; @@ -1677,12 +1670,13 @@ static int alloc_new_range(struct dma_ops_domain *dma_dom, */ for (i = dma_dom->aperture[index]->offset; i < dma_dom->aperture_size; - i += PAGE_SIZE) { - u64 *pte = fetch_pte(&dma_dom->domain, i); + i += pte_pgsize) { + u64 *pte = fetch_pte(&dma_dom->domain, i, &pte_pgsize); if (!pte || !IOMMU_PTE_PRESENT(*pte)) continue; - dma_ops_reserve_addresses(dma_dom, i >> PAGE_SHIFT, 1); + dma_ops_reserve_addresses(dma_dom, i >> PAGE_SHIFT, + pte_pgsize >> 12); } update_domain(&dma_dom->domain); @@ -2427,16 +2421,6 @@ static int device_change_notifier(struct notifier_block *nb, dev_data = get_dev_data(dev); switch (action) { - case BUS_NOTIFY_UNBOUND_DRIVER: - - domain = domain_for_device(dev); - - if (!domain) - goto out; - if (dev_data->passthrough) - break; - detach_device(dev); - break; case BUS_NOTIFY_ADD_DEVICE: iommu_init_device(dev); @@ -2472,7 +2456,7 @@ static int device_change_notifier(struct notifier_block *nb, dev->archdata.dma_ops = &amd_iommu_dma_ops; break; - case BUS_NOTIFY_DEL_DEVICE: + case BUS_NOTIFY_REMOVED_DEVICE: iommu_uninit_device(dev); @@ -2928,38 +2912,42 @@ static void *alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr, gfp_t flag, struct dma_attrs *attrs) { - unsigned long flags; - void *virt_addr; - struct protection_domain *domain; - phys_addr_t paddr; u64 dma_mask = dev->coherent_dma_mask; + struct protection_domain *domain; + unsigned long flags; + struct page *page; INC_STATS_COUNTER(cnt_alloc_coherent); domain = get_domain(dev); if (PTR_ERR(domain) == -EINVAL) { - virt_addr = (void *)__get_free_pages(flag, get_order(size)); - *dma_addr = __pa(virt_addr); - return virt_addr; + page = alloc_pages(flag, get_order(size)); + *dma_addr = page_to_phys(page); + return page_address(page); } else if (IS_ERR(domain)) return NULL; + size = PAGE_ALIGN(size); dma_mask = dev->coherent_dma_mask; flag &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32); - flag |= __GFP_ZERO; - virt_addr = (void *)__get_free_pages(flag, get_order(size)); - if (!virt_addr) - return NULL; + page = alloc_pages(flag | __GFP_NOWARN, get_order(size)); + if (!page) { + if (!(flag & __GFP_WAIT)) + return NULL; - paddr = virt_to_phys(virt_addr); + page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT, + get_order(size)); + if (!page) + return NULL; + } if (!dma_mask) dma_mask = *dev->dma_mask; spin_lock_irqsave(&domain->lock, flags); - *dma_addr = __map_single(dev, domain->priv, paddr, + *dma_addr = __map_single(dev, domain->priv, page_to_phys(page), size, DMA_BIDIRECTIONAL, true, dma_mask); if (*dma_addr == DMA_ERROR_CODE) { @@ -2971,11 +2959,12 @@ static void *alloc_coherent(struct device *dev, size_t size, spin_unlock_irqrestore(&domain->lock, flags); - return virt_addr; + return page_address(page); out_free: - free_pages((unsigned long)virt_addr, get_order(size)); + if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT)) + __free_pages(page, get_order(size)); return NULL; } @@ -2987,11 +2976,15 @@ static void free_coherent(struct device *dev, size_t size, void *virt_addr, dma_addr_t dma_addr, struct dma_attrs *attrs) { - unsigned long flags; struct protection_domain *domain; + unsigned long flags; + struct page *page; INC_STATS_COUNTER(cnt_free_coherent); + page = virt_to_page(virt_addr); + size = PAGE_ALIGN(size); + domain = get_domain(dev); if (IS_ERR(domain)) goto free_mem; @@ -3005,7 +2998,8 @@ static void free_coherent(struct device *dev, size_t size, spin_unlock_irqrestore(&domain->lock, flags); free_mem: - free_pages((unsigned long)virt_addr, get_order(size)); + if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT)) + __free_pages(page, get_order(size)); } /* @@ -3387,27 +3381,21 @@ static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom, dma_addr_t iova) { struct protection_domain *domain = to_pdomain(dom); - unsigned long offset_mask; - phys_addr_t paddr; + unsigned long offset_mask, pte_pgsize; u64 *pte, __pte; if (domain->mode == PAGE_MODE_NONE) return iova; - pte = fetch_pte(domain, iova); + pte = fetch_pte(domain, iova, &pte_pgsize); if (!pte || !IOMMU_PTE_PRESENT(*pte)) return 0; - if (PM_PTE_LEVEL(*pte) == 0) - offset_mask = PAGE_SIZE - 1; - else - offset_mask = PTE_PAGE_SIZE(*pte) - 1; - - __pte = *pte & PM_ADDR_MASK; - paddr = (__pte & ~offset_mask) | (iova & offset_mask); + offset_mask = pte_pgsize - 1; + __pte = *pte & PM_ADDR_MASK; - return paddr; + return (__pte & ~offset_mask) | (iova & offset_mask); } static bool amd_iommu_capable(enum iommu_cap cap) |