From fb31fbeb647ed1910290712c13f11f1db09e5506 Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Tue, 3 Jul 2012 14:35:47 +0800 Subject: leds: heartbeat: fix bug on panic With commit 49dca5aebfdeadd4bf27b6cb4c60392147dc35a4 I introduced a bug (visible if CONFIG_PROVE_RCU is enabled) which occures when a panic has happened: [ 1526.520230] =============================== [ 1526.520230] [ INFO: suspicious RCU usage. ] [ 1526.520230] 3.5.0-rc1+ #12 Not tainted [ 1526.520230] ------------------------------- [ 1526.520230] /c/kernel-tests/mm/include/linux/rcupdate.h:436 Illegal context switch in RCU read-side critical section! [ 1526.520230] [ 1526.520230] other info that might help us debug this: [ 1526.520230] [ 1526.520230] [ 1526.520230] rcu_scheduler_active = 1, debug_locks = 0 [ 1526.520230] 3 locks held by net.agent/3279: [ 1526.520230] #0: (&mm->mmap_sem){++++++}, at: [] do_page_fault+0x193/0x390 [ 1526.520230] #1: (panic_lock){+.+...}, at: [] panic+0x37/0x1d3 [ 1526.520230] #2: (rcu_read_lock){.+.+..}, at: [] rcu_lock_acquire+0x0/0x29 [ 1526.520230] [ 1526.520230] stack backtrace: [ 1526.520230] Pid: 3279, comm: net.agent Not tainted 3.5.0-rc1+ #12 [ 1526.520230] Call Trace: [ 1526.520230] [] lockdep_rcu_suspicious+0x109/0x112 [ 1526.520230] [] rcu_preempt_sleep_check+0x45/0x47 [ 1526.520230] [] __might_sleep+0x1e/0x19a [ 1526.520230] [] down_write+0x26/0x81 [ 1526.520230] [] led_trigger_unregister+0x1f/0x9c [ 1526.520230] [] heartbeat_reboot_notifier+0x15/0x19 [ 1526.520230] [] notifier_call_chain+0x96/0xcd [ 1526.520230] [] __atomic_notifier_call_chain+0x8e/0xff [ 1526.520230] [] ? kmsg_dump+0x37/0x1eb [ 1526.520230] [] atomic_notifier_call_chain+0x14/0x16 [ 1526.520230] [] panic+0xe8/0x1d3 [ 1526.520230] [] out_of_memory+0x15d/0x1d3 So in case of a panic, now just turn of the LED. Other approaches like scheduling a work to unregister the trigger aren't working because there isn't much which still runs after a panic occured (except timers). Signed-off-by: Alexander Holler Signed-off-by: Bryan Wu --- drivers/leds/ledtrig-heartbeat.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/leds/ledtrig-heartbeat.c') diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/ledtrig-heartbeat.c index 41dc76db431..a019fbb7088 100644 --- a/drivers/leds/ledtrig-heartbeat.c +++ b/drivers/leds/ledtrig-heartbeat.c @@ -21,6 +21,8 @@ #include #include "leds.h" +static int panic_heartbeats; + struct heartbeat_trig_data { unsigned int phase; unsigned int period; @@ -34,6 +36,11 @@ static void led_heartbeat_function(unsigned long data) unsigned long brightness = LED_OFF; unsigned long delay = 0; + if (unlikely(panic_heartbeats)) { + led_set_brightness(led_cdev, LED_OFF); + return; + } + /* acts like an actual heart beat -- ie thump-thump-pause... */ switch (heartbeat_data->phase) { case 0: @@ -111,12 +118,19 @@ static int heartbeat_reboot_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +static int heartbeat_panic_notifier(struct notifier_block *nb, + unsigned long code, void *unused) +{ + panic_heartbeats = 1; + return NOTIFY_DONE; +} + static struct notifier_block heartbeat_reboot_nb = { .notifier_call = heartbeat_reboot_notifier, }; static struct notifier_block heartbeat_panic_nb = { - .notifier_call = heartbeat_reboot_notifier, + .notifier_call = heartbeat_panic_notifier, }; static int __init heartbeat_trig_init(void) -- cgit v1.2.3-70-g09d2 From 0da3e65ba892b8c63d55fa5ec197b6fae55f75f8 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 13 Jun 2012 10:01:37 +0800 Subject: leds: Rename led_set_brightness() to __led_set_brightness() Rename leds internal interface led_set_brightness() to __led_set_brightness() to reduce confusion between led_set_brightness() and the external interface led_brightness_set(). led_brightness_set() cancels the timer and then calls led_set_brightness(). Signed-off-by: Shuah Khan Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 6 +++--- drivers/leds/led-core.c | 4 ++-- drivers/leds/led-triggers.c | 2 +- drivers/leds/leds.h | 2 +- drivers/leds/ledtrig-backlight.c | 8 ++++---- drivers/leds/ledtrig-default-on.c | 2 +- drivers/leds/ledtrig-gpio.c | 6 +++--- drivers/leds/ledtrig-heartbeat.c | 2 +- drivers/leds/ledtrig-oneshot.c | 4 ++-- drivers/leds/ledtrig-transient.c | 8 ++++---- 10 files changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers/leds/ledtrig-heartbeat.c') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 81eb0916b44..cb0a6eb72a2 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -53,7 +53,7 @@ static ssize_t led_brightness_store(struct device *dev, if (state == LED_OFF) led_trigger_remove(led_cdev); - led_set_brightness(led_cdev, state); + __led_set_brightness(led_cdev, state); return size; } @@ -82,7 +82,7 @@ static void led_timer_function(unsigned long data) unsigned long delay; if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { - led_set_brightness(led_cdev, LED_OFF); + __led_set_brightness(led_cdev, LED_OFF); return; } @@ -105,7 +105,7 @@ static void led_timer_function(unsigned long data) delay = led_cdev->blink_delay_off; } - led_set_brightness(led_cdev, brightness); + __led_set_brightness(led_cdev, brightness); /* Return in next iteration if led is in one-shot mode and we are in * the final blink state so that the led is toggled each delay_on + diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 31f1f78ef40..176961b2db6 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -45,7 +45,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev, /* never off - just set to brightness */ if (!delay_off) { - led_set_brightness(led_cdev, led_cdev->blink_brightness); + __led_set_brightness(led_cdev, led_cdev->blink_brightness); return; } @@ -111,6 +111,6 @@ void led_brightness_set(struct led_classdev *led_cdev, led_cdev->blink_delay_on = 0; led_cdev->blink_delay_off = 0; - led_set_brightness(led_cdev, brightness); + __led_set_brightness(led_cdev, brightness); } EXPORT_SYMBOL(led_brightness_set); diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index fa0b9be019e..f8b14dd8c2b 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -224,7 +224,7 @@ void led_trigger_event(struct led_trigger *trig, struct led_classdev *led_cdev; led_cdev = list_entry(entry, struct led_classdev, trig_list); - led_set_brightness(led_cdev, brightness); + __led_set_brightness(led_cdev, brightness); } read_unlock(&trig->leddev_list_lock); } diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index e77c7f8dcdd..d02acd49612 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -17,7 +17,7 @@ #include #include -static inline void led_set_brightness(struct led_classdev *led_cdev, +static inline void __led_set_brightness(struct led_classdev *led_cdev, enum led_brightness value) { if (value > led_cdev->max_brightness) diff --git a/drivers/leds/ledtrig-backlight.c b/drivers/leds/ledtrig-backlight.c index e2726867c5d..b941685f222 100644 --- a/drivers/leds/ledtrig-backlight.c +++ b/drivers/leds/ledtrig-backlight.c @@ -46,9 +46,9 @@ static int fb_notifier_callback(struct notifier_block *p, if ((n->old_status == UNBLANK) ^ n->invert) { n->brightness = led->brightness; - led_set_brightness(led, LED_OFF); + __led_set_brightness(led, LED_OFF); } else { - led_set_brightness(led, n->brightness); + __led_set_brightness(led, n->brightness); } n->old_status = new_status; @@ -87,9 +87,9 @@ static ssize_t bl_trig_invert_store(struct device *dev, /* After inverting, we need to update the LED. */ if ((n->old_status == BLANK) ^ n->invert) - led_set_brightness(led, LED_OFF); + __led_set_brightness(led, LED_OFF); else - led_set_brightness(led, n->brightness); + __led_set_brightness(led, n->brightness); return num; } diff --git a/drivers/leds/ledtrig-default-on.c b/drivers/leds/ledtrig-default-on.c index a4ef54b9d50..eac1f1b1ada 100644 --- a/drivers/leds/ledtrig-default-on.c +++ b/drivers/leds/ledtrig-default-on.c @@ -19,7 +19,7 @@ static void defon_trig_activate(struct led_classdev *led_cdev) { - led_set_brightness(led_cdev, led_cdev->max_brightness); + __led_set_brightness(led_cdev, led_cdev->max_brightness); } static struct led_trigger defon_led_trigger = { diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c index f057c101b89..ba215dc42f9 100644 --- a/drivers/leds/ledtrig-gpio.c +++ b/drivers/leds/ledtrig-gpio.c @@ -54,12 +54,12 @@ static void gpio_trig_work(struct work_struct *work) if (tmp) { if (gpio_data->desired_brightness) - led_set_brightness(gpio_data->led, + __led_set_brightness(gpio_data->led, gpio_data->desired_brightness); else - led_set_brightness(gpio_data->led, LED_FULL); + __led_set_brightness(gpio_data->led, LED_FULL); } else { - led_set_brightness(gpio_data->led, LED_OFF); + __led_set_brightness(gpio_data->led, LED_OFF); } } diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/ledtrig-heartbeat.c index a019fbb7088..1edc7463ce8 100644 --- a/drivers/leds/ledtrig-heartbeat.c +++ b/drivers/leds/ledtrig-heartbeat.c @@ -74,7 +74,7 @@ static void led_heartbeat_function(unsigned long data) break; } - led_set_brightness(led_cdev, brightness); + __led_set_brightness(led_cdev, brightness); mod_timer(&heartbeat_data->timer, jiffies + delay); } diff --git a/drivers/leds/ledtrig-oneshot.c b/drivers/leds/ledtrig-oneshot.c index 5c9edf7f489..5cbab41a8da 100644 --- a/drivers/leds/ledtrig-oneshot.c +++ b/drivers/leds/ledtrig-oneshot.c @@ -63,9 +63,9 @@ static ssize_t led_invert_store(struct device *dev, oneshot_data->invert = !!state; if (oneshot_data->invert) - led_set_brightness(led_cdev, LED_FULL); + __led_set_brightness(led_cdev, LED_FULL); else - led_set_brightness(led_cdev, LED_OFF); + __led_set_brightness(led_cdev, LED_OFF); return size; } diff --git a/drivers/leds/ledtrig-transient.c b/drivers/leds/ledtrig-transient.c index 83179f435e1..398f1042c43 100644 --- a/drivers/leds/ledtrig-transient.c +++ b/drivers/leds/ledtrig-transient.c @@ -41,7 +41,7 @@ static void transient_timer_function(unsigned long data) struct transient_trig_data *transient_data = led_cdev->trigger_data; transient_data->activate = 0; - led_set_brightness(led_cdev, transient_data->restore_state); + __led_set_brightness(led_cdev, transient_data->restore_state); } static ssize_t transient_activate_show(struct device *dev, @@ -72,7 +72,7 @@ static ssize_t transient_activate_store(struct device *dev, if (state == 0 && transient_data->activate == 1) { del_timer(&transient_data->timer); transient_data->activate = state; - led_set_brightness(led_cdev, transient_data->restore_state); + __led_set_brightness(led_cdev, transient_data->restore_state); return size; } @@ -80,7 +80,7 @@ static ssize_t transient_activate_store(struct device *dev, if (state == 1 && transient_data->activate == 0 && transient_data->duration != 0) { transient_data->activate = state; - led_set_brightness(led_cdev, transient_data->state); + __led_set_brightness(led_cdev, transient_data->state); transient_data->restore_state = (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL; mod_timer(&transient_data->timer, @@ -203,7 +203,7 @@ static void transient_trig_deactivate(struct led_classdev *led_cdev) if (led_cdev->activated) { del_timer_sync(&transient_data->timer); - led_set_brightness(led_cdev, transient_data->restore_state); + __led_set_brightness(led_cdev, transient_data->restore_state); device_remove_file(led_cdev->dev, &dev_attr_activate); device_remove_file(led_cdev->dev, &dev_attr_duration); device_remove_file(led_cdev->dev, &dev_attr_state); -- cgit v1.2.3-70-g09d2