cleanup
[linux-2.4.21-pre4.git] / include / asm-s390 / pgalloc.h
1 /*
2  *  include/asm-s390/bugs.h
3  *
4  *  S390 version
5  *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
6  *    Author(s): Hartmut Penner (hp@de.ibm.com)
7  *               Martin Schwidefsky (schwidefsky@de.ibm.com)
8  *
9  *  Derived from "include/asm-i386/pgalloc.h"
10  *    Copyright (C) 1994  Linus Torvalds
11  */
12
13 #ifndef _S390_PGALLOC_H
14 #define _S390_PGALLOC_H
15
16 #include <linux/config.h>
17 #include <asm/processor.h>
18 #include <linux/threads.h>
19
20 #define pgd_quicklist (S390_lowcore.cpu_data.pgd_quick)
21 #define pmd_quicklist ((unsigned long *)0)
22 #define pte_quicklist (S390_lowcore.cpu_data.pte_quick)
23 #define pgtable_cache_size (S390_lowcore.cpu_data.pgtable_cache_sz)
24
25 /*
26  * Allocate and free page tables. The xxx_kernel() versions are
27  * used to allocate a kernel page table - this turns on ASN bits
28  * if any.
29  */
30
31 extern __inline__ pgd_t* get_pgd_slow(void)
32 {
33         pgd_t *ret;
34         int i;
35
36         ret = (pgd_t *) __get_free_pages(GFP_KERNEL,1);
37         if (ret != NULL)
38                 for (i = 0; i < USER_PTRS_PER_PGD; i++)
39                         pmd_clear(pmd_offset(ret + i, i*PGDIR_SIZE));
40         return ret;
41 }
42
43 extern __inline__ pgd_t* get_pgd_fast(void)
44 {
45         unsigned long *ret = pgd_quicklist;
46         
47         if (ret != NULL) {
48                 pgd_quicklist = (unsigned long *)(*ret);
49                 ret[0] = ret[1];
50                 pgtable_cache_size -= 2;
51         }
52         return (pgd_t *)ret;
53 }
54
55 extern __inline__ pgd_t *pgd_alloc(struct mm_struct *mm)
56 {
57         pgd_t *pgd;
58
59         pgd = get_pgd_fast();
60         if (!pgd)
61                 pgd = get_pgd_slow();
62         return pgd;
63 }
64
65 extern __inline__ void free_pgd_fast(pgd_t *pgd)
66 {
67         *(unsigned long *)pgd = (unsigned long) pgd_quicklist;
68         pgd_quicklist = (unsigned long *) pgd;
69         pgtable_cache_size += 2;
70 }
71
72 extern __inline__ void free_pgd_slow(pgd_t *pgd)
73 {
74         free_pages((unsigned long) pgd, 1);
75 }
76
77 #define pgd_free(pgd)           free_pgd_fast(pgd)
78
79 /*
80  * page middle directory allocation/free routines.
81  * We don't use pmd cache, so these are dummy routines. This
82  * code never triggers because the pgd will always be present.
83  */
84 #define pmd_alloc_one_fast(mm, address) ({ BUG(); ((pmd_t *)1); })
85 #define pmd_alloc_one(mm,address)       ({ BUG(); ((pmd_t *)2); })
86 #define pmd_free(x)                     do { } while (0)
87 #define pmd_free_slow(x)                do { } while (0)
88 #define pmd_free_fast(x)                do { } while (0)
89 #define pgd_populate(mm, pmd, pte)      BUG()
90
91 extern inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, pte_t *pte)
92 {
93         pmd_val(pmd[0]) = _PAGE_TABLE + __pa(pte);
94         pmd_val(pmd[1]) = _PAGE_TABLE + __pa(pte+256);
95         pmd_val(pmd[2]) = _PAGE_TABLE + __pa(pte+512);
96         pmd_val(pmd[3]) = _PAGE_TABLE + __pa(pte+768);
97 }
98
99 /*
100  * page table entry allocation/free routines.
101  */
102 extern inline pte_t * pte_alloc_one(struct mm_struct *mm, unsigned long vmaddr)
103 {
104         pte_t *pte;
105         int i;
106
107         pte = (pte_t *) __get_free_page(GFP_KERNEL);
108         if (pte != NULL) {
109                 for (i=0; i < PTRS_PER_PTE; i++)
110                         pte_clear(pte+i);
111         }
112         return pte;
113 }
114
115 extern __inline__ pte_t *
116 pte_alloc_one_fast(struct mm_struct *mm, unsigned long address)
117 {
118         unsigned long *ret = (unsigned long *) pte_quicklist;
119
120         if (ret != NULL) {
121                 pte_quicklist = (unsigned long *)(*ret);
122                 ret[0] = ret[1];
123                 pgtable_cache_size--;
124         }
125         return (pte_t *)ret;
126 }
127
128 extern __inline__ void pte_free_fast(pte_t *pte)
129 {
130         *(unsigned long *)pte = (unsigned long) pte_quicklist;
131         pte_quicklist = (unsigned long *) pte;
132         pgtable_cache_size++;
133 }
134
135 extern __inline__ void pte_free_slow(pte_t *pte)
136 {
137         free_page((unsigned long) pte);
138 }
139
140 #define pte_free(pte)           pte_free_fast(pte)
141
142 extern int do_check_pgt_cache(int, int);
143
144 /*
145  * This establishes kernel virtual mappings (e.g., as a result of a
146  * vmalloc call).  Since s390-esame uses a separate kernel page table,
147  * there is nothing to do here... :)
148  */
149 #define set_pgdir(addr,entry) do { } while(0)
150
151 /*
152  * TLB flushing:
153  *
154  *  - flush_tlb() flushes the current mm struct TLBs
155  *  - flush_tlb_all() flushes all processes TLBs 
156  *    called only from vmalloc/vfree
157  *  - flush_tlb_mm(mm) flushes the specified mm context TLB's
158  *  - flush_tlb_page(vma, vmaddr) flushes one page
159  *  - flush_tlb_range(mm, start, end) flushes a range of pages
160  *  - flush_tlb_pgtables(mm, start, end) flushes a range of page tables
161  */
162
163 /*
164  * S/390 has three ways of flushing TLBs
165  * 'ptlb' does a flush of the local processor
166  * 'csp' flushes the TLBs on all PUs of a SMP
167  * 'ipte' invalidates a pte in a page table and flushes that out of
168  * the TLBs of all PUs of a SMP
169  */
170
171 #define local_flush_tlb() \
172 do {  __asm__ __volatile__("ptlb": : :"memory"); } while (0)
173
174
175 #ifndef CONFIG_SMP
176
177 /*
178  * We always need to flush, since s390 does not flush tlb
179  * on each context switch
180  */
181
182 static inline void flush_tlb(void)
183 {
184         local_flush_tlb();
185 }
186 static inline void flush_tlb_all(void)
187 {
188         local_flush_tlb();
189 }
190 static inline void flush_tlb_mm(struct mm_struct *mm) 
191 {
192         local_flush_tlb();
193 }
194 static inline void flush_tlb_page(struct vm_area_struct *vma,
195                                   unsigned long addr)
196 {
197         local_flush_tlb();
198 }
199 static inline void flush_tlb_range(struct mm_struct *mm,
200                                    unsigned long start, unsigned long end)
201 {
202         local_flush_tlb();
203 }
204
205 #else
206
207 #include <asm/smp.h>
208
209 extern void smp_ptlb_all(void);
210 static inline void global_flush_tlb_csp(void)
211 {
212         int cs1=0,dum=0;
213         int *adr;
214         long long dummy=0;
215         adr = (int*) (((int)(((int*) &dummy)+1) & 0xfffffffc)|1);
216         __asm__ __volatile__("lr    2,%0\n\t"
217                              "lr    3,%1\n\t"
218                              "lr    4,%2\n\t"
219                              "csp   2,4" :
220                              : "d" (cs1), "d" (dum), "d" (adr)
221                              : "2", "3", "4");
222 }
223 static inline void global_flush_tlb(void)
224 {
225         if (MACHINE_HAS_CSP)
226                 global_flush_tlb_csp();
227         else
228                 smp_ptlb_all();
229 }
230
231 /*
232  * We only have to do global flush of tlb if process run since last
233  * flush on any other pu than current. 
234  * If we have threads (mm->count > 1) we always do a global flush, 
235  * since the process runs on more than one processor at the same time.
236  */
237
238 static inline void __flush_tlb_mm(struct mm_struct * mm)
239 {
240         if ((smp_num_cpus > 1) &&
241             ((atomic_read(&mm->mm_count) != 1) ||
242              (mm->cpu_vm_mask != (1UL << smp_processor_id())))) {
243                 mm->cpu_vm_mask = (1UL << smp_processor_id());
244                 global_flush_tlb();
245         } else {                 
246                 local_flush_tlb();
247         }
248 }
249
250 static inline void flush_tlb(void)
251 {
252         __flush_tlb_mm(current->mm);
253 }
254 static inline void flush_tlb_all(void)
255 {
256         global_flush_tlb();
257 }
258 static inline void flush_tlb_mm(struct mm_struct *mm) 
259 {
260         __flush_tlb_mm(mm); 
261 }
262 static inline void flush_tlb_page(struct vm_area_struct *vma,
263                                   unsigned long addr)
264 {
265         __flush_tlb_mm(vma->vm_mm);
266 }
267 static inline void flush_tlb_range(struct mm_struct *mm,
268                                    unsigned long start, unsigned long end)
269 {
270         __flush_tlb_mm(mm); 
271 }
272
273 #endif
274
275 extern inline void flush_tlb_pgtables(struct mm_struct *mm,
276                                       unsigned long start, unsigned long end)
277 {
278         /* S/390 does not keep any page table caches in TLB */
279 }
280
281
282 static inline int ptep_test_and_clear_and_flush_young(struct vm_area_struct *vma, 
283                                                       unsigned long address, pte_t *ptep)
284 {
285         /* No need to flush TLB; bits are in storage key */
286         return ptep_test_and_clear_young(ptep);
287 }
288
289 static inline int ptep_test_and_clear_and_flush_dirty(struct vm_area_struct *vma, 
290                                                       unsigned long address, pte_t *ptep)
291 {
292         /* No need to flush TLB; bits are in storage key */
293         return ptep_test_and_clear_dirty(ptep);
294 }
295
296 static inline pte_t ptep_invalidate(struct vm_area_struct *vma, 
297                                     unsigned long address, pte_t *ptep)
298 {
299         pte_t pte = *ptep;
300         if (!(pte_val(pte) & _PAGE_INVALID)) {
301                 /* S390 has 1mb segments, we are emulating 4MB segments */
302                 pte_t *pto = (pte_t *) (((unsigned long) ptep) & 0x7ffffc00);
303                 __asm__ __volatile__ ("ipte %0,%1" : : "a" (pto), "a" (address));
304         }
305         pte_clear(ptep);
306         return pte;
307 }
308
309 static inline void ptep_establish(struct vm_area_struct *vma, 
310                                   unsigned long address, pte_t *ptep, pte_t entry)
311 {
312         ptep_invalidate(vma, address, ptep);
313         set_pte(ptep, entry);
314 }
315
316 #endif /* _S390_PGALLOC_H */