Merge Stephen Rothwell's patches
[linux-2.6.git] / arch / powerpc / platforms / iseries / htab.c
1 /*
2  * iSeries hashtable management.
3  *      Derived from pSeries_htab.c
4  *
5  * SMP scalability work:
6  *    Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version
11  * 2 of the License, or (at your option) any later version.
12  */
13 #include <asm/machdep.h>
14 #include <asm/pgtable.h>
15 #include <asm/mmu.h>
16 #include <asm/mmu_context.h>
17 #include <asm/iSeries/HvCallHpt.h>
18 #include <asm/abs_addr.h>
19 #include <linux/spinlock.h>
20
21 static spinlock_t iSeries_hlocks[64] __cacheline_aligned_in_smp =
22         { [0 ... 63] = SPIN_LOCK_UNLOCKED};
23
24 /*
25  * Very primitive algorithm for picking up a lock
26  */
27 static inline void iSeries_hlock(unsigned long slot)
28 {
29         if (slot & 0x8)
30                 slot = ~slot;
31         spin_lock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
32 }
33
34 static inline void iSeries_hunlock(unsigned long slot)
35 {
36         if (slot & 0x8)
37                 slot = ~slot;
38         spin_unlock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
39 }
40
41 static long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va,
42                                 unsigned long prpn, unsigned long vflags,
43                                 unsigned long rflags)
44 {
45         unsigned long arpn;
46         long slot;
47         hpte_t lhpte;
48         int secondary = 0;
49
50         /*
51          * The hypervisor tries both primary and secondary.
52          * If we are being called to insert in the secondary,
53          * it means we have already tried both primary and secondary,
54          * so we return failure immediately.
55          */
56         if (vflags & HPTE_V_SECONDARY)
57                 return -1;
58
59         iSeries_hlock(hpte_group);
60
61         slot = HvCallHpt_findValid(&lhpte, va >> PAGE_SHIFT);
62         BUG_ON(lhpte.v & HPTE_V_VALID);
63
64         if (slot == -1) { /* No available entry found in either group */
65                 iSeries_hunlock(hpte_group);
66                 return -1;
67         }
68
69         if (slot < 0) {         /* MSB set means secondary group */
70                 vflags |= HPTE_V_VALID;
71                 secondary = 1;
72                 slot &= 0x7fffffffffffffff;
73         }
74
75         arpn = phys_to_abs(prpn << PAGE_SHIFT) >> PAGE_SHIFT;
76
77         lhpte.v = (va >> 23) << HPTE_V_AVPN_SHIFT | vflags | HPTE_V_VALID;
78         lhpte.r = (arpn << HPTE_R_RPN_SHIFT) | rflags;
79
80         /* Now fill in the actual HPTE */
81         HvCallHpt_addValidate(slot, secondary, &lhpte);
82
83         iSeries_hunlock(hpte_group);
84
85         return (secondary << 3) | (slot & 7);
86 }
87
88 long iSeries_hpte_bolt_or_insert(unsigned long hpte_group,
89                 unsigned long va, unsigned long prpn, unsigned long vflags,
90                 unsigned long rflags)
91 {
92         long slot;
93         hpte_t lhpte;
94
95         slot = HvCallHpt_findValid(&lhpte, va >> PAGE_SHIFT);
96
97         if (lhpte.v & HPTE_V_VALID) {
98                 /* Bolt the existing HPTE */
99                 HvCallHpt_setSwBits(slot, 0x10, 0);
100                 HvCallHpt_setPp(slot, PP_RWXX);
101                 return 0;
102         }
103
104         return iSeries_hpte_insert(hpte_group, va, prpn, vflags, rflags);
105 }
106
107 static unsigned long iSeries_hpte_getword0(unsigned long slot)
108 {
109         hpte_t hpte;
110
111         HvCallHpt_get(&hpte, slot);
112         return hpte.v;
113 }
114
115 static long iSeries_hpte_remove(unsigned long hpte_group)
116 {
117         unsigned long slot_offset;
118         int i;
119         unsigned long hpte_v;
120
121         /* Pick a random slot to start at */
122         slot_offset = mftb() & 0x7;
123
124         iSeries_hlock(hpte_group);
125
126         for (i = 0; i < HPTES_PER_GROUP; i++) {
127                 hpte_v = iSeries_hpte_getword0(hpte_group + slot_offset);
128
129                 if (! (hpte_v & HPTE_V_BOLTED)) {
130                         HvCallHpt_invalidateSetSwBitsGet(hpte_group +
131                                                          slot_offset, 0, 0);
132                         iSeries_hunlock(hpte_group);
133                         return i;
134                 }
135
136                 slot_offset++;
137                 slot_offset &= 0x7;
138         }
139
140         iSeries_hunlock(hpte_group);
141
142         return -1;
143 }
144
145 /*
146  * The HyperVisor expects the "flags" argument in this form:
147  *      bits  0..59 : reserved
148  *      bit      60 : N
149  *      bits 61..63 : PP2,PP1,PP0
150  */
151 static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp,
152                                   unsigned long va, int large, int local)
153 {
154         hpte_t hpte;
155         unsigned long avpn = va >> 23;
156
157         iSeries_hlock(slot);
158
159         HvCallHpt_get(&hpte, slot);
160         if ((HPTE_V_AVPN_VAL(hpte.v) == avpn) && (hpte.v & HPTE_V_VALID)) {
161                 /*
162                  * Hypervisor expects bits as NPPP, which is
163                  * different from how they are mapped in our PP.
164                  */
165                 HvCallHpt_setPp(slot, (newpp & 0x3) | ((newpp & 0x4) << 1));
166                 iSeries_hunlock(slot);
167                 return 0;
168         }
169         iSeries_hunlock(slot);
170
171         return -1;
172 }
173
174 /*
175  * Functions used to find the PTE for a particular virtual address.
176  * Only used during boot when bolting pages.
177  *
178  * Input : vpn      : virtual page number
179  * Output: PTE index within the page table of the entry
180  *         -1 on failure
181  */
182 static long iSeries_hpte_find(unsigned long vpn)
183 {
184         hpte_t hpte;
185         long slot;
186
187         /*
188          * The HvCallHpt_findValid interface is as follows:
189          * 0xffffffffffffffff : No entry found.
190          * 0x00000000xxxxxxxx : Entry found in primary group, slot x
191          * 0x80000000xxxxxxxx : Entry found in secondary group, slot x
192          */
193         slot = HvCallHpt_findValid(&hpte, vpn);
194         if (hpte.v & HPTE_V_VALID) {
195                 if (slot < 0) {
196                         slot &= 0x7fffffffffffffff;
197                         slot = -slot;
198                 }
199         } else
200                 slot = -1;
201         return slot;
202 }
203
204 /*
205  * Update the page protection bits. Intended to be used to create
206  * guard pages for kernel data structures on pages which are bolted
207  * in the HPT. Assumes pages being operated on will not be stolen.
208  * Does not work on large pages.
209  *
210  * No need to lock here because we should be the only user.
211  */
212 static void iSeries_hpte_updateboltedpp(unsigned long newpp, unsigned long ea)
213 {
214         unsigned long vsid,va,vpn;
215         long slot;
216
217         vsid = get_kernel_vsid(ea);
218         va = (vsid << 28) | (ea & 0x0fffffff);
219         vpn = va >> PAGE_SHIFT;
220         slot = iSeries_hpte_find(vpn);
221         if (slot == -1)
222                 panic("updateboltedpp: Could not find page to bolt\n");
223         HvCallHpt_setPp(slot, newpp);
224 }
225
226 static void iSeries_hpte_invalidate(unsigned long slot, unsigned long va,
227                                     int large, int local)
228 {
229         unsigned long hpte_v;
230         unsigned long avpn = va >> 23;
231         unsigned long flags;
232
233         local_irq_save(flags);
234
235         iSeries_hlock(slot);
236
237         hpte_v = iSeries_hpte_getword0(slot);
238
239         if ((HPTE_V_AVPN_VAL(hpte_v) == avpn) && (hpte_v & HPTE_V_VALID))
240                 HvCallHpt_invalidateSetSwBitsGet(slot, 0, 0);
241
242         iSeries_hunlock(slot);
243
244         local_irq_restore(flags);
245 }
246
247 void hpte_init_iSeries(void)
248 {
249         ppc_md.hpte_invalidate  = iSeries_hpte_invalidate;
250         ppc_md.hpte_updatepp    = iSeries_hpte_updatepp;
251         ppc_md.hpte_updateboltedpp = iSeries_hpte_updateboltedpp;
252         ppc_md.hpte_insert      = iSeries_hpte_insert;
253         ppc_md.hpte_remove      = iSeries_hpte_remove;
254
255         htab_finish_init();
256 }