aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel
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/kernel
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/kernel')
-rw-r--r--arch/sparc/kernel/smp_64.c41
1 files changed, 37 insertions, 4 deletions
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index 537eb66abd06..ca64d2a86ec0 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -849,7 +849,7 @@ void smp_tsb_sync(struct mm_struct *mm)
849} 849}
850 850
851extern unsigned long xcall_flush_tlb_mm; 851extern unsigned long xcall_flush_tlb_mm;
852extern unsigned long xcall_flush_tlb_pending; 852extern unsigned long xcall_flush_tlb_page;
853extern unsigned long xcall_flush_tlb_kernel_range; 853extern unsigned long xcall_flush_tlb_kernel_range;
854extern unsigned long xcall_fetch_glob_regs; 854extern unsigned long xcall_fetch_glob_regs;
855extern unsigned long xcall_fetch_glob_pmu; 855extern unsigned long xcall_fetch_glob_pmu;
@@ -1074,23 +1074,56 @@ local_flush_and_out:
1074 put_cpu(); 1074 put_cpu();
1075} 1075}
1076 1076
1077struct tlb_pending_info {
1078 unsigned long ctx;
1079 unsigned long nr;
1080 unsigned long *vaddrs;
1081};
1082
1083static void tlb_pending_func(void *info)
1084{
1085 struct tlb_pending_info *t = info;
1086
1087 __flush_tlb_pending(t->ctx, t->nr, t->vaddrs);
1088}
1089
1077void smp_flush_tlb_pending(struct mm_struct *mm, unsigned long nr, unsigned long *vaddrs) 1090void smp_flush_tlb_pending(struct mm_struct *mm, unsigned long nr, unsigned long *vaddrs)
1078{ 1091{
1079 u32 ctx = CTX_HWBITS(mm->context); 1092 u32 ctx = CTX_HWBITS(mm->context);
1093 struct tlb_pending_info info;
1080 int cpu = get_cpu(); 1094 int cpu = get_cpu();
1081 1095
1096 info.ctx = ctx;
1097 info.nr = nr;
1098 info.vaddrs = vaddrs;
1099
1082 if (mm == current->mm && atomic_read(&mm->mm_users) == 1) 1100 if (mm == current->mm && atomic_read(&mm->mm_users) == 1)
1083 cpumask_copy(mm_cpumask(mm), cpumask_of(cpu)); 1101 cpumask_copy(mm_cpumask(mm), cpumask_of(cpu));
1084 else 1102 else
1085 smp_cross_call_masked(&xcall_flush_tlb_pending, 1103 smp_call_function_many(mm_cpumask(mm), tlb_pending_func,
1086 ctx, nr, (unsigned long) vaddrs, 1104 &info, 1);
1087 mm_cpumask(mm));
1088 1105
1089 __flush_tlb_pending(ctx, nr, vaddrs); 1106 __flush_tlb_pending(ctx, nr, vaddrs);
1090 1107
1091 put_cpu(); 1108 put_cpu();
1092} 1109}
1093 1110
1111void smp_flush_tlb_page(struct mm_struct *mm, unsigned long vaddr)
1112{
1113 unsigned long context = CTX_HWBITS(mm->context);
1114 int cpu = get_cpu();
1115
1116 if (mm == current->mm && atomic_read(&mm->mm_users) == 1)
1117 cpumask_copy(mm_cpumask(mm), cpumask_of(cpu));
1118 else
1119 smp_cross_call_masked(&xcall_flush_tlb_page,
1120 context, vaddr, 0,
1121 mm_cpumask(mm));
1122 __flush_tlb_page(context, vaddr);
1123
1124 put_cpu();
1125}
1126
1094void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end) 1127void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end)
1095{ 1128{
1096 start &= PAGE_MASK; 1129 start &= PAGE_MASK;