History

[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.

Andrew Morton committed 20 years ago

Commit dd287791 parent 41c28ff5 files · +130 −17Browse files

Unified · Split

5 files
  1. Documentation/kernel-parameters.txt +5 −0
  2. arch/i386/kernel/traps.c +3 −0
  3. arch/i386/mm/fault.c +23 −16
  4. include/linux/kernel.h +3 −0
  5. kernel/panic.c +96 −1
Documentation/kernel-parameters.txt+5−0
@@ -1126,6 +1126,11 @@ running once the system is up. · 1125 unmodified lines
11261126 pas16= [HW,SCSI]
11271127 See header of drivers/scsi/pas16.c.
11281128
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+
11291134 pcbit= [HW,ISDN]
11301135
11311136 pcd. [PARIDE]
arch/i386/kernel/traps.c+3−0
@@ -352,6 +352,8 @@ void die(const char * str, struct pt_regs * regs, long err) · 351 unmodified lines
352352 static int die_counter;
353353 unsigned long flags;
354354
355+ oops_enter();
356+
355357 if (die.lock_owner != raw_smp_processor_id()) {
356358 console_verbose();
357359 spin_lock_irqsave(&die.lock, flags);
@@ -404,6 +406,7 @@ void die(const char * str, struct pt_regs * regs, long err) · 46 unmodified lines
404406 ssleep(5);
405407 panic("Fatal exception");
406408 }
409+ oops_exit();
407410 do_exit(SIGSEGV);
408411 }
409412
arch/i386/mm/fault.c+23−16
@@ -509,24 +509,31 @@ no_context: · 508 unmodified lines
509509
510510 bust_spinlocks(1);
511511
512−#ifdef CONFIG_X86_PAE
513− if (error_code & 16) {
514− pte_t *pte = lookup_address(address);
515−
516− if (pte && pte_present(*pte) && !pte_exec_kernel(*pte))
517− printk(KERN_CRIT "kernel tried to execute NX-protected page - exploit attempt? (uid: %d)\n", current->uid);
512+ if (oops_may_print()) {
513+ #ifdef CONFIG_X86_PAE
514+ if (error_code & 16) {
515+ pte_t *pte = lookup_address(address);
516+
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);
518532 }
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);
527533 page = read_cr3();
528534 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);
530537 /*
531538 * We must not directly access the pte in the highpte
532539 * case, the page table might be allocated in highmem.
@@ -534,7 +541,7 @@ no_context: · 1 unmodified line
534541 * it's allocated already.
535542 */
536543 #ifndef CONFIG_HIGHPTE
537− if (page & 1) {
544+ if ((page & 1) && oops_may_print()) {
538545 page &= PAGE_MASK;
539546 address &= 0x003ff000;
540547 page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT];
include/linux/kernel.h+3−0
@@ -91,6 +91,9 @@ extern struct notifier_block *panic_notifier_list; · 90 unmodified lines
9191 extern long (*panic_blink)(long time);
9292 NORET_TYPE void panic(const char * fmt, ...)
9393 __attribute__ ((NORET_AND format (printf, 1, 2)));
94+extern void oops_enter(void);
95+extern void oops_exit(void);
96+extern int oops_may_print(void);
9497 fastcall NORET_TYPE void do_exit(long error_code)
9598 ATTRIB_NORET;
9699 NORET_TYPE void complete_and_exit(struct completion *, long)
kernel/panic.c+96−1
@@ -20,10 +20,13 @@ · 19 unmodified lines
2020 #include <linux/nmi.h>
2121 #include <linux/kexec.h>
2222
23−int panic_timeout;
2423 int panic_on_oops;
2524 int tainted;
25+static int pause_on_oops;
26+static int pause_on_oops_flag;
27+static DEFINE_SPINLOCK(pause_on_oops_lock);
2628
29+int panic_timeout;
2730 EXPORT_SYMBOL(panic_timeout);
2831
2932 struct notifier_block *panic_notifier_list;
@@ -174,3 +177,95 @@ void add_taint(unsigned flag) · 144 unmodified lines
174177 tainted |= flag;
175178 }
176179 EXPORT_SYMBOL(add_taint);
180+
181+static 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+
188+static 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+ */
202+static 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+ */
241+int 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+ */
259+void 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+ */
268+void oops_exit(void)
269+{
270+ do_oops_enter_exit();
271+}