diff options
Diffstat (limited to 'arch/powerpc/mm')
-rw-r--r-- | arch/powerpc/mm/slb.c | 34 |
1 files changed, 31 insertions, 3 deletions
diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c index 637afb2386b7..27922dff8b94 100644 --- a/arch/powerpc/mm/slb.c +++ b/arch/powerpc/mm/slb.c | |||
@@ -149,6 +149,35 @@ void slb_vmalloc_update(void) | |||
149 | slb_flush_and_rebolt(); | 149 | slb_flush_and_rebolt(); |
150 | } | 150 | } |
151 | 151 | ||
152 | /* Helper function to compare esids. There are four cases to handle. | ||
153 | * 1. The system is not 1T segment size capable. Use the GET_ESID compare. | ||
154 | * 2. The system is 1T capable, both addresses are < 1T, use the GET_ESID compare. | ||
155 | * 3. The system is 1T capable, only one of the two addresses is > 1T. This is not a match. | ||
156 | * 4. The system is 1T capable, both addresses are > 1T, use the GET_ESID_1T macro to compare. | ||
157 | */ | ||
158 | static inline int esids_match(unsigned long addr1, unsigned long addr2) | ||
159 | { | ||
160 | int esid_1t_count; | ||
161 | |||
162 | /* System is not 1T segment size capable. */ | ||
163 | if (!cpu_has_feature(CPU_FTR_1T_SEGMENT)) | ||
164 | return (GET_ESID(addr1) == GET_ESID(addr2)); | ||
165 | |||
166 | esid_1t_count = (((addr1 >> SID_SHIFT_1T) != 0) + | ||
167 | ((addr2 >> SID_SHIFT_1T) != 0)); | ||
168 | |||
169 | /* both addresses are < 1T */ | ||
170 | if (esid_1t_count == 0) | ||
171 | return (GET_ESID(addr1) == GET_ESID(addr2)); | ||
172 | |||
173 | /* One address < 1T, the other > 1T. Not a match */ | ||
174 | if (esid_1t_count == 1) | ||
175 | return 0; | ||
176 | |||
177 | /* Both addresses are > 1T. */ | ||
178 | return (GET_ESID_1T(addr1) == GET_ESID_1T(addr2)); | ||
179 | } | ||
180 | |||
152 | /* Flush all user entries from the segment table of the current processor. */ | 181 | /* Flush all user entries from the segment table of the current processor. */ |
153 | void switch_slb(struct task_struct *tsk, struct mm_struct *mm) | 182 | void switch_slb(struct task_struct *tsk, struct mm_struct *mm) |
154 | { | 183 | { |
@@ -194,15 +223,14 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm) | |||
194 | return; | 223 | return; |
195 | slb_allocate(pc); | 224 | slb_allocate(pc); |
196 | 225 | ||
197 | if (GET_ESID(pc) == GET_ESID(stack)) | 226 | if (esids_match(pc,stack)) |
198 | return; | 227 | return; |
199 | 228 | ||
200 | if (is_kernel_addr(stack)) | 229 | if (is_kernel_addr(stack)) |
201 | return; | 230 | return; |
202 | slb_allocate(stack); | 231 | slb_allocate(stack); |
203 | 232 | ||
204 | if ((GET_ESID(pc) == GET_ESID(unmapped_base)) | 233 | if (esids_match(pc,unmapped_base) || esids_match(stack,unmapped_base)) |
205 | || (GET_ESID(stack) == GET_ESID(unmapped_base))) | ||
206 | return; | 234 | return; |
207 | 235 | ||
208 | if (is_kernel_addr(unmapped_base)) | 236 | if (is_kernel_addr(unmapped_base)) |