diff options
Diffstat (limited to 'arch/ia64/mm/tlb.c')
| -rw-r--r-- | arch/ia64/mm/tlb.c | 161 |
1 files changed, 146 insertions, 15 deletions
diff --git a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c index 3d8903f936a5..d52ec4e83409 100644 --- a/arch/ia64/mm/tlb.c +++ b/arch/ia64/mm/tlb.c | |||
| @@ -11,6 +11,9 @@ | |||
| 11 | * Rohit Seth <rohit.seth@intel.com> | 11 | * Rohit Seth <rohit.seth@intel.com> |
| 12 | * Ken Chen <kenneth.w.chen@intel.com> | 12 | * Ken Chen <kenneth.w.chen@intel.com> |
| 13 | * Christophe de Dinechin <ddd@hp.com>: Avoid ptc.e on memory allocation | 13 | * Christophe de Dinechin <ddd@hp.com>: Avoid ptc.e on memory allocation |
| 14 | * Copyright (C) 2007 Intel Corp | ||
| 15 | * Fenghua Yu <fenghua.yu@intel.com> | ||
| 16 | * Add multiple ptc.g/ptc.ga instruction support in global tlb purge. | ||
| 14 | */ | 17 | */ |
| 15 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 16 | #include <linux/init.h> | 19 | #include <linux/init.h> |
| @@ -27,6 +30,7 @@ | |||
| 27 | #include <asm/tlbflush.h> | 30 | #include <asm/tlbflush.h> |
| 28 | #include <asm/dma.h> | 31 | #include <asm/dma.h> |
| 29 | #include <asm/processor.h> | 32 | #include <asm/processor.h> |
| 33 | #include <asm/sal.h> | ||
| 30 | #include <asm/tlb.h> | 34 | #include <asm/tlb.h> |
| 31 | 35 | ||
| 32 | static struct { | 36 | static struct { |
| @@ -90,14 +94,140 @@ wrap_mmu_context (struct mm_struct *mm) | |||
| 90 | local_flush_tlb_all(); | 94 | local_flush_tlb_all(); |
| 91 | } | 95 | } |
| 92 | 96 | ||
| 97 | /* | ||
| 98 | * Implement "spinaphores" ... like counting semaphores, but they | ||
| 99 | * spin instead of sleeping. If there are ever any other users for | ||
| 100 | * this primitive it can be moved up to a spinaphore.h header. | ||
| 101 | */ | ||
| 102 | struct spinaphore { | ||
| 103 | atomic_t cur; | ||
| 104 | }; | ||
| 105 | |||
| 106 | static inline void spinaphore_init(struct spinaphore *ss, int val) | ||
| 107 | { | ||
| 108 | atomic_set(&ss->cur, val); | ||
| 109 | } | ||
| 110 | |||
| 111 | static inline void down_spin(struct spinaphore *ss) | ||
| 112 | { | ||
| 113 | while (unlikely(!atomic_add_unless(&ss->cur, -1, 0))) | ||
| 114 | while (atomic_read(&ss->cur) == 0) | ||
| 115 | cpu_relax(); | ||
| 116 | } | ||
| 117 | |||
| 118 | static inline void up_spin(struct spinaphore *ss) | ||
| 119 | { | ||
| 120 | atomic_add(1, &ss->cur); | ||
| 121 | } | ||
| 122 | |||
| 123 | static struct spinaphore ptcg_sem; | ||
| 124 | static u16 nptcg = 1; | ||
| 125 | static int need_ptcg_sem = 1; | ||
| 126 | static int toolatetochangeptcgsem = 0; | ||
| 127 | |||
| 128 | /* | ||
| 129 | * Kernel parameter "nptcg=" overrides max number of concurrent global TLB | ||
| 130 | * purges which is reported from either PAL or SAL PALO. | ||
| 131 | * | ||
| 132 | * We don't have sanity checking for nptcg value. It's the user's responsibility | ||
| 133 | * for valid nptcg value on the platform. Otherwise, kernel may hang in some | ||
| 134 | * cases. | ||
| 135 | */ | ||
| 136 | static int __init | ||
| 137 | set_nptcg(char *str) | ||
| 138 | { | ||
| 139 | int value = 0; | ||
| 140 | |||
| 141 | get_option(&str, &value); | ||
| 142 | setup_ptcg_sem(value, NPTCG_FROM_KERNEL_PARAMETER); | ||
| 143 | |||
| 144 | return 1; | ||
| 145 | } | ||
| 146 | |||
| 147 | __setup("nptcg=", set_nptcg); | ||
| 148 | |||
| 149 | /* | ||
| 150 | * Maximum number of simultaneous ptc.g purges in the system can | ||
| 151 | * be defined by PAL_VM_SUMMARY (in which case we should take | ||
| 152 | * the smallest value for any cpu in the system) or by the PAL | ||
| 153 | * override table (in which case we should ignore the value from | ||
| 154 | * PAL_VM_SUMMARY). | ||
| 155 | * | ||
| 156 | * Kernel parameter "nptcg=" overrides maximum number of simultanesous ptc.g | ||
| 157 | * purges defined in either PAL_VM_SUMMARY or PAL override table. In this case, | ||
| 158 | * we should ignore the value from either PAL_VM_SUMMARY or PAL override table. | ||
| 159 | * | ||
| 160 | * Complicating the logic here is the fact that num_possible_cpus() | ||
| 161 | * isn't fully setup until we start bringing cpus online. | ||
| 162 | */ | ||
| 163 | void | ||
| 164 | setup_ptcg_sem(int max_purges, int nptcg_from) | ||
| 165 | { | ||
| 166 | static int kp_override; | ||
| 167 | static int palo_override; | ||
| 168 | static int firstcpu = 1; | ||
| 169 | |||
| 170 | if (toolatetochangeptcgsem) { | ||
| 171 | BUG_ON(max_purges < nptcg); | ||
| 172 | return; | ||
| 173 | } | ||
| 174 | |||
| 175 | if (nptcg_from == NPTCG_FROM_KERNEL_PARAMETER) { | ||
| 176 | kp_override = 1; | ||
| 177 | nptcg = max_purges; | ||
| 178 | goto resetsema; | ||
| 179 | } | ||
| 180 | if (kp_override) { | ||
| 181 | need_ptcg_sem = num_possible_cpus() > nptcg; | ||
| 182 | return; | ||
| 183 | } | ||
| 184 | |||
| 185 | if (nptcg_from == NPTCG_FROM_PALO) { | ||
| 186 | palo_override = 1; | ||
| 187 | |||
| 188 | /* In PALO max_purges == 0 really means it! */ | ||
| 189 | if (max_purges == 0) | ||
| 190 | panic("Whoa! Platform does not support global TLB purges.\n"); | ||
| 191 | nptcg = max_purges; | ||
| 192 | if (nptcg == PALO_MAX_TLB_PURGES) { | ||
| 193 | need_ptcg_sem = 0; | ||
| 194 | return; | ||
| 195 | } | ||
| 196 | goto resetsema; | ||
| 197 | } | ||
| 198 | if (palo_override) { | ||
| 199 | if (nptcg != PALO_MAX_TLB_PURGES) | ||
| 200 | need_ptcg_sem = (num_possible_cpus() > nptcg); | ||
| 201 | return; | ||
| 202 | } | ||
| 203 | |||
| 204 | /* In PAL_VM_SUMMARY max_purges == 0 actually means 1 */ | ||
| 205 | if (max_purges == 0) max_purges = 1; | ||
| 206 | |||
| 207 | if (firstcpu) { | ||
| 208 | nptcg = max_purges; | ||
| 209 | firstcpu = 0; | ||
| 210 | } | ||
| 211 | if (max_purges < nptcg) | ||
| 212 | nptcg = max_purges; | ||
| 213 | if (nptcg == PAL_MAX_PURGES) { | ||
| 214 | need_ptcg_sem = 0; | ||
| 215 | return; | ||
| 216 | } else | ||
| 217 | need_ptcg_sem = (num_possible_cpus() > nptcg); | ||
| 218 | |||
| 219 | resetsema: | ||
| 220 | spinaphore_init(&ptcg_sem, max_purges); | ||
| 221 | } | ||
| 222 | |||
| 93 | void | 223 | void |
| 94 | ia64_global_tlb_purge (struct mm_struct *mm, unsigned long start, | 224 | ia64_global_tlb_purge (struct mm_struct *mm, unsigned long start, |
| 95 | unsigned long end, unsigned long nbits) | 225 | unsigned long end, unsigned long nbits) |
| 96 | { | 226 | { |
| 97 | static DEFINE_SPINLOCK(ptcg_lock); | ||
| 98 | |||
| 99 | struct mm_struct *active_mm = current->active_mm; | 227 | struct mm_struct *active_mm = current->active_mm; |
| 100 | 228 | ||
| 229 | toolatetochangeptcgsem = 1; | ||
| 230 | |||
| 101 | if (mm != active_mm) { | 231 | if (mm != active_mm) { |
| 102 | /* Restore region IDs for mm */ | 232 | /* Restore region IDs for mm */ |
| 103 | if (mm && active_mm) { | 233 | if (mm && active_mm) { |
| @@ -108,19 +238,20 @@ ia64_global_tlb_purge (struct mm_struct *mm, unsigned long start, | |||
| 108 | } | 238 | } |
| 109 | } | 239 | } |
| 110 | 240 | ||
| 111 | /* HW requires global serialization of ptc.ga. */ | 241 | if (need_ptcg_sem) |
| 112 | spin_lock(&ptcg_lock); | 242 | down_spin(&ptcg_sem); |
| 113 | { | 243 | |
| 114 | do { | 244 | do { |
| 115 | /* | 245 | /* |
| 116 | * Flush ALAT entries also. | 246 | * Flush ALAT entries also. |
| 117 | */ | 247 | */ |
| 118 | ia64_ptcga(start, (nbits<<2)); | 248 | ia64_ptcga(start, (nbits << 2)); |
| 119 | ia64_srlz_i(); | 249 | ia64_srlz_i(); |
| 120 | start += (1UL << nbits); | 250 | start += (1UL << nbits); |
| 121 | } while (start < end); | 251 | } while (start < end); |
| 122 | } | 252 | |
| 123 | spin_unlock(&ptcg_lock); | 253 | if (need_ptcg_sem) |
| 254 | up_spin(&ptcg_sem); | ||
| 124 | 255 | ||
| 125 | if (mm != active_mm) { | 256 | if (mm != active_mm) { |
| 126 | activate_context(active_mm); | 257 | activate_context(active_mm); |
