aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2016-04-29 09:26:05 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2016-05-01 04:33:09 -0400
commit1a472c9dba6b9646fd36717968f6a531b4441c7d (patch)
tree3cab56eaa3a25ff717b38f4a712d430b48a78fb3
parent676012a66f651a98808459bc8ab75661828ed96f (diff)
powerpc/mm/radix: Add tlbflush routines
Core kernel doesn't track the page size of the VA range that we are invalidating. Hence we end up flushing TLB for the entire mm here. Later patches will improve this. We also don't flush page walk cache separetly instead use RIC=2 when flushing TLB, because we do a MMU gather flush after freeing page table. MMU_NO_CONTEXT is updated for hash. Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r--arch/powerpc/include/asm/book3s/64/mmu-hash.h1
-rw-r--r--arch/powerpc/include/asm/book3s/64/tlbflush-hash.h13
-rw-r--r--arch/powerpc/include/asm/book3s/64/tlbflush-radix.h33
-rw-r--r--arch/powerpc/include/asm/book3s/64/tlbflush.h20
-rw-r--r--arch/powerpc/include/asm/tlbflush.h1
-rw-r--r--arch/powerpc/kernel/mce_power.c3
-rw-r--r--arch/powerpc/mm/Makefile2
-rw-r--r--arch/powerpc/mm/tlb-radix.c242
8 files changed, 310 insertions, 5 deletions
diff --git a/arch/powerpc/include/asm/book3s/64/mmu-hash.h b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
index 7da61b85406b..290157e8d5b2 100644
--- a/arch/powerpc/include/asm/book3s/64/mmu-hash.h
+++ b/arch/powerpc/include/asm/book3s/64/mmu-hash.h
@@ -119,6 +119,7 @@
119#define POWER7_TLB_SETS 128 /* # sets in POWER7 TLB */ 119#define POWER7_TLB_SETS 128 /* # sets in POWER7 TLB */
120#define POWER8_TLB_SETS 512 /* # sets in POWER8 TLB */ 120#define POWER8_TLB_SETS 512 /* # sets in POWER8 TLB */
121#define POWER9_TLB_SETS_HASH 256 /* # sets in POWER9 TLB Hash mode */ 121#define POWER9_TLB_SETS_HASH 256 /* # sets in POWER9 TLB Hash mode */
122#define POWER9_TLB_SETS_RADIX 128 /* # sets in POWER9 TLB Radix mode */
122 123
123#ifndef __ASSEMBLY__ 124#ifndef __ASSEMBLY__
124 125
diff --git a/arch/powerpc/include/asm/book3s/64/tlbflush-hash.h b/arch/powerpc/include/asm/book3s/64/tlbflush-hash.h
index cc092ea0387c..f12ddf5e8de5 100644
--- a/arch/powerpc/include/asm/book3s/64/tlbflush-hash.h
+++ b/arch/powerpc/include/asm/book3s/64/tlbflush-hash.h
@@ -1,8 +1,6 @@
1#ifndef _ASM_POWERPC_BOOK3S_64_TLBFLUSH_HASH_H 1#ifndef _ASM_POWERPC_BOOK3S_64_TLBFLUSH_HASH_H
2#define _ASM_POWERPC_BOOK3S_64_TLBFLUSH_HASH_H 2#define _ASM_POWERPC_BOOK3S_64_TLBFLUSH_HASH_H
3 3
4#define MMU_NO_CONTEXT 0
5
6/* 4/*
7 * TLB flushing for 64-bit hash-MMU CPUs 5 * TLB flushing for 64-bit hash-MMU CPUs
8 */ 6 */
@@ -29,14 +27,21 @@ extern void __flush_tlb_pending(struct ppc64_tlb_batch *batch);
29 27
30static inline void arch_enter_lazy_mmu_mode(void) 28static inline void arch_enter_lazy_mmu_mode(void)
31{ 29{
32 struct ppc64_tlb_batch *batch = this_cpu_ptr(&ppc64_tlb_batch); 30 struct ppc64_tlb_batch *batch;
33 31
32 if (radix_enabled())
33 return;
34 batch = this_cpu_ptr(&ppc64_tlb_batch);
34 batch->active = 1; 35 batch->active = 1;
35} 36}
36 37
37static inline void arch_leave_lazy_mmu_mode(void) 38static inline void arch_leave_lazy_mmu_mode(void)
38{ 39{
39 struct ppc64_tlb_batch *batch = this_cpu_ptr(&ppc64_tlb_batch); 40 struct ppc64_tlb_batch *batch;
41
42 if (radix_enabled())
43 return;
44 batch = this_cpu_ptr(&ppc64_tlb_batch);
40 45
41 if (batch->index) 46 if (batch->index)
42 __flush_tlb_pending(batch); 47 __flush_tlb_pending(batch);
diff --git a/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h b/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h
new file mode 100644
index 000000000000..13ef38828dfe
--- /dev/null
+++ b/arch/powerpc/include/asm/book3s/64/tlbflush-radix.h
@@ -0,0 +1,33 @@
1#ifndef _ASM_POWERPC_TLBFLUSH_RADIX_H
2#define _ASM_POWERPC_TLBFLUSH_RADIX_H
3
4struct vm_area_struct;
5struct mm_struct;
6struct mmu_gather;
7
8static inline int mmu_get_ap(int psize)
9{
10 return mmu_psize_defs[psize].ap;
11}
12
13extern void radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
14 unsigned long end);
15extern void radix__flush_tlb_kernel_range(unsigned long start, unsigned long end);
16
17extern void radix__local_flush_tlb_mm(struct mm_struct *mm);
18extern void radix__local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
19extern void radix___local_flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
20 unsigned long ap, int nid);
21extern void radix__tlb_flush(struct mmu_gather *tlb);
22#ifdef CONFIG_SMP
23extern void radix__flush_tlb_mm(struct mm_struct *mm);
24extern void radix__flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
25extern void radix___flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
26 unsigned long ap, int nid);
27#else
28#define radix__flush_tlb_mm(mm) radix__local_flush_tlb_mm(mm)
29#define radix__flush_tlb_page(vma,addr) radix__local_flush_tlb_page(vma,addr)
30#define radix___flush_tlb_page(mm,addr,p,i) radix___local_flush_tlb_page(mm,addr,p,i)
31#endif
32
33#endif
diff --git a/arch/powerpc/include/asm/book3s/64/tlbflush.h b/arch/powerpc/include/asm/book3s/64/tlbflush.h
index 476ea24b0313..d98424ae356c 100644
--- a/arch/powerpc/include/asm/book3s/64/tlbflush.h
+++ b/arch/powerpc/include/asm/book3s/64/tlbflush.h
@@ -1,51 +1,71 @@
1#ifndef _ASM_POWERPC_BOOK3S_64_TLBFLUSH_H 1#ifndef _ASM_POWERPC_BOOK3S_64_TLBFLUSH_H
2#define _ASM_POWERPC_BOOK3S_64_TLBFLUSH_H 2#define _ASM_POWERPC_BOOK3S_64_TLBFLUSH_H
3 3
4#define MMU_NO_CONTEXT ~0UL
5
6
4#include <asm/book3s/64/tlbflush-hash.h> 7#include <asm/book3s/64/tlbflush-hash.h>
8#include <asm/book3s/64/tlbflush-radix.h>
5 9
6static inline void flush_tlb_range(struct vm_area_struct *vma, 10static inline void flush_tlb_range(struct vm_area_struct *vma,
7 unsigned long start, unsigned long end) 11 unsigned long start, unsigned long end)
8{ 12{
13 if (radix_enabled())
14 return radix__flush_tlb_range(vma, start, end);
9 return hash__flush_tlb_range(vma, start, end); 15 return hash__flush_tlb_range(vma, start, end);
10} 16}
11 17
12static inline void flush_tlb_kernel_range(unsigned long start, 18static inline void flush_tlb_kernel_range(unsigned long start,
13 unsigned long end) 19 unsigned long end)
14{ 20{
21 if (radix_enabled())
22 return radix__flush_tlb_kernel_range(start, end);
15 return hash__flush_tlb_kernel_range(start, end); 23 return hash__flush_tlb_kernel_range(start, end);
16} 24}
17 25
18static inline void local_flush_tlb_mm(struct mm_struct *mm) 26static inline void local_flush_tlb_mm(struct mm_struct *mm)
19{ 27{
28 if (radix_enabled())
29 return radix__local_flush_tlb_mm(mm);
20 return hash__local_flush_tlb_mm(mm); 30 return hash__local_flush_tlb_mm(mm);
21} 31}
22 32
23static inline void local_flush_tlb_page(struct vm_area_struct *vma, 33static inline void local_flush_tlb_page(struct vm_area_struct *vma,
24 unsigned long vmaddr) 34 unsigned long vmaddr)
25{ 35{
36 if (radix_enabled())
37 return radix__local_flush_tlb_page(vma, vmaddr);
26 return hash__local_flush_tlb_page(vma, vmaddr); 38 return hash__local_flush_tlb_page(vma, vmaddr);
27} 39}
28 40
29static inline void flush_tlb_page_nohash(struct vm_area_struct *vma, 41static inline void flush_tlb_page_nohash(struct vm_area_struct *vma,
30 unsigned long vmaddr) 42 unsigned long vmaddr)
31{ 43{
44 if (radix_enabled())
45 return radix__flush_tlb_page(vma, vmaddr);
32 return hash__flush_tlb_page_nohash(vma, vmaddr); 46 return hash__flush_tlb_page_nohash(vma, vmaddr);
33} 47}
34 48
35static inline void tlb_flush(struct mmu_gather *tlb) 49static inline void tlb_flush(struct mmu_gather *tlb)
36{ 50{
51 if (radix_enabled())
52 return radix__tlb_flush(tlb);
37 return hash__tlb_flush(tlb); 53 return hash__tlb_flush(tlb);
38} 54}
39 55
40#ifdef CONFIG_SMP 56#ifdef CONFIG_SMP
41static inline void flush_tlb_mm(struct mm_struct *mm) 57static inline void flush_tlb_mm(struct mm_struct *mm)
42{ 58{
59 if (radix_enabled())
60 return radix__flush_tlb_mm(mm);
43 return hash__flush_tlb_mm(mm); 61 return hash__flush_tlb_mm(mm);
44} 62}
45 63
46static inline void flush_tlb_page(struct vm_area_struct *vma, 64static inline void flush_tlb_page(struct vm_area_struct *vma,
47 unsigned long vmaddr) 65 unsigned long vmaddr)
48{ 66{
67 if (radix_enabled())
68 return radix__flush_tlb_page(vma, vmaddr);
49 return hash__flush_tlb_page(vma, vmaddr); 69 return hash__flush_tlb_page(vma, vmaddr);
50} 70}
51#else 71#else
diff --git a/arch/powerpc/include/asm/tlbflush.h b/arch/powerpc/include/asm/tlbflush.h
index 2fc4331c5bc5..1b38eea28e5a 100644
--- a/arch/powerpc/include/asm/tlbflush.h
+++ b/arch/powerpc/include/asm/tlbflush.h
@@ -58,6 +58,7 @@ extern void __flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
58 58
59#elif defined(CONFIG_PPC_STD_MMU_32) 59#elif defined(CONFIG_PPC_STD_MMU_32)
60 60
61#define MMU_NO_CONTEXT (0)
61/* 62/*
62 * TLB flushing for "classic" hash-MMU 32-bit CPUs, 6xx, 7xx, 7xxx 63 * TLB flushing for "classic" hash-MMU 32-bit CPUs, 6xx, 7xx, 7xxx
63 */ 64 */
diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c
index ee62b197502d..f64660254951 100644
--- a/arch/powerpc/kernel/mce_power.c
+++ b/arch/powerpc/kernel/mce_power.c
@@ -72,6 +72,9 @@ void __flush_tlb_power8(unsigned int action)
72 72
73void __flush_tlb_power9(unsigned int action) 73void __flush_tlb_power9(unsigned int action)
74{ 74{
75 if (radix_enabled())
76 flush_tlb_206(POWER9_TLB_SETS_RADIX, action);
77
75 flush_tlb_206(POWER9_TLB_SETS_HASH, action); 78 flush_tlb_206(POWER9_TLB_SETS_HASH, action);
76} 79}
77 80
diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile
index 9589236028f4..48aa11ae6a6b 100644
--- a/arch/powerpc/mm/Makefile
+++ b/arch/powerpc/mm/Makefile
@@ -15,7 +15,7 @@ obj-$(CONFIG_PPC_BOOK3E) += tlb_low_$(CONFIG_WORD_SIZE)e.o
15hash64-$(CONFIG_PPC_NATIVE) := hash_native_64.o 15hash64-$(CONFIG_PPC_NATIVE) := hash_native_64.o
16obj-$(CONFIG_PPC_BOOK3E_64) += pgtable-book3e.o 16obj-$(CONFIG_PPC_BOOK3E_64) += pgtable-book3e.o
17obj-$(CONFIG_PPC_STD_MMU_64) += pgtable-hash64.o hash_utils_64.o slb_low.o slb.o $(hash64-y) mmu_context_book3s64.o 17obj-$(CONFIG_PPC_STD_MMU_64) += pgtable-hash64.o hash_utils_64.o slb_low.o slb.o $(hash64-y) mmu_context_book3s64.o
18obj-$(CONFIG_PPC_RADIX_MMU) += pgtable-radix.o 18obj-$(CONFIG_PPC_RADIX_MMU) += pgtable-radix.o tlb-radix.o
19obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o hash_low_32.o mmu_context_hash32.o 19obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o hash_low_32.o mmu_context_hash32.o
20obj-$(CONFIG_PPC_STD_MMU) += tlb_hash$(CONFIG_WORD_SIZE).o 20obj-$(CONFIG_PPC_STD_MMU) += tlb_hash$(CONFIG_WORD_SIZE).o
21ifeq ($(CONFIG_PPC_STD_MMU_64),y) 21ifeq ($(CONFIG_PPC_STD_MMU_64),y)
diff --git a/arch/powerpc/mm/tlb-radix.c b/arch/powerpc/mm/tlb-radix.c
new file mode 100644
index 000000000000..ecfa00f81f1e
--- /dev/null
+++ b/arch/powerpc/mm/tlb-radix.c
@@ -0,0 +1,242 @@
1/*
2 * TLB flush routines for radix kernels.
3 *
4 * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/mm.h>
13#include <linux/hugetlb.h>
14#include <linux/memblock.h>
15
16#include <asm/tlb.h>
17#include <asm/tlbflush.h>
18
19static DEFINE_RAW_SPINLOCK(native_tlbie_lock);
20
21static inline void __tlbiel_pid(unsigned long pid, int set)
22{
23 unsigned long rb,rs,ric,prs,r;
24
25 rb = PPC_BIT(53); /* IS = 1 */
26 rb |= set << PPC_BITLSHIFT(51);
27 rs = ((unsigned long)pid) << PPC_BITLSHIFT(31);
28 prs = 1; /* process scoped */
29 r = 1; /* raidx format */
30 ric = 2; /* invalidate all the caches */
31
32 asm volatile("ptesync": : :"memory");
33 asm volatile(".long 0x7c000224 | (%0 << 11) | (%1 << 16) |"
34 "(%2 << 17) | (%3 << 18) | (%4 << 21)"
35 : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
36 asm volatile("ptesync": : :"memory");
37}
38
39/*
40 * We use 128 set in radix mode and 256 set in hpt mode.
41 */
42static inline void _tlbiel_pid(unsigned long pid)
43{
44 int set;
45
46 for (set = 0; set < POWER9_TLB_SETS_RADIX ; set++) {
47 __tlbiel_pid(pid, set);
48 }
49 return;
50}
51
52static inline void _tlbie_pid(unsigned long pid)
53{
54 unsigned long rb,rs,ric,prs,r;
55
56 rb = PPC_BIT(53); /* IS = 1 */
57 rs = pid << PPC_BITLSHIFT(31);
58 prs = 1; /* process scoped */
59 r = 1; /* raidx format */
60 ric = 2; /* invalidate all the caches */
61
62 asm volatile("ptesync": : :"memory");
63 asm volatile(".long 0x7c000264 | (%0 << 11) | (%1 << 16) |"
64 "(%2 << 17) | (%3 << 18) | (%4 << 21)"
65 : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
66 asm volatile("eieio; tlbsync; ptesync": : :"memory");
67}
68
69static inline void _tlbiel_va(unsigned long va, unsigned long pid,
70 unsigned long ap)
71{
72 unsigned long rb,rs,ric,prs,r;
73
74 rb = va & ~(PPC_BITMASK(52, 63));
75 rb |= ap << PPC_BITLSHIFT(58);
76 rs = pid << PPC_BITLSHIFT(31);
77 prs = 1; /* process scoped */
78 r = 1; /* raidx format */
79 ric = 0; /* no cluster flush yet */
80
81 asm volatile("ptesync": : :"memory");
82 asm volatile(".long 0x7c000224 | (%0 << 11) | (%1 << 16) |"
83 "(%2 << 17) | (%3 << 18) | (%4 << 21)"
84 : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
85 asm volatile("ptesync": : :"memory");
86}
87
88static inline void _tlbie_va(unsigned long va, unsigned long pid,
89 unsigned long ap)
90{
91 unsigned long rb,rs,ric,prs,r;
92
93 rb = va & ~(PPC_BITMASK(52, 63));
94 rb |= ap << PPC_BITLSHIFT(58);
95 rs = pid << PPC_BITLSHIFT(31);
96 prs = 1; /* process scoped */
97 r = 1; /* raidx format */
98 ric = 0; /* no cluster flush yet */
99
100 asm volatile("ptesync": : :"memory");
101 asm volatile(".long 0x7c000264 | (%0 << 11) | (%1 << 16) |"
102 "(%2 << 17) | (%3 << 18) | (%4 << 21)"
103 : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
104 asm volatile("eieio; tlbsync; ptesync": : :"memory");
105}
106
107/*
108 * Base TLB flushing operations:
109 *
110 * - flush_tlb_mm(mm) flushes the specified mm context TLB's
111 * - flush_tlb_page(vma, vmaddr) flushes one page
112 * - flush_tlb_range(vma, start, end) flushes a range of pages
113 * - flush_tlb_kernel_range(start, end) flushes kernel pages
114 *
115 * - local_* variants of page and mm only apply to the current
116 * processor
117 */
118void radix__local_flush_tlb_mm(struct mm_struct *mm)
119{
120 unsigned int pid;
121
122 preempt_disable();
123 pid = mm->context.id;
124 if (pid != MMU_NO_CONTEXT)
125 _tlbiel_pid(pid);
126 preempt_enable();
127}
128EXPORT_SYMBOL(radix__local_flush_tlb_mm);
129
130void radix___local_flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
131 unsigned long ap, int nid)
132{
133 unsigned int pid;
134
135 preempt_disable();
136 pid = mm ? mm->context.id : 0;
137 if (pid != MMU_NO_CONTEXT)
138 _tlbiel_va(vmaddr, pid, ap);
139 preempt_enable();
140}
141
142void radix__local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
143{
144 radix___local_flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr,
145 mmu_get_ap(mmu_virtual_psize), 0);
146}
147EXPORT_SYMBOL(radix__local_flush_tlb_page);
148
149#ifdef CONFIG_SMP
150static int mm_is_core_local(struct mm_struct *mm)
151{
152 return cpumask_subset(mm_cpumask(mm),
153 topology_sibling_cpumask(smp_processor_id()));
154}
155
156void radix__flush_tlb_mm(struct mm_struct *mm)
157{
158 unsigned int pid;
159
160 preempt_disable();
161 pid = mm->context.id;
162 if (unlikely(pid == MMU_NO_CONTEXT))
163 goto no_context;
164
165 if (!mm_is_core_local(mm)) {
166 int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
167
168 if (lock_tlbie)
169 raw_spin_lock(&native_tlbie_lock);
170 _tlbie_pid(pid);
171 if (lock_tlbie)
172 raw_spin_unlock(&native_tlbie_lock);
173 } else
174 _tlbiel_pid(pid);
175no_context:
176 preempt_enable();
177}
178EXPORT_SYMBOL(radix__flush_tlb_mm);
179
180void radix___flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
181 unsigned long ap, int nid)
182{
183 unsigned int pid;
184
185 preempt_disable();
186 pid = mm ? mm->context.id : 0;
187 if (unlikely(pid == MMU_NO_CONTEXT))
188 goto bail;
189 if (!mm_is_core_local(mm)) {
190 int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
191
192 if (lock_tlbie)
193 raw_spin_lock(&native_tlbie_lock);
194 _tlbie_va(vmaddr, pid, ap);
195 if (lock_tlbie)
196 raw_spin_unlock(&native_tlbie_lock);
197 } else
198 _tlbiel_va(vmaddr, pid, ap);
199bail:
200 preempt_enable();
201}
202
203void radix__flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
204{
205 radix___flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr,
206 mmu_get_ap(mmu_virtual_psize), 0);
207}
208EXPORT_SYMBOL(radix__flush_tlb_page);
209
210#endif /* CONFIG_SMP */
211
212void radix__flush_tlb_kernel_range(unsigned long start, unsigned long end)
213{
214 int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
215
216 if (lock_tlbie)
217 raw_spin_lock(&native_tlbie_lock);
218 _tlbie_pid(0);
219 if (lock_tlbie)
220 raw_spin_unlock(&native_tlbie_lock);
221}
222EXPORT_SYMBOL(radix__flush_tlb_kernel_range);
223
224/*
225 * Currently, for range flushing, we just do a full mm flush. Because
226 * we use this in code path where we don' track the page size.
227 */
228void radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
229 unsigned long end)
230
231{
232 struct mm_struct *mm = vma->vm_mm;
233 radix__flush_tlb_mm(mm);
234}
235EXPORT_SYMBOL(radix__flush_tlb_range);
236
237
238void radix__tlb_flush(struct mmu_gather *tlb)
239{
240 struct mm_struct *mm = tlb->mm;
241 radix__flush_tlb_mm(mm);
242}