aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/mm
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2013-04-19 17:26:26 -0400
committerDavid S. Miller <davem@davemloft.net>2013-04-19 17:26:26 -0400
commitf36391d2790d04993f48da6a45810033a2cdf847 (patch)
treeeb8672bd438756e49dd104006e443ac4ba991533 /arch/sparc/mm
parentcbf1ef6b3345d2cc7e62407eec6a6f72a8b1346f (diff)
sparc64: Fix race in TLB batch processing.
As reported by Dave Kleikamp, when we emit cross calls to do batched TLB flush processing we have a race because we do not synchronize on the sibling cpus completing the cross call. So meanwhile the TLB batch can be reset (tb->tlb_nr set to zero, etc.) and either flushes are missed or flushes will flush the wrong addresses. Fix this by using generic infrastructure to synchonize on the completion of the cross call. This first required getting the flush_tlb_pending() call out from switch_to() which operates with locks held and interrupts disabled. The problem is that smp_call_function_many() cannot be invoked with IRQs disabled and this is explicitly checked for with WARN_ON_ONCE(). We get the batch processing outside of locked IRQ disabled sections by using some ideas from the powerpc port. Namely, we only batch inside of arch_{enter,leave}_lazy_mmu_mode() calls. If we're not in such a region, we flush TLBs synchronously. 1) Get rid of xcall_flush_tlb_pending and per-cpu type implementations. 2) Do TLB batch cross calls instead via: smp_call_function_many() tlb_pending_func() __flush_tlb_pending() 3) Batch only in lazy mmu sequences: a) Add 'active' member to struct tlb_batch b) Define __HAVE_ARCH_ENTER_LAZY_MMU_MODE c) Set 'active' in arch_enter_lazy_mmu_mode() d) Run batch and clear 'active' in arch_leave_lazy_mmu_mode() e) Check 'active' in tlb_batch_add_one() and do a synchronous flush if it's clear. 4) Add infrastructure for synchronous TLB page flushes. a) Implement __flush_tlb_page and per-cpu variants, patch as needed. b) Likewise for xcall_flush_tlb_page. c) Implement smp_flush_tlb_page() to invoke the cross-call. d) Wire up global_flush_tlb_page() to the right routine based upon CONFIG_SMP 5) It turns out that singleton batches are very common, 2 out of every 3 batch flushes have only a single entry in them. The batch flush waiting is very expensive, both because of the poll on sibling cpu completeion, as well as because passing the tlb batch pointer to the sibling cpus invokes a shared memory dereference. Therefore, in flush_tlb_pending(), if there is only one entry in the batch perform a completely asynchronous global_flush_tlb_page() instead. Reported-by: Dave Kleikamp <dave.kleikamp@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net> Acked-by: Dave Kleikamp <dave.kleikamp@oracle.com>
Diffstat (limited to 'arch/sparc/mm')
-rw-r--r--arch/sparc/mm/tlb.c38
-rw-r--r--arch/sparc/mm/tsb.c57
-rw-r--r--arch/sparc/mm/ultra.S119
3 files changed, 171 insertions, 43 deletions
diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c
index ba6ae7ffdc2c..272aa4f7657e 100644
--- a/arch/sparc/mm/tlb.c
+++ b/arch/sparc/mm/tlb.c
@@ -24,11 +24,17 @@ static DEFINE_PER_CPU(struct tlb_batch, tlb_batch);
24void flush_tlb_pending(void) 24void flush_tlb_pending(void)
25{ 25{
26 struct tlb_batch *tb = &get_cpu_var(tlb_batch); 26 struct tlb_batch *tb = &get_cpu_var(tlb_batch);
27 struct mm_struct *mm = tb->mm;
27 28
28 if (tb->tlb_nr) { 29 if (!tb->tlb_nr)
29 flush_tsb_user(tb); 30 goto out;
30 31
31 if (CTX_VALID(tb->mm->context)) { 32 flush_tsb_user(tb);
33
34 if (CTX_VALID(mm->context)) {
35 if (tb->tlb_nr == 1) {
36 global_flush_tlb_page(mm, tb->vaddrs[0]);
37 } else {
32#ifdef CONFIG_SMP 38#ifdef CONFIG_SMP
33 smp_flush_tlb_pending(tb->mm, tb->tlb_nr, 39 smp_flush_tlb_pending(tb->mm, tb->tlb_nr,
34 &tb->vaddrs[0]); 40 &tb->vaddrs[0]);
@@ -37,12 +43,30 @@ void flush_tlb_pending(void)
37 tb->tlb_nr, &tb->vaddrs[0]); 43 tb->tlb_nr, &tb->vaddrs[0]);
38#endif 44#endif
39 } 45 }
40 tb->tlb_nr = 0;
41 } 46 }
42 47
48 tb->tlb_nr = 0;
49
50out:
43 put_cpu_var(tlb_batch); 51 put_cpu_var(tlb_batch);
44} 52}
45 53
54void arch_enter_lazy_mmu_mode(void)
55{
56 struct tlb_batch *tb = &__get_cpu_var(tlb_batch);
57
58 tb->active = 1;
59}
60
61void arch_leave_lazy_mmu_mode(void)
62{
63 struct tlb_batch *tb = &__get_cpu_var(tlb_batch);
64
65 if (tb->tlb_nr)
66 flush_tlb_pending();
67 tb->active = 0;
68}
69
46static void tlb_batch_add_one(struct mm_struct *mm, unsigned long vaddr, 70static void tlb_batch_add_one(struct mm_struct *mm, unsigned long vaddr,
47 bool exec) 71 bool exec)
48{ 72{
@@ -60,6 +84,12 @@ static void tlb_batch_add_one(struct mm_struct *mm, unsigned long vaddr,
60 nr = 0; 84 nr = 0;
61 } 85 }
62 86
87 if (!tb->active) {
88 global_flush_tlb_page(mm, vaddr);
89 flush_tsb_user_page(mm, vaddr);
90 return;
91 }
92
63 if (nr == 0) 93 if (nr == 0)
64 tb->mm = mm; 94 tb->mm = mm;
65 95
diff --git a/arch/sparc/mm/tsb.c b/arch/sparc/mm/tsb.c
index 428982b9becf..2cc3bce5ee91 100644
--- a/arch/sparc/mm/tsb.c
+++ b/arch/sparc/mm/tsb.c
@@ -7,11 +7,10 @@
7#include <linux/preempt.h> 7#include <linux/preempt.h>
8#include <linux/slab.h> 8#include <linux/slab.h>
9#include <asm/page.h> 9#include <asm/page.h>
10#include <asm/tlbflush.h>
11#include <asm/tlb.h>
12#include <asm/mmu_context.h>
13#include <asm/pgtable.h> 10#include <asm/pgtable.h>
11#include <asm/mmu_context.h>
14#include <asm/tsb.h> 12#include <asm/tsb.h>
13#include <asm/tlb.h>
15#include <asm/oplib.h> 14#include <asm/oplib.h>
16 15
17extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES]; 16extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
@@ -46,23 +45,27 @@ void flush_tsb_kernel_range(unsigned long start, unsigned long end)
46 } 45 }
47} 46}
48 47
49static void __flush_tsb_one(struct tlb_batch *tb, unsigned long hash_shift, 48static void __flush_tsb_one_entry(unsigned long tsb, unsigned long v,
50 unsigned long tsb, unsigned long nentries) 49 unsigned long hash_shift,
50 unsigned long nentries)
51{ 51{
52 unsigned long i; 52 unsigned long tag, ent, hash;
53 53
54 for (i = 0; i < tb->tlb_nr; i++) { 54 v &= ~0x1UL;
55 unsigned long v = tb->vaddrs[i]; 55 hash = tsb_hash(v, hash_shift, nentries);
56 unsigned long tag, ent, hash; 56 ent = tsb + (hash * sizeof(struct tsb));
57 tag = (v >> 22UL);
57 58
58 v &= ~0x1UL; 59 tsb_flush(ent, tag);
60}
59 61
60 hash = tsb_hash(v, hash_shift, nentries); 62static void __flush_tsb_one(struct tlb_batch *tb, unsigned long hash_shift,
61 ent = tsb + (hash * sizeof(struct tsb)); 63 unsigned long tsb, unsigned long nentries)
62 tag = (v >> 22UL); 64{
65 unsigned long i;
63 66
64 tsb_flush(ent, tag); 67 for (i = 0; i < tb->tlb_nr; i++)
65 } 68 __flush_tsb_one_entry(tsb, tb->vaddrs[i], hash_shift, nentries);
66} 69}
67 70
68void flush_tsb_user(struct tlb_batch *tb) 71void flush_tsb_user(struct tlb_batch *tb)
@@ -90,6 +93,30 @@ void flush_tsb_user(struct tlb_batch *tb)
90 spin_unlock_irqrestore(&mm->context.lock, flags); 93 spin_unlock_irqrestore(&mm->context.lock, flags);
91} 94}
92 95
96void flush_tsb_user_page(struct mm_struct *mm, unsigned long vaddr)
97{
98 unsigned long nentries, base, flags;
99
100 spin_lock_irqsave(&mm->context.lock, flags);
101
102 base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb;
103 nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries;
104 if (tlb_type == cheetah_plus || tlb_type == hypervisor)
105 base = __pa(base);
106 __flush_tsb_one_entry(base, vaddr, PAGE_SHIFT, nentries);
107
108#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
109 if (mm->context.tsb_block[MM_TSB_HUGE].tsb) {
110 base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb;
111 nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
112 if (tlb_type == cheetah_plus || tlb_type == hypervisor)
113 base = __pa(base);
114 __flush_tsb_one_entry(base, vaddr, HPAGE_SHIFT, nentries);
115 }
116#endif
117 spin_unlock_irqrestore(&mm->context.lock, flags);
118}
119
93#define HV_PGSZ_IDX_BASE HV_PGSZ_IDX_8K 120#define HV_PGSZ_IDX_BASE HV_PGSZ_IDX_8K
94#define HV_PGSZ_MASK_BASE HV_PGSZ_MASK_8K 121#define HV_PGSZ_MASK_BASE HV_PGSZ_MASK_8K
95 122
diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S
index f8e13d421fcb..432aa0cb1b38 100644
--- a/arch/sparc/mm/ultra.S
+++ b/arch/sparc/mm/ultra.S
@@ -53,6 +53,33 @@ __flush_tlb_mm: /* 18 insns */
53 nop 53 nop
54 54
55 .align 32 55 .align 32
56 .globl __flush_tlb_page
57__flush_tlb_page: /* 22 insns */
58 /* %o0 = context, %o1 = vaddr */
59 rdpr %pstate, %g7
60 andn %g7, PSTATE_IE, %g2
61 wrpr %g2, %pstate
62 mov SECONDARY_CONTEXT, %o4
63 ldxa [%o4] ASI_DMMU, %g2
64 stxa %o0, [%o4] ASI_DMMU
65 andcc %o1, 1, %g0
66 andn %o1, 1, %o3
67 be,pn %icc, 1f
68 or %o3, 0x10, %o3
69 stxa %g0, [%o3] ASI_IMMU_DEMAP
701: stxa %g0, [%o3] ASI_DMMU_DEMAP
71 membar #Sync
72 stxa %g2, [%o4] ASI_DMMU
73 sethi %hi(KERNBASE), %o4
74 flush %o4
75 retl
76 wrpr %g7, 0x0, %pstate
77 nop
78 nop
79 nop
80 nop
81
82 .align 32
56 .globl __flush_tlb_pending 83 .globl __flush_tlb_pending
57__flush_tlb_pending: /* 26 insns */ 84__flush_tlb_pending: /* 26 insns */
58 /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */ 85 /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
@@ -203,6 +230,31 @@ __cheetah_flush_tlb_mm: /* 19 insns */
203 retl 230 retl
204 wrpr %g7, 0x0, %pstate 231 wrpr %g7, 0x0, %pstate
205 232
233__cheetah_flush_tlb_page: /* 22 insns */
234 /* %o0 = context, %o1 = vaddr */
235 rdpr %pstate, %g7
236 andn %g7, PSTATE_IE, %g2
237 wrpr %g2, 0x0, %pstate
238 wrpr %g0, 1, %tl
239 mov PRIMARY_CONTEXT, %o4
240 ldxa [%o4] ASI_DMMU, %g2
241 srlx %g2, CTX_PGSZ1_NUC_SHIFT, %o3
242 sllx %o3, CTX_PGSZ1_NUC_SHIFT, %o3
243 or %o0, %o3, %o0 /* Preserve nucleus page size fields */
244 stxa %o0, [%o4] ASI_DMMU
245 andcc %o1, 1, %g0
246 be,pn %icc, 1f
247 andn %o1, 1, %o3
248 stxa %g0, [%o3] ASI_IMMU_DEMAP
2491: stxa %g0, [%o3] ASI_DMMU_DEMAP
250 membar #Sync
251 stxa %g2, [%o4] ASI_DMMU
252 sethi %hi(KERNBASE), %o4
253 flush %o4
254 wrpr %g0, 0, %tl
255 retl
256 wrpr %g7, 0x0, %pstate
257
206__cheetah_flush_tlb_pending: /* 27 insns */ 258__cheetah_flush_tlb_pending: /* 27 insns */
207 /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */ 259 /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
208 rdpr %pstate, %g7 260 rdpr %pstate, %g7
@@ -269,6 +321,20 @@ __hypervisor_flush_tlb_mm: /* 10 insns */
269 retl 321 retl
270 nop 322 nop
271 323
324__hypervisor_flush_tlb_page: /* 11 insns */
325 /* %o0 = context, %o1 = vaddr */
326 mov %o0, %g2
327 mov %o1, %o0 /* ARG0: vaddr + IMMU-bit */
328 mov %g2, %o1 /* ARG1: mmu context */
329 mov HV_MMU_ALL, %o2 /* ARG2: flags */
330 srlx %o0, PAGE_SHIFT, %o0
331 sllx %o0, PAGE_SHIFT, %o0
332 ta HV_MMU_UNMAP_ADDR_TRAP
333 brnz,pn %o0, __hypervisor_tlb_tl0_error
334 mov HV_MMU_UNMAP_ADDR_TRAP, %o1
335 retl
336 nop
337
272__hypervisor_flush_tlb_pending: /* 16 insns */ 338__hypervisor_flush_tlb_pending: /* 16 insns */
273 /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */ 339 /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
274 sllx %o1, 3, %g1 340 sllx %o1, 3, %g1
@@ -339,6 +405,13 @@ cheetah_patch_cachetlbops:
339 call tlb_patch_one 405 call tlb_patch_one
340 mov 19, %o2 406 mov 19, %o2
341 407
408 sethi %hi(__flush_tlb_page), %o0
409 or %o0, %lo(__flush_tlb_page), %o0
410 sethi %hi(__cheetah_flush_tlb_page), %o1
411 or %o1, %lo(__cheetah_flush_tlb_page), %o1
412 call tlb_patch_one
413 mov 22, %o2
414
342 sethi %hi(__flush_tlb_pending), %o0 415 sethi %hi(__flush_tlb_pending), %o0
343 or %o0, %lo(__flush_tlb_pending), %o0 416 or %o0, %lo(__flush_tlb_pending), %o0
344 sethi %hi(__cheetah_flush_tlb_pending), %o1 417 sethi %hi(__cheetah_flush_tlb_pending), %o1
@@ -397,10 +470,9 @@ xcall_flush_tlb_mm: /* 21 insns */
397 nop 470 nop
398 nop 471 nop
399 472
400 .globl xcall_flush_tlb_pending 473 .globl xcall_flush_tlb_page
401xcall_flush_tlb_pending: /* 21 insns */ 474xcall_flush_tlb_page: /* 17 insns */
402 /* %g5=context, %g1=nr, %g7=vaddrs[] */ 475 /* %g5=context, %g1=vaddr */
403 sllx %g1, 3, %g1
404 mov PRIMARY_CONTEXT, %g4 476 mov PRIMARY_CONTEXT, %g4
405 ldxa [%g4] ASI_DMMU, %g2 477 ldxa [%g4] ASI_DMMU, %g2
406 srlx %g2, CTX_PGSZ1_NUC_SHIFT, %g4 478 srlx %g2, CTX_PGSZ1_NUC_SHIFT, %g4
@@ -408,20 +480,16 @@ xcall_flush_tlb_pending: /* 21 insns */
408 or %g5, %g4, %g5 480 or %g5, %g4, %g5
409 mov PRIMARY_CONTEXT, %g4 481 mov PRIMARY_CONTEXT, %g4
410 stxa %g5, [%g4] ASI_DMMU 482 stxa %g5, [%g4] ASI_DMMU
4111: sub %g1, (1 << 3), %g1 483 andcc %g1, 0x1, %g0
412 ldx [%g7 + %g1], %g5
413 andcc %g5, 0x1, %g0
414 be,pn %icc, 2f 484 be,pn %icc, 2f
415 485 andn %g1, 0x1, %g5
416 andn %g5, 0x1, %g5
417 stxa %g0, [%g5] ASI_IMMU_DEMAP 486 stxa %g0, [%g5] ASI_IMMU_DEMAP
4182: stxa %g0, [%g5] ASI_DMMU_DEMAP 4872: stxa %g0, [%g5] ASI_DMMU_DEMAP
419 membar #Sync 488 membar #Sync
420 brnz,pt %g1, 1b
421 nop
422 stxa %g2, [%g4] ASI_DMMU 489 stxa %g2, [%g4] ASI_DMMU
423 retry 490 retry
424 nop 491 nop
492 nop
425 493
426 .globl xcall_flush_tlb_kernel_range 494 .globl xcall_flush_tlb_kernel_range
427xcall_flush_tlb_kernel_range: /* 25 insns */ 495xcall_flush_tlb_kernel_range: /* 25 insns */
@@ -656,15 +724,13 @@ __hypervisor_xcall_flush_tlb_mm: /* 21 insns */
656 membar #Sync 724 membar #Sync
657 retry 725 retry
658 726
659 .globl __hypervisor_xcall_flush_tlb_pending 727 .globl __hypervisor_xcall_flush_tlb_page
660__hypervisor_xcall_flush_tlb_pending: /* 21 insns */ 728__hypervisor_xcall_flush_tlb_page: /* 17 insns */
661 /* %g5=ctx, %g1=nr, %g7=vaddrs[], %g2,%g3,%g4,g6=scratch */ 729 /* %g5=ctx, %g1=vaddr */
662 sllx %g1, 3, %g1
663 mov %o0, %g2 730 mov %o0, %g2
664 mov %o1, %g3 731 mov %o1, %g3
665 mov %o2, %g4 732 mov %o2, %g4
6661: sub %g1, (1 << 3), %g1 733 mov %g1, %o0 /* ARG0: virtual address */
667 ldx [%g7 + %g1], %o0 /* ARG0: virtual address */
668 mov %g5, %o1 /* ARG1: mmu context */ 734 mov %g5, %o1 /* ARG1: mmu context */
669 mov HV_MMU_ALL, %o2 /* ARG2: flags */ 735 mov HV_MMU_ALL, %o2 /* ARG2: flags */
670 srlx %o0, PAGE_SHIFT, %o0 736 srlx %o0, PAGE_SHIFT, %o0
@@ -673,8 +739,6 @@ __hypervisor_xcall_flush_tlb_pending: /* 21 insns */
673 mov HV_MMU_UNMAP_ADDR_TRAP, %g6 739 mov HV_MMU_UNMAP_ADDR_TRAP, %g6
674 brnz,a,pn %o0, __hypervisor_tlb_xcall_error 740 brnz,a,pn %o0, __hypervisor_tlb_xcall_error
675 mov %o0, %g5 741 mov %o0, %g5
676 brnz,pt %g1, 1b
677 nop
678 mov %g2, %o0 742 mov %g2, %o0
679 mov %g3, %o1 743 mov %g3, %o1
680 mov %g4, %o2 744 mov %g4, %o2
@@ -757,6 +821,13 @@ hypervisor_patch_cachetlbops:
757 call tlb_patch_one 821 call tlb_patch_one
758 mov 10, %o2 822 mov 10, %o2
759 823
824 sethi %hi(__flush_tlb_page), %o0
825 or %o0, %lo(__flush_tlb_page), %o0
826 sethi %hi(__hypervisor_flush_tlb_page), %o1
827 or %o1, %lo(__hypervisor_flush_tlb_page), %o1
828 call tlb_patch_one
829 mov 11, %o2
830
760 sethi %hi(__flush_tlb_pending), %o0 831 sethi %hi(__flush_tlb_pending), %o0
761 or %o0, %lo(__flush_tlb_pending), %o0 832 or %o0, %lo(__flush_tlb_pending), %o0
762 sethi %hi(__hypervisor_flush_tlb_pending), %o1 833 sethi %hi(__hypervisor_flush_tlb_pending), %o1
@@ -788,12 +859,12 @@ hypervisor_patch_cachetlbops:
788 call tlb_patch_one 859 call tlb_patch_one
789 mov 21, %o2 860 mov 21, %o2
790 861
791 sethi %hi(xcall_flush_tlb_pending), %o0 862 sethi %hi(xcall_flush_tlb_page), %o0
792 or %o0, %lo(xcall_flush_tlb_pending), %o0 863 or %o0, %lo(xcall_flush_tlb_page), %o0
793 sethi %hi(__hypervisor_xcall_flush_tlb_pending), %o1 864 sethi %hi(__hypervisor_xcall_flush_tlb_page), %o1
794 or %o1, %lo(__hypervisor_xcall_flush_tlb_pending), %o1 865 or %o1, %lo(__hypervisor_xcall_flush_tlb_page), %o1
795 call tlb_patch_one 866 call tlb_patch_one
796 mov 21, %o2 867 mov 17, %o2
797 868
798 sethi %hi(xcall_flush_tlb_kernel_range), %o0 869 sethi %hi(xcall_flush_tlb_kernel_range), %o0
799 or %o0, %lo(xcall_flush_tlb_kernel_range), %o0 870 or %o0, %lo(xcall_flush_tlb_kernel_range), %o0