Commit ba12eedee321eeb5baecaada285daeb3462c35f5

Authored by Li Zhong
Committed by Benjamin Herrenschmidt
1 parent 22ecbe8dce

powerpc: Exception hooks for context tracking subsystem

This is the exception hooks for context tracking subsystem, including
data access, program check, single step, instruction breakpoint, machine check,
alignment, fp unavailable, altivec assist, unknown exception, whose handlers
might use RCU.

This patch corresponds to
[PATCH] x86: Exception hooks for userspace RCU extended QS
  commit 6ba3c97a38803883c2eee489505796cb0a727122

But after the exception handling moved to generic code, and some changes in
following two commits:
56dd9470d7c8734f055da2a6bac553caf4a468eb
  context_tracking: Move exception handling to generic code
6c1e0256fad84a843d915414e4b5973b7443d48d
  context_tracking: Restore correct previous context state on exception exit

it is able for exception hooks to use the generic code above instead of a
redundant arch implementation.

Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Showing 3 changed files with 112 additions and 45 deletions Side-by-side Diff

arch/powerpc/kernel/traps.c
... ... @@ -35,6 +35,7 @@
35 35 #include <linux/kdebug.h>
36 36 #include <linux/debugfs.h>
37 37 #include <linux/ratelimit.h>
  38 +#include <linux/context_tracking.h>
38 39  
39 40 #include <asm/emulated_ops.h>
40 41 #include <asm/pgtable.h>
... ... @@ -667,6 +668,7 @@
667 668  
668 669 void machine_check_exception(struct pt_regs *regs)
669 670 {
  671 + enum ctx_state prev_state = exception_enter();
670 672 int recover = 0;
671 673  
672 674 __get_cpu_var(irq_stat).mce_exceptions++;
... ... @@ -683,7 +685,7 @@
683 685 recover = cur_cpu_spec->machine_check(regs);
684 686  
685 687 if (recover > 0)
686   - return;
  688 + goto bail;
687 689  
688 690 #if defined(CONFIG_8xx) && defined(CONFIG_PCI)
689 691 /* the qspan pci read routines can cause machine checks -- Cort
690 692  
691 693  
692 694  
... ... @@ -693,20 +695,23 @@
693 695 * -- BenH
694 696 */
695 697 bad_page_fault(regs, regs->dar, SIGBUS);
696   - return;
  698 + goto bail;
697 699 #endif
698 700  
699 701 if (debugger_fault_handler(regs))
700   - return;
  702 + goto bail;
701 703  
702 704 if (check_io_access(regs))
703   - return;
  705 + goto bail;
704 706  
705 707 die("Machine check", regs, SIGBUS);
706 708  
707 709 /* Must die if the interrupt is not recoverable */
708 710 if (!(regs->msr & MSR_RI))
709 711 panic("Unrecoverable Machine check");
  712 +
  713 +bail:
  714 + exception_exit(prev_state);
710 715 }
711 716  
712 717 void SMIException(struct pt_regs *regs)
713 718  
714 719  
715 720  
716 721  
717 722  
... ... @@ -716,20 +721,29 @@
716 721  
717 722 void unknown_exception(struct pt_regs *regs)
718 723 {
  724 + enum ctx_state prev_state = exception_enter();
  725 +
719 726 printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n",
720 727 regs->nip, regs->msr, regs->trap);
721 728  
722 729 _exception(SIGTRAP, regs, 0, 0);
  730 +
  731 + exception_exit(prev_state);
723 732 }
724 733  
725 734 void instruction_breakpoint_exception(struct pt_regs *regs)
726 735 {
  736 + enum ctx_state prev_state = exception_enter();
  737 +
727 738 if (notify_die(DIE_IABR_MATCH, "iabr_match", regs, 5,
728 739 5, SIGTRAP) == NOTIFY_STOP)
729   - return;
  740 + goto bail;
730 741 if (debugger_iabr_match(regs))
731   - return;
  742 + goto bail;
732 743 _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
  744 +
  745 +bail:
  746 + exception_exit(prev_state);
733 747 }
734 748  
735 749 void RunModeException(struct pt_regs *regs)
736 750  
737 751  
738 752  
... ... @@ -739,15 +753,20 @@
739 753  
740 754 void __kprobes single_step_exception(struct pt_regs *regs)
741 755 {
  756 + enum ctx_state prev_state = exception_enter();
  757 +
742 758 clear_single_step(regs);
743 759  
744 760 if (notify_die(DIE_SSTEP, "single_step", regs, 5,
745 761 5, SIGTRAP) == NOTIFY_STOP)
746   - return;
  762 + goto bail;
747 763 if (debugger_sstep(regs))
748   - return;
  764 + goto bail;
749 765  
750 766 _exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
  767 +
  768 +bail:
  769 + exception_exit(prev_state);
751 770 }
752 771  
753 772 /*
... ... @@ -1005,6 +1024,7 @@
1005 1024  
1006 1025 void __kprobes program_check_exception(struct pt_regs *regs)
1007 1026 {
  1027 + enum ctx_state prev_state = exception_enter();
1008 1028 unsigned int reason = get_reason(regs);
1009 1029 extern int do_mathemu(struct pt_regs *regs);
1010 1030  
1011 1031  
1012 1032  
1013 1033  
1014 1034  
... ... @@ -1014,26 +1034,26 @@
1014 1034 if (reason & REASON_FP) {
1015 1035 /* IEEE FP exception */
1016 1036 parse_fpe(regs);
1017   - return;
  1037 + goto bail;
1018 1038 }
1019 1039 if (reason & REASON_TRAP) {
1020 1040 /* Debugger is first in line to stop recursive faults in
1021 1041 * rcu_lock, notify_die, or atomic_notifier_call_chain */
1022 1042 if (debugger_bpt(regs))
1023   - return;
  1043 + goto bail;
1024 1044  
1025 1045 /* trap exception */
1026 1046 if (notify_die(DIE_BPT, "breakpoint", regs, 5, 5, SIGTRAP)
1027 1047 == NOTIFY_STOP)
1028   - return;
  1048 + goto bail;
1029 1049  
1030 1050 if (!(regs->msr & MSR_PR) && /* not user-mode */
1031 1051 report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
1032 1052 regs->nip += 4;
1033   - return;
  1053 + goto bail;
1034 1054 }
1035 1055 _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
1036   - return;
  1056 + goto bail;
1037 1057 }
1038 1058 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
1039 1059 if (reason & REASON_TM) {
... ... @@ -1049,7 +1069,7 @@
1049 1069 if (!user_mode(regs) &&
1050 1070 report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) {
1051 1071 regs->nip += 4;
1052   - return;
  1072 + goto bail;
1053 1073 }
1054 1074 /* If usermode caused this, it's done something illegal and
1055 1075 * gets a SIGILL slap on the wrist. We call it an illegal
... ... @@ -1059,7 +1079,7 @@
1059 1079 */
1060 1080 if (user_mode(regs)) {
1061 1081 _exception(SIGILL, regs, ILL_ILLOPN, regs->nip);
1062   - return;
  1082 + goto bail;
1063 1083 } else {
1064 1084 printk(KERN_EMERG "Unexpected TM Bad Thing exception "
1065 1085 "at %lx (msr 0x%x)\n", regs->nip, reason);
1066 1086  
1067 1087  
... ... @@ -1083,16 +1103,16 @@
1083 1103 switch (do_mathemu(regs)) {
1084 1104 case 0:
1085 1105 emulate_single_step(regs);
1086   - return;
  1106 + goto bail;
1087 1107 case 1: {
1088 1108 int code = 0;
1089 1109 code = __parse_fpscr(current->thread.fpscr.val);
1090 1110 _exception(SIGFPE, regs, code, regs->nip);
1091   - return;
  1111 + goto bail;
1092 1112 }
1093 1113 case -EFAULT:
1094 1114 _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
1095   - return;
  1115 + goto bail;
1096 1116 }
1097 1117 /* fall through on any other errors */
1098 1118 #endif /* CONFIG_MATH_EMULATION */
1099 1119  
... ... @@ -1103,10 +1123,10 @@
1103 1123 case 0:
1104 1124 regs->nip += 4;
1105 1125 emulate_single_step(regs);
1106   - return;
  1126 + goto bail;
1107 1127 case -EFAULT:
1108 1128 _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip);
1109   - return;
  1129 + goto bail;
1110 1130 }
1111 1131 }
1112 1132  
1113 1133  
... ... @@ -1114,10 +1134,14 @@
1114 1134 _exception(SIGILL, regs, ILL_PRVOPC, regs->nip);
1115 1135 else
1116 1136 _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
  1137 +
  1138 +bail:
  1139 + exception_exit(prev_state);
1117 1140 }
1118 1141  
1119 1142 void alignment_exception(struct pt_regs *regs)
1120 1143 {
  1144 + enum ctx_state prev_state = exception_enter();
1121 1145 int sig, code, fixed = 0;
1122 1146  
1123 1147 /* We restore the interrupt state now */
... ... @@ -1131,7 +1155,7 @@
1131 1155 if (fixed == 1) {
1132 1156 regs->nip += 4; /* skip over emulated instruction */
1133 1157 emulate_single_step(regs);
1134   - return;
  1158 + goto bail;
1135 1159 }
1136 1160  
1137 1161 /* Operand address was bad */
... ... @@ -1146,6 +1170,9 @@
1146 1170 _exception(sig, regs, code, regs->dar);
1147 1171 else
1148 1172 bad_page_fault(regs, regs->dar, sig);
  1173 +
  1174 +bail:
  1175 + exception_exit(prev_state);
1149 1176 }
1150 1177  
1151 1178 void StackOverflow(struct pt_regs *regs)
1152 1179  
1153 1180  
1154 1181  
1155 1182  
... ... @@ -1174,23 +1201,32 @@
1174 1201  
1175 1202 void kernel_fp_unavailable_exception(struct pt_regs *regs)
1176 1203 {
  1204 + enum ctx_state prev_state = exception_enter();
  1205 +
1177 1206 printk(KERN_EMERG "Unrecoverable FP Unavailable Exception "
1178 1207 "%lx at %lx\n", regs->trap, regs->nip);
1179 1208 die("Unrecoverable FP Unavailable Exception", regs, SIGABRT);
  1209 +
  1210 + exception_exit(prev_state);
1180 1211 }
1181 1212  
1182 1213 void altivec_unavailable_exception(struct pt_regs *regs)
1183 1214 {
  1215 + enum ctx_state prev_state = exception_enter();
  1216 +
1184 1217 if (user_mode(regs)) {
1185 1218 /* A user program has executed an altivec instruction,
1186 1219 but this kernel doesn't support altivec. */
1187 1220 _exception(SIGILL, regs, ILL_ILLOPC, regs->nip);
1188   - return;
  1221 + goto bail;
1189 1222 }
1190 1223  
1191 1224 printk(KERN_EMERG "Unrecoverable VMX/Altivec Unavailable Exception "
1192 1225 "%lx at %lx\n", regs->trap, regs->nip);
1193 1226 die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
  1227 +
  1228 +bail:
  1229 + exception_exit(prev_state);
1194 1230 }
1195 1231  
1196 1232 void vsx_unavailable_exception(struct pt_regs *regs)
arch/powerpc/mm/fault.c
... ... @@ -32,6 +32,7 @@
32 32 #include <linux/perf_event.h>
33 33 #include <linux/magic.h>
34 34 #include <linux/ratelimit.h>
  35 +#include <linux/context_tracking.h>
35 36  
36 37 #include <asm/firmware.h>
37 38 #include <asm/page.h>
... ... @@ -196,6 +197,7 @@
196 197 int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
197 198 unsigned long error_code)
198 199 {
  200 + enum ctx_state prev_state = exception_enter();
199 201 struct vm_area_struct * vma;
200 202 struct mm_struct *mm = current->mm;
201 203 unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
... ... @@ -204,6 +206,7 @@
204 206 int trap = TRAP(regs);
205 207 int is_exec = trap == 0x400;
206 208 int fault;
  209 + int rc = 0;
207 210  
208 211 #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
209 212 /*
210 213  
211 214  
212 215  
213 216  
214 217  
... ... @@ -230,28 +233,30 @@
230 233 * look at it
231 234 */
232 235 if (error_code & ICSWX_DSI_UCT) {
233   - int rc = acop_handle_fault(regs, address, error_code);
  236 + rc = acop_handle_fault(regs, address, error_code);
234 237 if (rc)
235   - return rc;
  238 + goto bail;
236 239 }
237 240 #endif /* CONFIG_PPC_ICSWX */
238 241  
239 242 if (notify_page_fault(regs))
240   - return 0;
  243 + goto bail;
241 244  
242 245 if (unlikely(debugger_fault_handler(regs)))
243   - return 0;
  246 + goto bail;
244 247  
245 248 /* On a kernel SLB miss we can only check for a valid exception entry */
246   - if (!user_mode(regs) && (address >= TASK_SIZE))
247   - return SIGSEGV;
  249 + if (!user_mode(regs) && (address >= TASK_SIZE)) {
  250 + rc = SIGSEGV;
  251 + goto bail;
  252 + }
248 253  
249 254 #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE) || \
250 255 defined(CONFIG_PPC_BOOK3S_64))
251 256 if (error_code & DSISR_DABRMATCH) {
252 257 /* breakpoint match */
253 258 do_break(regs, address, error_code);
254   - return 0;
  259 + goto bail;
255 260 }
256 261 #endif
257 262  
... ... @@ -260,8 +265,10 @@
260 265 local_irq_enable();
261 266  
262 267 if (in_atomic() || mm == NULL) {
263   - if (!user_mode(regs))
264   - return SIGSEGV;
  268 + if (!user_mode(regs)) {
  269 + rc = SIGSEGV;
  270 + goto bail;
  271 + }
265 272 /* in_atomic() in user mode is really bad,
266 273 as is current->mm == NULL. */
267 274 printk(KERN_EMERG "Page fault in user mode with "
268 275  
... ... @@ -417,9 +424,11 @@
417 424 */
418 425 fault = handle_mm_fault(mm, vma, address, flags);
419 426 if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) {
420   - int rc = mm_fault_error(regs, address, fault);
  427 + rc = mm_fault_error(regs, address, fault);
421 428 if (rc >= MM_FAULT_RETURN)
422   - return rc;
  429 + goto bail;
  430 + else
  431 + rc = 0;
423 432 }
424 433  
425 434 /*
... ... @@ -454,7 +463,7 @@
454 463 }
455 464  
456 465 up_read(&mm->mmap_sem);
457   - return 0;
  466 + goto bail;
458 467  
459 468 bad_area:
460 469 up_read(&mm->mmap_sem);
... ... @@ -463,7 +472,7 @@
463 472 /* User mode accesses cause a SIGSEGV */
464 473 if (user_mode(regs)) {
465 474 _exception(SIGSEGV, regs, code, address);
466   - return 0;
  475 + goto bail;
467 476 }
468 477  
469 478 if (is_exec && (error_code & DSISR_PROTFAULT))
... ... @@ -471,7 +480,11 @@
471 480 " page (%lx) - exploit attempt? (uid: %d)\n",
472 481 address, from_kuid(&init_user_ns, current_uid()));
473 482  
474   - return SIGSEGV;
  483 + rc = SIGSEGV;
  484 +
  485 +bail:
  486 + exception_exit(prev_state);
  487 + return rc;
475 488  
476 489 }
477 490  
arch/powerpc/mm/hash_utils_64.c
... ... @@ -33,6 +33,7 @@
33 33 #include <linux/init.h>
34 34 #include <linux/signal.h>
35 35 #include <linux/memblock.h>
  36 +#include <linux/context_tracking.h>
36 37  
37 38 #include <asm/processor.h>
38 39 #include <asm/pgtable.h>
... ... @@ -954,6 +955,7 @@
954 955 */
955 956 int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
956 957 {
  958 + enum ctx_state prev_state = exception_enter();
957 959 pgd_t *pgdir;
958 960 unsigned long vsid;
959 961 struct mm_struct *mm;
... ... @@ -973,7 +975,8 @@
973 975 mm = current->mm;
974 976 if (! mm) {
975 977 DBG_LOW(" user region with no mm !\n");
976   - return 1;
  978 + rc = 1;
  979 + goto bail;
977 980 }
978 981 psize = get_slice_psize(mm, ea);
979 982 ssize = user_segment_size(ea);
980 983  
981 984  
... ... @@ -992,19 +995,23 @@
992 995 /* Not a valid range
993 996 * Send the problem up to do_page_fault
994 997 */
995   - return 1;
  998 + rc = 1;
  999 + goto bail;
996 1000 }
997 1001 DBG_LOW(" mm=%p, mm->pgdir=%p, vsid=%016lx\n", mm, mm->pgd, vsid);
998 1002  
999 1003 /* Bad address. */
1000 1004 if (!vsid) {
1001 1005 DBG_LOW("Bad address!\n");
1002   - return 1;
  1006 + rc = 1;
  1007 + goto bail;
1003 1008 }
1004 1009 /* Get pgdir */
1005 1010 pgdir = mm->pgd;
1006   - if (pgdir == NULL)
1007   - return 1;
  1011 + if (pgdir == NULL) {
  1012 + rc = 1;
  1013 + goto bail;
  1014 + }
1008 1015  
1009 1016 /* Check CPU locality */
1010 1017 tmp = cpumask_of(smp_processor_id());
... ... @@ -1027,7 +1034,8 @@
1027 1034 ptep = find_linux_pte_or_hugepte(pgdir, ea, &hugeshift);
1028 1035 if (ptep == NULL || !pte_present(*ptep)) {
1029 1036 DBG_LOW(" no PTE !\n");
1030   - return 1;
  1037 + rc = 1;
  1038 + goto bail;
1031 1039 }
1032 1040  
1033 1041 /* Add _PAGE_PRESENT to the required access perm */
1034 1042  
1035 1043  
... ... @@ -1038,13 +1046,16 @@
1038 1046 */
1039 1047 if (access & ~pte_val(*ptep)) {
1040 1048 DBG_LOW(" no access !\n");
1041   - return 1;
  1049 + rc = 1;
  1050 + goto bail;
1042 1051 }
1043 1052  
1044 1053 #ifdef CONFIG_HUGETLB_PAGE
1045   - if (hugeshift)
1046   - return __hash_page_huge(ea, access, vsid, ptep, trap, local,
  1054 + if (hugeshift) {
  1055 + rc = __hash_page_huge(ea, access, vsid, ptep, trap, local,
1047 1056 ssize, hugeshift, psize);
  1057 + goto bail;
  1058 + }
1048 1059 #endif /* CONFIG_HUGETLB_PAGE */
1049 1060  
1050 1061 #ifndef CONFIG_PPC_64K_PAGES
... ... @@ -1124,6 +1135,9 @@
1124 1135 pte_val(*(ptep + PTRS_PER_PTE)));
1125 1136 #endif
1126 1137 DBG_LOW(" -> rc=%d\n", rc);
  1138 +
  1139 +bail:
  1140 + exception_exit(prev_state);
1127 1141 return rc;
1128 1142 }
1129 1143 EXPORT_SYMBOL_GPL(hash_page);
... ... @@ -1259,6 +1273,8 @@
1259 1273 */
1260 1274 void low_hash_fault(struct pt_regs *regs, unsigned long address, int rc)
1261 1275 {
  1276 + enum ctx_state prev_state = exception_enter();
  1277 +
1262 1278 if (user_mode(regs)) {
1263 1279 #ifdef CONFIG_PPC_SUBPAGE_PROT
1264 1280 if (rc == -2)
... ... @@ -1268,6 +1284,8 @@
1268 1284 _exception(SIGBUS, regs, BUS_ADRERR, address);
1269 1285 } else
1270 1286 bad_page_fault(regs, address, SIGBUS);
  1287 +
  1288 + exception_exit(prev_state);
1271 1289 }
1272 1290  
1273 1291 long hpte_insert_repeating(unsigned long hash, unsigned long vpn,