aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorRabin Vincent <rabin.vincent@stericsson.com>2012-06-15 05:23:32 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-07-09 12:39:37 -0400
commit02df19b4227e5b799e4642e88b568f9474fa78d0 (patch)
treeb83b03c19a4a0908aec006be85feb0cf2df8baa8 /arch
parentffae894035a1e0c46869eb81d53ea88ca4eaf2f0 (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.c78
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
236static int __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs) 236static 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
265static DEFINE_RAW_SPINLOCK(die_lock); 265static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED;
266static int die_owner = -1;
267static unsigned int die_nest_count;
266 268
267/* 269static unsigned long oops_begin(void)
268 * This function is protected against re-entrancy.
269 */
270void 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)) 292static 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 */
318void 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
303void arm_notify_die(const char *str, struct pt_regs *regs, 335void arm_notify_die(const char *str, struct pt_regs *regs,