panic: keep blinking in spite of long spin timer mode
authorTAMUKI Shoichi <tamuki@linet.gr.jp>
Wed, 11 Aug 2010 01:03:28 +0000 (18:03 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 11 Aug 2010 15:59:22 +0000 (08:59 -0700)
To keep panic_timeout accuracy when running under a hypervisor, the
current implementation only spins on long time (1 second) calls to mdelay.
 That brings a good effect, but the problem is the keyboard LEDs don't
blink at all on that situation.

This patch changes to call to panic_blink_enter() between every mdelay and
keeps blinking in spite of long spin timer mode.

The time to call to mdelay is now 100ms.  Even this change will keep
panic_timeout accuracy enough when running under a hypervisor.

Signed-off-by: TAMUKI Shoichi <tamuki@linet.gr.jp>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Russell King <linux@arm.linux.org.uk>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Anton Blanchard <anton@samba.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Documentation/kernel-parameters.txt
arch/arm/mach-s3c2440/mach-gta02.c
drivers/input/serio/i8042.c
include/linux/kernel.h
kernel/panic.c

index d529b13..873b680 100644 (file)
@@ -915,9 +915,6 @@ and is between 256 and 4096 characters. It is defined in the file
                             controller
        i8042.nopnp     [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX
                             controllers
-       i8042.panicblink=
-                       [HW] Frequency with which keyboard LEDs should blink
-                            when kernel panics (default is 0.5 sec)
        i8042.reset     [HW] Reset the controller during init and cleanup
        i8042.unlock    [HW] Unlock (ignore) the keylock
 
index 9e39faa..deaabe8 100644 (file)
 static struct pcf50633 *gta02_pcf;
 
 /*
- * This gets called every 1ms when we paniced.
+ * This gets called frequently when we paniced.
  */
 
-static long gta02_panic_blink(long count)
+static long gta02_panic_blink(int state)
 {
        long delay = 0;
-       static long last_blink;
-       static char led;
+       char led;
 
-       /* Fast blink: 200ms period. */
-       if (count - last_blink < 100)
-               return 0;
-
-       led ^= 1;
+       led = (state) ? 1 : 0;
        gpio_direction_output(GTA02_GPIO_AUX_LED, led);
 
-       last_blink = count;
-
        return delay;
 }
 
@@ -556,7 +549,7 @@ static void gta02_poweroff(void)
 
 static void __init gta02_machine_init(void)
 {
-       /* Set the panic callback to make AUX LED blink at ~5Hz. */
+       /* Set the panic callback to turn AUX LED on or off. */
        panic_blink = gta02_panic_blink;
 
        s3c_pm_init();
index 258b98b..46e4ba0 100644 (file)
@@ -61,10 +61,6 @@ static bool i8042_noloop;
 module_param_named(noloop, i8042_noloop, bool, 0);
 MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
 
-static unsigned int i8042_blink_frequency = 500;
-module_param_named(panicblink, i8042_blink_frequency, uint, 0600);
-MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics");
-
 #ifdef CONFIG_X86
 static bool i8042_dritek;
 module_param_named(dritek, i8042_dritek, bool, 0);
@@ -1030,8 +1026,8 @@ static void i8042_controller_reset(void)
 
 
 /*
- * i8042_panic_blink() will flash the keyboard LEDs and is called when
- * kernel panics. Flashing LEDs is useful for users running X who may
+ * i8042_panic_blink() will turn the keyboard LEDs on or off and is called
+ * when kernel panics. Flashing LEDs is useful for users running X who may
  * not see the console and will help distingushing panics from "real"
  * lockups.
  *
@@ -1041,22 +1037,12 @@ static void i8042_controller_reset(void)
 
 #define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0)
 
-static long i8042_panic_blink(long count)
+static long i8042_panic_blink(int state)
 {
        long delay = 0;
-       static long last_blink;
-       static char led;
-
-       /*
-        * We expect frequency to be about 1/2s. KDB uses about 1s.
-        * Make sure they are different.
-        */
-       if (!i8042_blink_frequency)
-               return 0;
-       if (count - last_blink < i8042_blink_frequency)
-               return 0;
+       char led;
 
-       led ^= 0x01 | 0x04;
+       led = (state) ? 0x01 | 0x04 : 0;
        while (i8042_read_status() & I8042_STR_IBF)
                DELAY;
        dbg("%02x -> i8042 (panic blink)", 0xed);
@@ -1069,7 +1055,6 @@ static long i8042_panic_blink(long count)
        dbg("%02x -> i8042 (panic blink)", led);
        i8042_write_data(led);
        DELAY;
-       last_blink = count;
        return delay;
 }
 
index 5b57236..452833d 100644 (file)
@@ -177,7 +177,7 @@ struct va_format {
 };
 
 extern struct atomic_notifier_head panic_notifier_list;
-extern long (*panic_blink)(long time);
+extern long (*panic_blink)(int state);
 NORET_TYPE void panic(const char * fmt, ...)
        __attribute__ ((NORET_AND format (printf, 1, 2))) __cold;
 extern void oops_enter(void);
index 3b16cd9..3e9037a 100644 (file)
@@ -24,6 +24,9 @@
 #include <linux/nmi.h>
 #include <linux/dmi.h>
 
+#define PANIC_TIMER_STEP 100
+#define PANIC_BLINK_SPD 18
+
 int panic_on_oops;
 static unsigned long tainted_mask;
 static int pause_on_oops;
@@ -36,36 +39,15 @@ ATOMIC_NOTIFIER_HEAD(panic_notifier_list);
 
 EXPORT_SYMBOL(panic_notifier_list);
 
-/* Returns how long it waited in ms */
-long (*panic_blink)(long time);
-EXPORT_SYMBOL(panic_blink);
-
-static void panic_blink_one_second(void)
+static long no_blink(int state)
 {
-       static long i = 0, end;
-
-       if (panic_blink) {
-               end = i + MSEC_PER_SEC;
-
-               while (i < end) {
-                       i += panic_blink(i);
-                       mdelay(1);
-                       i++;
-               }
-       } else {
-               /*
-                * When running under a hypervisor a small mdelay may get
-                * rounded up to the hypervisor timeslice. For example, with
-                * a 1ms in 10ms hypervisor timeslice we might inflate a
-                * mdelay(1) loop by 10x.
-                *
-                * If we have nothing to blink, spin on 1 second calls to
-                * mdelay to avoid this.
-                */
-               mdelay(MSEC_PER_SEC);
-       }
+       return 0;
 }
 
+/* Returns how long it waited in ms */
+long (*panic_blink)(int state);
+EXPORT_SYMBOL(panic_blink);
+
 /**
  *     panic - halt the system
  *     @fmt: The text string to print
@@ -78,7 +60,8 @@ NORET_TYPE void panic(const char * fmt, ...)
 {
        static char buf[1024];
        va_list args;
-       long i;
+       long i, i_next = 0;
+       int state = 0;
 
        /*
         * It's possible to come here directly from a panic-assertion and
@@ -117,6 +100,9 @@ NORET_TYPE void panic(const char * fmt, ...)
 
        bust_spinlocks(0);
 
+       if (!panic_blink)
+               panic_blink = no_blink;
+
        if (panic_timeout > 0) {
                /*
                 * Delay timeout seconds before rebooting the machine.
@@ -124,9 +110,13 @@ NORET_TYPE void panic(const char * fmt, ...)
                 */
                printk(KERN_EMERG "Rebooting in %d seconds..", panic_timeout);
 
-               for (i = 0; i < panic_timeout; i++) {
+               for (i = 0; i < panic_timeout * 1000; i += PANIC_TIMER_STEP) {
                        touch_nmi_watchdog();
-                       panic_blink_one_second();
+                       if (i >= i_next) {
+                               i += panic_blink(state ^= 1);
+                               i_next = i + 3600 / PANIC_BLINK_SPD;
+                       }
+                       mdelay(PANIC_TIMER_STEP);
                }
                /*
                 * This will not be a clean reboot, with everything
@@ -152,9 +142,13 @@ NORET_TYPE void panic(const char * fmt, ...)
        }
 #endif
        local_irq_enable();
-       while (1) {
+       for (i = 0; ; i += PANIC_TIMER_STEP) {
                touch_softlockup_watchdog();
-               panic_blink_one_second();
+               if (i >= i_next) {
+                       i += panic_blink(state ^= 1);
+                       i_next = i + 3600 / PANIC_BLINK_SPD;
+               }
+               mdelay(PANIC_TIMER_STEP);
        }
 }