aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/mm/tlb.c34
1 files changed, 29 insertions, 5 deletions
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index a1561957dccb..5bfe61a5e8e3 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -151,6 +151,34 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next,
151 local_irq_restore(flags); 151 local_irq_restore(flags);
152} 152}
153 153
154static void sync_current_stack_to_mm(struct mm_struct *mm)
155{
156 unsigned long sp = current_stack_pointer;
157 pgd_t *pgd = pgd_offset(mm, sp);
158
159 if (CONFIG_PGTABLE_LEVELS > 4) {
160 if (unlikely(pgd_none(*pgd))) {
161 pgd_t *pgd_ref = pgd_offset_k(sp);
162
163 set_pgd(pgd, *pgd_ref);
164 }
165 } else {
166 /*
167 * "pgd" is faked. The top level entries are "p4d"s, so sync
168 * the p4d. This compiles to approximately the same code as
169 * the 5-level case.
170 */
171 p4d_t *p4d = p4d_offset(pgd, sp);
172
173 if (unlikely(p4d_none(*p4d))) {
174 pgd_t *pgd_ref = pgd_offset_k(sp);
175 p4d_t *p4d_ref = p4d_offset(pgd_ref, sp);
176
177 set_p4d(p4d, *p4d_ref);
178 }
179 }
180}
181
154void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, 182void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
155 struct task_struct *tsk) 183 struct task_struct *tsk)
156{ 184{
@@ -226,11 +254,7 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
226 * mapped in the new pgd, we'll double-fault. Forcibly 254 * mapped in the new pgd, we'll double-fault. Forcibly
227 * map it. 255 * map it.
228 */ 256 */
229 unsigned int index = pgd_index(current_stack_pointer); 257 sync_current_stack_to_mm(next);
230 pgd_t *pgd = next->pgd + index;
231
232 if (unlikely(pgd_none(*pgd)))
233 set_pgd(pgd, init_mm.pgd[index]);
234 } 258 }
235 259
236 /* Stop remote flushes for the previous mm */ 260 /* Stop remote flushes for the previous mm */