Skip to content

Commit 1060786

Browse files
author
Martin Schwidefsky
committed
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: Martin Schwidefsky <schwidefsky@de.ibm.com>
1 parent be39f19 commit 1060786

File tree

6 files changed

+21
-51
lines changed

6 files changed

+21
-51
lines changed

arch/s390/include/asm/processor.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,7 @@ struct stack_frame {
144144
regs->psw.mask = PSW_USER_BITS | PSW_MASK_BA; \
145145
regs->psw.addr = new_psw | PSW_ADDR_AMODE; \
146146
regs->gprs[15] = new_stackp; \
147-
__tlb_flush_mm(current->mm); \
148147
crst_table_downgrade(current->mm, 1UL << 31); \
149-
update_mm(current->mm, current); \
150148
execve_tail(); \
151149
} while (0)
152150

arch/s390/kernel/entry.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ asmlinkage void do_syscall_trace_exit(struct pt_regs *regs);
2323

2424
void do_protection_exception(struct pt_regs *regs);
2525
void do_dat_exception(struct pt_regs *regs);
26-
void do_asce_exception(struct pt_regs *regs);
2726

2827
void addressing_exception(struct pt_regs *regs);
2928
void data_exception(struct pt_regs *regs);

arch/s390/kernel/pgm_check.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ PGM_CHECK_DEFAULT /* 34 */
7878
PGM_CHECK_DEFAULT /* 35 */
7979
PGM_CHECK_DEFAULT /* 36 */
8080
PGM_CHECK_DEFAULT /* 37 */
81-
PGM_CHECK_64BIT(do_asce_exception) /* 38 */
81+
PGM_CHECK_DEFAULT /* 38 */
8282
PGM_CHECK_64BIT(do_dat_exception) /* 39 */
8383
PGM_CHECK_64BIT(do_dat_exception) /* 3a */
8484
PGM_CHECK_64BIT(do_dat_exception) /* 3b */

arch/s390/mm/fault.c

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -423,43 +423,6 @@ void __kprobes do_dat_exception(struct pt_regs *regs)
423423
do_fault_error(regs, fault);
424424
}
425425

426-
#ifdef CONFIG_64BIT
427-
void __kprobes do_asce_exception(struct pt_regs *regs)
428-
{
429-
struct mm_struct *mm = current->mm;
430-
struct vm_area_struct *vma;
431-
unsigned long trans_exc_code;
432-
433-
/*
434-
* The instruction that caused the program check has
435-
* been nullified. Don't signal single step via SIGTRAP.
436-
*/
437-
clear_tsk_thread_flag(current, TIF_PER_TRAP);
438-
439-
trans_exc_code = regs->int_parm_long;
440-
if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
441-
goto no_context;
442-
443-
down_read(&mm->mmap_sem);
444-
vma = find_vma(mm, trans_exc_code & __FAIL_ADDR_MASK);
445-
up_read(&mm->mmap_sem);
446-
447-
if (vma) {
448-
update_mm(mm, current);
449-
return;
450-
}
451-
452-
/* User mode accesses just cause a SIGSEGV */
453-
if (user_mode(regs)) {
454-
do_sigsegv(regs, SEGV_MAPERR);
455-
return;
456-
}
457-
458-
no_context:
459-
do_no_context(regs);
460-
}
461-
#endif
462-
463426
int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write)
464427
{
465428
struct pt_regs regs;

arch/s390/mm/mmap.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,12 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
101101

102102
int s390_mmap_check(unsigned long addr, unsigned long len, unsigned long flags)
103103
{
104-
int rc;
105-
106104
if (is_compat_task() || (TASK_SIZE >= (1UL << 53)))
107105
return 0;
108106
if (!(flags & MAP_FIXED))
109107
addr = 0;
110-
if ((addr + len) >= TASK_SIZE) {
111-
rc = crst_table_upgrade(current->mm, 1UL << 53);
112-
if (rc)
113-
return rc;
114-
update_mm(current->mm, current);
115-
}
108+
if ((addr + len) >= TASK_SIZE)
109+
return crst_table_upgrade(current->mm, 1UL << 53);
116110
return 0;
117111
}
118112

@@ -132,7 +126,6 @@ s390_get_unmapped_area(struct file *filp, unsigned long addr,
132126
rc = crst_table_upgrade(mm, 1UL << 53);
133127
if (rc)
134128
return (unsigned long) rc;
135-
update_mm(mm, current);
136129
area = arch_get_unmapped_area(filp, addr, len, pgoff, flags);
137130
}
138131
return area;
@@ -155,7 +148,6 @@ s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr,
155148
rc = crst_table_upgrade(mm, 1UL << 53);
156149
if (rc)
157150
return (unsigned long) rc;
158-
update_mm(mm, current);
159151
area = arch_get_unmapped_area_topdown(filp, addr, len,
160152
pgoff, flags);
161153
}

arch/s390/mm/pgtable.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,23 @@ void crst_table_free(struct mm_struct *mm, unsigned long *table)
4848
}
4949

5050
#ifdef CONFIG_64BIT
51+
static void __crst_table_upgrade(void *arg)
52+
{
53+
struct mm_struct *mm = arg;
54+
55+
if (current->active_mm == mm)
56+
update_mm(mm, current);
57+
__tlb_flush_local();
58+
}
59+
5160
int crst_table_upgrade(struct mm_struct *mm, unsigned long limit)
5261
{
5362
unsigned long *table, *pgd;
5463
unsigned long entry;
64+
int flush;
5565

5666
BUG_ON(limit > (1UL << 53));
67+
flush = 0;
5768
repeat:
5869
table = crst_table_alloc(mm);
5970
if (!table)
@@ -79,19 +90,24 @@ int crst_table_upgrade(struct mm_struct *mm, unsigned long limit)
7990
mm->pgd = (pgd_t *) table;
8091
mm->task_size = mm->context.asce_limit;
8192
table = NULL;
93+
flush = 1;
8294
}
8395
spin_unlock_bh(&mm->page_table_lock);
8496
if (table)
8597
crst_table_free(mm, table);
8698
if (mm->context.asce_limit < limit)
8799
goto repeat;
100+
if (flush)
101+
on_each_cpu(__crst_table_upgrade, mm, 0);
88102
return 0;
89103
}
90104

91105
void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
92106
{
93107
pgd_t *pgd;
94108

109+
if (current->active_mm == mm)
110+
__tlb_flush_mm(mm);
95111
while (mm->context.asce_limit > limit) {
96112
pgd = mm->pgd;
97113
switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) {
@@ -114,6 +130,8 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
114130
mm->task_size = mm->context.asce_limit;
115131
crst_table_free(mm, (unsigned long *) pgd);
116132
}
133+
if (current->active_mm == mm)
134+
update_mm(mm, current);
117135
}
118136
#endif
119137

0 commit comments

Comments
 (0)