aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2013-10-28 09:48:30 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2013-11-04 07:51:47 -0500
commit106078641f32a6a10d9759f809f809725695cb09 (patch)
treea65201fd31994348a067c63e820530ad15387476
parentbe39f1968e33ca641af120a2d659421ad2225dea (diff)
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>
-rw-r--r--arch/s390/include/asm/processor.h2
-rw-r--r--arch/s390/kernel/entry.h1
-rw-r--r--arch/s390/kernel/pgm_check.S2
-rw-r--r--arch/s390/mm/fault.c37
-rw-r--r--arch/s390/mm/mmap.c12
-rw-r--r--arch/s390/mm/pgtable.c18
6 files changed, 21 insertions, 51 deletions
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index a56e63483e0f..0a876bc543d3 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -144,9 +144,7 @@ struct stack_frame {
144 regs->psw.mask = PSW_USER_BITS | PSW_MASK_BA; \ 144 regs->psw.mask = PSW_USER_BITS | PSW_MASK_BA; \
145 regs->psw.addr = new_psw | PSW_ADDR_AMODE; \ 145 regs->psw.addr = new_psw | PSW_ADDR_AMODE; \
146 regs->gprs[15] = new_stackp; \ 146 regs->gprs[15] = new_stackp; \
147 __tlb_flush_mm(current->mm); \
148 crst_table_downgrade(current->mm, 1UL << 31); \ 147 crst_table_downgrade(current->mm, 1UL << 31); \
149 update_mm(current->mm, current); \
150 execve_tail(); \ 148 execve_tail(); \
151} while (0) 149} while (0)
152 150
diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h
index e9b04c33d383..cb533f78c09e 100644
--- a/arch/s390/kernel/entry.h
+++ b/arch/s390/kernel/entry.h
@@ -23,7 +23,6 @@ asmlinkage void do_syscall_trace_exit(struct pt_regs *regs);
23 23
24void do_protection_exception(struct pt_regs *regs); 24void do_protection_exception(struct pt_regs *regs);
25void do_dat_exception(struct pt_regs *regs); 25void do_dat_exception(struct pt_regs *regs);
26void do_asce_exception(struct pt_regs *regs);
27 26
28void addressing_exception(struct pt_regs *regs); 27void addressing_exception(struct pt_regs *regs);
29void data_exception(struct pt_regs *regs); 28void data_exception(struct pt_regs *regs);
diff --git a/arch/s390/kernel/pgm_check.S b/arch/s390/kernel/pgm_check.S
index 14bdecb61923..4a460c44e17e 100644
--- a/arch/s390/kernel/pgm_check.S
+++ b/arch/s390/kernel/pgm_check.S
@@ -78,7 +78,7 @@ PGM_CHECK_DEFAULT /* 34 */
78PGM_CHECK_DEFAULT /* 35 */ 78PGM_CHECK_DEFAULT /* 35 */
79PGM_CHECK_DEFAULT /* 36 */ 79PGM_CHECK_DEFAULT /* 36 */
80PGM_CHECK_DEFAULT /* 37 */ 80PGM_CHECK_DEFAULT /* 37 */
81PGM_CHECK_64BIT(do_asce_exception) /* 38 */ 81PGM_CHECK_DEFAULT /* 38 */
82PGM_CHECK_64BIT(do_dat_exception) /* 39 */ 82PGM_CHECK_64BIT(do_dat_exception) /* 39 */
83PGM_CHECK_64BIT(do_dat_exception) /* 3a */ 83PGM_CHECK_64BIT(do_dat_exception) /* 3a */
84PGM_CHECK_64BIT(do_dat_exception) /* 3b */ 84PGM_CHECK_64BIT(do_dat_exception) /* 3b */
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index 8f29762671cf..d95265b2719f 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -423,43 +423,6 @@ void __kprobes do_dat_exception(struct pt_regs *regs)
423 do_fault_error(regs, fault); 423 do_fault_error(regs, fault);
424} 424}
425 425
426#ifdef CONFIG_64BIT
427void __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
458no_context:
459 do_no_context(regs);
460}
461#endif
462
463int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write) 426int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write)
464{ 427{
465 struct pt_regs regs; 428 struct pt_regs regs;
diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c
index 40023290ee5b..6bcb045d2bd2 100644
--- a/arch/s390/mm/mmap.c
+++ b/arch/s390/mm/mmap.c
@@ -101,18 +101,12 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
101 101
102int s390_mmap_check(unsigned long addr, unsigned long len, unsigned long flags) 102int s390_mmap_check(unsigned long addr, unsigned long len, unsigned long flags)
103{ 103{
104 int rc;
105
106 if (is_compat_task() || (TASK_SIZE >= (1UL << 53))) 104 if (is_compat_task() || (TASK_SIZE >= (1UL << 53)))
107 return 0; 105 return 0;
108 if (!(flags & MAP_FIXED)) 106 if (!(flags & MAP_FIXED))
109 addr = 0; 107 addr = 0;
110 if ((addr + len) >= TASK_SIZE) { 108 if ((addr + len) >= TASK_SIZE)
111 rc = crst_table_upgrade(current->mm, 1UL << 53); 109 return crst_table_upgrade(current->mm, 1UL << 53);
112 if (rc)
113 return rc;
114 update_mm(current->mm, current);
115 }
116 return 0; 110 return 0;
117} 111}
118 112
@@ -132,7 +126,6 @@ s390_get_unmapped_area(struct file *filp, unsigned long addr,
132 rc = crst_table_upgrade(mm, 1UL << 53); 126 rc = crst_table_upgrade(mm, 1UL << 53);
133 if (rc) 127 if (rc)
134 return (unsigned long) rc; 128 return (unsigned long) rc;
135 update_mm(mm, current);
136 area = arch_get_unmapped_area(filp, addr, len, pgoff, flags); 129 area = arch_get_unmapped_area(filp, addr, len, pgoff, flags);
137 } 130 }
138 return area; 131 return area;
@@ -155,7 +148,6 @@ s390_get_unmapped_area_topdown(struct file *filp, const unsigned long addr,
155 rc = crst_table_upgrade(mm, 1UL << 53); 148 rc = crst_table_upgrade(mm, 1UL << 53);
156 if (rc) 149 if (rc)
157 return (unsigned long) rc; 150 return (unsigned long) rc;
158 update_mm(mm, current);
159 area = arch_get_unmapped_area_topdown(filp, addr, len, 151 area = arch_get_unmapped_area_topdown(filp, addr, len,
160 pgoff, flags); 152 pgoff, flags);
161 } 153 }
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index a9be08899b0c..0a2e5e086749 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -48,12 +48,23 @@ void crst_table_free(struct mm_struct *mm, unsigned long *table)
48} 48}
49 49
50#ifdef CONFIG_64BIT 50#ifdef CONFIG_64BIT
51static 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
51int crst_table_upgrade(struct mm_struct *mm, unsigned long limit) 60int crst_table_upgrade(struct mm_struct *mm, unsigned long limit)
52{ 61{
53 unsigned long *table, *pgd; 62 unsigned long *table, *pgd;
54 unsigned long entry; 63 unsigned long entry;
64 int flush;
55 65
56 BUG_ON(limit > (1UL << 53)); 66 BUG_ON(limit > (1UL << 53));
67 flush = 0;
57repeat: 68repeat:
58 table = crst_table_alloc(mm); 69 table = crst_table_alloc(mm);
59 if (!table) 70 if (!table)
@@ -79,12 +90,15 @@ repeat:
79 mm->pgd = (pgd_t *) table; 90 mm->pgd = (pgd_t *) table;
80 mm->task_size = mm->context.asce_limit; 91 mm->task_size = mm->context.asce_limit;
81 table = NULL; 92 table = NULL;
93 flush = 1;
82 } 94 }
83 spin_unlock_bh(&mm->page_table_lock); 95 spin_unlock_bh(&mm->page_table_lock);
84 if (table) 96 if (table)
85 crst_table_free(mm, table); 97 crst_table_free(mm, table);
86 if (mm->context.asce_limit < limit) 98 if (mm->context.asce_limit < limit)
87 goto repeat; 99 goto repeat;
100 if (flush)
101 on_each_cpu(__crst_table_upgrade, mm, 0);
88 return 0; 102 return 0;
89} 103}
90 104
@@ -92,6 +106,8 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
92{ 106{
93 pgd_t *pgd; 107 pgd_t *pgd;
94 108
109 if (current->active_mm == mm)
110 __tlb_flush_mm(mm);
95 while (mm->context.asce_limit > limit) { 111 while (mm->context.asce_limit > limit) {
96 pgd = mm->pgd; 112 pgd = mm->pgd;
97 switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) { 113 switch (pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) {
@@ -114,6 +130,8 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
114 mm->task_size = mm->context.asce_limit; 130 mm->task_size = mm->context.asce_limit;
115 crst_table_free(mm, (unsigned long *) pgd); 131 crst_table_free(mm, (unsigned long *) pgd);
116 } 132 }
133 if (current->active_mm == mm)
134 update_mm(mm, current);
117} 135}
118#endif 136#endif
119 137