diff options
-rw-r--r-- | arch/sparc/include/asm/pgtable_64.h | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/pgtable.h | 11 | ||||
-rw-r--r-- | include/asm-generic/pgtable.h | 2 | ||||
-rw-r--r-- | include/linux/mm_types.h | 44 | ||||
-rw-r--r-- | kernel/fork.c | 1 | ||||
-rw-r--r-- | mm/huge_memory.c | 7 | ||||
-rw-r--r-- | mm/mprotect.c | 2 | ||||
-rw-r--r-- | mm/pgtable-generic.c | 5 |
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 |
622 | static inline unsigned long pte_accessible(pte_t a) | 622 | static 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 |
455 | static inline int pte_accessible(pte_t a) | 455 | static 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 | ||
460 | static inline int pte_hidden(pte_t pte) | 467 | static 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 | */ | ||
477 | static inline bool mm_tlb_flush_pending(struct mm_struct *mm) | ||
478 | { | ||
479 | barrier(); | ||
480 | return mm->tlb_flush_pending; | ||
481 | } | ||
482 | static 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. */ | ||
488 | static inline void clear_tlb_flush_pending(struct mm_struct *mm) | ||
489 | { | ||
490 | barrier(); | ||
491 | mm->tlb_flush_pending = false; | ||
492 | } | ||
493 | #else | ||
494 | static inline bool mm_tlb_flush_pending(struct mm_struct *mm) | ||
495 | { | ||
496 | return false; | ||
497 | } | ||
498 | static inline void set_tlb_flush_pending(struct mm_struct *mm) | ||
499 | { | ||
500 | } | ||
501 | static 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, | |||
110 | pte_t ptep_clear_flush(struct vm_area_struct *vma, unsigned long address, | 110 | pte_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 | } |