[RFC PATCH v5 7/8] mm: tlb: Pass struct mmu_gather to flush_tlb_range

From: Zhenyu Ye
Date: Tue Mar 31 2020 - 10:30:33 EST


A few new fields were added to mmu_gather to make TLB flush smarter for
huge page by telling what level of page table is changed. However, we
can not get these infomations in flush_tlb_range() now.

This patch passes struct mmu_gather to flush_tlb_range interface, and
arm64 and power9 can all benefit from this.

Signed-off-by: Zhenyu Ye <yezhenyu2@xxxxxxxxxx>
---
Documentation/core-api/cachetlb.rst | 8 +++++--
arch/alpha/include/asm/tlbflush.h | 8 +++----
arch/alpha/kernel/smp.c | 3 ++-
arch/arc/include/asm/tlbflush.h | 6 ++---
arch/arc/mm/tlb.c | 4 ++--
arch/arm/include/asm/tlbflush.h | 12 ++++++----
arch/arm/kernel/smp_tlb.c | 4 ++--
arch/arm/mach-rpc/ecard.c | 8 +++++--
arch/arm64/crypto/aes-glue.c | 1 -
arch/arm64/include/asm/tlbflush.h | 5 ++--
arch/arm64/mm/hugetlbpage.c | 10 ++++++--
arch/csky/include/asm/tlb.h | 2 +-
arch/csky/include/asm/tlbflush.h | 6 ++---
arch/csky/mm/tlb.c | 4 ++--
arch/hexagon/include/asm/tlbflush.h | 2 +-
arch/hexagon/mm/vm_tlb.c | 4 ++--
arch/ia64/include/asm/tlbflush.h | 6 +++--
arch/ia64/mm/tlb.c | 5 +++-
arch/m68k/include/asm/tlbflush.h | 10 ++++----
arch/microblaze/include/asm/tlbflush.h | 5 ++--
arch/mips/include/asm/hugetlb.h | 6 ++++-
arch/mips/include/asm/tlbflush.h | 9 ++++----
arch/mips/kernel/smp.c | 3 ++-
arch/nds32/include/asm/tlbflush.h | 3 ++-
arch/nios2/include/asm/tlbflush.h | 9 ++++----
arch/nios2/mm/tlb.c | 8 +++++--
arch/openrisc/include/asm/tlbflush.h | 10 ++++----
arch/openrisc/kernel/smp.c | 2 +-
arch/parisc/include/asm/tlbflush.h | 2 +-
arch/parisc/kernel/cache.c | 13 ++++++++---
arch/powerpc/include/asm/book3s/32/tlbflush.h | 4 ++--
arch/powerpc/include/asm/book3s/64/tlbflush.h | 3 ++-
arch/powerpc/include/asm/nohash/tlbflush.h | 7 +++---
arch/powerpc/mm/book3s32/tlb.c | 6 ++---
arch/powerpc/mm/book3s64/radix_tlb.c | 2 +-
arch/powerpc/mm/nohash/tlb.c | 6 ++---
arch/riscv/include/asm/tlbflush.h | 7 +++---
arch/riscv/mm/tlbflush.c | 4 ++--
arch/s390/include/asm/tlbflush.h | 5 ++--
arch/sh/include/asm/tlbflush.h | 8 +++----
arch/sh/kernel/smp.c | 2 +-
arch/sparc/include/asm/tlbflush_32.h | 2 +-
arch/sparc/include/asm/tlbflush_64.h | 3 ++-
arch/sparc/mm/tlb.c | 5 +++-
arch/um/include/asm/tlbflush.h | 6 ++---
arch/um/kernel/tlb.c | 4 ++--
arch/unicore32/include/asm/tlbflush.h | 5 ++--
arch/x86/include/asm/tlbflush.h | 4 ++--
arch/x86/mm/pgtable.c | 10 ++++++--
arch/xtensa/include/asm/tlbflush.h | 10 ++++----
arch/xtensa/kernel/smp.c | 2 +-
include/asm-generic/pgtable.h | 6 +++--
include/asm-generic/tlb.h | 2 +-
mm/huge_memory.c | 19 ++++++++++++---
mm/hugetlb.c | 2 +-
mm/mapping_dirty_helpers.c | 23 +++++++++++++------
mm/migrate.c | 8 +++++--
mm/mprotect.c | 8 ++++---
mm/mremap.c | 17 +++++++++++---
mm/pgtable-generic.c | 8 ++++++-
mm/rmap.c | 6 ++++-
61 files changed, 247 insertions(+), 135 deletions(-)

diff --git a/Documentation/core-api/cachetlb.rst b/Documentation/core-api/cachetlb.rst
index 93cb65d52720..05f9522dca17 100644
--- a/Documentation/core-api/cachetlb.rst
+++ b/Documentation/core-api/cachetlb.rst
@@ -50,7 +50,7 @@ changes occur:
page table operations such as what happens during
fork, and exec.

-3) ``void flush_tlb_range(struct vm_area_struct *vma,
+3) ``void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
unsigned long start, unsigned long end)``

Here we are flushing a specific range of (user) virtual
@@ -61,6 +61,10 @@ changes occur:
running, there will be no entries in the TLB for 'mm' for
virtual addresses in the range 'start' to 'end-1'.

+ The "tlb" is an opaque type used for passing around any data
+ needed by arch specific code for flush_tlb_range. For example,
+ it can pass the level information of TLBI instructions.
+
The "vma" is the backing store being used for the region.
Primarily, this is used for munmap() type operations.

@@ -111,7 +115,7 @@ the sequence will be in one of the following forms::

2) flush_cache_range(vma, start, end);
change_range_of_page_tables(mm, start, end);
- flush_tlb_range(vma, start, end);
+ flush_tlb_range(tlb, vma, start, end);

3) flush_cache_page(vma, addr, pfn);
set_pte(pte_pointer, new_pte_val);
diff --git a/arch/alpha/include/asm/tlbflush.h b/arch/alpha/include/asm/tlbflush.h
index f8b492408f51..7512d817acee 100644
--- a/arch/alpha/include/asm/tlbflush.h
+++ b/arch/alpha/include/asm/tlbflush.h
@@ -128,8 +128,8 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
/* Flush a specified range of user mapping. On the Alpha we flush
the whole user tlb. */
static inline void
-flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
+flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
flush_tlb_mm(vma->vm_mm);
}
@@ -139,8 +139,8 @@ flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *);
extern void flush_tlb_page(struct vm_area_struct *, unsigned long);
-extern void flush_tlb_range(struct vm_area_struct *, unsigned long,
- unsigned long);
+extern void flush_tlb_range(struct mmu_gather *, struct vm_area_struct *,
+ unsigned long, unsigned long);

#endif /* CONFIG_SMP */

diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
index 5f90df30be20..e57c5f5007e0 100644
--- a/arch/alpha/kernel/smp.c
+++ b/arch/alpha/kernel/smp.c
@@ -722,7 +722,8 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
EXPORT_SYMBOL(flush_tlb_page);

void
-flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
/* On the Alpha we always flush the whole user tlb. */
flush_tlb_mm(vma->vm_mm);
diff --git a/arch/arc/include/asm/tlbflush.h b/arch/arc/include/asm/tlbflush.h
index 49e4e5b59bb2..92f336840baf 100644
--- a/arch/arc/include/asm/tlbflush.h
+++ b/arch/arc/include/asm/tlbflush.h
@@ -20,7 +20,7 @@ void local_flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
#endif

#ifndef CONFIG_SMP
-#define flush_tlb_range(vma, s, e) local_flush_tlb_range(vma, s, e)
+#define flush_tlb_range(tlb, vma, s, e) local_flush_tlb_range(vma, s, e)
#define flush_tlb_page(vma, page) local_flush_tlb_page(vma, page)
#define flush_tlb_kernel_range(s, e) local_flush_tlb_kernel_range(s, e)
#define flush_tlb_all() local_flush_tlb_all()
@@ -29,8 +29,8 @@ void local_flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
#define flush_pmd_tlb_range(tlb, vma, s, e) local_flush_pmd_tlb_range(vma, s, e)
#endif
#else
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
extern void flush_tlb_all(void);
diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c
index 10b2a2373dc0..2f85c5a19a40 100644
--- a/arch/arc/mm/tlb.c
+++ b/arch/arc/mm/tlb.c
@@ -451,8 +451,8 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_page, &ta, 1);
}

-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
struct tlb_args ta = {
.ta_vma = vma,
diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h
index 24cbfc112dfa..cf52a76f97c1 100644
--- a/arch/arm/include/asm/tlbflush.h
+++ b/arch/arm/include/asm/tlbflush.h
@@ -253,10 +253,11 @@ extern struct cpu_tlb_fns cpu_tlb;
* space.
* - mm - mm_struct describing address space
*
- * flush_tlb_range(mm,start,end)
+ * flush_tlb_range(tlb, mm, start, end)
*
* Invalidate a range of TLB entries in the specified
* address space.
+ * - tlb - mmu_gather contains any data needed by tlbi interface
* - mm - mm_struct describing address space
* - start - start address (may not be aligned)
* - end - end address (exclusive, may not be aligned)
@@ -609,7 +610,8 @@ static inline void clean_pmd_entry(void *pmd)
#define flush_tlb_mm local_flush_tlb_mm
#define flush_tlb_page local_flush_tlb_page
#define flush_tlb_kernel_page local_flush_tlb_kernel_page
-#define flush_tlb_range local_flush_tlb_range
+#define flush_tlb_range(tlb, vma, start, end) \
+ local_flush_tlb_range(vma, start, end)
#define flush_tlb_kernel_range local_flush_tlb_kernel_range
#define flush_bp_all local_flush_bp_all
#else
@@ -617,7 +619,8 @@ extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *mm);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr);
extern void flush_tlb_kernel_page(unsigned long kaddr);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
extern void flush_bp_all(void);
#endif
@@ -657,7 +660,8 @@ extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *mm);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr);
extern void flush_tlb_kernel_page(unsigned long kaddr);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
extern void flush_bp_all(void);
#endif /* __ASSEMBLY__ */
diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
index d4908b3736d8..7a0437dd3b64 100644
--- a/arch/arm/kernel/smp_tlb.c
+++ b/arch/arm/kernel/smp_tlb.c
@@ -217,8 +217,8 @@ void flush_tlb_kernel_page(unsigned long kaddr)
broadcast_tlb_a15_erratum();
}

-void flush_tlb_range(struct vm_area_struct *vma,
- unsigned long start, unsigned long end)
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
if (tlb_ops_need_broadcast()) {
struct tlb_args ta;
diff --git a/arch/arm/mach-rpc/ecard.c b/arch/arm/mach-rpc/ecard.c
index 75cfad2cb143..4bad2bdb1755 100644
--- a/arch/arm/mach-rpc/ecard.c
+++ b/arch/arm/mach-rpc/ecard.c
@@ -49,6 +49,7 @@
#include <asm/irq.h>
#include <asm/mmu_context.h>
#include <asm/mach/irq.h>
+#include <asm/tlb.h>
#include <asm/tlbflush.h>

#include "ecard.h"
@@ -214,6 +215,7 @@ static DEFINE_MUTEX(ecard_mutex);
static void ecard_init_pgtables(struct mm_struct *mm)
{
struct vm_area_struct vma = TLB_FLUSH_VMA(mm, VM_EXEC);
+ struct mmu_gather tlb;

/* We want to set up the page tables for the following mapping:
* Virtual Physical
@@ -238,8 +240,10 @@ static void ecard_init_pgtables(struct mm_struct *mm)

memcpy(dst_pgd, src_pgd, sizeof(pgd_t) * (EASI_SIZE / PGDIR_SIZE));

- flush_tlb_range(&vma, IO_START, IO_START + IO_SIZE);
- flush_tlb_range(&vma, EASI_START, EASI_START + EASI_SIZE);
+ tlb_gather_mmu(&tlb, mm, 0, -1);
+ flush_tlb_range(&tlb, &vma, IO_START, IO_START + IO_SIZE);
+ flush_tlb_range(&tlb, &vma, EASI_START, EASI_START + EASI_SIZE);
+ tlb_finish_mmu(&tlb, 0, -1);
}

static int ecard_init_mm(void)
diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c
index ed5409c6abf4..116da8026154 100644
--- a/arch/arm64/crypto/aes-glue.c
+++ b/arch/arm64/crypto/aes-glue.c
@@ -18,7 +18,6 @@
#include <linux/module.h>
#include <linux/cpufeature.h>
#include <crypto/xts.h>
-
#include "aes-ce-setkey.h"

#ifdef USE_V8_CRYPTO_EXTENSIONS
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index 892f33235dc7..0b4d75a2270b 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -121,7 +121,7 @@
* Invalidate an entire user address space on all CPUs.
* The 'mm' argument identifies the ASID to invalidate.
*
- * flush_tlb_range(vma, start, end)
+ * flush_tlb_range(tlb, vma, start, end)
* Invalidate the virtual-address range '[start, end)' on all
* CPUs for the user address space corresponding to 'vma->mm'.
* Note that this operation also invalidates any walk-cache
@@ -247,7 +247,8 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma,
dsb(ish);
}

-static inline void flush_tlb_range(struct vm_area_struct *vma,
+static inline void flush_tlb_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
/*
diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index bbeb6a5a6ba6..50ae5c300f8a 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -141,7 +141,10 @@ static pte_t get_clear_flush(struct mm_struct *mm,

if (valid) {
struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0);
- flush_tlb_range(&vma, saddr, addr);
+ struct mmu_gather tlb;
+ tlb_gather_mmu(&tlb, mm, saddr, addr);
+ flush_tlb_range(&tlb, &vma, saddr, addr);
+ tlb_finish_mmu(&tlb, saddr, addr);
}
return orig_pte;
}
@@ -162,12 +165,15 @@ static void clear_flush(struct mm_struct *mm,
unsigned long ncontig)
{
struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0);
+ struct mmu_gather tlb;
unsigned long i, saddr = addr;

for (i = 0; i < ncontig; i++, addr += pgsize, ptep++)
pte_clear(mm, addr, ptep);

- flush_tlb_range(&vma, saddr, addr);
+ tlb_gather_mmu(&tlb, mm, saddr, addr);
+ flush_tlb_range(&tlb, &vma, saddr, addr);
+ tlb_finish_mmu(&tlb, saddr, addr);
}

void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
diff --git a/arch/csky/include/asm/tlb.h b/arch/csky/include/asm/tlb.h
index fdff9b8d70c8..05f756b32e5f 100644
--- a/arch/csky/include/asm/tlb.h
+++ b/arch/csky/include/asm/tlb.h
@@ -15,7 +15,7 @@
#define tlb_end_vma(tlb, vma) \
do { \
if (!(tlb)->fullmm) \
- flush_tlb_range(vma, (vma)->vm_start, (vma)->vm_end); \
+ flush_tlb_range(tlb, vma, (vma)->vm_start, (vma)->vm_end); \
} while (0)

#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm)
diff --git a/arch/csky/include/asm/tlbflush.h b/arch/csky/include/asm/tlbflush.h
index 6845b0667703..8f922a693e11 100644
--- a/arch/csky/include/asm/tlbflush.h
+++ b/arch/csky/include/asm/tlbflush.h
@@ -10,14 +10,14 @@
* - flush_tlb_all() flushes all processes TLB entries
* - flush_tlb_mm(mm) flushes the specified mm context TLB entries
* - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
*/
extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *mm);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);

extern void flush_tlb_one(unsigned long vaddr);
diff --git a/arch/csky/mm/tlb.c b/arch/csky/mm/tlb.c
index eb3ba6c9c927..52e9087c45a7 100644
--- a/arch/csky/mm/tlb.c
+++ b/arch/csky/mm/tlb.c
@@ -44,8 +44,8 @@ do { \
} while (0)
#endif

-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
unsigned long newpid = cpu_asid(vma->vm_mm);

diff --git a/arch/hexagon/include/asm/tlbflush.h b/arch/hexagon/include/asm/tlbflush.h
index a7c9ab398cab..837deece2876 100644
--- a/arch/hexagon/include/asm/tlbflush.h
+++ b/arch/hexagon/include/asm/tlbflush.h
@@ -24,7 +24,7 @@
extern void tlb_flush_all(void);
extern void flush_tlb_mm(struct mm_struct *mm);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr);
-extern void flush_tlb_range(struct vm_area_struct *vma,
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
unsigned long start, unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
extern void flush_tlb_one(unsigned long);
diff --git a/arch/hexagon/mm/vm_tlb.c b/arch/hexagon/mm/vm_tlb.c
index 53482f2a9ff9..31ab187b4e44 100644
--- a/arch/hexagon/mm/vm_tlb.c
+++ b/arch/hexagon/mm/vm_tlb.c
@@ -22,8 +22,8 @@
* processors must be induced to flush the copies in their local TLBs,
* but Hexagon thread-based virtual processors share the same MMU.
*/
-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
struct mm_struct *mm = vma->vm_mm;

diff --git a/arch/ia64/include/asm/tlbflush.h b/arch/ia64/include/asm/tlbflush.h
index ceac10c4d6e2..b67b5527a7ba 100644
--- a/arch/ia64/include/asm/tlbflush.h
+++ b/arch/ia64/include/asm/tlbflush.h
@@ -92,7 +92,8 @@ flush_tlb_mm (struct mm_struct *mm)
#endif
}

-extern void flush_tlb_range (struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);

/*
* Page-granular tlb flush.
@@ -101,7 +102,8 @@ static inline void
flush_tlb_page (struct vm_area_struct *vma, unsigned long addr)
{
#ifdef CONFIG_SMP
- flush_tlb_range(vma, (addr & PAGE_MASK), (addr & PAGE_MASK) + PAGE_SIZE);
+ flush_tlb_range(NULL, vma, (addr & PAGE_MASK),
+ (addr & PAGE_MASK) + PAGE_SIZE);
#else
if (vma->vm_mm == current->active_mm)
ia64_ptcl(addr, (PAGE_SHIFT << 2));
diff --git a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c
index 72cc568bc841..84b11faaaf0c 100644
--- a/arch/ia64/mm/tlb.c
+++ b/arch/ia64/mm/tlb.c
@@ -347,7 +347,10 @@ __flush_tlb_range (struct vm_area_struct *vma, unsigned long start,
ia64_srlz_i(); /* srlz.i implies srlz.d */
}

-void flush_tlb_range(struct vm_area_struct *vma,
+/* struct mmu_gather *tlb might be NULL in this architecture. See
+ * arch/ia64/include/asm/tlbflush.h: flush_tlb_page().
+ */
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
if (unlikely(end - start >= 1024*1024*1024*1024UL
diff --git a/arch/m68k/include/asm/tlbflush.h b/arch/m68k/include/asm/tlbflush.h
index 191e75a6bb24..bad618573ea3 100644
--- a/arch/m68k/include/asm/tlbflush.h
+++ b/arch/m68k/include/asm/tlbflush.h
@@ -92,7 +92,8 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr
}
}

-static inline void flush_tlb_range(struct vm_area_struct *vma,
+static inline void flush_tlb_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
if (vma->vm_mm == current->active_mm)
@@ -189,8 +190,9 @@ static inline void flush_tlb_page (struct vm_area_struct *vma,
}
/* Flush a range of pages from TLB. */

-static inline void flush_tlb_range (struct vm_area_struct *vma,
- unsigned long start, unsigned long end)
+static inline void flush_tlb_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
struct mm_struct *mm = vma->vm_mm;
unsigned char seg, oldctx;
@@ -263,7 +265,7 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr
BUG();
}

-static inline void flush_tlb_range(struct mm_struct *mm,
+static inline void flush_tlb_range(struct mmu_gather *tlb, struct mm_struct *mm,
unsigned long start, unsigned long end)
{
BUG();
diff --git a/arch/microblaze/include/asm/tlbflush.h b/arch/microblaze/include/asm/tlbflush.h
index 2e1353c2d18d..2c0f8b1a3b0b 100644
--- a/arch/microblaze/include/asm/tlbflush.h
+++ b/arch/microblaze/include/asm/tlbflush.h
@@ -44,7 +44,8 @@ static inline void local_flush_tlb_range(struct vm_area_struct *vma,
#define flush_tlb_all local_flush_tlb_all
#define flush_tlb_mm local_flush_tlb_mm
#define flush_tlb_page local_flush_tlb_page
-#define flush_tlb_range local_flush_tlb_range
+#define flush_tlb_range(tlb, mm, start, end) \
+ local_flush_tlb_range(mm, start, end)

/*
* This is called in munmap when we have freed up some page-table
@@ -60,7 +61,7 @@ static inline void flush_tlb_pgtables(struct mm_struct *mm,
#define flush_tlb_all() BUG()
#define flush_tlb_mm(mm) BUG()
#define flush_tlb_page(vma, addr) BUG()
-#define flush_tlb_range(mm, start, end) BUG()
+#define flush_tlb_range(tlb, mm, start, end) BUG()
#define flush_tlb_pgtables(mm, start, end) BUG()
#define flush_tlb_kernel_range(start, end) BUG()

diff --git a/arch/mips/include/asm/hugetlb.h b/arch/mips/include/asm/hugetlb.h
index 425bb6fc3bda..26acebd3a58a 100644
--- a/arch/mips/include/asm/hugetlb.h
+++ b/arch/mips/include/asm/hugetlb.h
@@ -10,6 +10,7 @@
#define __ASM_HUGETLB_H

#include <asm/page.h>
+#include <asm/tlb.h>

static inline int is_hugepage_only_range(struct mm_struct *mm,
unsigned long addr,
@@ -72,12 +73,15 @@ static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma,
int changed = !pte_same(*ptep, pte);

if (changed) {
+ struct mmu_gather tlb;
set_pte_at(vma->vm_mm, addr, ptep, pte);
/*
* There could be some standard sized pages in there,
* get them all.
*/
- flush_tlb_range(vma, addr, addr + HPAGE_SIZE);
+ tlb_gather_mmu(&tlb, vma->vm_mm, addr, addr + HPAGE_SIZE);
+ flush_tlb_range(&tlb, vma, addr, addr + HPAGE_SIZE);
+ tlb_finish_mmu(&tlb, addr, addr + HPAGE_SIZE);
}
return changed;
}
diff --git a/arch/mips/include/asm/tlbflush.h b/arch/mips/include/asm/tlbflush.h
index 9789e7a32def..ad5ae304f2fa 100644
--- a/arch/mips/include/asm/tlbflush.h
+++ b/arch/mips/include/asm/tlbflush.h
@@ -10,7 +10,7 @@
* - flush_tlb_all() flushes all processes TLB entries
* - flush_tlb_mm(mm) flushes the specified mm context TLB entries
* - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
*/
extern void local_flush_tlb_all(void);
@@ -28,8 +28,8 @@ extern void local_flush_tlb_one(unsigned long vaddr);

extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long,
- unsigned long);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long, unsigned long);
extern void flush_tlb_kernel_range(unsigned long, unsigned long);
extern void flush_tlb_page(struct vm_area_struct *, unsigned long);
extern void flush_tlb_one(unsigned long vaddr);
@@ -38,7 +38,8 @@ extern void flush_tlb_one(unsigned long vaddr);

#define flush_tlb_all() local_flush_tlb_all()
#define flush_tlb_mm(mm) drop_mmu_context(mm)
-#define flush_tlb_range(vma, vmaddr, end) local_flush_tlb_range(vma, vmaddr, end)
+#define flush_tlb_range(tlb, vma, vmaddr, end) \
+ local_flush_tlb_range(vma, vmaddr, end)
#define flush_tlb_kernel_range(vmaddr,end) \
local_flush_tlb_kernel_range(vmaddr, end)
#define flush_tlb_page(vma, page) local_flush_tlb_page(vma, page)
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
index f510c00bda88..9c3a40d23314 100644
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -563,7 +563,8 @@ static void flush_tlb_range_ipi(void *info)
local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2);
}

-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
struct mm_struct *mm = vma->vm_mm;
unsigned long addr;
diff --git a/arch/nds32/include/asm/tlbflush.h b/arch/nds32/include/asm/tlbflush.h
index 97155366ea01..81ba671759f9 100644
--- a/arch/nds32/include/asm/tlbflush.h
+++ b/arch/nds32/include/asm/tlbflush.h
@@ -36,7 +36,8 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long addr);

#define flush_tlb_all local_flush_tlb_all
#define flush_tlb_mm local_flush_tlb_mm
-#define flush_tlb_range local_flush_tlb_range
+#define flush_tlb_range(tlb, vma, start, end) \
+ local_flush_tlb_range(vma, start, end)
#define flush_tlb_page local_flush_tlb_page
#define flush_tlb_kernel_range local_flush_tlb_kernel_range

diff --git a/arch/nios2/include/asm/tlbflush.h b/arch/nios2/include/asm/tlbflush.h
index 362d6da09d02..823ea991b6d7 100644
--- a/arch/nios2/include/asm/tlbflush.h
+++ b/arch/nios2/include/asm/tlbflush.h
@@ -7,13 +7,14 @@
#define _ASM_NIOS2_TLBFLUSH_H

struct mm_struct;
+struct mmu_gather;

/*
* TLB flushing:
*
* - flush_tlb_all() flushes all processes TLB entries
* - flush_tlb_mm(mm) flushes the specified mm context TLB entries
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
* - flush_tlb_page(vma, address) flushes a page
* - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
* - flush_tlb_kernel_page(address) flushes a kernel page
@@ -23,14 +24,14 @@ struct mm_struct;
*/
extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *mm);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);

static inline void flush_tlb_page(struct vm_area_struct *vma,
unsigned long address)
{
- flush_tlb_range(vma, address, address + PAGE_SIZE);
+ flush_tlb_range(NULL, vma, tlb_start, tlb_end);
}

static inline void flush_tlb_kernel_page(unsigned long address)
diff --git a/arch/nios2/mm/tlb.c b/arch/nios2/mm/tlb.c
index 7fea59e53f94..5cb6d9a64082 100644
--- a/arch/nios2/mm/tlb.c
+++ b/arch/nios2/mm/tlb.c
@@ -100,8 +100,12 @@ static void reload_tlb_one_pid(unsigned long addr, unsigned long mmu_pid, pte_t
replace_tlb_one_pid(addr, mmu_pid, pte_val(pte));
}

-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
+/*
+ * struct mmu_gather *tlb might be NULL in this architecture. See
+ * arch/nios2/include/asm/tlbflush.h: flush_tlb_page().
+ */
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context);

diff --git a/arch/openrisc/include/asm/tlbflush.h b/arch/openrisc/include/asm/tlbflush.h
index e9a7f0b35a15..67e8e492d5f0 100644
--- a/arch/openrisc/include/asm/tlbflush.h
+++ b/arch/openrisc/include/asm/tlbflush.h
@@ -27,7 +27,7 @@
* - flush_tlb_all() flushes all processes TLBs
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(mm, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, mm, start, end) flushes a range of pages
*/
extern void local_flush_tlb_all(void);
extern void local_flush_tlb_mm(struct mm_struct *mm);
@@ -41,13 +41,13 @@ extern void local_flush_tlb_range(struct vm_area_struct *vma,
#define flush_tlb_all local_flush_tlb_all
#define flush_tlb_mm local_flush_tlb_mm
#define flush_tlb_page local_flush_tlb_page
-#define flush_tlb_range local_flush_tlb_range
+#define flush_tlb_range(tlb, vma, start, end) local_flush_tlb_range(vma, start, end)
#else
extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *mm);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
#endif

static inline void flush_tlb(void)
@@ -58,7 +58,7 @@ static inline void flush_tlb(void)
static inline void flush_tlb_kernel_range(unsigned long start,
unsigned long end)
{
- flush_tlb_range(NULL, start, end);
+ flush_tlb_range(NULL, NULL, start, end);
}

#endif /* __ASM_OPENRISC_TLBFLUSH_H */
diff --git a/arch/openrisc/kernel/smp.c b/arch/openrisc/kernel/smp.c
index 7d518ee8bddc..b22d5afb8daa 100644
--- a/arch/openrisc/kernel/smp.c
+++ b/arch/openrisc/kernel/smp.c
@@ -238,7 +238,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
on_each_cpu(ipi_flush_tlb_all, NULL, 1);
}

-void flush_tlb_range(struct vm_area_struct *vma,
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
on_each_cpu(ipi_flush_tlb_all, NULL, 1);
diff --git a/arch/parisc/include/asm/tlbflush.h b/arch/parisc/include/asm/tlbflush.h
index c5ded01d45be..a46d27e7b9b0 100644
--- a/arch/parisc/include/asm/tlbflush.h
+++ b/arch/parisc/include/asm/tlbflush.h
@@ -16,7 +16,7 @@ extern void flush_tlb_all_local(void *);
int __flush_tlb_range(unsigned long sid,
unsigned long start, unsigned long end);

-#define flush_tlb_range(vma, start, end) \
+#define flush_tlb_range(tlb, vma, start, end) \
__flush_tlb_range((vma)->vm_mm->context, start, end)

#define flush_tlb_kernel_range(start, end) \
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index 1eedfecc5137..341eeff566c9 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -22,6 +22,7 @@
#include <asm/pdc.h>
#include <asm/cache.h>
#include <asm/cacheflush.h>
+#include <asm/tlb.h>
#include <asm/tlbflush.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
@@ -563,11 +564,14 @@ void flush_cache_mm(struct mm_struct *mm)
}

if (mm->context == mfsp(3)) {
+ struct mmu_gather tlb;
for (vma = mm->mmap; vma; vma = vma->vm_next) {
flush_user_dcache_range_asm(vma->vm_start, vma->vm_end);
if (vma->vm_flags & VM_EXEC)
flush_user_icache_range_asm(vma->vm_start, vma->vm_end);
- flush_tlb_range(vma, vma->vm_start, vma->vm_end);
+ tlb_gather_mmu(&tlb, mm, vma->vm_start, vma->vm_end);
+ flush_tlb_range(&tlb, vma, vma->vm_start, vma->vm_end);
+ tlb_finish_mmu(&tlb, vma->vm_start, vma->vm_end);
}
return;
}
@@ -600,11 +604,13 @@ void flush_cache_range(struct vm_area_struct *vma,
{
pgd_t *pgd;
unsigned long addr;
+ struct mmu_gather tlb;

+ tlb_gather_mmu(&tlb, vma->vm_mm, start, end);
if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
end - start >= parisc_cache_flush_threshold) {
if (vma->vm_mm->context)
- flush_tlb_range(vma, start, end);
+ flush_tlb_range(&tlb, vma, start, end);
flush_cache_all();
return;
}
@@ -613,9 +619,10 @@ void flush_cache_range(struct vm_area_struct *vma,
flush_user_dcache_range_asm(start, end);
if (vma->vm_flags & VM_EXEC)
flush_user_icache_range_asm(start, end);
- flush_tlb_range(vma, start, end);
+ flush_tlb_range(&tlb, vma, start, end);
return;
}
+ tlb_finish_mmu(&tlb, start, end);

pgd = vma->vm_mm->pgd;
for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {
diff --git a/arch/powerpc/include/asm/book3s/32/tlbflush.h b/arch/powerpc/include/asm/book3s/32/tlbflush.h
index 068085b709fb..79790529fc7c 100644
--- a/arch/powerpc/include/asm/book3s/32/tlbflush.h
+++ b/arch/powerpc/include/asm/book3s/32/tlbflush.h
@@ -9,8 +9,8 @@
extern void flush_tlb_mm(struct mm_struct *mm);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
extern void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
static inline void local_flush_tlb_page(struct vm_area_struct *vma,
unsigned long vmaddr)
diff --git a/arch/powerpc/include/asm/book3s/64/tlbflush.h b/arch/powerpc/include/asm/book3s/64/tlbflush.h
index 968f10ef3d51..b2af60c9a5ca 100644
--- a/arch/powerpc/include/asm/book3s/64/tlbflush.h
+++ b/arch/powerpc/include/asm/book3s/64/tlbflush.h
@@ -67,7 +67,8 @@ static inline void flush_hugetlb_tlb_range(struct mmu_gather *tlb,
return hash__flush_tlb_range(vma, start, end);
}

-static inline void flush_tlb_range(struct vm_area_struct *vma,
+static inline void flush_tlb_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
if (radix_enabled())
diff --git a/arch/powerpc/include/asm/nohash/tlbflush.h b/arch/powerpc/include/asm/nohash/tlbflush.h
index b1d8fec29169..471ce66d32c2 100644
--- a/arch/powerpc/include/asm/nohash/tlbflush.h
+++ b/arch/powerpc/include/asm/nohash/tlbflush.h
@@ -11,7 +11,7 @@
* the local processor
* - local_flush_tlb_page(vma, vmaddr) flushes one page on the local processor
* - flush_tlb_page_nohash(vma, vmaddr) flushes one page if SW loaded TLB
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
*
*/
@@ -26,11 +26,12 @@

struct vm_area_struct;
struct mm_struct;
+struct mmu_gather;

#define MMU_NO_CONTEXT ((unsigned int)-1)

-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);

extern void local_flush_tlb_mm(struct mm_struct *mm);
diff --git a/arch/powerpc/mm/book3s32/tlb.c b/arch/powerpc/mm/book3s32/tlb.c
index 2fcd321040ff..96dbdcffc140 100644
--- a/arch/powerpc/mm/book3s32/tlb.c
+++ b/arch/powerpc/mm/book3s32/tlb.c
@@ -63,7 +63,7 @@ void tlb_flush(struct mmu_gather *tlb)
*
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes kernel pages
*
* since the hardware hash table functions as an extension of the
@@ -156,8 +156,8 @@ EXPORT_SYMBOL(flush_tlb_page);
* and check _PAGE_HASHPTE bit; if it is set, find and destroy
* the corresponding HPTE.
*/
-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
flush_range(vma->vm_mm, start, end);
}
diff --git a/arch/powerpc/mm/book3s64/radix_tlb.c b/arch/powerpc/mm/book3s64/radix_tlb.c
index 03f43c924e00..78ea6e4192d1 100644
--- a/arch/powerpc/mm/book3s64/radix_tlb.c
+++ b/arch/powerpc/mm/book3s64/radix_tlb.c
@@ -557,7 +557,7 @@ static inline void _tlbiel_va_range_multicast(struct mm_struct *mm,
*
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes kernel pages
*
* - local_* variants of page and mm only apply to the current
diff --git a/arch/powerpc/mm/nohash/tlb.c b/arch/powerpc/mm/nohash/tlb.c
index 696f568253a0..1fd5c837630e 100644
--- a/arch/powerpc/mm/nohash/tlb.c
+++ b/arch/powerpc/mm/nohash/tlb.c
@@ -181,7 +181,7 @@ EXPORT_PER_CPU_SYMBOL(next_tlbcam_idx);
*
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes kernel pages
*
* - local_* variants of page and mm only apply to the current
@@ -379,8 +379,8 @@ EXPORT_SYMBOL(flush_tlb_kernel_range);
* some implementation can stack multiple tlbivax before a tlbsync but
* for now, we keep it that way
*/
-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)

{
if (end - start == PAGE_SIZE && !(start & ~PAGE_MASK))
diff --git a/arch/riscv/include/asm/tlbflush.h b/arch/riscv/include/asm/tlbflush.h
index 394cfbccdcd9..d1ede9c434ec 100644
--- a/arch/riscv/include/asm/tlbflush.h
+++ b/arch/riscv/include/asm/tlbflush.h
@@ -30,14 +30,15 @@ static inline void local_flush_tlb_page(unsigned long addr)
void flush_tlb_all(void);
void flush_tlb_mm(struct mm_struct *mm);
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr);
-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end);
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
#else /* CONFIG_SMP && CONFIG_MMU */

#define flush_tlb_all() local_flush_tlb_all()
#define flush_tlb_page(vma, addr) local_flush_tlb_page(addr)

-static inline void flush_tlb_range(struct vm_area_struct *vma,
+static inline void flush_tlb_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
local_flush_tlb_all();
diff --git a/arch/riscv/mm/tlbflush.c b/arch/riscv/mm/tlbflush.c
index 720b443c4528..4c679f942b14 100644
--- a/arch/riscv/mm/tlbflush.c
+++ b/arch/riscv/mm/tlbflush.c
@@ -49,8 +49,8 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), addr, PAGE_SIZE);
}

-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
__sbi_tlb_flush_range(mm_cpumask(vma->vm_mm), start, end - start);
}
diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h
index 82703e03f35d..c9d372bbbb3e 100644
--- a/arch/s390/include/asm/tlbflush.h
+++ b/arch/s390/include/asm/tlbflush.h
@@ -99,7 +99,7 @@ static inline void __tlb_flush_mm_lazy(struct mm_struct * mm)
* flush_tlb_all() - flushes all processes TLBs
* flush_tlb_mm(mm) - flushes the specified mm context TLB's
* flush_tlb_page(vma, vmaddr) - flushes one page
- * flush_tlb_range(vma, start, end) - flushes a range of pages
+ * flush_tlb_range(tlb, vma, start, end) - flushes a range of pages
* flush_tlb_kernel_range(start, end) - flushes a range of kernel pages
*/

@@ -120,7 +120,8 @@ static inline void flush_tlb_mm(struct mm_struct *mm)
__tlb_flush_mm_lazy(mm);
}

-static inline void flush_tlb_range(struct vm_area_struct *vma,
+static inline void flush_tlb_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
__tlb_flush_mm_lazy(vma->vm_mm);
diff --git a/arch/sh/include/asm/tlbflush.h b/arch/sh/include/asm/tlbflush.h
index 8f180cd3bcd6..bfaf0e9677c2 100644
--- a/arch/sh/include/asm/tlbflush.h
+++ b/arch/sh/include/asm/tlbflush.h
@@ -8,7 +8,7 @@
* - flush_tlb_all() flushes all processes TLBs
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
*/
extern void local_flush_tlb_all(void);
@@ -28,8 +28,8 @@ extern void __flush_tlb_global(void);

extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *mm);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
extern void flush_tlb_one(unsigned long asid, unsigned long page);
@@ -41,7 +41,7 @@ extern void flush_tlb_one(unsigned long asid, unsigned long page);
#define flush_tlb_page(vma, page) local_flush_tlb_page(vma, page)
#define flush_tlb_one(asid, page) local_flush_tlb_one(asid, page)

-#define flush_tlb_range(vma, start, end) \
+#define flush_tlb_range(tlb, vma, start, end) \
local_flush_tlb_range(vma, start, end)

#define flush_tlb_kernel_range(start, end) \
diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c
index 372acdc9033e..8bee178f6882 100644
--- a/arch/sh/kernel/smp.c
+++ b/arch/sh/kernel/smp.c
@@ -387,7 +387,7 @@ static void flush_tlb_range_ipi(void *info)
local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2);
}

-void flush_tlb_range(struct vm_area_struct *vma,
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
struct mm_struct *mm = vma->vm_mm;
diff --git a/arch/sparc/include/asm/tlbflush_32.h b/arch/sparc/include/asm/tlbflush_32.h
index 470531991a08..231f705cd314 100644
--- a/arch/sparc/include/asm/tlbflush_32.h
+++ b/arch/sparc/include/asm/tlbflush_32.h
@@ -8,7 +8,7 @@
sparc32_cachetlb_ops->tlb_all()
#define flush_tlb_mm(mm) \
sparc32_cachetlb_ops->tlb_mm(mm)
-#define flush_tlb_range(vma, start, end) \
+#define flush_tlb_range(tlb, vma, start, end) \
sparc32_cachetlb_ops->tlb_range(vma, start, end)
#define flush_tlb_page(vma, addr) \
sparc32_cachetlb_ops->tlb_page(vma, addr)
diff --git a/arch/sparc/include/asm/tlbflush_64.h b/arch/sparc/include/asm/tlbflush_64.h
index 8b8cdaa69272..c371f46c71e8 100644
--- a/arch/sparc/include/asm/tlbflush_64.h
+++ b/arch/sparc/include/asm/tlbflush_64.h
@@ -32,7 +32,8 @@ static inline void flush_tlb_page(struct vm_area_struct *vma,
{
}

-static inline void flush_tlb_range(struct vm_area_struct *vma,
+static inline void flush_tlb_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
}
diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c
index 3d72d2deb13b..9bb1fd1c2668 100644
--- a/arch/sparc/mm/tlb.c
+++ b/arch/sparc/mm/tlb.c
@@ -245,10 +245,13 @@ pmd_t pmdp_invalidate(struct vm_area_struct *vma, unsigned long address,
pmd_t *pmdp)
{
pmd_t old, entry;
+ struct mmu_gather tlb;

entry = __pmd(pmd_val(*pmdp) & ~_PAGE_VALID);
old = pmdp_establish(vma, address, pmdp, entry);
- flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
+ tlb_gather_mmu(&tlb, vma->vm_mm, address, address + HPAGE_PMD_SIZE);
+ flush_tlb_range(&tlb, vma, address, address + HPAGE_PMD_SIZE);
+ tlb_finish_mmu(&tlb, address, address + HPAGE_PMD_SIZE);

/*
* set_pmd_at() will not be called in a way to decrement
diff --git a/arch/um/include/asm/tlbflush.h b/arch/um/include/asm/tlbflush.h
index a5bda890390d..e41a03181aa8 100644
--- a/arch/um/include/asm/tlbflush.h
+++ b/arch/um/include/asm/tlbflush.h
@@ -16,13 +16,13 @@
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
* - flush_tlb_kernel_vm() flushes the kernel vm area
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
*/

extern void flush_tlb_all(void);
extern void flush_tlb_mm(struct mm_struct *mm);
-extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end);
+extern void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long address);
extern void flush_tlb_kernel_vm(void);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c
index 80a358c6d652..5aa1b8100f73 100644
--- a/arch/um/kernel/tlb.c
+++ b/arch/um/kernel/tlb.c
@@ -574,8 +574,8 @@ static void fix_range(struct mm_struct *mm, unsigned long start_addr,
fix_range_common(mm, start_addr, end_addr, force);
}

-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
{
if (vma->vm_mm == NULL)
flush_tlb_kernel_range_common(start, end);
diff --git a/arch/unicore32/include/asm/tlbflush.h b/arch/unicore32/include/asm/tlbflush.h
index 1cf18ef55515..69ea6c3079e7 100644
--- a/arch/unicore32/include/asm/tlbflush.h
+++ b/arch/unicore32/include/asm/tlbflush.h
@@ -38,7 +38,7 @@ extern void __cpu_flush_kern_tlb_range(unsigned long, unsigned long);
* space.
* - mm - mm_struct describing address space
*
- * flush_tlb_range(mm,start,end)
+ * flush_tlb_range(tlb,mm,start,end)
*
* Invalidate a range of TLB entries in the specified
* address space.
@@ -173,7 +173,8 @@ static inline void clean_pmd_entry(pmd_t *pmd)
#define flush_tlb_mm local_flush_tlb_mm
#define flush_tlb_page local_flush_tlb_page
#define flush_tlb_kernel_page local_flush_tlb_kernel_page
-#define flush_tlb_range local_flush_tlb_range
+#define flush_tlb_range(tlb, vma, start, end) \
+ local_flush_tlb_range(vma, start, end)
#define flush_tlb_kernel_range local_flush_tlb_kernel_range

/*
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 6f66d841262d..217e37da8271 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -531,7 +531,7 @@ static inline void __flush_tlb_one_kernel(unsigned long addr)
* - flush_tlb_all() flushes all processes TLBs
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
* - flush_tlb_others(cpumask, info) flushes TLBs on other cpus
*
@@ -568,7 +568,7 @@ struct flush_tlb_info {
#define flush_tlb_mm(mm) \
flush_tlb_mm_range(mm, 0UL, TLB_FLUSH_ALL, 0UL, true)

-#define flush_tlb_range(vma, start, end) \
+#define flush_tlb_range(tlb, vma, start, end) \
flush_tlb_mm_range((vma)->vm_mm, start, end, \
((vma)->vm_flags & VM_HUGETLB) \
? huge_page_shift(hstate_vma(vma)) \
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index 7bd2c3a52297..7ff38507086c 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -596,8 +596,14 @@ int pmdp_clear_flush_young(struct vm_area_struct *vma,
VM_BUG_ON(address & ~HPAGE_PMD_MASK);

young = pmdp_test_and_clear_young(vma, address, pmdp);
- if (young)
- flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
+ if (young) {
+ struct mmu_gather tlb;
+ unsigned long tlb_start = address;
+ unsigned long tlb_end = address + HPAGE_PMD_SIZE;
+ tlb_gather_mmu(&tlb, vma->vm_mm, tlb_start, tlb_end);
+ flush_tlb_range(&tlb, vma, tlb_start, tlb_end);
+ tlb_finish_mmu(&tlb, tlb_start, tlb_end);
+ }

return young;
}
diff --git a/arch/xtensa/include/asm/tlbflush.h b/arch/xtensa/include/asm/tlbflush.h
index 856e2da2e397..613127ebabfb 100644
--- a/arch/xtensa/include/asm/tlbflush.h
+++ b/arch/xtensa/include/asm/tlbflush.h
@@ -27,7 +27,7 @@
* - flush_tlb_all() flushes all processes TLB entries
* - flush_tlb_mm(mm) flushes the specified mm context TLB entries
* - flush_tlb_page(mm, vmaddr) flushes a single page
- * - flush_tlb_range(mm, start, end) flushes a range of pages
+ * - flush_tlb_range(tlb, mm, start, end) flushes a range of pages
*/

void local_flush_tlb_all(void);
@@ -43,8 +43,8 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end);
void flush_tlb_all(void);
void flush_tlb_mm(struct mm_struct *);
void flush_tlb_page(struct vm_area_struct *, unsigned long);
-void flush_tlb_range(struct vm_area_struct *, unsigned long,
- unsigned long);
+void flush_tlb_range(struct mmu_gather *, struct vm_area_struct *,
+ unsigned long, unsigned long);
void flush_tlb_kernel_range(unsigned long start, unsigned long end);

#else /* !CONFIG_SMP */
@@ -52,8 +52,8 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end);
#define flush_tlb_all() local_flush_tlb_all()
#define flush_tlb_mm(mm) local_flush_tlb_mm(mm)
#define flush_tlb_page(vma, page) local_flush_tlb_page(vma, page)
-#define flush_tlb_range(vma, vmaddr, end) local_flush_tlb_range(vma, vmaddr, \
- end)
+#define flush_tlb_range(tlb, vma, vmaddr, end) \
+ local_flush_tlb_range(vma, vmaddr, end)
#define flush_tlb_kernel_range(start, end) local_flush_tlb_kernel_range(start, \
end)

diff --git a/arch/xtensa/kernel/smp.c b/arch/xtensa/kernel/smp.c
index 83b244ce61ee..6fec29ea865a 100644
--- a/arch/xtensa/kernel/smp.c
+++ b/arch/xtensa/kernel/smp.c
@@ -509,7 +509,7 @@ static void ipi_flush_tlb_range(void *arg)
local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2);
}

-void flush_tlb_range(struct vm_area_struct *vma,
+void flush_tlb_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
struct flush_data fd = {
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index 1c67a744877e..884be388fe38 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -1160,8 +1160,10 @@ static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)
* invalidate the entire TLB which is not desitable.
* e.g. see arch/arc: flush_pmd_tlb_range
*/
-#define flush_pmd_tlb_range(tlb, vma, addr, end) flush_tlb_range(vma, addr, end)
-#define flush_pud_tlb_range(tlb, vma, addr, end) flush_tlb_range(vma, addr, end)
+#define flush_pmd_tlb_range(tlb, vma, addr, end) \
+ flush_tlb_range(tlb, vma, addr, end)
+#define flush_pud_tlb_range(tlb, vma, addr, end) \
+ flush_tlb_range(tlb, vma, addr, end)
#else
#define flush_pmd_tlb_range(tlb, vma, addr, end) BUILD_BUG()
#define flush_pud_tlb_range(tlb, vma, addr, end) BUILD_BUG()
diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h
index f391f6b500b4..1aa1ff9f72a2 100644
--- a/include/asm-generic/tlb.h
+++ b/include/asm-generic/tlb.h
@@ -380,7 +380,7 @@ static inline void tlb_flush(struct mmu_gather *tlb)
(tlb->vma_huge ? VM_HUGETLB : 0),
};

- flush_tlb_range(&vma, tlb->start, tlb->end);
+ flush_tlb_range(tlb, &vma, tlb->start, tlb->end);
}
}

diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 24ad53b4dfc0..5f1d29c409df 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1646,7 +1646,13 @@ vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t pmd)
* mapping or not. Hence use the tlb range variant
*/
if (mm_tlb_flush_pending(vma->vm_mm)) {
- flush_tlb_range(vma, haddr, haddr + HPAGE_PMD_SIZE);
+ struct mmu_gather tlb;
+ unsigned long tlb_start = haddr;
+ unsigned long tlb_end = haddr + HPAGE_PMD_SIZE;
+ tlb_gather_mmu(&tlb, vma->vm_mm, tlb_start, tlb_end);
+ tlb.cleared_pmds = 1;
+ flush_tlb_range(&tlb, vma, tlb_start, tlb_end);
+ tlb_finish_mmu(&tlb, tlb_start, tlb_end);
/*
* change_huge_pmd() released the pmd lock before
* invalidating the secondary MMUs sharing the primary
@@ -1917,8 +1923,15 @@ bool move_huge_pmd(struct vm_area_struct *vma, unsigned long old_addr,
}
pmd = move_soft_dirty_pmd(pmd);
set_pmd_at(mm, new_addr, new_pmd, pmd);
- if (force_flush)
- flush_tlb_range(vma, old_addr, old_addr + PMD_SIZE);
+ if (force_flush) {
+ struct mmu_gather tlb;
+ unsigned long tlb_start = old_addr;
+ unsigned long tlb_end = old_addr + PMD_SIZE;
+ tlb_gather_mmu(&tlb, vma->vm_mm, tlb_start, tlb_end);
+ tlb.cleared_pmds = 1;
+ flush_tlb_range(&tlb, vma, tlb_start, tlb_end);
+ tlb_finish_mmu(&tlb, tlb_start, tlb_end);
+ }
if (new_ptl != old_ptl)
spin_unlock(new_ptl);
spin_unlock(old_ptl);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index f913ce0b4831..f4a2c9a9e478 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -4442,7 +4442,7 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
* implement this.
*/
#define flush_hugetlb_tlb_range(tlb, vma, addr, end) \
- flush_tlb_range(vma, addr, end)
+ flush_tlb_range(tlb, vma, addr, end)
#endif

unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
diff --git a/mm/mapping_dirty_helpers.c b/mm/mapping_dirty_helpers.c
index 71070dda9643..6b9df57b6301 100644
--- a/mm/mapping_dirty_helpers.c
+++ b/mm/mapping_dirty_helpers.c
@@ -175,13 +175,22 @@ static int wp_clean_pre_vma(unsigned long start, unsigned long end,
static void wp_clean_post_vma(struct mm_walk *walk)
{
struct wp_walk *wpwalk = walk->private;
-
- if (mm_tlb_flush_nested(walk->mm))
- flush_tlb_range(walk->vma, wpwalk->range.start,
- wpwalk->range.end);
- else if (wpwalk->tlbflush_end > wpwalk->tlbflush_start)
- flush_tlb_range(walk->vma, wpwalk->tlbflush_start,
- wpwalk->tlbflush_end);
+ struct mmu_gather tlb;
+ unsigned long tlb_start, tlb_end;
+
+ if (mm_tlb_flush_nested(walk->mm)) {
+ tlb_start = wpwalk->range.start;
+ tlb_end = wpwalk->range.end;
+ tlb_gather_mmu(&tlb, walk->mm, tlb_start, tlb_end);
+ flush_tlb_range(&tlb, walk->vma, tlb_start, tlb_end);
+ tlb_finish_mmu(&tlb, tlb_start, tlb_end);
+ } else if (wpwalk->tlbflush_end > wpwalk->tlbflush_start) {
+ tlb_start = wpwalk->tlbflush_start;
+ tlb_end = wpwalk->tlbflush_end;
+ tlb_gather_mmu(&tlb, walk->mm, tlb_start, tlb_end);
+ flush_tlb_range(&tlb, walk->vma, tlb_start, tlb_end);
+ tlb_finish_mmu(&tlb, tlb_start, tlb_end);
+ }

mmu_notifier_invalidate_range_end(&wpwalk->range);
dec_tlb_flush_pending(walk->mm);
diff --git a/mm/migrate.c b/mm/migrate.c
index b1092876e537..7f62b16ef36b 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -2340,8 +2340,12 @@ static int migrate_vma_collect_pmd(pmd_t *pmdp,
pte_unmap_unlock(ptep - 1, ptl);

/* Only flush the TLB if we actually modified any entries */
- if (unmapped)
- flush_tlb_range(walk->vma, start, end);
+ if (unmapped) {
+ struct mmu_gather tlb;
+ tlb_gather_mmu(&tlb, mm, start, end);
+ flush_tlb_range(&tlb, walk->vma, start, end);
+ tlb_finish_mmu(&tlb, start, end);
+ }

return 0;
}
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 311c0dadf71c..1e79254776b4 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -31,6 +31,7 @@
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
#include <asm/mmu_context.h>
+#include <asm/tlb.h>
#include <asm/tlbflush.h>

#include "internal.h"
@@ -303,6 +304,7 @@ static unsigned long change_protection_range(struct vm_area_struct *vma,
int dirty_accountable, int prot_numa)
{
struct mm_struct *mm = vma->vm_mm;
+ struct mmu_gather tlb;
pgd_t *pgd;
unsigned long next;
unsigned long start = addr;
@@ -311,7 +313,7 @@ static unsigned long change_protection_range(struct vm_area_struct *vma,
BUG_ON(addr >= end);
pgd = pgd_offset(mm, addr);
flush_cache_range(vma, addr, end);
- inc_tlb_flush_pending(mm);
+ tlb_gather_mmu(&tlb, mm, start, end);
do {
next = pgd_addr_end(addr, end);
if (pgd_none_or_clear_bad(pgd))
@@ -322,8 +324,8 @@ static unsigned long change_protection_range(struct vm_area_struct *vma,

/* Only flush the TLB if we actually modified any entries: */
if (pages)
- flush_tlb_range(vma, start, end);
- dec_tlb_flush_pending(mm);
+ flush_tlb_range(&tlb, vma, start, end);
+ tlb_finish_mmu(&tlb, start, end);

return pages;
}
diff --git a/mm/mremap.c b/mm/mremap.c
index af363063ea23..28cfe2f820ea 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -26,6 +26,7 @@
#include <linux/userfaultfd_k.h>

#include <asm/cacheflush.h>
+#include <asm/tlb.h>
#include <asm/tlbflush.h>

#include "internal.h"
@@ -181,8 +182,14 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd,
}

arch_leave_lazy_mmu_mode();
- if (force_flush)
- flush_tlb_range(vma, old_end - len, old_end);
+ if (force_flush) {
+ struct mmu_gather tlb;
+ tlb_gather_mmu(&tlb, mm, old_end - len, old_end);
+ tlb.cleared_ptes = 1;
+ flush_tlb_range(&tlb, vma, old_end - len, old_end);
+ tlb_finish_mmu(&tlb, old_end - len, old_end);
+ }
+
if (new_ptl != old_ptl)
spin_unlock(new_ptl);
pte_unmap(new_pte - 1);
@@ -198,6 +205,7 @@ static bool move_normal_pmd(struct vm_area_struct *vma, unsigned long old_addr,
{
spinlock_t *old_ptl, *new_ptl;
struct mm_struct *mm = vma->vm_mm;
+ struct mmu_gather tlb;
pmd_t pmd;

if ((old_addr & ~PMD_MASK) || (new_addr & ~PMD_MASK)
@@ -228,7 +236,10 @@ static bool move_normal_pmd(struct vm_area_struct *vma, unsigned long old_addr,

/* Set the new pmd */
set_pmd_at(mm, new_addr, new_pmd, pmd);
- flush_tlb_range(vma, old_addr, old_addr + PMD_SIZE);
+ tlb_gather_mmu(&tlb, mm, old_addr, old_addr + PMD_SIZE);
+ tlb.cleared_pmds = 1;
+ flush_tlb_range(&tlb, vma, old_addr, old_addr + PMD_SIZE);
+ tlb_finish_mmu(&tlb, old_addr, old_addr + PMD_SIZE);
if (new_ptl != old_ptl)
spin_unlock(new_ptl);
spin_unlock(old_ptl);
diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c
index 9ab9d8f698ea..ef3ac1752033 100644
--- a/mm/pgtable-generic.c
+++ b/mm/pgtable-generic.c
@@ -240,13 +240,19 @@ pmd_t pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long address,
* use the same function.
*/
pmd_t pmd;
+ struct mmu_gather tlb;
+ unsigned long tlb_start = address;
+ unsigned long tlb_end = address + HPAGE_PMD_SIZE;

VM_BUG_ON(address & ~HPAGE_PMD_MASK);
VM_BUG_ON(pmd_trans_huge(*pmdp));
pmd = pmdp_huge_get_and_clear(vma->vm_mm, address, pmdp);

/* collapse entails shooting down ptes not pmd */
- flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
+ tlb_gather_mmu(&tlb, vma->vm_mm, tlb_start, tlb_end);
+ tlb.cleared_ptes = 1;
+ flush_tlb_range(&tlb, vma, tlb_start, tlb_end);
+ tlb_finish_mmu(&tlb, tlb_start, tlb_end);
return pmd;
}
#endif
diff --git a/mm/rmap.c b/mm/rmap.c
index b3e381919835..72133bfb1c07 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -67,6 +67,7 @@
#include <linux/memremap.h>
#include <linux/userfaultfd_k.h>

+#include <asm/tlb.h>
#include <asm/tlbflush.h>

#include <trace/events/tlb.h>
@@ -1377,6 +1378,7 @@ static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
bool ret = true;
struct mmu_notifier_range range;
enum ttu_flags flags = (enum ttu_flags)arg;
+ struct mmu_gather tlb;

/* munlock has nothing to gain from examining un-locked vmas */
if ((flags & TTU_MUNLOCK) && !(vma->vm_flags & VM_LOCKED))
@@ -1462,7 +1464,9 @@ static bool try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
* already adjusted above to cover this range.
*/
flush_cache_range(vma, range.start, range.end);
- flush_tlb_range(vma, range.start, range.end);
+ tlb_gather_mmu(&tlb, mm, range.start, range.end);
+ flush_tlb_range(&tlb, vma, range.start, range.end);
+ tlb_finish_mmu(&tlb, range.start, range.end);
mmu_notifier_invalidate_range(mm, range.start,
range.end);

--
2.19.1