diff options
author | Anton Blanchard <anton@samba.org> | 2011-11-29 19:23:13 -0500 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-12-07 22:02:23 -0500 |
commit | 760ca4dc90e624eb8f7ff85a5925151e25577758 (patch) | |
tree | ecdcf387c51b5752206fb546945b1f1ccd27b884 /arch/powerpc/kernel | |
parent | 8c27474a252e84e8a71ae4a43e18f0193a08e3f7 (diff) |
powerpc: Rework die()
Our die() code was based off a very old x86 version. Update it to
mirror the current x86 code.
Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r-- | arch/powerpc/kernel/traps.c | 128 |
1 files changed, 73 insertions, 55 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 014f88f03d3f..c091527efd89 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c | |||
@@ -98,18 +98,14 @@ static void pmac_backlight_unblank(void) | |||
98 | static inline void pmac_backlight_unblank(void) { } | 98 | static inline void pmac_backlight_unblank(void) { } |
99 | #endif | 99 | #endif |
100 | 100 | ||
101 | int die(const char *str, struct pt_regs *regs, long err) | 101 | static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED; |
102 | static int die_owner = -1; | ||
103 | static unsigned int die_nest_count; | ||
104 | static int die_counter; | ||
105 | |||
106 | static unsigned __kprobes long oops_begin(struct pt_regs *regs) | ||
102 | { | 107 | { |
103 | static struct { | 108 | int cpu; |
104 | raw_spinlock_t lock; | ||
105 | u32 lock_owner; | ||
106 | int lock_owner_depth; | ||
107 | } die = { | ||
108 | .lock = __RAW_SPIN_LOCK_UNLOCKED(die.lock), | ||
109 | .lock_owner = -1, | ||
110 | .lock_owner_depth = 0 | ||
111 | }; | ||
112 | static int die_counter; | ||
113 | unsigned long flags; | 109 | unsigned long flags; |
114 | 110 | ||
115 | if (debugger(regs)) | 111 | if (debugger(regs)) |
@@ -117,50 +113,37 @@ int die(const char *str, struct pt_regs *regs, long err) | |||
117 | 113 | ||
118 | oops_enter(); | 114 | oops_enter(); |
119 | 115 | ||
120 | if (die.lock_owner != raw_smp_processor_id()) { | 116 | /* racy, but better than risking deadlock. */ |
121 | console_verbose(); | 117 | raw_local_irq_save(flags); |
122 | raw_spin_lock_irqsave(&die.lock, flags); | 118 | cpu = smp_processor_id(); |
123 | die.lock_owner = smp_processor_id(); | 119 | if (!arch_spin_trylock(&die_lock)) { |
124 | die.lock_owner_depth = 0; | 120 | if (cpu == die_owner) |
125 | bust_spinlocks(1); | 121 | /* nested oops. should stop eventually */; |
126 | if (machine_is(powermac)) | 122 | else |
127 | pmac_backlight_unblank(); | 123 | arch_spin_lock(&die_lock); |
128 | } else { | ||
129 | local_save_flags(flags); | ||
130 | } | ||
131 | |||
132 | if (++die.lock_owner_depth < 3) { | ||
133 | printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); | ||
134 | #ifdef CONFIG_PREEMPT | ||
135 | printk("PREEMPT "); | ||
136 | #endif | ||
137 | #ifdef CONFIG_SMP | ||
138 | printk("SMP NR_CPUS=%d ", NR_CPUS); | ||
139 | #endif | ||
140 | #ifdef CONFIG_DEBUG_PAGEALLOC | ||
141 | printk("DEBUG_PAGEALLOC "); | ||
142 | #endif | ||
143 | #ifdef CONFIG_NUMA | ||
144 | printk("NUMA "); | ||
145 | #endif | ||
146 | printk("%s\n", ppc_md.name ? ppc_md.name : ""); | ||
147 | |||
148 | if (notify_die(DIE_OOPS, str, regs, err, 255, | ||
149 | SIGSEGV) == NOTIFY_STOP) | ||
150 | return 1; | ||
151 | |||
152 | print_modules(); | ||
153 | show_regs(regs); | ||
154 | } else { | ||
155 | printk("Recursive die() failure, output suppressed\n"); | ||
156 | } | 124 | } |
125 | die_nest_count++; | ||
126 | die_owner = cpu; | ||
127 | console_verbose(); | ||
128 | bust_spinlocks(1); | ||
129 | if (machine_is(powermac)) | ||
130 | pmac_backlight_unblank(); | ||
131 | return flags; | ||
132 | } | ||
157 | 133 | ||
134 | static void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, | ||
135 | int signr) | ||
136 | { | ||
158 | bust_spinlocks(0); | 137 | bust_spinlocks(0); |
159 | die.lock_owner = -1; | 138 | die_owner = -1; |
160 | add_taint(TAINT_DIE); | 139 | add_taint(TAINT_DIE); |
140 | die_nest_count--; | ||
161 | oops_exit(); | 141 | oops_exit(); |
162 | printk("\n"); | 142 | printk("\n"); |
163 | raw_spin_unlock_irqrestore(&die.lock, flags); | 143 | if (!die_nest_count) |
144 | /* Nest count reaches zero, release the lock. */ | ||
145 | arch_spin_unlock(&die_lock); | ||
146 | raw_local_irq_restore(flags); | ||
164 | 147 | ||
165 | /* | 148 | /* |
166 | * A system reset (0x100) is a request to dump, so we always send | 149 | * A system reset (0x100) is a request to dump, so we always send |
@@ -177,6 +160,9 @@ int die(const char *str, struct pt_regs *regs, long err) | |||
177 | crash_kexec_secondary(regs); | 160 | crash_kexec_secondary(regs); |
178 | } | 161 | } |
179 | 162 | ||
163 | if (!signr) | ||
164 | return; | ||
165 | |||
180 | /* | 166 | /* |
181 | * While our oops output is serialised by a spinlock, output | 167 | * While our oops output is serialised by a spinlock, output |
182 | * from panic() called below can race and corrupt it. If we | 168 | * from panic() called below can race and corrupt it. If we |
@@ -190,15 +176,46 @@ int die(const char *str, struct pt_regs *regs, long err) | |||
190 | 176 | ||
191 | if (in_interrupt()) | 177 | if (in_interrupt()) |
192 | panic("Fatal exception in interrupt"); | 178 | panic("Fatal exception in interrupt"); |
193 | |||
194 | if (panic_on_oops) | 179 | if (panic_on_oops) |
195 | panic("Fatal exception"); | 180 | panic("Fatal exception"); |
181 | do_exit(signr); | ||
182 | } | ||
196 | 183 | ||
197 | do_exit(err); | 184 | static int __kprobes __die(const char *str, struct pt_regs *regs, long err) |
185 | { | ||
186 | printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); | ||
187 | #ifdef CONFIG_PREEMPT | ||
188 | printk("PREEMPT "); | ||
189 | #endif | ||
190 | #ifdef CONFIG_SMP | ||
191 | printk("SMP NR_CPUS=%d ", NR_CPUS); | ||
192 | #endif | ||
193 | #ifdef CONFIG_DEBUG_PAGEALLOC | ||
194 | printk("DEBUG_PAGEALLOC "); | ||
195 | #endif | ||
196 | #ifdef CONFIG_NUMA | ||
197 | printk("NUMA "); | ||
198 | #endif | ||
199 | printk("%s\n", ppc_md.name ? ppc_md.name : ""); | ||
200 | |||
201 | if (notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV) == NOTIFY_STOP) | ||
202 | return 1; | ||
203 | |||
204 | print_modules(); | ||
205 | show_regs(regs); | ||
198 | 206 | ||
199 | return 0; | 207 | return 0; |
200 | } | 208 | } |
201 | 209 | ||
210 | void die(const char *str, struct pt_regs *regs, long err) | ||
211 | { | ||
212 | unsigned long flags = oops_begin(regs); | ||
213 | |||
214 | if (__die(str, regs, err)) | ||
215 | err = 0; | ||
216 | oops_end(flags, regs, err); | ||
217 | } | ||
218 | |||
202 | void user_single_step_siginfo(struct task_struct *tsk, | 219 | void user_single_step_siginfo(struct task_struct *tsk, |
203 | struct pt_regs *regs, siginfo_t *info) | 220 | struct pt_regs *regs, siginfo_t *info) |
204 | { | 221 | { |
@@ -217,10 +234,11 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) | |||
217 | "at %016lx nip %016lx lr %016lx code %x\n"; | 234 | "at %016lx nip %016lx lr %016lx code %x\n"; |
218 | 235 | ||
219 | if (!user_mode(regs)) { | 236 | if (!user_mode(regs)) { |
220 | if (die("Exception in kernel mode", regs, signr)) | 237 | die("Exception in kernel mode", regs, signr); |
221 | return; | 238 | return; |
222 | } else if (show_unhandled_signals && | 239 | } |
223 | unhandled_signal(current, signr)) { | 240 | |
241 | if (show_unhandled_signals && unhandled_signal(current, signr)) { | ||
224 | printk_ratelimited(regs->msr & MSR_64BIT ? fmt64 : fmt32, | 242 | printk_ratelimited(regs->msr & MSR_64BIT ? fmt64 : fmt32, |
225 | current->comm, current->pid, signr, | 243 | current->comm, current->pid, signr, |
226 | addr, regs->nip, regs->link, code); | 244 | addr, regs->nip, regs->link, code); |