aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/sparc/include/asm/pgtable_64.h4
-rw-r--r--arch/x86/include/asm/pgtable.h11
-rw-r--r--include/asm-generic/pgtable.h2
-rw-r--r--include/linux/mm_types.h44
-rw-r--r--kernel/fork.c1
-rw-r--r--mm/huge_memory.c7
-rw-r--r--mm/mprotect.c2
-rw-r--r--mm/pgtable-generic.c5
8 files changed, 69 insertions, 7 deletions
diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h
index 8358dc144959..0f9e94537eee 100644
--- a/arch/sparc/include/asm/pgtable_64.h
+++ b/arch/sparc/include/asm/pgtable_64.h
@@ -619,7 +619,7 @@ static inline unsigned long pte_present(pte_t pte)
619} 619}
620 620
621#define pte_accessible pte_accessible 621#define pte_accessible pte_accessible
622static inline unsigned long pte_accessible(pte_t a) 622static inline unsigned long pte_accessible(struct mm_struct *mm, pte_t a)
623{ 623{
624 return pte_val(a) & _PAGE_VALID; 624 return pte_val(a) & _PAGE_VALID;
625} 625}
@@ -847,7 +847,7 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
847 * SUN4V NOTE: _PAGE_VALID is the same value in both the SUN4U 847 * SUN4V NOTE: _PAGE_VALID is the same value in both the SUN4U
848 * and SUN4V pte layout, so this inline test is fine. 848 * and SUN4V pte layout, so this inline test is fine.
849 */ 849 */
850 if (likely(mm != &init_mm) && pte_accessible(orig)) 850 if (likely(mm != &init_mm) && pte_accessible(mm, orig))
851 tlb_batch_add(mm, addr, ptep, orig, fullmm); 851 tlb_batch_add(mm, addr, ptep, orig, fullmm);
852} 852}
853 853
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 3d1999458709..bbc8b12fa443 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -452,9 +452,16 @@ static inline int pte_present(pte_t a)
452} 452}
453 453
454#define pte_accessible pte_accessible 454#define pte_accessible pte_accessible
455static inline int pte_accessible(pte_t a) 455static inline bool pte_accessible(struct mm_struct *mm, pte_t a)
456{ 456{
457 return pte_flags(a) & _PAGE_PRESENT; 457 if (pte_flags(a) & _PAGE_PRESENT)
458 return true;
459
460 if ((pte_flags(a) & (_PAGE_PROTNONE | _PAGE_NUMA)) &&
461 mm_tlb_flush_pending(mm))
462 return true;
463
464 return false;
458} 465}
459 466
460static inline int pte_hidden(pte_t pte) 467static inline int pte_hidden(pte_t pte)
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index f330d28e4d0e..b12079afbd5f 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -217,7 +217,7 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b)
217#endif 217#endif
218 218
219#ifndef pte_accessible 219#ifndef pte_accessible
220# define pte_accessible(pte) ((void)(pte),1) 220# define pte_accessible(mm, pte) ((void)(pte), 1)
221#endif 221#endif
222 222
223#ifndef flush_tlb_fix_spurious_fault 223#ifndef flush_tlb_fix_spurious_fault
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index bd299418a934..e5c49c30460f 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -443,6 +443,14 @@ struct mm_struct {
443 /* numa_scan_seq prevents two threads setting pte_numa */ 443 /* numa_scan_seq prevents two threads setting pte_numa */
444 int numa_scan_seq; 444 int numa_scan_seq;
445#endif 445#endif
446#if defined(CONFIG_NUMA_BALANCING) || defined(CONFIG_COMPACTION)
447 /*
448 * An operation with batched TLB flushing is going on. Anything that
449 * can move process memory needs to flush the TLB when moving a
450 * PROT_NONE or PROT_NUMA mapped page.
451 */
452 bool tlb_flush_pending;
453#endif
446 struct uprobes_state uprobes_state; 454 struct uprobes_state uprobes_state;
447}; 455};
448 456
@@ -459,4 +467,40 @@ static inline cpumask_t *mm_cpumask(struct mm_struct *mm)
459 return mm->cpu_vm_mask_var; 467 return mm->cpu_vm_mask_var;
460} 468}
461 469
470#if defined(CONFIG_NUMA_BALANCING) || defined(CONFIG_COMPACTION)
471/*
472 * Memory barriers to keep this state in sync are graciously provided by
473 * the page table locks, outside of which no page table modifications happen.
474 * The barriers below prevent the compiler from re-ordering the instructions
475 * around the memory barriers that are already present in the code.
476 */
477static inline bool mm_tlb_flush_pending(struct mm_struct *mm)
478{
479 barrier();
480 return mm->tlb_flush_pending;
481}
482static inline void set_tlb_flush_pending(struct mm_struct *mm)
483{
484 mm->tlb_flush_pending = true;
485 barrier();
486}
487/* Clearing is done after a TLB flush, which also provides a barrier. */
488static inline void clear_tlb_flush_pending(struct mm_struct *mm)
489{
490 barrier();
491 mm->tlb_flush_pending = false;
492}
493#else
494static inline bool mm_tlb_flush_pending(struct mm_struct *mm)
495{
496 return false;
497}
498static inline void set_tlb_flush_pending(struct mm_struct *mm)
499{
500}
501static inline void clear_tlb_flush_pending(struct mm_struct *mm)
502{
503}
504#endif
505
462#endif /* _LINUX_MM_TYPES_H */ 506#endif /* _LINUX_MM_TYPES_H */
diff --git a/kernel/fork.c b/kernel/fork.c
index 728d5be9548c..5721f0e3f2da 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -537,6 +537,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
537 spin_lock_init(&mm->page_table_lock); 537 spin_lock_init(&mm->page_table_lock);
538 mm_init_aio(mm); 538 mm_init_aio(mm);
539 mm_init_owner(mm, p); 539 mm_init_owner(mm, p);
540 clear_tlb_flush_pending(mm);
540 541
541 if (likely(!mm_alloc_pgd(mm))) { 542 if (likely(!mm_alloc_pgd(mm))) {
542 mm->def_flags = 0; 543 mm->def_flags = 0;
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 7de1bf85f683..3d2783e10596 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1377,6 +1377,13 @@ int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
1377 } 1377 }
1378 1378
1379 /* 1379 /*
1380 * The page_table_lock above provides a memory barrier
1381 * with change_protection_range.
1382 */
1383 if (mm_tlb_flush_pending(mm))
1384 flush_tlb_range(vma, haddr, haddr + HPAGE_PMD_SIZE);
1385
1386 /*
1380 * Migrate the THP to the requested node, returns with page unlocked 1387 * Migrate the THP to the requested node, returns with page unlocked
1381 * and pmd_numa cleared. 1388 * and pmd_numa cleared.
1382 */ 1389 */
diff --git a/mm/mprotect.c b/mm/mprotect.c
index f8421722acb9..bb53a6591aea 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -188,6 +188,7 @@ static unsigned long change_protection_range(struct vm_area_struct *vma,
188 BUG_ON(addr >= end); 188 BUG_ON(addr >= end);
189 pgd = pgd_offset(mm, addr); 189 pgd = pgd_offset(mm, addr);
190 flush_cache_range(vma, addr, end); 190 flush_cache_range(vma, addr, end);
191 set_tlb_flush_pending(mm);
191 do { 192 do {
192 next = pgd_addr_end(addr, end); 193 next = pgd_addr_end(addr, end);
193 if (pgd_none_or_clear_bad(pgd)) 194 if (pgd_none_or_clear_bad(pgd))
@@ -199,6 +200,7 @@ static unsigned long change_protection_range(struct vm_area_struct *vma,
199 /* Only flush the TLB if we actually modified any entries: */ 200 /* Only flush the TLB if we actually modified any entries: */
200 if (pages) 201 if (pages)
201 flush_tlb_range(vma, start, end); 202 flush_tlb_range(vma, start, end);
203 clear_tlb_flush_pending(mm);
202 204
203 return pages; 205 return pages;
204} 206}
diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c
index e84cad27a801..a8b919925934 100644
--- a/mm/pgtable-generic.c
+++ b/mm/pgtable-generic.c
@@ -110,9 +110,10 @@ int pmdp_clear_flush_young(struct vm_area_struct *vma,
110pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long address, 110pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long address,
111 pte_t *ptep) 111 pte_t *ptep)
112{ 112{
113 struct mm_struct *mm = (vma)->vm_mm;
113 pte_t pte; 114 pte_t pte;
114 pte = ptep_get_and_clear((vma)->vm_mm, address, ptep); 115 pte = ptep_get_and_clear(mm, address, ptep);
115 if (pte_accessible(pte)) 116 if (pte_accessible(mm, pte))
116 flush_tlb_page(vma, address); 117 flush_tlb_page(vma, address);
117 return pte; 118 return pte;
118} 119}