aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc
diff options
context:
space:
mode:
authorNitin Gupta <nitin.m.gupta@oracle.com>2016-03-30 14:17:13 -0400
committerDavid S. Miller <davem@davemloft.net>2016-05-20 21:44:27 -0400
commit24e49ee3d76b70853a96520e46b8837e5eae65b2 (patch)
tree4893dd3011dc7a4db11082ce6953d1e1d2c13803 /arch/sparc
parentb1ac6b7b4061f6c92bacf6938f94fb61b2fbf7f3 (diff)
sparc64: Reduce TLB flushes during hugepte changes
During hugepage map/unmap, TSB and TLB flushes are currently issued at every PAGE_SIZE'd boundary which is unnecessary. We now issue the flush at REAL_HPAGE_SIZE boundaries only. Without this patch workloads which unmap a large hugepage backed VMA region get CPU lockups due to excessive TLB flush calls. Orabug: 22365539, 22643230, 22995196 Signed-off-by: Nitin Gupta <nitin.m.gupta@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc')
-rw-r--r--arch/sparc/include/asm/pgtable_64.h43
-rw-r--r--arch/sparc/include/asm/tlbflush_64.h3
-rw-r--r--arch/sparc/mm/hugetlbpage.c33
-rw-r--r--arch/sparc/mm/init_64.c12
-rw-r--r--arch/sparc/mm/tlb.c25
-rw-r--r--arch/sparc/mm/tsb.c32
6 files changed, 97 insertions, 51 deletions
diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h
index f089cfa249f3..5a189bf3c8ac 100644
--- a/arch/sparc/include/asm/pgtable_64.h
+++ b/arch/sparc/include/asm/pgtable_64.h
@@ -375,7 +375,7 @@ static inline pgprot_t pgprot_noncached(pgprot_t prot)
375#define pgprot_noncached pgprot_noncached 375#define pgprot_noncached pgprot_noncached
376 376
377#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 377#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
378static inline pte_t pte_mkhuge(pte_t pte) 378static inline unsigned long __pte_huge_mask(void)
379{ 379{
380 unsigned long mask; 380 unsigned long mask;
381 381
@@ -390,8 +390,19 @@ static inline pte_t pte_mkhuge(pte_t pte)
390 : "=r" (mask) 390 : "=r" (mask)
391 : "i" (_PAGE_SZHUGE_4U), "i" (_PAGE_SZHUGE_4V)); 391 : "i" (_PAGE_SZHUGE_4U), "i" (_PAGE_SZHUGE_4V));
392 392
393 return __pte(pte_val(pte) | mask); 393 return mask;
394}
395
396static inline pte_t pte_mkhuge(pte_t pte)
397{
398 return __pte(pte_val(pte) | __pte_huge_mask());
399}
400
401static inline bool is_hugetlb_pte(pte_t pte)
402{
403 return !!(pte_val(pte) & __pte_huge_mask());
394} 404}
405
395#ifdef CONFIG_TRANSPARENT_HUGEPAGE 406#ifdef CONFIG_TRANSPARENT_HUGEPAGE
396static inline pmd_t pmd_mkhuge(pmd_t pmd) 407static inline pmd_t pmd_mkhuge(pmd_t pmd)
397{ 408{
@@ -403,6 +414,11 @@ static inline pmd_t pmd_mkhuge(pmd_t pmd)
403 return __pmd(pte_val(pte)); 414 return __pmd(pte_val(pte));
404} 415}
405#endif 416#endif
417#else
418static inline bool is_hugetlb_pte(pte_t pte)
419{
420 return false;
421}
406#endif 422#endif
407 423
408static inline pte_t pte_mkdirty(pte_t pte) 424static inline pte_t pte_mkdirty(pte_t pte)
@@ -858,6 +874,19 @@ static inline unsigned long pud_pfn(pud_t pud)
858void tlb_batch_add(struct mm_struct *mm, unsigned long vaddr, 874void tlb_batch_add(struct mm_struct *mm, unsigned long vaddr,
859 pte_t *ptep, pte_t orig, int fullmm); 875 pte_t *ptep, pte_t orig, int fullmm);
860 876
877static void maybe_tlb_batch_add(struct mm_struct *mm, unsigned long vaddr,
878 pte_t *ptep, pte_t orig, int fullmm)
879{
880 /* It is more efficient to let flush_tlb_kernel_range()
881 * handle init_mm tlb flushes.
882 *
883 * SUN4V NOTE: _PAGE_VALID is the same value in both the SUN4U
884 * and SUN4V pte layout, so this inline test is fine.
885 */
886 if (likely(mm != &init_mm) && pte_accessible(mm, orig))
887 tlb_batch_add(mm, vaddr, ptep, orig, fullmm);
888}
889
861#define __HAVE_ARCH_PMDP_HUGE_GET_AND_CLEAR 890#define __HAVE_ARCH_PMDP_HUGE_GET_AND_CLEAR
862static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm, 891static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
863 unsigned long addr, 892 unsigned long addr,
@@ -874,15 +903,7 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
874 pte_t orig = *ptep; 903 pte_t orig = *ptep;
875 904
876 *ptep = pte; 905 *ptep = pte;
877 906 maybe_tlb_batch_add(mm, addr, ptep, orig, fullmm);
878 /* It is more efficient to let flush_tlb_kernel_range()
879 * handle init_mm tlb flushes.
880 *
881 * SUN4V NOTE: _PAGE_VALID is the same value in both the SUN4U
882 * and SUN4V pte layout, so this inline test is fine.
883 */
884 if (likely(mm != &init_mm) && pte_accessible(mm, orig))
885 tlb_batch_add(mm, addr, ptep, orig, fullmm);
886} 907}
887 908
888#define set_pte_at(mm,addr,ptep,pte) \ 909#define set_pte_at(mm,addr,ptep,pte) \
diff --git a/arch/sparc/include/asm/tlbflush_64.h b/arch/sparc/include/asm/tlbflush_64.h
index dea1cfa2122b..a8e192e90700 100644
--- a/arch/sparc/include/asm/tlbflush_64.h
+++ b/arch/sparc/include/asm/tlbflush_64.h
@@ -8,6 +8,7 @@
8#define TLB_BATCH_NR 192 8#define TLB_BATCH_NR 192
9 9
10struct tlb_batch { 10struct tlb_batch {
11 bool huge;
11 struct mm_struct *mm; 12 struct mm_struct *mm;
12 unsigned long tlb_nr; 13 unsigned long tlb_nr;
13 unsigned long active; 14 unsigned long active;
@@ -16,7 +17,7 @@ struct tlb_batch {
16 17
17void flush_tsb_kernel_range(unsigned long start, unsigned long end); 18void flush_tsb_kernel_range(unsigned long start, unsigned long end);
18void flush_tsb_user(struct tlb_batch *tb); 19void flush_tsb_user(struct tlb_batch *tb);
19void flush_tsb_user_page(struct mm_struct *mm, unsigned long vaddr); 20void flush_tsb_user_page(struct mm_struct *mm, unsigned long vaddr, bool huge);
20 21
21/* TLB flush operations. */ 22/* TLB flush operations. */
22 23
diff --git a/arch/sparc/mm/hugetlbpage.c b/arch/sparc/mm/hugetlbpage.c
index 4977800e9770..ba52e6466a82 100644
--- a/arch/sparc/mm/hugetlbpage.c
+++ b/arch/sparc/mm/hugetlbpage.c
@@ -176,17 +176,31 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
176 pte_t *ptep, pte_t entry) 176 pte_t *ptep, pte_t entry)
177{ 177{
178 int i; 178 int i;
179 pte_t orig[2];
180 unsigned long nptes;
179 181
180 if (!pte_present(*ptep) && pte_present(entry)) 182 if (!pte_present(*ptep) && pte_present(entry))
181 mm->context.huge_pte_count++; 183 mm->context.huge_pte_count++;
182 184
183 addr &= HPAGE_MASK; 185 addr &= HPAGE_MASK;
184 for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { 186
185 set_pte_at(mm, addr, ptep, entry); 187 nptes = 1 << HUGETLB_PAGE_ORDER;
188 orig[0] = *ptep;
189 orig[1] = *(ptep + nptes / 2);
190 for (i = 0; i < nptes; i++) {
191 *ptep = entry;
186 ptep++; 192 ptep++;
187 addr += PAGE_SIZE; 193 addr += PAGE_SIZE;
188 pte_val(entry) += PAGE_SIZE; 194 pte_val(entry) += PAGE_SIZE;
189 } 195 }
196
197 /* Issue TLB flush at REAL_HPAGE_SIZE boundaries */
198 addr -= REAL_HPAGE_SIZE;
199 ptep -= nptes / 2;
200 maybe_tlb_batch_add(mm, addr, ptep, orig[1], 0);
201 addr -= REAL_HPAGE_SIZE;
202 ptep -= nptes / 2;
203 maybe_tlb_batch_add(mm, addr, ptep, orig[0], 0);
190} 204}
191 205
192pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, 206pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
@@ -194,19 +208,28 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
194{ 208{
195 pte_t entry; 209 pte_t entry;
196 int i; 210 int i;
211 unsigned long nptes;
197 212
198 entry = *ptep; 213 entry = *ptep;
199 if (pte_present(entry)) 214 if (pte_present(entry))
200 mm->context.huge_pte_count--; 215 mm->context.huge_pte_count--;
201 216
202 addr &= HPAGE_MASK; 217 addr &= HPAGE_MASK;
203 218 nptes = 1 << HUGETLB_PAGE_ORDER;
204 for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) { 219 for (i = 0; i < nptes; i++) {
205 pte_clear(mm, addr, ptep); 220 *ptep = __pte(0UL);
206 addr += PAGE_SIZE; 221 addr += PAGE_SIZE;
207 ptep++; 222 ptep++;
208 } 223 }
209 224
225 /* Issue TLB flush at REAL_HPAGE_SIZE boundaries */
226 addr -= REAL_HPAGE_SIZE;
227 ptep -= nptes / 2;
228 maybe_tlb_batch_add(mm, addr, ptep, entry, 0);
229 addr -= REAL_HPAGE_SIZE;
230 ptep -= nptes / 2;
231 maybe_tlb_batch_add(mm, addr, ptep, entry, 0);
232
210 return entry; 233 return entry;
211} 234}
212 235
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 09e838801e39..652683cb4b4b 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -324,18 +324,6 @@ static void __update_mmu_tsb_insert(struct mm_struct *mm, unsigned long tsb_inde
324 tsb_insert(tsb, tag, tte); 324 tsb_insert(tsb, tag, tte);
325} 325}
326 326
327#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
328static inline bool is_hugetlb_pte(pte_t pte)
329{
330 if ((tlb_type == hypervisor &&
331 (pte_val(pte) & _PAGE_SZALL_4V) == _PAGE_SZHUGE_4V) ||
332 (tlb_type != hypervisor &&
333 (pte_val(pte) & _PAGE_SZALL_4U) == _PAGE_SZHUGE_4U))
334 return true;
335 return false;
336}
337#endif
338
339void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep) 327void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
340{ 328{
341 struct mm_struct *mm; 329 struct mm_struct *mm;
diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c
index 9df2190c097e..f81cd9736700 100644
--- a/arch/sparc/mm/tlb.c
+++ b/arch/sparc/mm/tlb.c
@@ -67,7 +67,7 @@ void arch_leave_lazy_mmu_mode(void)
67} 67}
68 68
69static void tlb_batch_add_one(struct mm_struct *mm, unsigned long vaddr, 69static void tlb_batch_add_one(struct mm_struct *mm, unsigned long vaddr,
70 bool exec) 70 bool exec, bool huge)
71{ 71{
72 struct tlb_batch *tb = &get_cpu_var(tlb_batch); 72 struct tlb_batch *tb = &get_cpu_var(tlb_batch);
73 unsigned long nr; 73 unsigned long nr;
@@ -84,13 +84,21 @@ static void tlb_batch_add_one(struct mm_struct *mm, unsigned long vaddr,
84 } 84 }
85 85
86 if (!tb->active) { 86 if (!tb->active) {
87 flush_tsb_user_page(mm, vaddr); 87 flush_tsb_user_page(mm, vaddr, huge);
88 global_flush_tlb_page(mm, vaddr); 88 global_flush_tlb_page(mm, vaddr);
89 goto out; 89 goto out;
90 } 90 }
91 91
92 if (nr == 0) 92 if (nr == 0) {
93 tb->mm = mm; 93 tb->mm = mm;
94 tb->huge = huge;
95 }
96
97 if (tb->huge != huge) {
98 flush_tlb_pending();
99 tb->huge = huge;
100 nr = 0;
101 }
94 102
95 tb->vaddrs[nr] = vaddr; 103 tb->vaddrs[nr] = vaddr;
96 tb->tlb_nr = ++nr; 104 tb->tlb_nr = ++nr;
@@ -104,6 +112,8 @@ out:
104void tlb_batch_add(struct mm_struct *mm, unsigned long vaddr, 112void tlb_batch_add(struct mm_struct *mm, unsigned long vaddr,
105 pte_t *ptep, pte_t orig, int fullmm) 113 pte_t *ptep, pte_t orig, int fullmm)
106{ 114{
115 bool huge = is_hugetlb_pte(orig);
116
107 if (tlb_type != hypervisor && 117 if (tlb_type != hypervisor &&
108 pte_dirty(orig)) { 118 pte_dirty(orig)) {
109 unsigned long paddr, pfn = pte_pfn(orig); 119 unsigned long paddr, pfn = pte_pfn(orig);
@@ -129,7 +139,7 @@ void tlb_batch_add(struct mm_struct *mm, unsigned long vaddr,
129 139
130no_cache_flush: 140no_cache_flush:
131 if (!fullmm) 141 if (!fullmm)
132 tlb_batch_add_one(mm, vaddr, pte_exec(orig)); 142 tlb_batch_add_one(mm, vaddr, pte_exec(orig), huge);
133} 143}
134 144
135#ifdef CONFIG_TRANSPARENT_HUGEPAGE 145#ifdef CONFIG_TRANSPARENT_HUGEPAGE
@@ -145,7 +155,7 @@ static void tlb_batch_pmd_scan(struct mm_struct *mm, unsigned long vaddr,
145 if (pte_val(*pte) & _PAGE_VALID) { 155 if (pte_val(*pte) & _PAGE_VALID) {
146 bool exec = pte_exec(*pte); 156 bool exec = pte_exec(*pte);
147 157
148 tlb_batch_add_one(mm, vaddr, exec); 158 tlb_batch_add_one(mm, vaddr, exec, false);
149 } 159 }
150 pte++; 160 pte++;
151 vaddr += PAGE_SIZE; 161 vaddr += PAGE_SIZE;
@@ -185,8 +195,9 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr,
185 pte_t orig_pte = __pte(pmd_val(orig)); 195 pte_t orig_pte = __pte(pmd_val(orig));
186 bool exec = pte_exec(orig_pte); 196 bool exec = pte_exec(orig_pte);
187 197
188 tlb_batch_add_one(mm, addr, exec); 198 tlb_batch_add_one(mm, addr, exec, true);
189 tlb_batch_add_one(mm, addr + REAL_HPAGE_SIZE, exec); 199 tlb_batch_add_one(mm, addr + REAL_HPAGE_SIZE, exec,
200 true);
190 } else { 201 } else {
191 tlb_batch_pmd_scan(mm, addr, orig); 202 tlb_batch_pmd_scan(mm, addr, orig);
192 } 203 }
diff --git a/arch/sparc/mm/tsb.c b/arch/sparc/mm/tsb.c
index a06576683c38..a0604a493a36 100644
--- a/arch/sparc/mm/tsb.c
+++ b/arch/sparc/mm/tsb.c
@@ -76,14 +76,15 @@ void flush_tsb_user(struct tlb_batch *tb)
76 76
77 spin_lock_irqsave(&mm->context.lock, flags); 77 spin_lock_irqsave(&mm->context.lock, flags);
78 78
79 base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb; 79 if (!tb->huge) {
80 nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries; 80 base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb;
81 if (tlb_type == cheetah_plus || tlb_type == hypervisor) 81 nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries;
82 base = __pa(base); 82 if (tlb_type == cheetah_plus || tlb_type == hypervisor)
83 __flush_tsb_one(tb, PAGE_SHIFT, base, nentries); 83 base = __pa(base);
84 84 __flush_tsb_one(tb, PAGE_SHIFT, base, nentries);
85 }
85#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 86#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
86 if (mm->context.tsb_block[MM_TSB_HUGE].tsb) { 87 if (tb->huge && mm->context.tsb_block[MM_TSB_HUGE].tsb) {
87 base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb; 88 base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb;
88 nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries; 89 nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
89 if (tlb_type == cheetah_plus || tlb_type == hypervisor) 90 if (tlb_type == cheetah_plus || tlb_type == hypervisor)
@@ -94,20 +95,21 @@ void flush_tsb_user(struct tlb_batch *tb)
94 spin_unlock_irqrestore(&mm->context.lock, flags); 95 spin_unlock_irqrestore(&mm->context.lock, flags);
95} 96}
96 97
97void flush_tsb_user_page(struct mm_struct *mm, unsigned long vaddr) 98void flush_tsb_user_page(struct mm_struct *mm, unsigned long vaddr, bool huge)
98{ 99{
99 unsigned long nentries, base, flags; 100 unsigned long nentries, base, flags;
100 101
101 spin_lock_irqsave(&mm->context.lock, flags); 102 spin_lock_irqsave(&mm->context.lock, flags);
102 103
103 base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb; 104 if (!huge) {
104 nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries; 105 base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb;
105 if (tlb_type == cheetah_plus || tlb_type == hypervisor) 106 nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries;
106 base = __pa(base); 107 if (tlb_type == cheetah_plus || tlb_type == hypervisor)
107 __flush_tsb_one_entry(base, vaddr, PAGE_SHIFT, nentries); 108 base = __pa(base);
108 109 __flush_tsb_one_entry(base, vaddr, PAGE_SHIFT, nentries);
110 }
109#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) 111#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
110 if (mm->context.tsb_block[MM_TSB_HUGE].tsb) { 112 if (huge && mm->context.tsb_block[MM_TSB_HUGE].tsb) {
111 base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb; 113 base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb;
112 nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries; 114 nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
113 if (tlb_type == cheetah_plus || tlb_type == hypervisor) 115 if (tlb_type == cheetah_plus || tlb_type == hypervisor)