aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2006-03-23 06:00:57 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-23 10:38:16 -0500
commitdd287796d608fcdc3fe5e8fdb5bf762a8f1bc32a (patch)
tree84be163fdc5fe36eb8d3f1aa5e60bfd1d794c641
parent41c28ff1635e71af072c4711ff5fadd5855d48e7 (diff)
[PATCH] pause_on_oops command line option
Attempt to fix the problem wherein people's oops reports scroll off the screen due to repeated oopsing or to oopses on other CPUs. If this happens the user can reboot with the `pause_on_oops=<seconds>' option. It will allow the first oopsing CPU to print an oops record just a single time. Second oopsing attempts, or oopses on other CPUs will cause those CPUs to enter a tight loop until the specified number of seconds have elapsed. The patch implements the infrastructure generically in the expectation that architectures other than x86 will find it useful. Cc: Dave Jones <davej@codemonkey.org.uk> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--Documentation/kernel-parameters.txt5
-rw-r--r--arch/i386/kernel/traps.c3
-rw-r--r--arch/i386/mm/fault.c39
-rw-r--r--include/linux/kernel.h3
-rw-r--r--kernel/panic.c97
5 files changed, 130 insertions, 17 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 880be3a30d8d..7b7382d0f758 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1126,6 +1126,11 @@ running once the system is up.
1126 pas16= [HW,SCSI] 1126 pas16= [HW,SCSI]
1127 See header of drivers/scsi/pas16.c. 1127 See header of drivers/scsi/pas16.c.
1128 1128
1129 pause_on_oops=
1130 Halt all CPUs after the first oops has been printed for
1131 the specified number of seconds. This is to be used if
1132 your oopses keep scrolling off the screen.
1133
1129 pcbit= [HW,ISDN] 1134 pcbit= [HW,ISDN]
1130 1135
1131 pcd. [PARIDE] 1136 pcd. [PARIDE]
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index 1b7ad4115d81..de5386b01d38 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -352,6 +352,8 @@ void die(const char * str, struct pt_regs * regs, long err)
352 static int die_counter; 352 static int die_counter;
353 unsigned long flags; 353 unsigned long flags;
354 354
355 oops_enter();
356
355 if (die.lock_owner != raw_smp_processor_id()) { 357 if (die.lock_owner != raw_smp_processor_id()) {
356 console_verbose(); 358 console_verbose();
357 spin_lock_irqsave(&die.lock, flags); 359 spin_lock_irqsave(&die.lock, flags);
@@ -404,6 +406,7 @@ void die(const char * str, struct pt_regs * regs, long err)
404 ssleep(5); 406 ssleep(5);
405 panic("Fatal exception"); 407 panic("Fatal exception");
406 } 408 }
409 oops_exit();
407 do_exit(SIGSEGV); 410 do_exit(SIGSEGV);
408} 411}
409 412
diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c
index 47a3b72ec7b6..7f0fcf219a26 100644
--- a/arch/i386/mm/fault.c
+++ b/arch/i386/mm/fault.c
@@ -509,24 +509,31 @@ no_context:
509 509
510 bust_spinlocks(1); 510 bust_spinlocks(1);
511 511
512#ifdef CONFIG_X86_PAE 512 if (oops_may_print()) {
513 if (error_code & 16) { 513 #ifdef CONFIG_X86_PAE
514 pte_t *pte = lookup_address(address); 514 if (error_code & 16) {
515 515 pte_t *pte = lookup_address(address);
516 if (pte && pte_present(*pte) && !pte_exec_kernel(*pte)) 516
517 printk(KERN_CRIT "kernel tried to execute NX-protected page - exploit attempt? (uid: %d)\n", current->uid); 517 if (pte && pte_present(*pte) && !pte_exec_kernel(*pte))
518 printk(KERN_CRIT "kernel tried to execute "
519 "NX-protected page - exploit attempt? "
520 "(uid: %d)\n", current->uid);
521 }
522 #endif
523 if (address < PAGE_SIZE)
524 printk(KERN_ALERT "BUG: unable to handle kernel NULL "
525 "pointer dereference");
526 else
527 printk(KERN_ALERT "BUG: unable to handle kernel paging"
528 " request");
529 printk(" at virtual address %08lx\n",address);
530 printk(KERN_ALERT " printing eip:\n");
531 printk("%08lx\n", regs->eip);
518 } 532 }
519#endif
520 if (address < PAGE_SIZE)
521 printk(KERN_ALERT "BUG: unable to handle kernel NULL pointer dereference");
522 else
523 printk(KERN_ALERT "BUG: unable to handle kernel paging request");
524 printk(" at virtual address %08lx\n",address);
525 printk(KERN_ALERT " printing eip:\n");
526 printk("%08lx\n", regs->eip);
527 page = read_cr3(); 533 page = read_cr3();
528 page = ((unsigned long *) __va(page))[address >> 22]; 534 page = ((unsigned long *) __va(page))[address >> 22];
529 printk(KERN_ALERT "*pde = %08lx\n", page); 535 if (oops_may_print())
536 printk(KERN_ALERT "*pde = %08lx\n", page);
530 /* 537 /*
531 * We must not directly access the pte in the highpte 538 * We must not directly access the pte in the highpte
532 * case, the page table might be allocated in highmem. 539 * case, the page table might be allocated in highmem.
@@ -534,7 +541,7 @@ no_context:
534 * it's allocated already. 541 * it's allocated already.
535 */ 542 */
536#ifndef CONFIG_HIGHPTE 543#ifndef CONFIG_HIGHPTE
537 if (page & 1) { 544 if ((page & 1) && oops_may_print()) {
538 page &= PAGE_MASK; 545 page &= PAGE_MASK;
539 address &= 0x003ff000; 546 address &= 0x003ff000;
540 page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; 547 page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT];
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 3b507bf05d09..bb6e7ddee2fd 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -91,6 +91,9 @@ extern struct notifier_block *panic_notifier_list;
91extern long (*panic_blink)(long time); 91extern long (*panic_blink)(long time);
92NORET_TYPE void panic(const char * fmt, ...) 92NORET_TYPE void panic(const char * fmt, ...)
93 __attribute__ ((NORET_AND format (printf, 1, 2))); 93 __attribute__ ((NORET_AND format (printf, 1, 2)));
94extern void oops_enter(void);
95extern void oops_exit(void);
96extern int oops_may_print(void);
94fastcall NORET_TYPE void do_exit(long error_code) 97fastcall NORET_TYPE void do_exit(long error_code)
95 ATTRIB_NORET; 98 ATTRIB_NORET;
96NORET_TYPE void complete_and_exit(struct completion *, long) 99NORET_TYPE void complete_and_exit(struct completion *, long)
diff --git a/kernel/panic.c b/kernel/panic.c
index 126dc43f1c74..acd95adddb93 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -20,10 +20,13 @@
20#include <linux/nmi.h> 20#include <linux/nmi.h>
21#include <linux/kexec.h> 21#include <linux/kexec.h>
22 22
23int panic_timeout;
24int panic_on_oops; 23int panic_on_oops;
25int tainted; 24int tainted;
25static int pause_on_oops;
26static int pause_on_oops_flag;
27static DEFINE_SPINLOCK(pause_on_oops_lock);
26 28
29int panic_timeout;
27EXPORT_SYMBOL(panic_timeout); 30EXPORT_SYMBOL(panic_timeout);
28 31
29struct notifier_block *panic_notifier_list; 32struct notifier_block *panic_notifier_list;
@@ -174,3 +177,95 @@ void add_taint(unsigned flag)
174 tainted |= flag; 177 tainted |= flag;
175} 178}
176EXPORT_SYMBOL(add_taint); 179EXPORT_SYMBOL(add_taint);
180
181static int __init pause_on_oops_setup(char *str)
182{
183 pause_on_oops = simple_strtoul(str, NULL, 0);
184 return 1;
185}
186__setup("pause_on_oops=", pause_on_oops_setup);
187
188static void spin_msec(int msecs)
189{
190 int i;
191
192 for (i = 0; i < msecs; i++) {
193 touch_nmi_watchdog();
194 mdelay(1);
195 }
196}
197
198/*
199 * It just happens that oops_enter() and oops_exit() are identically
200 * implemented...
201 */
202static void do_oops_enter_exit(void)
203{
204 unsigned long flags;
205 static int spin_counter;
206
207 if (!pause_on_oops)
208 return;
209
210 spin_lock_irqsave(&pause_on_oops_lock, flags);
211 if (pause_on_oops_flag == 0) {
212 /* This CPU may now print the oops message */
213 pause_on_oops_flag = 1;
214 } else {
215 /* We need to stall this CPU */
216 if (!spin_counter) {
217 /* This CPU gets to do the counting */
218 spin_counter = pause_on_oops;
219 do {
220 spin_unlock(&pause_on_oops_lock);
221 spin_msec(MSEC_PER_SEC);
222 spin_lock(&pause_on_oops_lock);
223 } while (--spin_counter);
224 pause_on_oops_flag = 0;
225 } else {
226 /* This CPU waits for a different one */
227 while (spin_counter) {
228 spin_unlock(&pause_on_oops_lock);
229 spin_msec(1);
230 spin_lock(&pause_on_oops_lock);
231 }
232 }
233 }
234 spin_unlock_irqrestore(&pause_on_oops_lock, flags);
235}
236
237/*
238 * Return true if the calling CPU is allowed to print oops-related info. This
239 * is a bit racy..
240 */
241int oops_may_print(void)
242{
243 return pause_on_oops_flag == 0;
244}
245
246/*
247 * Called when the architecture enters its oops handler, before it prints
248 * anything. If this is the first CPU to oops, and it's oopsing the first time
249 * then let it proceed.
250 *
251 * This is all enabled by the pause_on_oops kernel boot option. We do all this
252 * to ensure that oopses don't scroll off the screen. It has the side-effect
253 * of preventing later-oopsing CPUs from mucking up the display, too.
254 *
255 * It turns out that the CPU which is allowed to print ends up pausing for the
256 * right duration, whereas all the other CPUs pause for twice as long: once in
257 * oops_enter(), once in oops_exit().
258 */
259void oops_enter(void)
260{
261 do_oops_enter_exit();
262}
263
264/*
265 * Called when the architecture exits its oops handler, after printing
266 * everything.
267 */
268void oops_exit(void)
269{
270 do_oops_enter_exit();
271}