OSDN Git Service

iommu/io-pgtable-fast: optimize statically allocated pages
authorShiraz Hashim <shashim@codeaurora.org>
Wed, 15 Feb 2017 11:13:01 +0000 (16:43 +0530)
committerShiraz Hashim <shashim@codeaurora.org>
Wed, 1 Mar 2017 06:32:07 +0000 (12:02 +0530)
Presently fastmap iommu feature allocates page tables for
full 4GB virtual address space. This can be optimized to
consider virtual address range [base, size] needed by
client and prepare page tables only for applicable
region.

Change-Id: Ie6c23cb8e1702a823567e126f452b1e72d851f71
Signed-off-by: Shiraz Hashim <shashim@codeaurora.org>
drivers/iommu/arm-smmu.c
drivers/iommu/dma-mapping-fast.c
drivers/iommu/io-pgtable-fast.c
drivers/iommu/io-pgtable.h
include/linux/io-pgtable-fast.h

index ce15e15..ab716dd 100644 (file)
@@ -1844,6 +1844,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
                        .oas            = oas,
                        .tlb            = &arm_smmu_gather_ops,
                        .iommu_dev      = smmu->dev,
+                       .iova_base      = domain->geometry.aperture_start,
+                       .iova_end       = domain->geometry.aperture_end,
                };
        }
 
index 6d90221..2acb924 100644 (file)
@@ -151,7 +151,9 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping,
 
                iommu_tlbiall(mapping->domain);
                mapping->have_stale_tlbs = false;
-               av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, skip_sync);
+               av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, mapping->base,
+                               mapping->base + mapping->size - 1,
+                               skip_sync);
        }
 
        return (bit << FAST_PAGE_SHIFT) + mapping->base;
@@ -328,7 +330,7 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page,
        if (unlikely(iova == DMA_ERROR_CODE))
                goto fail;
 
-       pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+       pmd = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, iova);
 
        if (unlikely(av8l_fast_map_public(pmd, phys_to_map, len, prot)))
                goto fail_free_iova;
@@ -351,7 +353,8 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova,
 {
        struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
        unsigned long flags;
-       av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+       av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+                                       mapping->base, iova);
        unsigned long offset = iova & ~FAST_PAGE_MASK;
        size_t len = ALIGN(size + offset, FAST_PAGE_SIZE);
        int nptes = len >> FAST_PAGE_SHIFT;
@@ -373,7 +376,8 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev,
                dma_addr_t iova, size_t size, enum dma_data_direction dir)
 {
        struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
-       av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+       av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+                                       mapping->base, iova);
        unsigned long offset = iova & ~FAST_PAGE_MASK;
        struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
 
@@ -385,7 +389,8 @@ static void fast_smmu_sync_single_for_device(struct device *dev,
                dma_addr_t iova, size_t size, enum dma_data_direction dir)
 {
        struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
-       av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+       av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+                                       mapping->base, iova);
        unsigned long offset = iova & ~FAST_PAGE_MASK;
        struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
 
@@ -513,7 +518,8 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
        while (sg_miter_next(&miter)) {
                int nptes = miter.length >> FAST_PAGE_SHIFT;
 
-               ptep = iopte_pmd_offset(mapping->pgtbl_pmds, iova_iter);
+               ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base,
+                                       iova_iter);
                if (unlikely(av8l_fast_map_public(
                                     ptep, page_to_phys(miter.page),
                                     miter.length, prot))) {
@@ -541,7 +547,7 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
 out_unmap:
        /* need to take the lock again for page tables and iova */
        spin_lock_irqsave(&mapping->lock, flags);
-       ptep = iopte_pmd_offset(mapping->pgtbl_pmds, dma_addr);
+       ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_addr);
        av8l_fast_unmap_public(ptep, size);
        fast_dmac_clean_range(mapping, ptep, ptep + count);
 out_free_iova:
@@ -573,7 +579,7 @@ static void fast_smmu_free(struct device *dev, size_t size,
 
        pages = area->pages;
        dma_common_free_remap(vaddr, size, VM_USERMAP, false);
-       ptep = iopte_pmd_offset(mapping->pgtbl_pmds, dma_handle);
+       ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_handle);
        spin_lock_irqsave(&mapping->lock, flags);
        av8l_fast_unmap_public(ptep, size);
        fast_dmac_clean_range(mapping, ptep, ptep + count);
@@ -745,6 +751,9 @@ int fast_smmu_attach_device(struct device *dev,
        mapping->fast->domain = domain;
        mapping->fast->dev = dev;
 
+       domain->geometry.aperture_start = mapping->base;
+       domain->geometry.aperture_end = mapping->base + size - 1;
+
        if (iommu_attach_device(domain, dev))
                return -EINVAL;
 
index 5d32a38..603b983 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/io-pgtable-fast.h>
+#include <linux/mm.h>
 #include <asm/cacheflush.h>
 #include <linux/vmalloc.h>
 
@@ -42,6 +43,9 @@ struct av8l_fast_io_pgtable {
        av8l_fast_iopte          *puds[4];
        av8l_fast_iopte          *pmds;
        struct page             **pages; /* page table memory */
+       int                     nr_pages;
+       dma_addr_t              base;
+       dma_addr_t              end;
 };
 
 /* Page table bits */
@@ -168,12 +172,14 @@ static void __av8l_check_for_stale_tlb(av8l_fast_iopte *ptep)
        }
 }
 
-void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, bool skip_sync)
+void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, u64 base,
+               u64 end, bool skip_sync)
 {
        int i;
        av8l_fast_iopte *pmdp = pmds;
 
-       for (i = 0; i < ((SZ_1G * 4UL) >> AV8L_FAST_PAGE_SHIFT); ++i) {
+       for (i = base >> AV8L_FAST_PAGE_SHIFT;
+                       i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) {
                if (!(*pmdp & AV8L_FAST_PTE_VALID)) {
                        *pmdp = 0;
                        if (!skip_sync)
@@ -224,7 +230,7 @@ static int av8l_fast_map(struct io_pgtable_ops *ops, unsigned long iova,
                         phys_addr_t paddr, size_t size, int prot)
 {
        struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
-       av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova);
+       av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
        unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT;
 
        av8l_fast_map_public(ptep, paddr, size, prot);
@@ -255,7 +261,7 @@ static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova,
                              size_t size)
 {
        struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
-       av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova);
+       av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
        unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT;
 
        __av8l_fast_unmap(ptep, size, false);
@@ -333,7 +339,7 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg)
 }
 
 /*
- * We need 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and
+ * We need max 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and
  * 2048 pages for pmds (each pud page contains 512 table entries, each
  * pointing to a pmd).
  */
@@ -342,12 +348,38 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg)
 #define NUM_PMD_PAGES 2048
 #define NUM_PGTBL_PAGES (NUM_PGD_PAGES + NUM_PUD_PAGES + NUM_PMD_PAGES)
 
+/* undefine arch specific definitions which depends on page table format */
+#undef pud_index
+#undef pud_mask
+#undef pud_next
+#undef pmd_index
+#undef pmd_mask
+#undef pmd_next
+
+#define pud_index(addr)                (((addr) >> 30) & 0x3)
+#define pud_mask(addr)         ((addr) & ~((1UL << 30) - 1))
+#define pud_next(addr, end)                                    \
+({     unsigned long __boundary = pud_mask(addr + (1UL << 30));\
+       (__boundary - 1 < (end) - 1) ? __boundary : (end);      \
+})
+
+#define pmd_index(addr)                (((addr) >> 21) & 0x1ff)
+#define pmd_mask(addr)         ((addr) & ~((1UL << 21) - 1))
+#define pmd_next(addr, end)                                    \
+({     unsigned long __boundary = pmd_mask(addr + (1UL << 21));\
+       (__boundary - 1 < (end) - 1) ? __boundary : (end);      \
+})
+
 static int
 av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
                               struct io_pgtable_cfg *cfg, void *cookie)
 {
        int i, j, pg = 0;
        struct page **pages, *page;
+       dma_addr_t base = cfg->iova_base;
+       dma_addr_t end = cfg->iova_end;
+       dma_addr_t pud, pmd;
+       int pmd_pg_index;
 
        pages = kmalloc(sizeof(*pages) * NUM_PGTBL_PAGES, __GFP_NOWARN |
                                                        __GFP_NORETRY);
@@ -365,10 +397,11 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
        data->pgd = page_address(page);
 
        /*
-        * We need 2048 entries at level 2 to map 4GB of VA space. A page
-        * can hold 512 entries, so we need 4 pages.
+        * We need max 2048 entries at level 2 to map 4GB of VA space. A page
+        * can hold 512 entries, so we need max 4 pages.
         */
-       for (i = 0; i < 4; ++i) {
+       for (i = pud_index(base), pud = base; pud < end;
+                       ++i, pud = pud_next(pud, end)) {
                av8l_fast_iopte pte, *ptep;
 
                page = alloc_page(GFP_KERNEL | __GFP_ZERO);
@@ -383,12 +416,15 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
        dmac_clean_range(data->pgd, data->pgd + 4);
 
        /*
-        * We have 4 puds, each of which can point to 512 pmds, so we'll
-        * have 2048 pmds, each of which can hold 512 ptes, for a grand
+        * We have max 4 puds, each of which can point to 512 pmds, so we'll
+        * have max 2048 pmds, each of which can hold 512 ptes, for a grand
         * total of 2048*512=1048576 PTEs.
         */
-       for (i = 0; i < 4; ++i) {
-               for (j = 0; j < 512; ++j) {
+       pmd_pg_index = pg;
+       for (i = pud_index(base), pud = base; pud < end;
+                       ++i, pud = pud_next(pud, end)) {
+               for (j = pmd_index(pud), pmd = pud; pmd < pud_next(pud, end);
+                               ++j, pmd = pmd_next(pmd, end)) {
                        av8l_fast_iopte pte, *pudp;
 
                        page = alloc_page(GFP_KERNEL | __GFP_ZERO);
@@ -402,21 +438,21 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
                dmac_clean_range(data->puds[i], data->puds[i] + 512);
        }
 
-       if (WARN_ON(pg != NUM_PGTBL_PAGES))
-               goto err_free_pages;
-
        /*
         * We map the pmds into a virtually contiguous space so that we
         * don't have to traverse the first two levels of the page tables
         * to find the appropriate pud.  Instead, it will be a simple
         * offset from the virtual base of the pmds.
         */
-       data->pmds = vmap(&pages[NUM_PGD_PAGES + NUM_PUD_PAGES], NUM_PMD_PAGES,
+       data->pmds = vmap(&pages[pmd_pg_index], pg - pmd_pg_index,
                          VM_IOREMAP, PAGE_KERNEL);
        if (!data->pmds)
                goto err_free_pages;
 
        data->pages = pages;
+       data->nr_pages = pg;
+       data->base = base;
+       data->end = end;
        return 0;
 
 err_free_pages:
@@ -516,7 +552,7 @@ static void av8l_fast_free_pgtable(struct io_pgtable *iop)
        struct av8l_fast_io_pgtable *data = iof_pgtable_to_data(iop);
 
        vunmap(data->pmds);
-       for (i = 0; i < NUM_PGTBL_PAGES; ++i)
+       for (i = 0; i < data->nr_pages; ++i)
                __free_page(data->pages[i]);
        kvfree(data->pages);
        kfree(data);
@@ -588,6 +624,7 @@ static int __init av8l_fast_positive_testing(void)
        struct av8l_fast_io_pgtable *data;
        av8l_fast_iopte *pmds;
        u64 max = SZ_1G * 4ULL - 1;
+       u64 base = 0;
 
        cfg = (struct io_pgtable_cfg) {
                .quirks = 0,
@@ -595,6 +632,8 @@ static int __init av8l_fast_positive_testing(void)
                .ias = 32,
                .oas = 32,
                .pgsize_bitmap = SZ_4K,
+               .iova_base = base,
+               .iova_end = max,
        };
 
        cfg_cookie = &cfg;
@@ -607,81 +646,81 @@ static int __init av8l_fast_positive_testing(void)
        pmds = data->pmds;
 
        /* map the entire 4GB VA space with 4K map calls */
-       for (iova = 0; iova < max; iova += SZ_4K) {
+       for (iova = base; iova < max; iova += SZ_4K) {
                if (WARN_ON(ops->map(ops, iova, iova, SZ_4K, IOMMU_READ))) {
                        failed++;
                        continue;
                }
        }
-       if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
-                                                         max)))
+       if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+                                       base, max - base)))
                failed++;
 
        /* unmap it all */
-       for (iova = 0; iova < max; iova += SZ_4K) {
+       for (iova = base; iova < max; iova += SZ_4K) {
                if (WARN_ON(ops->unmap(ops, iova, SZ_4K) != SZ_4K))
                        failed++;
        }
 
        /* sweep up TLB proving PTEs */
-       av8l_fast_clear_stale_ptes(pmds, false);
+       av8l_fast_clear_stale_ptes(pmds, base, max, false);
 
        /* map the entire 4GB VA space with 8K map calls */
-       for (iova = 0; iova < max; iova += SZ_8K) {
+       for (iova = base; iova < max; iova += SZ_8K) {
                if (WARN_ON(ops->map(ops, iova, iova, SZ_8K, IOMMU_READ))) {
                        failed++;
                        continue;
                }
        }
 
-       if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
-                                                         max)))
+       if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+                                       base, max - base)))
                failed++;
 
        /* unmap it all with 8K unmap calls */
-       for (iova = 0; iova < max; iova += SZ_8K) {
+       for (iova = base; iova < max; iova += SZ_8K) {
                if (WARN_ON(ops->unmap(ops, iova, SZ_8K) != SZ_8K))
                        failed++;
        }
 
        /* sweep up TLB proving PTEs */
-       av8l_fast_clear_stale_ptes(pmds, false);
+       av8l_fast_clear_stale_ptes(pmds, base, max, false);
 
        /* map the entire 4GB VA space with 16K map calls */
-       for (iova = 0; iova < max; iova += SZ_16K) {
+       for (iova = base; iova < max; iova += SZ_16K) {
                if (WARN_ON(ops->map(ops, iova, iova, SZ_16K, IOMMU_READ))) {
                        failed++;
                        continue;
                }
        }
 
-       if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
-                                                         max)))
+       if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+                                       base, max - base)))
                failed++;
 
        /* unmap it all */
-       for (iova = 0; iova < max; iova += SZ_16K) {
+       for (iova = base; iova < max; iova += SZ_16K) {
                if (WARN_ON(ops->unmap(ops, iova, SZ_16K) != SZ_16K))
                        failed++;
        }
 
        /* sweep up TLB proving PTEs */
-       av8l_fast_clear_stale_ptes(pmds, false);
+       av8l_fast_clear_stale_ptes(pmds, base, max, false);
 
        /* map the entire 4GB VA space with 64K map calls */
-       for (iova = 0; iova < max; iova += SZ_64K) {
+       for (iova = base; iova < max; iova += SZ_64K) {
                if (WARN_ON(ops->map(ops, iova, iova, SZ_64K, IOMMU_READ))) {
                        failed++;
                        continue;
                }
        }
 
-       if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
-                                                         max)))
+       if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+                                       base, max - base)))
                failed++;
 
        /* unmap it all at once */
-       if (WARN_ON(ops->unmap(ops, 0, max) != max))
+       if (WARN_ON(ops->unmap(ops, base, max - base) != (max - base)))
                failed++;
 
        free_io_pgtable_ops(ops);
index a3f366f..26ce663 100644 (file)
@@ -68,6 +68,8 @@ struct io_pgtable_cfg {
        unsigned int                    oas;
        const struct iommu_gather_ops   *tlb;
        struct device                   *iommu_dev;
+       dma_addr_t                      iova_base;
+       dma_addr_t                      iova_end;
 
        /* Low-level data specific to the table format */
        union {
index ab5a1dc..029e11f 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -17,7 +17,7 @@
 
 typedef u64 av8l_fast_iopte;
 
-#define iopte_pmd_offset(pmds, iova) (pmds + (iova >> 12))
+#define iopte_pmd_offset(pmds, base, iova) (pmds + ((iova - base) >> 12))
 
 int av8l_fast_map_public(av8l_fast_iopte *ptep, phys_addr_t paddr, size_t size,
                         int prot);
@@ -36,7 +36,8 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size);
  */
 #define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0xa
 
-void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, bool skip_sync);
+void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, u64 base,
+                               u64 end, bool skip_sync);
 void av8l_register_notify(struct notifier_block *nb);
 
 #else  /* !CONFIG_IOMMU_IO_PGTABLE_FAST_PROVE_TLB */
@@ -44,6 +45,8 @@ void av8l_register_notify(struct notifier_block *nb);
 #define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0
 
 static inline void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds,
+                                             u64 base,
+                                             u64 end,
                                              bool skip_sync)
 {
 }