Merge branch 'core' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu into...
authorVlastimil Babka <vbabka@suse.cz>
Thu, 6 Jan 2022 17:03:29 +0000 (18:03 +0100)
committerVlastimil Babka <vbabka@suse.cz>
Thu, 6 Jan 2022 17:03:29 +0000 (18:03 +0100)
Merge iommu tree for a series that removes usage of struct page
'freelist' field.

drivers/iommu/amd/io_pgtable.c
drivers/iommu/dma-iommu.c
drivers/iommu/intel/iommu.c
drivers/iommu/io-pgtable-arm.c
drivers/iommu/iommu.c
drivers/iommu/iova.c
drivers/vdpa/vdpa_user/iova_domain.c
include/linux/iommu.h
include/linux/iova.h
include/trace/events/iommu.h

index 182c93a..b1bf412 100644 (file)
@@ -74,87 +74,61 @@ static u64 *first_pte_l7(u64 *pte, unsigned long *page_size,
  *
  ****************************************************************************/
 
-static void free_page_list(struct page *freelist)
+static void free_pt_page(u64 *pt, struct list_head *freelist)
 {
-       while (freelist != NULL) {
-               unsigned long p = (unsigned long)page_address(freelist);
+       struct page *p = virt_to_page(pt);
 
-               freelist = freelist->freelist;
-               free_page(p);
-       }
+       list_add_tail(&p->lru, freelist);
 }
 
-static struct page *free_pt_page(unsigned long pt, struct page *freelist)
+static void free_pt_lvl(u64 *pt, struct list_head *freelist, int lvl)
 {
-       struct page *p = virt_to_page((void *)pt);
+       u64 *p;
+       int i;
 
-       p->freelist = freelist;
+       for (i = 0; i < 512; ++i) {
+               /* PTE present? */
+               if (!IOMMU_PTE_PRESENT(pt[i]))
+                       continue;
 
-       return p;
-}
+               /* Large PTE? */
+               if (PM_PTE_LEVEL(pt[i]) == 0 ||
+                   PM_PTE_LEVEL(pt[i]) == 7)
+                       continue;
 
-#define DEFINE_FREE_PT_FN(LVL, FN)                                             \
-static struct page *free_pt_##LVL (unsigned long __pt, struct page *freelist)  \
-{                                                                              \
-       unsigned long p;                                                        \
-       u64 *pt;                                                                \
-       int i;                                                                  \
-                                                                               \
-       pt = (u64 *)__pt;                                                       \
-                                                                               \
-       for (i = 0; i < 512; ++i) {                                             \
-               /* PTE present? */                                              \
-               if (!IOMMU_PTE_PRESENT(pt[i]))                                  \
-                       continue;                                               \
-                                                                               \
-               /* Large PTE? */                                                \
-               if (PM_PTE_LEVEL(pt[i]) == 0 ||                                 \
-                   PM_PTE_LEVEL(pt[i]) == 7)                                   \
-                       continue;                                               \
-                                                                               \
-               p = (unsigned long)IOMMU_PTE_PAGE(pt[i]);                       \
-               freelist = FN(p, freelist);                                     \
-       }                                                                       \
-                                                                               \
-       return free_pt_page((unsigned long)pt, freelist);                       \
-}
+               /*
+                * Free the next level. No need to look at l1 tables here since
+                * they can only contain leaf PTEs; just free them directly.
+                */
+               p = IOMMU_PTE_PAGE(pt[i]);
+               if (lvl > 2)
+                       free_pt_lvl(p, freelist, lvl - 1);
+               else
+                       free_pt_page(p, freelist);
+       }
 
-DEFINE_FREE_PT_FN(l2, free_pt_page)
-DEFINE_FREE_PT_FN(l3, free_pt_l2)
-DEFINE_FREE_PT_FN(l4, free_pt_l3)
-DEFINE_FREE_PT_FN(l5, free_pt_l4)
-DEFINE_FREE_PT_FN(l6, free_pt_l5)
+       free_pt_page(pt, freelist);
+}
 
-static struct page *free_sub_pt(unsigned long root, int mode,
-                               struct page *freelist)
+static void free_sub_pt(u64 *root, int mode, struct list_head *freelist)
 {
        switch (mode) {
        case PAGE_MODE_NONE:
        case PAGE_MODE_7_LEVEL:
                break;
        case PAGE_MODE_1_LEVEL:
-               freelist = free_pt_page(root, freelist);
+               free_pt_page(root, freelist);
                break;
        case PAGE_MODE_2_LEVEL:
-               freelist = free_pt_l2(root, freelist);
-               break;
        case PAGE_MODE_3_LEVEL:
-               freelist = free_pt_l3(root, freelist);
-               break;
        case PAGE_MODE_4_LEVEL:
-               freelist = free_pt_l4(root, freelist);
-               break;
        case PAGE_MODE_5_LEVEL:
-               freelist = free_pt_l5(root, freelist);
-               break;
        case PAGE_MODE_6_LEVEL:
-               freelist = free_pt_l6(root, freelist);
+               free_pt_lvl(root, freelist, mode);
                break;
        default:
                BUG();
        }
-
-       return freelist;
 }
 
 void amd_iommu_domain_set_pgtable(struct protection_domain *domain,
@@ -362,9 +336,9 @@ static u64 *fetch_pte(struct amd_io_pgtable *pgtable,
        return pte;
 }
 
-static struct page *free_clear_pte(u64 *pte, u64 pteval, struct page *freelist)
+static void free_clear_pte(u64 *pte, u64 pteval, struct list_head *freelist)
 {
-       unsigned long pt;
+       u64 *pt;
        int mode;
 
        while (cmpxchg64(pte, pteval, 0) != pteval) {
@@ -373,12 +347,12 @@ static struct page *free_clear_pte(u64 *pte, u64 pteval, struct page *freelist)
        }
 
        if (!IOMMU_PTE_PRESENT(pteval))
-               return freelist;
+               return;
 
-       pt   = (unsigned long)IOMMU_PTE_PAGE(pteval);
+       pt   = IOMMU_PTE_PAGE(pteval);
        mode = IOMMU_PTE_MODE(pteval);
 
-       return free_sub_pt(pt, mode, freelist);
+       free_sub_pt(pt, mode, freelist);
 }
 
 /*
@@ -392,7 +366,7 @@ static int iommu_v1_map_page(struct io_pgtable_ops *ops, unsigned long iova,
                          phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
        struct protection_domain *dom = io_pgtable_ops_to_domain(ops);
-       struct page *freelist = NULL;
+       LIST_HEAD(freelist);
        bool updated = false;
        u64 __pte, *pte;
        int ret, i, count;
@@ -412,9 +386,9 @@ static int iommu_v1_map_page(struct io_pgtable_ops *ops, unsigned long iova,
                goto out;
 
        for (i = 0; i < count; ++i)
-               freelist = free_clear_pte(&pte[i], pte[i], freelist);
+               free_clear_pte(&pte[i], pte[i], &freelist);
 
-       if (freelist != NULL)
+       if (!list_empty(&freelist))
                updated = true;
 
        if (count > 1) {
@@ -449,7 +423,7 @@ out:
        }
 
        /* Everything flushed out, free pages now */
-       free_page_list(freelist);
+       put_pages_list(&freelist);
 
        return ret;
 }
@@ -511,8 +485,7 @@ static void v1_free_pgtable(struct io_pgtable *iop)
 {
        struct amd_io_pgtable *pgtable = container_of(iop, struct amd_io_pgtable, iop);
        struct protection_domain *dom;
-       struct page *freelist = NULL;
-       unsigned long root;
+       LIST_HEAD(freelist);
 
        if (pgtable->mode == PAGE_MODE_NONE)
                return;
@@ -529,10 +502,9 @@ static void v1_free_pgtable(struct io_pgtable *iop)
        BUG_ON(pgtable->mode < PAGE_MODE_NONE ||
               pgtable->mode > PAGE_MODE_6_LEVEL);
 
-       root = (unsigned long)pgtable->root;
-       freelist = free_sub_pt(root, pgtable->mode, freelist);
+       free_sub_pt(pgtable->root, pgtable->mode, &freelist);
 
-       free_page_list(freelist);
+       put_pages_list(&freelist);
 }
 
 static struct io_pgtable *v1_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
index b42e38a..d85d54f 100644 (file)
@@ -9,9 +9,12 @@
  */
 
 #include <linux/acpi_iort.h>
+#include <linux/atomic.h>
+#include <linux/crash_dump.h>
 #include <linux/device.h>
-#include <linux/dma-map-ops.h>
+#include <linux/dma-direct.h>
 #include <linux/dma-iommu.h>
+#include <linux/dma-map-ops.h>
 #include <linux/gfp.h>
 #include <linux/huge_mm.h>
 #include <linux/iommu.h>
 #include <linux/mm.h>
 #include <linux/mutex.h>
 #include <linux/pci.h>
-#include <linux/swiotlb.h>
 #include <linux/scatterlist.h>
+#include <linux/spinlock.h>
+#include <linux/swiotlb.h>
 #include <linux/vmalloc.h>
-#include <linux/crash_dump.h>
-#include <linux/dma-direct.h>
 
 struct iommu_dma_msi_page {
        struct list_head        list;
@@ -41,7 +43,19 @@ struct iommu_dma_cookie {
        enum iommu_dma_cookie_type      type;
        union {
                /* Full allocator for IOMMU_DMA_IOVA_COOKIE */
-               struct iova_domain      iovad;
+               struct {
+                       struct iova_domain      iovad;
+
+                       struct iova_fq __percpu *fq;    /* Flush queue */
+                       /* Number of TLB flushes that have been started */
+                       atomic64_t              fq_flush_start_cnt;
+                       /* Number of TLB flushes that have been finished */
+                       atomic64_t              fq_flush_finish_cnt;
+                       /* Timer to regularily empty the flush queues */
+                       struct timer_list       fq_timer;
+                       /* 1 when timer is active, 0 when not */
+                       atomic_t                fq_timer_on;
+               };
                /* Trivial linear page allocator for IOMMU_DMA_MSI_COOKIE */
                dma_addr_t              msi_iova;
        };
@@ -64,16 +78,203 @@ static int __init iommu_dma_forcedac_setup(char *str)
 }
 early_param("iommu.forcedac", iommu_dma_forcedac_setup);
 
-static void iommu_dma_entry_dtor(unsigned long data)
+/* Number of entries per flush queue */
+#define IOVA_FQ_SIZE   256
+
+/* Timeout (in ms) after which entries are flushed from the queue */
+#define IOVA_FQ_TIMEOUT        10
+
+/* Flush queue entry for deferred flushing */
+struct iova_fq_entry {
+       unsigned long iova_pfn;
+       unsigned long pages;
+       struct list_head freelist;
+       u64 counter; /* Flush counter when this entry was added */
+};
+
+/* Per-CPU flush queue structure */
+struct iova_fq {
+       struct iova_fq_entry entries[IOVA_FQ_SIZE];
+       unsigned int head, tail;
+       spinlock_t lock;
+};
+
+#define fq_ring_for_each(i, fq) \
+       for ((i) = (fq)->head; (i) != (fq)->tail; (i) = ((i) + 1) % IOVA_FQ_SIZE)
+
+static inline bool fq_full(struct iova_fq *fq)
+{
+       assert_spin_locked(&fq->lock);
+       return (((fq->tail + 1) % IOVA_FQ_SIZE) == fq->head);
+}
+
+static inline unsigned int fq_ring_add(struct iova_fq *fq)
+{
+       unsigned int idx = fq->tail;
+
+       assert_spin_locked(&fq->lock);
+
+       fq->tail = (idx + 1) % IOVA_FQ_SIZE;
+
+       return idx;
+}
+
+static void fq_ring_free(struct iommu_dma_cookie *cookie, struct iova_fq *fq)
+{
+       u64 counter = atomic64_read(&cookie->fq_flush_finish_cnt);
+       unsigned int idx;
+
+       assert_spin_locked(&fq->lock);
+
+       fq_ring_for_each(idx, fq) {
+
+               if (fq->entries[idx].counter >= counter)
+                       break;
+
+               put_pages_list(&fq->entries[idx].freelist);
+               free_iova_fast(&cookie->iovad,
+                              fq->entries[idx].iova_pfn,
+                              fq->entries[idx].pages);
+
+               fq->head = (fq->head + 1) % IOVA_FQ_SIZE;
+       }
+}
+
+static void fq_flush_iotlb(struct iommu_dma_cookie *cookie)
 {
-       struct page *freelist = (struct page *)data;
+       atomic64_inc(&cookie->fq_flush_start_cnt);
+       cookie->fq_domain->ops->flush_iotlb_all(cookie->fq_domain);
+       atomic64_inc(&cookie->fq_flush_finish_cnt);
+}
+
+static void fq_flush_timeout(struct timer_list *t)
+{
+       struct iommu_dma_cookie *cookie = from_timer(cookie, t, fq_timer);
+       int cpu;
+
+       atomic_set(&cookie->fq_timer_on, 0);
+       fq_flush_iotlb(cookie);
+
+       for_each_possible_cpu(cpu) {
+               unsigned long flags;
+               struct iova_fq *fq;
+
+               fq = per_cpu_ptr(cookie->fq, cpu);
+               spin_lock_irqsave(&fq->lock, flags);
+               fq_ring_free(cookie, fq);
+               spin_unlock_irqrestore(&fq->lock, flags);
+       }
+}
+
+static void queue_iova(struct iommu_dma_cookie *cookie,
+               unsigned long pfn, unsigned long pages,
+               struct list_head *freelist)
+{
+       struct iova_fq *fq;
+       unsigned long flags;
+       unsigned int idx;
+
+       /*
+        * Order against the IOMMU driver's pagetable update from unmapping
+        * @pte, to guarantee that fq_flush_iotlb() observes that if called
+        * from a different CPU before we release the lock below. Full barrier
+        * so it also pairs with iommu_dma_init_fq() to avoid seeing partially
+        * written fq state here.
+        */
+       smp_mb();
 
-       while (freelist) {
-               unsigned long p = (unsigned long)page_address(freelist);
+       fq = raw_cpu_ptr(cookie->fq);
+       spin_lock_irqsave(&fq->lock, flags);
+
+       /*
+        * First remove all entries from the flush queue that have already been
+        * flushed out on another CPU. This makes the fq_full() check below less
+        * likely to be true.
+        */
+       fq_ring_free(cookie, fq);
 
-               freelist = freelist->freelist;
-               free_page(p);
+       if (fq_full(fq)) {
+               fq_flush_iotlb(cookie);
+               fq_ring_free(cookie, fq);
        }
+
+       idx = fq_ring_add(fq);
+
+       fq->entries[idx].iova_pfn = pfn;
+       fq->entries[idx].pages    = pages;
+       fq->entries[idx].counter  = atomic64_read(&cookie->fq_flush_start_cnt);
+       list_splice(freelist, &fq->entries[idx].freelist);
+
+       spin_unlock_irqrestore(&fq->lock, flags);
+
+       /* Avoid false sharing as much as possible. */
+       if (!atomic_read(&cookie->fq_timer_on) &&
+           !atomic_xchg(&cookie->fq_timer_on, 1))
+               mod_timer(&cookie->fq_timer,
+                         jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT));
+}
+
+static void iommu_dma_free_fq(struct iommu_dma_cookie *cookie)
+{
+       int cpu, idx;
+
+       if (!cookie->fq)
+               return;
+
+       del_timer_sync(&cookie->fq_timer);
+       /* The IOVAs will be torn down separately, so just free our queued pages */
+       for_each_possible_cpu(cpu) {
+               struct iova_fq *fq = per_cpu_ptr(cookie->fq, cpu);
+
+               fq_ring_for_each(idx, fq)
+                       put_pages_list(&fq->entries[idx].freelist);
+       }
+
+       free_percpu(cookie->fq);
+}
+
+/* sysfs updates are serialised by the mutex of the group owning @domain */
+int iommu_dma_init_fq(struct iommu_domain *domain)
+{
+       struct iommu_dma_cookie *cookie = domain->iova_cookie;
+       struct iova_fq __percpu *queue;
+       int i, cpu;
+
+       if (cookie->fq_domain)
+               return 0;
+
+       atomic64_set(&cookie->fq_flush_start_cnt,  0);
+       atomic64_set(&cookie->fq_flush_finish_cnt, 0);
+
+       queue = alloc_percpu(struct iova_fq);
+       if (!queue) {
+               pr_warn("iova flush queue initialization failed\n");
+               return -ENOMEM;
+       }
+
+       for_each_possible_cpu(cpu) {
+               struct iova_fq *fq = per_cpu_ptr(queue, cpu);
+
+               fq->head = 0;
+               fq->tail = 0;
+
+               spin_lock_init(&fq->lock);
+
+               for (i = 0; i < IOVA_FQ_SIZE; i++)
+                       INIT_LIST_HEAD(&fq->entries[i].freelist);
+       }
+
+       cookie->fq = queue;
+
+       timer_setup(&cookie->fq_timer, fq_flush_timeout, 0);
+       atomic_set(&cookie->fq_timer_on, 0);
+       /*
+        * Prevent incomplete fq state being observable. Pairs with path from
+        * __iommu_dma_unmap() through iommu_dma_free_iova() to queue_iova()
+        */
+       smp_wmb();
+       WRITE_ONCE(cookie->fq_domain, domain);
+       return 0;
 }
 
 static inline size_t cookie_msi_granule(struct iommu_dma_cookie *cookie)
@@ -156,8 +357,10 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
        if (!cookie)
                return;
 
-       if (cookie->type == IOMMU_DMA_IOVA_COOKIE && cookie->iovad.granule)
+       if (cookie->type == IOMMU_DMA_IOVA_COOKIE && cookie->iovad.granule) {
+               iommu_dma_free_fq(cookie);
                put_iova_domain(&cookie->iovad);
+       }
 
        list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
                list_del(&msi->list);
@@ -294,17 +497,6 @@ static int iova_reserve_iommu_regions(struct device *dev,
        return ret;
 }
 
-static void iommu_dma_flush_iotlb_all(struct iova_domain *iovad)
-{
-       struct iommu_dma_cookie *cookie;
-       struct iommu_domain *domain;
-
-       cookie = container_of(iovad, struct iommu_dma_cookie, iovad);
-       domain = cookie->fq_domain;
-
-       domain->ops->flush_iotlb_all(domain);
-}
-
 static bool dev_is_untrusted(struct device *dev)
 {
        return dev_is_pci(dev) && to_pci_dev(dev)->untrusted;
@@ -315,30 +507,6 @@ static bool dev_use_swiotlb(struct device *dev)
        return IS_ENABLED(CONFIG_SWIOTLB) && dev_is_untrusted(dev);
 }
 
-/* sysfs updates are serialised by the mutex of the group owning @domain */
-int iommu_dma_init_fq(struct iommu_domain *domain)
-{
-       struct iommu_dma_cookie *cookie = domain->iova_cookie;
-       int ret;
-
-       if (cookie->fq_domain)
-               return 0;
-
-       ret = init_iova_flush_queue(&cookie->iovad, iommu_dma_flush_iotlb_all,
-                                   iommu_dma_entry_dtor);
-       if (ret) {
-               pr_warn("iova flush queue initialization failed\n");
-               return ret;
-       }
-       /*
-        * Prevent incomplete iovad->fq being observable. Pairs with path from
-        * __iommu_dma_unmap() through iommu_dma_free_iova() to queue_iova()
-        */
-       smp_wmb();
-       WRITE_ONCE(cookie->fq_domain, domain);
-       return 0;
-}
-
 /**
  * iommu_dma_init_domain - Initialise a DMA mapping domain
  * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
@@ -442,14 +610,6 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain,
 
        shift = iova_shift(iovad);
        iova_len = size >> shift;
-       /*
-        * Freeing non-power-of-two-sized allocations back into the IOVA caches
-        * will come back to bite us badly, so we have to waste a bit of space
-        * rounding up anything cacheable to make sure that can't happen. The
-        * order of the unadjusted size will still match upon freeing.
-        */
-       if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
-               iova_len = roundup_pow_of_two(iova_len);
 
        dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit);
 
@@ -477,9 +637,9 @@ static void iommu_dma_free_iova(struct iommu_dma_cookie *cookie,
        if (cookie->type == IOMMU_DMA_MSI_COOKIE)
                cookie->msi_iova -= size;
        else if (gather && gather->queued)
-               queue_iova(iovad, iova_pfn(iovad, iova),
+               queue_iova(cookie, iova_pfn(iovad, iova),
                                size >> iova_shift(iovad),
-                               (unsigned long)gather->freelist);
+                               &gather->freelist);
        else
                free_iova_fast(iovad, iova_pfn(iovad, iova),
                                size >> iova_shift(iovad));
index b6a8f32..17b3d97 100644 (file)
@@ -1303,35 +1303,30 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
    know the hardware page-walk will no longer touch them.
    The 'pte' argument is the *parent* PTE, pointing to the page that is to
    be freed. */
-static struct page *dma_pte_list_pagetables(struct dmar_domain *domain,
-                                           int level, struct dma_pte *pte,
-                                           struct page *freelist)
+static void dma_pte_list_pagetables(struct dmar_domain *domain,
+                                   int level, struct dma_pte *pte,
+                                   struct list_head *freelist)
 {
        struct page *pg;
 
        pg = pfn_to_page(dma_pte_addr(pte) >> PAGE_SHIFT);
-       pg->freelist = freelist;
-       freelist = pg;
+       list_add_tail(&pg->lru, freelist);
 
        if (level == 1)
-               return freelist;
+               return;
 
        pte = page_address(pg);
        do {
                if (dma_pte_present(pte) && !dma_pte_superpage(pte))
-                       freelist = dma_pte_list_pagetables(domain, level - 1,
-                                                          pte, freelist);
+                       dma_pte_list_pagetables(domain, level - 1, pte, freelist);
                pte++;
        } while (!first_pte_in_page(pte));
-
-       return freelist;
 }
 
-static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
-                                       struct dma_pte *pte, unsigned long pfn,
-                                       unsigned long start_pfn,
-                                       unsigned long last_pfn,
-                                       struct page *freelist)
+static void dma_pte_clear_level(struct dmar_domain *domain, int level,
+                               struct dma_pte *pte, unsigned long pfn,
+                               unsigned long start_pfn, unsigned long last_pfn,
+                               struct list_head *freelist)
 {
        struct dma_pte *first_pte = NULL, *last_pte = NULL;
 
@@ -1350,7 +1345,7 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
                        /* These suborbinate page tables are going away entirely. Don't
                           bother to clear them; we're just going to *free* them. */
                        if (level > 1 && !dma_pte_superpage(pte))
-                               freelist = dma_pte_list_pagetables(domain, level - 1, pte, freelist);
+                               dma_pte_list_pagetables(domain, level - 1, pte, freelist);
 
                        dma_clear_pte(pte);
                        if (!first_pte)
@@ -1358,10 +1353,10 @@ static struct page *dma_pte_clear_level(struct dmar_domain *domain, int level,
                        last_pte = pte;
                } else if (level > 1) {
                        /* Recurse down into a level that isn't *entirely* obsolete */
-                       freelist = dma_pte_clear_level(domain, level - 1,
-                                                      phys_to_virt(dma_pte_addr(pte)),
-                                                      level_pfn, start_pfn, last_pfn,
-                                                      freelist);
+                       dma_pte_clear_level(domain, level - 1,
+                                           phys_to_virt(dma_pte_addr(pte)),
+                                           level_pfn, start_pfn, last_pfn,
+                                           freelist);
                }
 next:
                pfn = level_pfn + level_size(level);
@@ -1370,47 +1365,28 @@ next:
        if (first_pte)
                domain_flush_cache(domain, first_pte,
                                   (void *)++last_pte - (void *)first_pte);
-
-       return freelist;
 }
 
 /* We can't just free the pages because the IOMMU may still be walking
    the page tables, and may have cached the intermediate levels. The
    pages can only be freed after the IOTLB flush has been done. */
-static struct page *domain_unmap(struct dmar_domain *domain,
-                                unsigned long start_pfn,
-                                unsigned long last_pfn,
-                                struct page *freelist)
+static void domain_unmap(struct dmar_domain *domain, unsigned long start_pfn,
+                        unsigned long last_pfn, struct list_head *freelist)
 {
        BUG_ON(!domain_pfn_supported(domain, start_pfn));
        BUG_ON(!domain_pfn_supported(domain, last_pfn));
        BUG_ON(start_pfn > last_pfn);
 
        /* we don't need lock here; nobody else touches the iova range */
-       freelist = dma_pte_clear_level(domain, agaw_to_level(domain->agaw),
-                                      domain->pgd, 0, start_pfn, last_pfn,
-                                      freelist);
+       dma_pte_clear_level(domain, agaw_to_level(domain->agaw),
+                           domain->pgd, 0, start_pfn, last_pfn, freelist);
 
        /* free pgd */
        if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
                struct page *pgd_page = virt_to_page(domain->pgd);
-               pgd_page->freelist = freelist;
-               freelist = pgd_page;
-
+               list_add_tail(&pgd_page->lru, freelist);
                domain->pgd = NULL;
        }
-
-       return freelist;
-}
-
-static void dma_free_pagelist(struct page *freelist)
-{
-       struct page *pg;
-
-       while ((pg = freelist)) {
-               freelist = pg->freelist;
-               free_pgtable_page(page_address(pg));
-       }
 }
 
 /* iommu handling */
@@ -2095,11 +2071,10 @@ static void domain_exit(struct dmar_domain *domain)
        domain_remove_dev_info(domain);
 
        if (domain->pgd) {
-               struct page *freelist;
+               LIST_HEAD(freelist);
 
-               freelist = domain_unmap(domain, 0,
-                                       DOMAIN_MAX_PFN(domain->gaw), NULL);
-               dma_free_pagelist(freelist);
+               domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw), &freelist);
+               put_pages_list(&freelist);
        }
 
        free_domain_mem(domain);
@@ -4192,19 +4167,17 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb,
                {
                        struct dmar_drhd_unit *drhd;
                        struct intel_iommu *iommu;
-                       struct page *freelist;
+                       LIST_HEAD(freelist);
 
-                       freelist = domain_unmap(si_domain,
-                                               start_vpfn, last_vpfn,
-                                               NULL);
+                       domain_unmap(si_domain, start_vpfn, last_vpfn, &freelist);
 
                        rcu_read_lock();
                        for_each_active_iommu(iommu, drhd)
                                iommu_flush_iotlb_psi(iommu, si_domain,
                                        start_vpfn, mhp->nr_pages,
-                                       !freelist, 0);
+                                       list_empty(&freelist), 0);
                        rcu_read_unlock();
-                       dma_free_pagelist(freelist);
+                       put_pages_list(&freelist);
                }
                break;
        }
@@ -5211,8 +5184,7 @@ static size_t intel_iommu_unmap(struct iommu_domain *domain,
        start_pfn = iova >> VTD_PAGE_SHIFT;
        last_pfn = (iova + size - 1) >> VTD_PAGE_SHIFT;
 
-       gather->freelist = domain_unmap(dmar_domain, start_pfn,
-                                       last_pfn, gather->freelist);
+       domain_unmap(dmar_domain, start_pfn, last_pfn, &gather->freelist);
 
        if (dmar_domain->max_addr == iova + size)
                dmar_domain->max_addr = iova;
@@ -5248,9 +5220,10 @@ static void intel_iommu_tlb_sync(struct iommu_domain *domain,
 
        for_each_domain_iommu(iommu_id, dmar_domain)
                iommu_flush_iotlb_psi(g_iommus[iommu_id], dmar_domain,
-                                     start_pfn, nrpages, !gather->freelist, 0);
+                                     start_pfn, nrpages,
+                                     list_empty(&gather->freelist), 0);
 
-       dma_free_pagelist(gather->freelist);
+       put_pages_list(&gather->freelist);
 }
 
 static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
index dd9e471..94ff319 100644 (file)
@@ -315,11 +315,12 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
 static arm_lpae_iopte arm_lpae_install_table(arm_lpae_iopte *table,
                                             arm_lpae_iopte *ptep,
                                             arm_lpae_iopte curr,
-                                            struct io_pgtable_cfg *cfg)
+                                            struct arm_lpae_io_pgtable *data)
 {
        arm_lpae_iopte old, new;
+       struct io_pgtable_cfg *cfg = &data->iop.cfg;
 
-       new = __pa(table) | ARM_LPAE_PTE_TYPE_TABLE;
+       new = paddr_to_iopte(__pa(table), data) | ARM_LPAE_PTE_TYPE_TABLE;
        if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
                new |= ARM_LPAE_PTE_NSTABLE;
 
@@ -380,7 +381,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
                if (!cptep)
                        return -ENOMEM;
 
-               pte = arm_lpae_install_table(cptep, ptep, 0, cfg);
+               pte = arm_lpae_install_table(cptep, ptep, 0, data);
                if (pte)
                        __arm_lpae_free_pages(cptep, tblsz, cfg);
        } else if (!cfg->coherent_walk && !(pte & ARM_LPAE_PTE_SW_SYNC)) {
@@ -592,7 +593,7 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
                __arm_lpae_init_pte(data, blk_paddr, pte, lvl, 1, &tablep[i]);
        }
 
-       pte = arm_lpae_install_table(tablep, ptep, blk_pte, cfg);
+       pte = arm_lpae_install_table(tablep, ptep, blk_pte, data);
        if (pte != blk_pte) {
                __arm_lpae_free_pages(tablep, tablesz, cfg);
                /*
index dd7863e..8b86406 100644 (file)
@@ -288,11 +288,11 @@ int iommu_probe_device(struct device *dev)
         */
        mutex_lock(&group->mutex);
        iommu_alloc_default_domain(group, dev);
-       mutex_unlock(&group->mutex);
 
        if (group->default_domain) {
                ret = __iommu_attach_device(group->default_domain, dev);
                if (ret) {
+                       mutex_unlock(&group->mutex);
                        iommu_group_put(group);
                        goto err_release;
                }
@@ -300,6 +300,7 @@ int iommu_probe_device(struct device *dev)
 
        iommu_create_device_direct_mappings(group, dev);
 
+       mutex_unlock(&group->mutex);
        iommu_group_put(group);
 
        if (ops->probe_finalize)
index 9e8bc80..b28c943 100644 (file)
@@ -24,8 +24,6 @@ static unsigned long iova_rcache_get(struct iova_domain *iovad,
 static void init_iova_rcaches(struct iova_domain *iovad);
 static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad);
 static void free_iova_rcaches(struct iova_domain *iovad);
-static void fq_destroy_all_entries(struct iova_domain *iovad);
-static void fq_flush_timeout(struct timer_list *t);
 
 static int iova_cpuhp_dead(unsigned int cpu, struct hlist_node *node)
 {
@@ -63,8 +61,6 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule,
        iovad->start_pfn = start_pfn;
        iovad->dma_32bit_pfn = 1UL << (32 - iova_shift(iovad));
        iovad->max32_alloc_size = iovad->dma_32bit_pfn;
-       iovad->flush_cb = NULL;
-       iovad->fq = NULL;
        iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR;
        rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node);
        rb_insert_color(&iovad->anchor.node, &iovad->rbroot);
@@ -73,62 +69,6 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule,
 }
 EXPORT_SYMBOL_GPL(init_iova_domain);
 
-static bool has_iova_flush_queue(struct iova_domain *iovad)
-{
-       return !!iovad->fq;
-}
-
-static void free_iova_flush_queue(struct iova_domain *iovad)
-{
-       if (!has_iova_flush_queue(iovad))
-               return;
-
-       if (timer_pending(&iovad->fq_timer))
-               del_timer(&iovad->fq_timer);
-
-       fq_destroy_all_entries(iovad);
-
-       free_percpu(iovad->fq);
-
-       iovad->fq         = NULL;
-       iovad->flush_cb   = NULL;
-       iovad->entry_dtor = NULL;
-}
-
-int init_iova_flush_queue(struct iova_domain *iovad,
-                         iova_flush_cb flush_cb, iova_entry_dtor entry_dtor)
-{
-       struct iova_fq __percpu *queue;
-       int cpu;
-
-       atomic64_set(&iovad->fq_flush_start_cnt,  0);
-       atomic64_set(&iovad->fq_flush_finish_cnt, 0);
-
-       queue = alloc_percpu(struct iova_fq);
-       if (!queue)
-               return -ENOMEM;
-
-       iovad->flush_cb   = flush_cb;
-       iovad->entry_dtor = entry_dtor;
-
-       for_each_possible_cpu(cpu) {
-               struct iova_fq *fq;
-
-               fq = per_cpu_ptr(queue, cpu);
-               fq->head = 0;
-               fq->tail = 0;
-
-               spin_lock_init(&fq->lock);
-       }
-
-       iovad->fq = queue;
-
-       timer_setup(&iovad->fq_timer, fq_flush_timeout, 0);
-       atomic_set(&iovad->fq_timer_on, 0);
-
-       return 0;
-}
-
 static struct rb_node *
 __get_cached_rbnode(struct iova_domain *iovad, unsigned long limit_pfn)
 {
@@ -497,6 +437,15 @@ alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
        unsigned long iova_pfn;
        struct iova *new_iova;
 
+       /*
+        * Freeing non-power-of-two-sized allocations back into the IOVA caches
+        * will come back to bite us badly, so we have to waste a bit of space
+        * rounding up anything cacheable to make sure that can't happen. The
+        * order of the unadjusted size will still match upon freeing.
+        */
+       if (size < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
+               size = roundup_pow_of_two(size);
+
        iova_pfn = iova_rcache_get(iovad, size, limit_pfn + 1);
        if (iova_pfn)
                return iova_pfn;
@@ -539,144 +488,6 @@ free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size)
 }
 EXPORT_SYMBOL_GPL(free_iova_fast);
 
-#define fq_ring_for_each(i, fq) \
-       for ((i) = (fq)->head; (i) != (fq)->tail; (i) = ((i) + 1) % IOVA_FQ_SIZE)
-
-static inline bool fq_full(struct iova_fq *fq)
-{
-       assert_spin_locked(&fq->lock);
-       return (((fq->tail + 1) % IOVA_FQ_SIZE) == fq->head);
-}
-
-static inline unsigned fq_ring_add(struct iova_fq *fq)
-{
-       unsigned idx = fq->tail;
-
-       assert_spin_locked(&fq->lock);
-
-       fq->tail = (idx + 1) % IOVA_FQ_SIZE;
-
-       return idx;
-}
-
-static void fq_ring_free(struct iova_domain *iovad, struct iova_fq *fq)
-{
-       u64 counter = atomic64_read(&iovad->fq_flush_finish_cnt);
-       unsigned idx;
-
-       assert_spin_locked(&fq->lock);
-
-       fq_ring_for_each(idx, fq) {
-
-               if (fq->entries[idx].counter >= counter)
-                       break;
-
-               if (iovad->entry_dtor)
-                       iovad->entry_dtor(fq->entries[idx].data);
-
-               free_iova_fast(iovad,
-                              fq->entries[idx].iova_pfn,
-                              fq->entries[idx].pages);
-
-               fq->head = (fq->head + 1) % IOVA_FQ_SIZE;
-       }
-}
-
-static void iova_domain_flush(struct iova_domain *iovad)
-{
-       atomic64_inc(&iovad->fq_flush_start_cnt);
-       iovad->flush_cb(iovad);
-       atomic64_inc(&iovad->fq_flush_finish_cnt);
-}
-
-static void fq_destroy_all_entries(struct iova_domain *iovad)
-{
-       int cpu;
-
-       /*
-        * This code runs when the iova_domain is being detroyed, so don't
-        * bother to free iovas, just call the entry_dtor on all remaining
-        * entries.
-        */
-       if (!iovad->entry_dtor)
-               return;
-
-       for_each_possible_cpu(cpu) {
-               struct iova_fq *fq = per_cpu_ptr(iovad->fq, cpu);
-               int idx;
-
-               fq_ring_for_each(idx, fq)
-                       iovad->entry_dtor(fq->entries[idx].data);
-       }
-}
-
-static void fq_flush_timeout(struct timer_list *t)
-{
-       struct iova_domain *iovad = from_timer(iovad, t, fq_timer);
-       int cpu;
-
-       atomic_set(&iovad->fq_timer_on, 0);
-       iova_domain_flush(iovad);
-
-       for_each_possible_cpu(cpu) {
-               unsigned long flags;
-               struct iova_fq *fq;
-
-               fq = per_cpu_ptr(iovad->fq, cpu);
-               spin_lock_irqsave(&fq->lock, flags);
-               fq_ring_free(iovad, fq);
-               spin_unlock_irqrestore(&fq->lock, flags);
-       }
-}
-
-void queue_iova(struct iova_domain *iovad,
-               unsigned long pfn, unsigned long pages,
-               unsigned long data)
-{
-       struct iova_fq *fq;
-       unsigned long flags;
-       unsigned idx;
-
-       /*
-        * Order against the IOMMU driver's pagetable update from unmapping
-        * @pte, to guarantee that iova_domain_flush() observes that if called
-        * from a different CPU before we release the lock below. Full barrier
-        * so it also pairs with iommu_dma_init_fq() to avoid seeing partially
-        * written fq state here.
-        */
-       smp_mb();
-
-       fq = raw_cpu_ptr(iovad->fq);
-       spin_lock_irqsave(&fq->lock, flags);
-
-       /*
-        * First remove all entries from the flush queue that have already been
-        * flushed out on another CPU. This makes the fq_full() check below less
-        * likely to be true.
-        */
-       fq_ring_free(iovad, fq);
-
-       if (fq_full(fq)) {
-               iova_domain_flush(iovad);
-               fq_ring_free(iovad, fq);
-       }
-
-       idx = fq_ring_add(fq);
-
-       fq->entries[idx].iova_pfn = pfn;
-       fq->entries[idx].pages    = pages;
-       fq->entries[idx].data     = data;
-       fq->entries[idx].counter  = atomic64_read(&iovad->fq_flush_start_cnt);
-
-       spin_unlock_irqrestore(&fq->lock, flags);
-
-       /* Avoid false sharing as much as possible. */
-       if (!atomic_read(&iovad->fq_timer_on) &&
-           !atomic_xchg(&iovad->fq_timer_on, 1))
-               mod_timer(&iovad->fq_timer,
-                         jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT));
-}
-
 /**
  * put_iova_domain - destroys the iova domain
  * @iovad: - iova domain in question.
@@ -688,8 +499,6 @@ void put_iova_domain(struct iova_domain *iovad)
 
        cpuhp_state_remove_instance_nocalls(CPUHP_IOMMU_IOVA_DEAD,
                                            &iovad->cpuhp_dead);
-
-       free_iova_flush_queue(iovad);
        free_iova_rcaches(iovad);
        rbtree_postorder_for_each_entry_safe(iova, tmp, &iovad->rbroot, node)
                free_iova_mem(iova);
index 1daae26..2b1143f 100644 (file)
@@ -292,14 +292,6 @@ vduse_domain_alloc_iova(struct iova_domain *iovad,
        unsigned long iova_len = iova_align(iovad, size) >> shift;
        unsigned long iova_pfn;
 
-       /*
-        * Freeing non-power-of-two-sized allocations back into the IOVA caches
-        * will come back to bite us badly, so we have to waste a bit of space
-        * rounding up anything cacheable to make sure that can't happen. The
-        * order of the unadjusted size will still match upon freeing.
-        */
-       if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
-               iova_len = roundup_pow_of_two(iova_len);
        iova_pfn = alloc_iova_fast(iovad, iova_len, limit >> shift, true);
 
        return iova_pfn << shift;
index d2f3435..de0c57a 100644 (file)
@@ -186,7 +186,7 @@ struct iommu_iotlb_gather {
        unsigned long           start;
        unsigned long           end;
        size_t                  pgsize;
-       struct page             *freelist;
+       struct list_head        freelist;
        bool                    queued;
 };
 
@@ -399,6 +399,7 @@ static inline void iommu_iotlb_gather_init(struct iommu_iotlb_gather *gather)
 {
        *gather = (struct iommu_iotlb_gather) {
                .start  = ULONG_MAX,
+               .freelist = LIST_HEAD_INIT(gather->freelist),
        };
 }
 
index 71d8a2d..cea79cb 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/rbtree.h>
-#include <linux/atomic.h>
 #include <linux/dma-mapping.h>
 
 /* iova structure */
@@ -35,35 +34,6 @@ struct iova_rcache {
        struct iova_cpu_rcache __percpu *cpu_rcaches;
 };
 
-struct iova_domain;
-
-/* Call-Back from IOVA code into IOMMU drivers */
-typedef void (* iova_flush_cb)(struct iova_domain *domain);
-
-/* Destructor for per-entry data */
-typedef void (* iova_entry_dtor)(unsigned long data);
-
-/* Number of entries per Flush Queue */
-#define IOVA_FQ_SIZE   256
-
-/* Timeout (in ms) after which entries are flushed from the Flush-Queue */
-#define IOVA_FQ_TIMEOUT        10
-
-/* Flush Queue entry for defered flushing */
-struct iova_fq_entry {
-       unsigned long iova_pfn;
-       unsigned long pages;
-       unsigned long data;
-       u64 counter; /* Flush counter when this entrie was added */
-};
-
-/* Per-CPU Flush Queue structure */
-struct iova_fq {
-       struct iova_fq_entry entries[IOVA_FQ_SIZE];
-       unsigned head, tail;
-       spinlock_t lock;
-};
-
 /* holds all the iova translations for a domain */
 struct iova_domain {
        spinlock_t      iova_rbtree_lock; /* Lock to protect update of rbtree */
@@ -74,27 +44,9 @@ struct iova_domain {
        unsigned long   start_pfn;      /* Lower limit for this domain */
        unsigned long   dma_32bit_pfn;
        unsigned long   max32_alloc_size; /* Size of last failed allocation */
-       struct iova_fq __percpu *fq;    /* Flush Queue */
-
-       atomic64_t      fq_flush_start_cnt;     /* Number of TLB flushes that
-                                                  have been started */
-
-       atomic64_t      fq_flush_finish_cnt;    /* Number of TLB flushes that
-                                                  have been finished */
-
        struct iova     anchor;         /* rbtree lookup anchor */
-       struct iova_rcache rcaches[IOVA_RANGE_CACHE_MAX_SIZE];  /* IOVA range caches */
-
-       iova_flush_cb   flush_cb;       /* Call-Back function to flush IOMMU
-                                          TLBs */
 
-       iova_entry_dtor entry_dtor;     /* IOMMU driver specific destructor for
-                                          iova entry */
-
-       struct timer_list fq_timer;             /* Timer to regularily empty the
-                                                  flush-queues */
-       atomic_t fq_timer_on;                   /* 1 when timer is active, 0
-                                                  when not */
+       struct iova_rcache rcaches[IOVA_RANGE_CACHE_MAX_SIZE];  /* IOVA range caches */
        struct hlist_node       cpuhp_dead;
 };
 
@@ -144,17 +96,12 @@ struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size,
        bool size_aligned);
 void free_iova_fast(struct iova_domain *iovad, unsigned long pfn,
                    unsigned long size);
-void queue_iova(struct iova_domain *iovad,
-               unsigned long pfn, unsigned long pages,
-               unsigned long data);
 unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
                              unsigned long limit_pfn, bool flush_rcache);
 struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo,
        unsigned long pfn_hi);
 void init_iova_domain(struct iova_domain *iovad, unsigned long granule,
        unsigned long start_pfn);
-int init_iova_flush_queue(struct iova_domain *iovad,
-                         iova_flush_cb flush_cb, iova_entry_dtor entry_dtor);
 struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn);
 void put_iova_domain(struct iova_domain *iovad);
 #else
@@ -189,12 +136,6 @@ static inline void free_iova_fast(struct iova_domain *iovad,
 {
 }
 
-static inline void queue_iova(struct iova_domain *iovad,
-                             unsigned long pfn, unsigned long pages,
-                             unsigned long data)
-{
-}
-
 static inline unsigned long alloc_iova_fast(struct iova_domain *iovad,
                                            unsigned long size,
                                            unsigned long limit_pfn,
@@ -216,13 +157,6 @@ static inline void init_iova_domain(struct iova_domain *iovad,
 {
 }
 
-static inline int init_iova_flush_queue(struct iova_domain *iovad,
-                                       iova_flush_cb flush_cb,
-                                       iova_entry_dtor entry_dtor)
-{
-       return -ENODEV;
-}
-
 static inline struct iova *find_iova(struct iova_domain *iovad,
                                     unsigned long pfn)
 {
index 72b4582..29096fe 100644 (file)
@@ -101,8 +101,9 @@ TRACE_EVENT(map,
                __entry->size = size;
        ),
 
-       TP_printk("IOMMU: iova=0x%016llx paddr=0x%016llx size=%zu",
-                       __entry->iova, __entry->paddr, __entry->size
+       TP_printk("IOMMU: iova=0x%016llx - 0x%016llx paddr=0x%016llx size=%zu",
+                 __entry->iova, __entry->iova + __entry->size, __entry->paddr,
+                 __entry->size
        )
 );
 
@@ -124,8 +125,9 @@ TRACE_EVENT(unmap,
                __entry->unmapped_size = unmapped_size;
        ),
 
-       TP_printk("IOMMU: iova=0x%016llx size=%zu unmapped_size=%zu",
-                       __entry->iova, __entry->size, __entry->unmapped_size
+       TP_printk("IOMMU: iova=0x%016llx - 0x%016llx size=%zu unmapped_size=%zu",
+                 __entry->iova, __entry->iova + __entry->size,
+                 __entry->size, __entry->unmapped_size
        )
 );