Commit 10607864 authored by Martin Schwidefsky's avatar Martin Schwidefsky

s390/mm,tlb: correct tlb flush on page table upgrade

The IDTE instruction used to flush TLB entries for a specific address
space uses the address-space-control element (ASCE) to identify
affected TLB entries. The upgrade of a page table adds a new top
level page table which changes the ASCE. The TLB entries associated
with the old ASCE need to be flushed and the ASCE for the address space
needs to be replaced synchronously on all CPUs which currently use it.
The concept of a lazy ASCE update with an exception handler is broken.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent be39f196
...@@ -144,9 +144,7 @@ struct stack_frame { ...@@ -144,9 +144,7 @@ struct stack_frame {
regs->psw.mask = PSW_USER_BITS | PSW_MASK_BA; \ regs->psw.mask = PSW_USER_BITS | PSW_MASK_BA; \
regs->psw.addr = new_psw | PSW_ADDR_AMODE; \ regs->psw.addr = new_psw | PSW_ADDR_AMODE; \
regs->gprs[15] = new_stackp; \ regs->gprs[15] = new_stackp; \
__tlb_flush_mm(current->mm); \
crst_table_downgrade(current->mm, 1UL << 31); \ crst_table_downgrade(current->mm, 1UL << 31); \
update_mm(current->mm, current); \
execve_tail(); \ execve_tail(); \
} while (0) } while (0)
......
...@@ -23,7 +23,6 @@ asmlinkage void do_syscall_trace_exit(struct pt_regs *regs); ...@@ -23,7 +23,6 @@ asmlinkage void do_syscall_trace_exit(struct pt_regs *regs);
void do_protection_exception(struct pt_regs *regs); void do_protection_exception(struct pt_regs *regs);
void do_dat_exception(struct pt_regs *regs); void do_dat_exception(struct pt_regs *regs);
void do_asce_exception(struct pt_regs *regs);
void addressing_exception(struct pt_regs *regs); void addressing_exception(struct pt_regs *regs);
void data_exception(struct pt_regs *regs); void data_exception(struct pt_regs *regs);
......
...@@ -78,7 +78,7 @@ PGM_CHECK_DEFAULT /* 34 */ ...@@ -78,7 +78,7 @@ PGM_CHECK_DEFAULT /* 34 */
PGM_CHECK_DEFAULT /* 35 */ PGM_CHECK_DEFAULT /* 35 */
PGM_CHECK_DEFAULT /* 36 */ PGM_CHECK_DEFAULT /* 36 */
PGM_CHECK_DEFAULT /* 37 */ PGM_CHECK_DEFAULT /* 37 */
PGM_CHECK_64BIT(do_asce_exception) /* 38 */ PGM_CHECK_DEFAULT /* 38 */
PGM_CHECK_64BIT(do_dat_exception) /* 39 */ PGM_CHECK_64BIT(do_dat_exception) /* 39 */
PGM_CHECK_64BIT(do_dat_exception) /* 3a */ PGM_CHECK_64BIT(do_dat_exception) /* 3a */
PGM_CHECK_64BIT(do_dat_exception) /* 3b */ PGM_CHECK_64BIT(do_dat_exception) /* 3b */
......
...@@ -423,43 +423,6 @@ void __kprobes do_dat_exception(struct pt_regs *regs) ...@@ -423,43 +423,6 @@ void __kprobes do_dat_exception(struct pt_regs *regs)
do_fault_error(regs, fault); do_fault_error(regs, fault);
} }
#ifdef CONFIG_64BIT
void __kprobes do_asce_exception(struct pt_regs *regs)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
unsigned long trans_exc_code;
/*
* The instruction that caused the program check has
* been nullified. Don't signal single step via SIGTRAP.
*/
clear_tsk_thread_flag(current, TIF_PER_TRAP);
trans_exc_code = regs->int_parm_long;
if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
goto no_context;
down_read(&mm->mmap_sem);
vma = find_vma(mm, trans_exc_code & __FAIL_ADDR_MASK);
up_read(&mm->mmap_sem);
if (vma) {
update_mm(mm, current);
return;
}
/* User mode accesses just cause a SIGSEGV */
if (user_mode(regs)) {
do_sigsegv(regs, SEGV_MAPERR);
return;
}
no_context:
do_no_context(regs);
}
#endif
int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write) int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write)
{ {
struct pt_regs regs; struct pt_regs regs;
......
...@@ -101,18 +101,12 @@ void arch_pick_mmap_layout(struct mm_struct *mm) ...@@ -101,18 +101,12 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
int s390_mmap_check(unsigned long addr, unsigned long len, unsigned long flags) int s390_mmap_check(unsigned long addr, unsigned long len, unsigned long flags)
{ {
int rc;
if (is_compat_task() || (TASK_SIZE >= (1UL << 53))) if (is_compat_task() || (TASK_SIZE >= (1UL << 53)))
return 0; return 0;
if (!(flags & MAP_FIXED)) if (!(flags & MAP_FIXED))
addr = 0; addr = 0;
if ((addr + len) >= TASK_SIZE) { if ((addr + len) >= TASK_SIZE)
rc = crst_table_upgrade(current->mm, 1UL << 53); return crst_table_upgrade(current->mm, 1UL << 53);
if (rc)
return rc;
update_mm(current->mm, current);
}
return 0; return 0;
} }
...@@ -132,7 +126,6 @@ s390_get_unmapped_area(struct file *filp, unsigned long addr, ...@@ -132,7 +126,6 @@ s390_get_unmapped_area(struct file *filp, unsigned long addr,
rc = crst_table_upgrade(mm, 1UL << 53); rc = crst_table_upgrade(mm, 1UL << 53);
if (rc) if (rc)
return (unsigned long) rc; return (unsigned long) rc;
update_mm(mm, current);
area = arch_get_unmapped_area(filp, addr, len, pgoff, flags); area = arch_get_unmapped_area(filp, addr, len, pgoff, flags);
} }
return area; return area;
...@@ -155,7 +148,6 @@ s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr, ...@@ -155,7 +148,6 @@ s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr,
rc = crst_table_upgrade(mm, 1UL << 53); rc = crst_table_upgrade(mm, 1UL << 53);
if (rc) if (rc)
return (unsigned long) rc; return (unsigned long) rc;
update_mm(mm, current);
area = arch_get_unmapped_area_topdown(filp, addr, len, area = arch_get_unmapped_area_topdown(filp, addr, len,
pgoff, flags); pgoff, flags);
} }
......
...@@ -48,12 +48,23 @@ void crst_table_free(struct mm_struct *mm, unsigned long *table) ...@@ -48,12 +48,23 @@ void crst_table_free(struct mm_struct *mm, unsigned long *table)
} }
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
static void __crst_table_upgrade(void *arg)
{
struct mm_struct *mm = arg;
if (current->active_mm == mm)
update_mm(mm, current);
__tlb_flush_local();
}
int crst_table_upgrade(struct mm_struct *mm, unsigned long limit) int crst_table_upgrade(struct mm_struct *mm, unsigned long limit)
{ {
unsigned long *table, *pgd; unsigned long *table, *pgd;
unsigned long entry; unsigned long entry;
int flush;
BUG_ON(limit > (1UL << 53)); BUG_ON(limit > (1UL << 53));
flush = 0;
repeat: repeat:
table = crst_table_alloc(mm); table = crst_table_alloc(mm);
if (!table) if (!table)
...@@ -79,12 +90,15 @@ int crst_table_upgrade(struct mm_struct *mm, unsigned long limit) ...@@ -79,12 +90,15 @@ int crst_table_upgrade(struct mm_struct *mm, unsigned long limit)
mm->pgd = (pgd_t *) table; mm->pgd = (pgd_t *) table;
mm->task_size = mm->context.asce_limit; mm->task_size = mm->context.asce_limit;
table = NULL; table = NULL;
flush = 1;
} }
spin_unlock_bh(&mm->page_table_lock); spin_unlock_bh(&mm->page_table_lock);
if (table) if (table)
crst_table_free(mm, table); crst_table_free(mm, table);
if (mm->context.asce_limit < limit) if (mm->context.asce_limit < limit)
goto repeat; goto repeat;
if (flush)
on_each_cpu(__crst_table_upgrade, mm, 0);
return 0; return 0;
} }
...@@ -92,6 +106,8 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit) ...@@ -92,6 +106,8 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
{ {
pgd_t *pgd; pgd_t *pgd;
if (current->active_mm == mm)
__tlb_flush_mm(mm);
while (mm->context.asce_limit > limit) { while (mm->context.asce_limit > limit) {
pgd = mm->pgd; pgd = mm->pgd;
switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) { switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) {
...@@ -114,6 +130,8 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit) ...@@ -114,6 +130,8 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
mm->task_size = mm->context.asce_limit; mm->task_size = mm->context.asce_limit;
crst_table_free(mm, (unsigned long *) pgd); crst_table_free(mm, (unsigned long *) pgd);
} }
if (current->active_mm == mm)
update_mm(mm, current);
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment