diff options
author | Rabin Vincent <rabin.vincent@stericsson.com> | 2012-06-15 05:23:32 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-07-09 12:39:37 -0400 |
commit | 02df19b4227e5b799e4642e88b568f9474fa78d0 (patch) | |
tree | b83b03c19a4a0908aec006be85feb0cf2df8baa8 /arch | |
parent | ffae894035a1e0c46869eb81d53ea88ca4eaf2f0 (diff) |
ARM: 7424/1: update die handler from x86
Robustify ARM's die() handling with improvements from x86:
- Fix for a deadlock (before panic in the case of panic_on_oops) if we
oops under a spinlock which is also used from interrupt handler,
since the old code was unconditionally enabling interrupts.
- Usage of arch spinlock so lockdep etc doesn't get involved while
we're trying to dump out oopses.
- Deadlock prevention in the unlikely event that die() recurses.
The changes all touch the same few lines of code, so they're done
together in one patch.
Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/kernel/traps.c | 78 |
1 files changed, 55 insertions, 23 deletions
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 4928d89758f4..2df8715c36c0 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -233,9 +233,9 @@ void show_stack(struct task_struct *tsk, unsigned long *sp) | |||
233 | #define S_ISA " ARM" | 233 | #define S_ISA " ARM" |
234 | #endif | 234 | #endif |
235 | 235 | ||
236 | static int __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs) | 236 | static int __die(const char *str, int err, struct pt_regs *regs) |
237 | { | 237 | { |
238 | struct task_struct *tsk = thread->task; | 238 | struct task_struct *tsk = current; |
239 | static int die_counter; | 239 | static int die_counter; |
240 | int ret; | 240 | int ret; |
241 | 241 | ||
@@ -245,12 +245,12 @@ static int __die(const char *str, int err, struct thread_info *thread, struct pt | |||
245 | /* trap and error numbers are mostly meaningless on ARM */ | 245 | /* trap and error numbers are mostly meaningless on ARM */ |
246 | ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, SIGSEGV); | 246 | ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, SIGSEGV); |
247 | if (ret == NOTIFY_STOP) | 247 | if (ret == NOTIFY_STOP) |
248 | return ret; | 248 | return 1; |
249 | 249 | ||
250 | print_modules(); | 250 | print_modules(); |
251 | __show_regs(regs); | 251 | __show_regs(regs); |
252 | printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", | 252 | printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", |
253 | TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1); | 253 | TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), end_of_stack(tsk)); |
254 | 254 | ||
255 | if (!user_mode(regs) || in_interrupt()) { | 255 | if (!user_mode(regs) || in_interrupt()) { |
256 | dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp, | 256 | dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp, |
@@ -259,45 +259,77 @@ static int __die(const char *str, int err, struct thread_info *thread, struct pt | |||
259 | dump_instr(KERN_EMERG, regs); | 259 | dump_instr(KERN_EMERG, regs); |
260 | } | 260 | } |
261 | 261 | ||
262 | return ret; | 262 | return 0; |
263 | } | 263 | } |
264 | 264 | ||
265 | static DEFINE_RAW_SPINLOCK(die_lock); | 265 | static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED; |
266 | static int die_owner = -1; | ||
267 | static unsigned int die_nest_count; | ||
266 | 268 | ||
267 | /* | 269 | static unsigned long oops_begin(void) |
268 | * This function is protected against re-entrancy. | ||
269 | */ | ||
270 | void die(const char *str, struct pt_regs *regs, int err) | ||
271 | { | 270 | { |
272 | struct thread_info *thread = current_thread_info(); | 271 | int cpu; |
273 | int ret; | 272 | unsigned long flags; |
274 | enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE; | ||
275 | 273 | ||
276 | oops_enter(); | 274 | oops_enter(); |
277 | 275 | ||
278 | raw_spin_lock_irq(&die_lock); | 276 | /* racy, but better than risking deadlock. */ |
277 | raw_local_irq_save(flags); | ||
278 | cpu = smp_processor_id(); | ||
279 | if (!arch_spin_trylock(&die_lock)) { | ||
280 | if (cpu == die_owner) | ||
281 | /* nested oops. should stop eventually */; | ||
282 | else | ||
283 | arch_spin_lock(&die_lock); | ||
284 | } | ||
285 | die_nest_count++; | ||
286 | die_owner = cpu; | ||
279 | console_verbose(); | 287 | console_verbose(); |
280 | bust_spinlocks(1); | 288 | bust_spinlocks(1); |
281 | if (!user_mode(regs)) | 289 | return flags; |
282 | bug_type = report_bug(regs->ARM_pc, regs); | 290 | } |
283 | if (bug_type != BUG_TRAP_TYPE_NONE) | ||
284 | str = "Oops - BUG"; | ||
285 | ret = __die(str, err, thread, regs); | ||
286 | 291 | ||
287 | if (regs && kexec_should_crash(thread->task)) | 292 | static void oops_end(unsigned long flags, struct pt_regs *regs, int signr) |
293 | { | ||
294 | if (regs && kexec_should_crash(current)) | ||
288 | crash_kexec(regs); | 295 | crash_kexec(regs); |
289 | 296 | ||
290 | bust_spinlocks(0); | 297 | bust_spinlocks(0); |
298 | die_owner = -1; | ||
291 | add_taint(TAINT_DIE); | 299 | add_taint(TAINT_DIE); |
292 | raw_spin_unlock_irq(&die_lock); | 300 | die_nest_count--; |
301 | if (!die_nest_count) | ||
302 | /* Nest count reaches zero, release the lock. */ | ||
303 | arch_spin_unlock(&die_lock); | ||
304 | raw_local_irq_restore(flags); | ||
293 | oops_exit(); | 305 | oops_exit(); |
294 | 306 | ||
295 | if (in_interrupt()) | 307 | if (in_interrupt()) |
296 | panic("Fatal exception in interrupt"); | 308 | panic("Fatal exception in interrupt"); |
297 | if (panic_on_oops) | 309 | if (panic_on_oops) |
298 | panic("Fatal exception"); | 310 | panic("Fatal exception"); |
299 | if (ret != NOTIFY_STOP) | 311 | if (signr) |
300 | do_exit(SIGSEGV); | 312 | do_exit(signr); |
313 | } | ||
314 | |||
315 | /* | ||
316 | * This function is protected against re-entrancy. | ||
317 | */ | ||
318 | void die(const char *str, struct pt_regs *regs, int err) | ||
319 | { | ||
320 | enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE; | ||
321 | unsigned long flags = oops_begin(); | ||
322 | int sig = SIGSEGV; | ||
323 | |||
324 | if (!user_mode(regs)) | ||
325 | bug_type = report_bug(regs->ARM_pc, regs); | ||
326 | if (bug_type != BUG_TRAP_TYPE_NONE) | ||
327 | str = "Oops - BUG"; | ||
328 | |||
329 | if (__die(str, err, regs)) | ||
330 | sig = 0; | ||
331 | |||
332 | oops_end(flags, regs, sig); | ||
301 | } | 333 | } |
302 | 334 | ||
303 | void arm_notify_die(const char *str, struct pt_regs *regs, | 335 | void arm_notify_die(const char *str, struct pt_regs *regs, |