s390/mm: simplify the TLB flushing code
[linux-2.6-block.git] / arch / s390 / include / asm / tlbflush.h
1 #ifndef _S390_TLBFLUSH_H
2 #define _S390_TLBFLUSH_H
3
4 #include <linux/mm.h>
5 #include <linux/sched.h>
6 #include <asm/processor.h>
7 #include <asm/pgalloc.h>
8 #include <asm/pgtable.h>
9
10 /*
11  * Flush all TLB entries on the local CPU.
12  */
13 static inline void __tlb_flush_local(void)
14 {
15         asm volatile("ptlb" : : : "memory");
16 }
17
18 /*
19  * Flush TLB entries for a specific ASCE on all CPUs
20  */
21 static inline void __tlb_flush_idte(unsigned long asce)
22 {
23         /* Global TLB flush for the mm */
24         asm volatile(
25                 "       .insn   rrf,0xb98e0000,0,%0,%1,0"
26                 : : "a" (2048), "a" (asce) : "cc");
27 }
28
29 /*
30  * Flush TLB entries for a specific ASCE on the local CPU
31  */
32 static inline void __tlb_flush_idte_local(unsigned long asce)
33 {
34         /* Local TLB flush for the mm */
35         asm volatile(
36                 "       .insn   rrf,0xb98e0000,0,%0,%1,1"
37                 : : "a" (2048), "a" (asce) : "cc");
38 }
39
40 #ifdef CONFIG_SMP
41 void smp_ptlb_all(void);
42
43 /*
44  * Flush all TLB entries on all CPUs.
45  */
46 static inline void __tlb_flush_global(void)
47 {
48         unsigned int dummy = 0;
49
50         csp(&dummy, 0, 0);
51 }
52
53 /*
54  * Flush TLB entries for a specific mm on all CPUs (in case gmap is used
55  * this implicates multiple ASCEs!).
56  */
57 static inline void __tlb_flush_full(struct mm_struct *mm)
58 {
59         preempt_disable();
60         atomic_inc(&mm->context.flush_count);
61         if (cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) {
62                 /* Local TLB flush */
63                 __tlb_flush_local();
64         } else {
65                 /* Global TLB flush */
66                 __tlb_flush_global();
67                 /* Reset TLB flush mask */
68                 if (MACHINE_HAS_TLB_LC)
69                         cpumask_copy(mm_cpumask(mm),
70                                      &mm->context.cpu_attach_mask);
71         }
72         atomic_dec(&mm->context.flush_count);
73         preempt_enable();
74 }
75
76 /*
77  * Flush TLB entries for a specific ASCE on all CPUs.
78  */
79 static inline void __tlb_flush_asce(struct mm_struct *mm, unsigned long asce)
80 {
81         preempt_disable();
82         atomic_inc(&mm->context.flush_count);
83         if (MACHINE_HAS_TLB_LC &&
84             cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) {
85                 __tlb_flush_idte_local(asce);
86         } else {
87                 if (MACHINE_HAS_IDTE)
88                         __tlb_flush_idte(asce);
89                 else
90                         __tlb_flush_global();
91                 /* Reset TLB flush mask */
92                 if (MACHINE_HAS_TLB_LC)
93                         cpumask_copy(mm_cpumask(mm),
94                                      &mm->context.cpu_attach_mask);
95         }
96         atomic_dec(&mm->context.flush_count);
97         preempt_enable();
98 }
99
100 static inline void __tlb_flush_kernel(void)
101 {
102         if (MACHINE_HAS_IDTE)
103                 __tlb_flush_idte(init_mm.context.asce);
104         else
105                 __tlb_flush_global();
106 }
107 #else
108 #define __tlb_flush_global()    __tlb_flush_local()
109 #define __tlb_flush_full(mm)    __tlb_flush_local()
110
111 /*
112  * Flush TLB entries for a specific ASCE on all CPUs.
113  */
114 static inline void __tlb_flush_asce(struct mm_struct *mm, unsigned long asce)
115 {
116         if (MACHINE_HAS_TLB_LC)
117                 __tlb_flush_idte_local(asce);
118         else
119                 __tlb_flush_local();
120 }
121
122 static inline void __tlb_flush_kernel(void)
123 {
124         if (MACHINE_HAS_TLB_LC)
125                 __tlb_flush_idte_local(init_mm.context.asce);
126         else
127                 __tlb_flush_local();
128 }
129 #endif
130
131 static inline void __tlb_flush_mm(struct mm_struct * mm)
132 {
133         /*
134          * If the machine has IDTE we prefer to do a per mm flush
135          * on all cpus instead of doing a local flush if the mm
136          * only ran on the local cpu.
137          */
138         if (MACHINE_HAS_IDTE && list_empty(&mm->context.gmap_list))
139                 __tlb_flush_asce(mm, mm->context.asce);
140         else
141                 __tlb_flush_full(mm);
142 }
143
144 static inline void __tlb_flush_mm_lazy(struct mm_struct * mm)
145 {
146         if (mm->context.flush_mm) {
147                 __tlb_flush_mm(mm);
148                 mm->context.flush_mm = 0;
149         }
150 }
151
152 /*
153  * TLB flushing:
154  *  flush_tlb() - flushes the current mm struct TLBs
155  *  flush_tlb_all() - flushes all processes TLBs
156  *  flush_tlb_mm(mm) - flushes the specified mm context TLB's
157  *  flush_tlb_page(vma, vmaddr) - flushes one page
158  *  flush_tlb_range(vma, start, end) - flushes a range of pages
159  *  flush_tlb_kernel_range(start, end) - flushes a range of kernel pages
160  */
161
162 /*
163  * flush_tlb_mm goes together with ptep_set_wrprotect for the
164  * copy_page_range operation and flush_tlb_range is related to
165  * ptep_get_and_clear for change_protection. ptep_set_wrprotect and
166  * ptep_get_and_clear do not flush the TLBs directly if the mm has
167  * only one user. At the end of the update the flush_tlb_mm and
168  * flush_tlb_range functions need to do the flush.
169  */
170 #define flush_tlb()                             do { } while (0)
171 #define flush_tlb_all()                         do { } while (0)
172 #define flush_tlb_page(vma, addr)               do { } while (0)
173
174 static inline void flush_tlb_mm(struct mm_struct *mm)
175 {
176         __tlb_flush_mm_lazy(mm);
177 }
178
179 static inline void flush_tlb_range(struct vm_area_struct *vma,
180                                    unsigned long start, unsigned long end)
181 {
182         __tlb_flush_mm_lazy(vma->vm_mm);
183 }
184
185 static inline void flush_tlb_kernel_range(unsigned long start,
186                                           unsigned long end)
187 {
188         __tlb_flush_kernel();
189 }
190
191 #endif /* _S390_TLBFLUSH_H */