- update to 2.6.1-rc2 -- first cut.
[linux-flexiantxendom0-3.2.10.git] / arch / ppc64 / kernel / pSeries_htab.c
1 /*
2  * pSeries hashtable management.
3  *
4  * SMP scalability work:
5  *    Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
6  * 
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version
10  * 2 of the License, or (at your option) any later version.
11  */
12 #include <linux/spinlock.h>
13 #include <linux/bitops.h>
14 #include <linux/threads.h>
15 #include <linux/smp.h>
16
17 #include <asm/abs_addr.h>
18 #include <asm/machdep.h>
19 #include <asm/mmu.h>
20 #include <asm/mmu_context.h>
21 #include <asm/pgtable.h>
22 #include <asm/tlbflush.h>
23 #include <asm/tlb.h>
24 #include <asm/cputable.h>
25
26 #define HPTE_LOCK_BIT 3
27
28 static inline void pSeries_lock_hpte(HPTE *hptep)
29 {
30         unsigned long *word = &hptep->dw0.dword0;
31
32         while (1) {
33                 if (!test_and_set_bit(HPTE_LOCK_BIT, word))
34                         break;
35                 while(test_bit(HPTE_LOCK_BIT, word))
36                         cpu_relax();
37         }
38 }
39
40 static inline void pSeries_unlock_hpte(HPTE *hptep)
41 {
42         unsigned long *word = &hptep->dw0.dword0;
43
44         asm volatile("lwsync":::"memory");
45         clear_bit(HPTE_LOCK_BIT, word);
46 }
47
48 static spinlock_t pSeries_tlbie_lock = SPIN_LOCK_UNLOCKED;
49
50 long pSeries_hpte_insert(unsigned long hpte_group, unsigned long va,
51                          unsigned long prpn, int secondary,
52                          unsigned long hpteflags, int bolted, int large)
53 {
54         unsigned long arpn = physRpn_to_absRpn(prpn);
55         HPTE *hptep = htab_data.htab + hpte_group;
56         Hpte_dword0 dw0;
57         HPTE lhpte;
58         int i;
59
60         for (i = 0; i < HPTES_PER_GROUP; i++) {
61                 dw0 = hptep->dw0.dw0;
62
63                 if (!dw0.v) {
64                         /* retry with lock held */
65                         pSeries_lock_hpte(hptep);
66                         dw0 = hptep->dw0.dw0;
67                         if (!dw0.v)
68                                 break;
69                         pSeries_unlock_hpte(hptep);
70                 }
71
72                 hptep++;
73         }
74
75         if (i == HPTES_PER_GROUP)
76                 return -1;
77
78         lhpte.dw1.dword1      = 0;
79         lhpte.dw1.dw1.rpn     = arpn;
80         lhpte.dw1.flags.flags = hpteflags;
81
82         lhpte.dw0.dword0      = 0;
83         lhpte.dw0.dw0.avpn    = va >> 23;
84         lhpte.dw0.dw0.h       = secondary;
85         lhpte.dw0.dw0.bolted  = bolted;
86         lhpte.dw0.dw0.v       = 1;
87
88         if (large) {
89                 lhpte.dw0.dw0.l = 1;
90                 lhpte.dw0.dw0.avpn &= ~0x1UL;
91         }
92
93         hptep->dw1.dword1 = lhpte.dw1.dword1;
94
95         /* Guarantee the second dword is visible before the valid bit */
96         __asm__ __volatile__ ("eieio" : : : "memory");
97
98         /*
99          * Now set the first dword including the valid bit
100          * NOTE: this also unlocks the hpte
101          */
102         hptep->dw0.dword0 = lhpte.dw0.dword0;
103
104         __asm__ __volatile__ ("ptesync" : : : "memory");
105
106         return i;
107 }
108
109 static long pSeries_hpte_remove(unsigned long hpte_group)
110 {
111         HPTE *hptep;
112         Hpte_dword0 dw0;
113         int i;
114         int slot_offset;
115
116         /* pick a random entry to start at */
117         slot_offset = mftb() & 0x7;
118
119         for (i = 0; i < HPTES_PER_GROUP; i++) {
120                 hptep = htab_data.htab + hpte_group + slot_offset;
121                 dw0 = hptep->dw0.dw0;
122
123                 if (dw0.v && !dw0.bolted) {
124                         /* retry with lock held */
125                         pSeries_lock_hpte(hptep);
126                         dw0 = hptep->dw0.dw0;
127                         if (dw0.v && !dw0.bolted)
128                                 break;
129                         pSeries_unlock_hpte(hptep);
130                 }
131
132                 slot_offset++;
133                 slot_offset &= 0x7;
134         }
135
136         if (i == HPTES_PER_GROUP)
137                 return -1;
138
139         /* Invalidate the hpte. NOTE: this also unlocks it */
140         hptep->dw0.dword0 = 0;
141
142         return i;
143 }
144
145 static inline void set_pp_bit(unsigned long pp, HPTE *addr)
146 {
147         unsigned long old;
148         unsigned long *p = &addr->dw1.dword1;
149
150         __asm__ __volatile__(
151         "1:     ldarx   %0,0,%3\n\
152                 rldimi  %0,%2,0,61\n\
153                 stdcx.  %0,0,%3\n\
154                 bne     1b"
155         : "=&r" (old), "=m" (*p)
156         : "r" (pp), "r" (p), "m" (*p)
157         : "cc");
158 }
159
160 /*
161  * Only works on small pages. Yes its ugly to have to check each slot in
162  * the group but we only use this during bootup.
163  */
164 static long pSeries_hpte_find(unsigned long vpn)
165 {
166         HPTE *hptep;
167         unsigned long hash;
168         unsigned long i, j;
169         long slot;
170         Hpte_dword0 dw0;
171
172         hash = hpt_hash(vpn, 0);
173
174         for (j = 0; j < 2; j++) {
175                 slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP;
176                 for (i = 0; i < HPTES_PER_GROUP; i++) {
177                         hptep = htab_data.htab + slot;
178                         dw0 = hptep->dw0.dw0;
179
180                         if ((dw0.avpn == (vpn >> 11)) && dw0.v &&
181                             (dw0.h == j)) {
182                                 /* HPTE matches */
183                                 if (j)
184                                         slot = -slot;
185                                 return slot;
186                         }
187                         ++slot;
188                 }
189                 hash = ~hash;
190         }
191
192         return -1;
193 }
194
195 static long pSeries_hpte_updatepp(unsigned long slot, unsigned long newpp,
196                                   unsigned long va, int large, int local)
197 {
198         HPTE *hptep = htab_data.htab + slot;
199         Hpte_dword0 dw0;
200         unsigned long avpn = va >> 23;
201         unsigned long flags;
202         int ret = 0;
203
204         if (large)
205                 avpn &= ~0x1UL;
206
207         pSeries_lock_hpte(hptep);
208
209         dw0 = hptep->dw0.dw0;
210
211         /* Even if we miss, we need to invalidate the TLB */
212         if ((dw0.avpn != avpn) || !dw0.v) {
213                 pSeries_unlock_hpte(hptep);
214                 ret = -1;
215         } else {
216                 set_pp_bit(newpp, hptep);
217                 pSeries_unlock_hpte(hptep);
218         }
219
220         /* Ensure it is out of the tlb too */
221         if ((cur_cpu_spec->cpu_features & CPU_FTR_TLBIEL) && !large && local) {
222                 _tlbiel(va);
223         } else {
224                 spin_lock_irqsave(&pSeries_tlbie_lock, flags);
225                 _tlbie(va, large);
226                 spin_unlock_irqrestore(&pSeries_tlbie_lock, flags);
227         }
228
229         return ret;
230 }
231
232 /*
233  * Update the page protection bits. Intended to be used to create
234  * guard pages for kernel data structures on pages which are bolted
235  * in the HPT. Assumes pages being operated on will not be stolen.
236  * Does not work on large pages.
237  *
238  * No need to lock here because we should be the only user.
239  */
240 static void pSeries_hpte_updateboltedpp(unsigned long newpp, unsigned long ea)
241 {
242         unsigned long vsid, va, vpn, flags;
243         long slot;
244         HPTE *hptep;
245
246         vsid = get_kernel_vsid(ea);
247         va = (vsid << 28) | (ea & 0x0fffffff);
248         vpn = va >> PAGE_SHIFT;
249
250         slot = pSeries_hpte_find(vpn);
251         if (slot == -1)
252                 panic("could not find page to bolt\n");
253         hptep = htab_data.htab + slot;
254
255         set_pp_bit(newpp, hptep);
256
257         /* Ensure it is out of the tlb too */
258         spin_lock_irqsave(&pSeries_tlbie_lock, flags);
259         _tlbie(va, 0);
260         spin_unlock_irqrestore(&pSeries_tlbie_lock, flags);
261 }
262
263 static void pSeries_hpte_invalidate(unsigned long slot, unsigned long va,
264                                     int large, int local)
265 {
266         HPTE *hptep = htab_data.htab + slot;
267         Hpte_dword0 dw0;
268         unsigned long avpn = va >> 23;
269         unsigned long flags;
270
271         if (large)
272                 avpn &= ~0x1UL;
273
274         pSeries_lock_hpte(hptep);
275
276         dw0 = hptep->dw0.dw0;
277
278         /* Even if we miss, we need to invalidate the TLB */
279         if ((dw0.avpn != avpn) || !dw0.v) {
280                 pSeries_unlock_hpte(hptep);
281         } else {
282                 /* Invalidate the hpte. NOTE: this also unlocks it */
283                 hptep->dw0.dword0 = 0;
284         }
285
286         /* Invalidate the tlb */
287         if ((cur_cpu_spec->cpu_features & CPU_FTR_TLBIEL) && !large && local) {
288                 _tlbiel(va);
289         } else {
290                 spin_lock_irqsave(&pSeries_tlbie_lock, flags);
291                 _tlbie(va, large);
292                 spin_unlock_irqrestore(&pSeries_tlbie_lock, flags);
293         }
294 }
295
296 static void pSeries_flush_hash_range(unsigned long context,
297                                      unsigned long number, int local)
298 {
299         unsigned long vsid, vpn, va, hash, secondary, slot, flags, avpn;
300         int i, j;
301         HPTE *hptep;
302         Hpte_dword0 dw0;
303         struct ppc64_tlb_batch *batch = &ppc64_tlb_batch[smp_processor_id()];
304
305         /* XXX fix for large ptes */
306         unsigned long large = 0;
307
308         j = 0;
309         for (i = 0; i < number; i++) {
310                 if ((batch->addr[i] >= USER_START) &&
311                     (batch->addr[i] <= USER_END))
312                         vsid = get_vsid(context, batch->addr[i]);
313                 else
314                         vsid = get_kernel_vsid(batch->addr[i]);
315
316                 va = (vsid << 28) | (batch->addr[i] & 0x0fffffff);
317                 batch->vaddr[j] = va;
318                 if (large)
319                         vpn = va >> LARGE_PAGE_SHIFT;
320                 else
321                         vpn = va >> PAGE_SHIFT;
322                 hash = hpt_hash(vpn, large);
323                 secondary = (pte_val(batch->pte[i]) & _PAGE_SECONDARY) >> 15;
324                 if (secondary)
325                         hash = ~hash;
326                 slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP;
327                 slot += (pte_val(batch->pte[i]) & _PAGE_GROUP_IX) >> 12;
328
329                 hptep = htab_data.htab + slot;
330
331                 avpn = va >> 23;
332                 if (large)
333                         avpn &= ~0x1UL;
334
335                 pSeries_lock_hpte(hptep);
336
337                 dw0 = hptep->dw0.dw0;
338
339                 /* Even if we miss, we need to invalidate the TLB */
340                 if ((dw0.avpn != avpn) || !dw0.v) {
341                         pSeries_unlock_hpte(hptep);
342                 } else {
343                         /* Invalidate the hpte. NOTE: this also unlocks it */
344                         hptep->dw0.dword0 = 0;
345                 }
346
347                 j++;
348         }
349
350         if ((cur_cpu_spec->cpu_features & CPU_FTR_TLBIEL) && !large && local) {
351                 asm volatile("ptesync":::"memory");
352
353                 for (i = 0; i < j; i++) {
354                         asm volatile("\n\
355                         clrldi  %0,%0,16\n\
356                         tlbiel   %0"
357                         : : "r" (batch->vaddr[i]) : "memory" );
358                 }
359
360                 asm volatile("ptesync":::"memory");
361         } else {
362                 /* XXX double check that it is safe to take this late */
363                 spin_lock_irqsave(&pSeries_tlbie_lock, flags);
364
365                 asm volatile("ptesync":::"memory");
366
367                 for (i = 0; i < j; i++) {
368                         asm volatile("\n\
369                         clrldi  %0,%0,16\n\
370                         tlbie   %0"
371                         : : "r" (batch->vaddr[i]) : "memory" );
372                 }
373
374                 asm volatile("eieio; tlbsync; ptesync":::"memory");
375
376                 spin_unlock_irqrestore(&pSeries_tlbie_lock, flags);
377         }
378 }
379
380 void hpte_init_pSeries(void)
381 {
382         struct device_node *root;
383         const char *model;
384
385         ppc_md.hpte_invalidate  = pSeries_hpte_invalidate;
386         ppc_md.hpte_updatepp    = pSeries_hpte_updatepp;
387         ppc_md.hpte_updateboltedpp = pSeries_hpte_updateboltedpp;
388         ppc_md.hpte_insert      = pSeries_hpte_insert;
389         ppc_md.hpte_remove      = pSeries_hpte_remove;
390
391         /* Disable TLB batching on nighthawk */
392         root = find_path_device("/");
393         if (root) {
394                 model = get_property(root, "model", NULL);
395                 if (strcmp(model, "CHRP IBM,9076-N81"))
396                         ppc_md.flush_hash_range = pSeries_flush_hash_range;
397         }
398 }