2006-03-31 18:31:04 +08:00
|
|
|
/*
|
|
|
|
* LED Class Core
|
|
|
|
*
|
|
|
|
* Copyright 2005-2006 Openedhand Ltd.
|
|
|
|
*
|
|
|
|
* Author: Richard Purdie <rpurdie@openedhand.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
2014-08-07 20:10:22 +08:00
|
|
|
#include <linux/leds.h>
|
2006-03-31 18:31:04 +08:00
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/module.h>
|
2014-08-07 20:10:22 +08:00
|
|
|
#include <linux/mutex.h>
|
2008-01-01 07:09:44 +08:00
|
|
|
#include <linux/rwsem.h>
|
2006-03-31 18:31:04 +08:00
|
|
|
#include "leds.h"
|
|
|
|
|
2008-01-01 07:09:44 +08:00
|
|
|
DECLARE_RWSEM(leds_list_lock);
|
2008-03-10 04:59:57 +08:00
|
|
|
EXPORT_SYMBOL_GPL(leds_list_lock);
|
2006-03-31 18:31:04 +08:00
|
|
|
|
2008-03-10 04:59:57 +08:00
|
|
|
LIST_HEAD(leds_list);
|
2006-03-31 18:31:04 +08:00
|
|
|
EXPORT_SYMBOL_GPL(leds_list);
|
2012-03-24 06:02:14 +08:00
|
|
|
|
2016-02-17 03:21:56 +08:00
|
|
|
static int __led_set_brightness(struct led_classdev *led_cdev,
|
|
|
|
enum led_brightness value)
|
|
|
|
{
|
|
|
|
if (!led_cdev->brightness_set)
|
|
|
|
return -ENOTSUPP;
|
|
|
|
|
|
|
|
led_cdev->brightness_set(led_cdev, value);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __led_set_brightness_blocking(struct led_classdev *led_cdev,
|
|
|
|
enum led_brightness value)
|
|
|
|
{
|
|
|
|
if (!led_cdev->brightness_set_blocking)
|
|
|
|
return -ENOTSUPP;
|
|
|
|
|
|
|
|
return led_cdev->brightness_set_blocking(led_cdev, value);
|
|
|
|
}
|
|
|
|
|
2015-09-28 20:38:04 +08:00
|
|
|
static void led_timer_function(unsigned long data)
|
|
|
|
{
|
|
|
|
struct led_classdev *led_cdev = (void *)data;
|
|
|
|
unsigned long brightness;
|
|
|
|
unsigned long delay;
|
|
|
|
|
|
|
|
if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
|
2015-10-07 17:10:41 +08:00
|
|
|
led_set_brightness_nosleep(led_cdev, LED_OFF);
|
2016-11-08 21:38:46 +08:00
|
|
|
clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
|
2015-09-28 20:38:04 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-08 21:38:46 +08:00
|
|
|
if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
|
|
|
|
&led_cdev->work_flags)) {
|
|
|
|
clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
|
2015-09-28 20:38:04 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
brightness = led_get_brightness(led_cdev);
|
|
|
|
if (!brightness) {
|
|
|
|
/* Time to switch the LED on. */
|
|
|
|
brightness = led_cdev->blink_brightness;
|
|
|
|
delay = led_cdev->blink_delay_on;
|
|
|
|
} else {
|
|
|
|
/* Store the current brightness value to be able
|
|
|
|
* to restore it when the delay_off period is over.
|
2015-10-07 17:10:39 +08:00
|
|
|
* Do it only if there is no pending blink brightness
|
|
|
|
* change, to avoid overwriting the new value.
|
2015-09-28 20:38:04 +08:00
|
|
|
*/
|
2016-11-08 21:38:46 +08:00
|
|
|
if (!test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
|
|
|
|
&led_cdev->work_flags))
|
2015-10-07 17:10:39 +08:00
|
|
|
led_cdev->blink_brightness = brightness;
|
2015-09-28 20:38:04 +08:00
|
|
|
brightness = LED_OFF;
|
|
|
|
delay = led_cdev->blink_delay_off;
|
|
|
|
}
|
|
|
|
|
2015-10-07 17:10:41 +08:00
|
|
|
led_set_brightness_nosleep(led_cdev, brightness);
|
2015-09-28 20:38:04 +08:00
|
|
|
|
|
|
|
/* 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 +
|
|
|
|
* delay_off milliseconds in worst case.
|
|
|
|
*/
|
2016-11-08 21:38:46 +08:00
|
|
|
if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags)) {
|
|
|
|
if (test_bit(LED_BLINK_INVERT, &led_cdev->work_flags)) {
|
2015-09-28 20:38:04 +08:00
|
|
|
if (brightness)
|
2016-11-08 21:38:46 +08:00
|
|
|
set_bit(LED_BLINK_ONESHOT_STOP,
|
|
|
|
&led_cdev->work_flags);
|
2015-09-28 20:38:04 +08:00
|
|
|
} else {
|
|
|
|
if (!brightness)
|
2016-11-08 21:38:46 +08:00
|
|
|
set_bit(LED_BLINK_ONESHOT_STOP,
|
|
|
|
&led_cdev->work_flags);
|
2015-09-28 20:38:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_brightness_delayed(struct work_struct *ws)
|
|
|
|
{
|
|
|
|
struct led_classdev *led_cdev =
|
|
|
|
container_of(ws, struct led_classdev, set_brightness_work);
|
2015-10-19 15:04:01 +08:00
|
|
|
int ret = 0;
|
2015-09-28 20:38:04 +08:00
|
|
|
|
2016-11-08 21:38:46 +08:00
|
|
|
if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
|
2015-10-07 17:10:39 +08:00
|
|
|
led_cdev->delayed_set_value = LED_OFF;
|
|
|
|
led_stop_software_blink(led_cdev);
|
|
|
|
}
|
2015-09-28 20:38:04 +08:00
|
|
|
|
2016-02-17 03:21:56 +08:00
|
|
|
ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
|
|
|
|
if (ret == -ENOTSUPP)
|
|
|
|
ret = __led_set_brightness_blocking(led_cdev,
|
|
|
|
led_cdev->delayed_set_value);
|
2016-01-23 04:43:48 +08:00
|
|
|
if (ret < 0 &&
|
|
|
|
/* LED HW might have been unplugged, therefore don't warn */
|
|
|
|
!(ret == -ENODEV && (led_cdev->flags & LED_UNREGISTERING) &&
|
|
|
|
(led_cdev->flags & LED_HW_PLUGGABLE)))
|
2015-10-19 15:04:01 +08:00
|
|
|
dev_err(led_cdev->dev,
|
|
|
|
"Setting an LED's brightness failed (%d)\n", ret);
|
2015-09-28 20:38:04 +08:00
|
|
|
}
|
|
|
|
|
2012-03-24 06:02:14 +08:00
|
|
|
static void led_set_software_blink(struct led_classdev *led_cdev,
|
|
|
|
unsigned long delay_on,
|
|
|
|
unsigned long delay_off)
|
|
|
|
{
|
|
|
|
int current_brightness;
|
|
|
|
|
|
|
|
current_brightness = led_get_brightness(led_cdev);
|
|
|
|
if (current_brightness)
|
|
|
|
led_cdev->blink_brightness = current_brightness;
|
|
|
|
if (!led_cdev->blink_brightness)
|
|
|
|
led_cdev->blink_brightness = led_cdev->max_brightness;
|
|
|
|
|
|
|
|
led_cdev->blink_delay_on = delay_on;
|
|
|
|
led_cdev->blink_delay_off = delay_off;
|
|
|
|
|
2014-02-04 16:11:42 +08:00
|
|
|
/* never on - just set to off */
|
|
|
|
if (!delay_on) {
|
2015-10-07 17:10:41 +08:00
|
|
|
led_set_brightness_nosleep(led_cdev, LED_OFF);
|
2012-03-24 06:02:14 +08:00
|
|
|
return;
|
2014-02-04 16:11:42 +08:00
|
|
|
}
|
2012-03-24 06:02:14 +08:00
|
|
|
|
|
|
|
/* never off - just set to brightness */
|
|
|
|
if (!delay_off) {
|
2015-10-07 17:10:41 +08:00
|
|
|
led_set_brightness_nosleep(led_cdev,
|
|
|
|
led_cdev->blink_brightness);
|
2012-03-24 06:02:14 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-08 21:38:46 +08:00
|
|
|
set_bit(LED_BLINK_SW, &led_cdev->work_flags);
|
Revert "leds: convert blink timer to workqueue"
This reverts commit 8b37e1bef5a6b60e949e28a4db3006e4b00bd758.
It's broken as it changes led_blink_set() in a way that it can now sleep
(while synchronously waiting for workqueue to be cancelled). That's a
problem, because it's possible that this function gets called from atomic
context (tpt_trig_timer() takes a readlock and thus disables preemption).
This has been brought up 3 weeks ago already [1] but no proper fix has
materialized, and I keep seeing the problem since 3.17-rc1.
[1] https://lkml.org/lkml/2014/8/16/128
BUG: sleeping function called from invalid context at kernel/workqueue.c:2650
in_atomic(): 1, irqs_disabled(): 0, pid: 2335, name: wpa_supplicant
5 locks held by wpa_supplicant/2335:
#0: (rtnl_mutex){+.+.+.}, at: [<ffffffff814c7c92>] rtnl_lock+0x12/0x20
#1: (&wdev->mtx){+.+.+.}, at: [<ffffffffc06e649c>] cfg80211_mgd_wext_siwessid+0x5c/0x180 [cfg80211]
#2: (&local->mtx){+.+.+.}, at: [<ffffffffc0817dea>] ieee80211_prep_connection+0x17a/0x9a0 [mac80211]
#3: (&local->chanctx_mtx){+.+.+.}, at: [<ffffffffc08081ed>] ieee80211_vif_use_channel+0x5d/0x2a0 [mac80211]
#4: (&trig->leddev_list_lock){.+.+..}, at: [<ffffffffc081e68c>] tpt_trig_timer+0xec/0x170 [mac80211]
CPU: 0 PID: 2335 Comm: wpa_supplicant Not tainted 3.17.0-rc3 #1
Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008
ffff8800360b5a50 ffff8800751f76d8 ffffffff8159e97f ffff8800360b5a30
ffff8800751f76e8 ffffffff810739a5 ffff8800751f77b0 ffffffff8106862f
ffffffff810685d0 0aa2209200000000 ffff880000000004 ffff8800361c59d0
Call Trace:
[<ffffffff8159e97f>] dump_stack+0x4d/0x66
[<ffffffff810739a5>] __might_sleep+0xe5/0x120
[<ffffffff8106862f>] flush_work+0x5f/0x270
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffff810945ca>] ? mark_held_locks+0x6a/0x90
[<ffffffff81068a5f>] ? __cancel_work_timer+0x6f/0x100
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffffc081ecdd>] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211]
[<ffffffffc07e4278>] __ieee80211_recalc_idle+0x98/0x140 [mac80211]
[<ffffffffc07e59ce>] ieee80211_idle_off+0xe/0x10 [mac80211]
[<ffffffffc0804e5b>] ieee80211_add_chanctx+0x3b/0x220 [mac80211]
[<ffffffffc08062e4>] ieee80211_new_chanctx+0x44/0xf0 [mac80211]
[<ffffffffc080838a>] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211]
[<ffffffffc0817df8>] ieee80211_prep_connection+0x188/0x9a0 [mac80211]
[<ffffffffc081c246>] ieee80211_mgd_auth+0x256/0x2e0 [mac80211]
[<ffffffffc07eab33>] ieee80211_auth+0x13/0x20 [mac80211]
[<ffffffffc06cb006>] cfg80211_mlme_auth+0x106/0x270 [cfg80211]
[<ffffffffc06ce085>] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211]
[<ffffffffc06cf670>] cfg80211_connect+0x3f0/0x540 [cfg80211]
[<ffffffffc06e6148>] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211]
[<ffffffffc06e651e>] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211]
[<ffffffffc06e36c0>] ? cfg80211_wext_giwessid+0x50/0x50 [cfg80211]
[<ffffffffc06e36dd>] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211]
[<ffffffff81584d0c>] ioctl_standard_iw_point+0x14c/0x3e0
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff8158502a>] ioctl_standard_call+0x8a/0xd0
[<ffffffff81584fa0>] ? ioctl_standard_iw_point+0x3e0/0x3e0
[<ffffffff81584b76>] wireless_process_ioctl.constprop.10+0xb6/0x100
[<ffffffff8158521d>] wext_handle_ioctl+0x5d/0xb0
[<ffffffff814cfb29>] dev_ioctl+0x329/0x620
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff8149c7f2>] sock_ioctl+0x142/0x2e0
[<ffffffff811b0140>] do_vfs_ioctl+0x300/0x520
[<ffffffff815a67fb>] ? sysret_check+0x1b/0x56
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff811b03e1>] SyS_ioctl+0x81/0xa0
[<ffffffff815a67d6>] system_call_fastpath+0x1a/0x1f
wlan0: send auth to 00:0b:6b:3c:8c:e4 (try 1/3)
wlan0: authenticated
wlan0: associate with 00:0b:6b:3c:8c:e4 (try 1/3)
wlan0: RX AssocResp from 00:0b:6b:3c:8c:e4 (capab=0x431 status=0 aid=2)
wlan0: associated
IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
cfg80211: Calling CRDA for country: NA
wlan0: Limiting TX power to 27 (27 - 0) dBm as advertised by 00:0b:6b:3c:8c:e4
=================================
[ INFO: inconsistent lock state ]
3.17.0-rc3 #1 Not tainted
---------------------------------
inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage.
swapper/0/0 [HC0[0]:SC1[1]:HE1:SE0] takes:
((&(&led_cdev->blink_work)->work)){+.?...}, at: [<ffffffff810685d0>] flush_work+0x0/0x270
{SOFTIRQ-ON-W} state was registered at:
[<ffffffff81094dbe>] __lock_acquire+0x30e/0x1a30
[<ffffffff81096c81>] lock_acquire+0x91/0x110
[<ffffffff81068608>] flush_work+0x38/0x270
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffffc081ecdd>] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211]
[<ffffffffc07e4278>] __ieee80211_recalc_idle+0x98/0x140 [mac80211]
[<ffffffffc07e59ce>] ieee80211_idle_off+0xe/0x10 [mac80211]
[<ffffffffc0804e5b>] ieee80211_add_chanctx+0x3b/0x220 [mac80211]
[<ffffffffc08062e4>] ieee80211_new_chanctx+0x44/0xf0 [mac80211]
[<ffffffffc080838a>] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211]
[<ffffffffc0817df8>] ieee80211_prep_connection+0x188/0x9a0 [mac80211]
[<ffffffffc081c246>] ieee80211_mgd_auth+0x256/0x2e0 [mac80211]
[<ffffffffc07eab33>] ieee80211_auth+0x13/0x20 [mac80211]
[<ffffffffc06cb006>] cfg80211_mlme_auth+0x106/0x270 [cfg80211]
[<ffffffffc06ce085>] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211]
[<ffffffffc06cf670>] cfg80211_connect+0x3f0/0x540 [cfg80211]
[<ffffffffc06e6148>] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211]
[<ffffffffc06e651e>] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211]
[<ffffffffc06e36dd>] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211]
[<ffffffff81584d0c>] ioctl_standard_iw_point+0x14c/0x3e0
[<ffffffff8158502a>] ioctl_standard_call+0x8a/0xd0
[<ffffffff81584b76>] wireless_process_ioctl.constprop.10+0xb6/0x100
[<ffffffff8158521d>] wext_handle_ioctl+0x5d/0xb0
[<ffffffff814cfb29>] dev_ioctl+0x329/0x620
[<ffffffff8149c7f2>] sock_ioctl+0x142/0x2e0
[<ffffffff811b0140>] do_vfs_ioctl+0x300/0x520
[<ffffffff811b03e1>] SyS_ioctl+0x81/0xa0
[<ffffffff815a67d6>] system_call_fastpath+0x1a/0x1f
irq event stamp: 493416
hardirqs last enabled at (493416): [<ffffffff81068a5f>] __cancel_work_timer+0x6f/0x100
hardirqs last disabled at (493415): [<ffffffff81067e9f>] try_to_grab_pending+0x1f/0x160
softirqs last enabled at (493408): [<ffffffff81053ced>] _local_bh_enable+0x1d/0x50
softirqs last disabled at (493409): [<ffffffff81054c75>] irq_exit+0xa5/0xb0
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
----
lock((&(&led_cdev->blink_work)->work));
<Interrupt>
lock((&(&led_cdev->blink_work)->work));
*** DEADLOCK ***
2 locks held by swapper/0/0:
#0: (((&tpt_trig->timer))){+.-...}, at: [<ffffffff810b4c50>] call_timer_fn+0x0/0x180
#1: (&trig->leddev_list_lock){.+.?..}, at: [<ffffffffc081e68c>] tpt_trig_timer+0xec/0x170 [mac80211]
stack backtrace:
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.17.0-rc3 #1
Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008
ffffffff8246eb30 ffff88007c203b00 ffffffff8159e97f ffffffff81a194c0
ffff88007c203b50 ffffffff81599c29 0000000000000001 ffffffff00000001
ffff880000000000 0000000000000006 ffffffff81a194c0 ffffffff81093ad0
Call Trace:
<IRQ> [<ffffffff8159e97f>] dump_stack+0x4d/0x66
[<ffffffff81599c29>] print_usage_bug+0x1f4/0x205
[<ffffffff81093ad0>] ? check_usage_backwards+0x140/0x140
[<ffffffff810944d3>] mark_lock+0x223/0x2b0
[<ffffffff81094d60>] __lock_acquire+0x2b0/0x1a30
[<ffffffff81096c81>] lock_acquire+0x91/0x110
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff81068608>] flush_work+0x38/0x270
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffff810945ca>] ? mark_held_locks+0x6a/0x90
[<ffffffff81068a5f>] ? __cancel_work_timer+0x6f/0x100
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff8109469d>] ? trace_hardirqs_on_caller+0xad/0x1c0
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffff810b4cc5>] call_timer_fn+0x75/0x180
[<ffffffff810b4c50>] ? process_timeout+0x10/0x10
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff810b50ac>] run_timer_softirq+0x1fc/0x2f0
[<ffffffff81054805>] __do_softirq+0x115/0x2e0
[<ffffffff81054c75>] irq_exit+0xa5/0xb0
[<ffffffff810049b3>] do_IRQ+0x53/0xf0
[<ffffffff815a74af>] common_interrupt+0x6f/0x6f
<EOI> [<ffffffff8147b56e>] ? cpuidle_enter_state+0x6e/0x180
[<ffffffff8147b732>] cpuidle_enter+0x12/0x20
[<ffffffff8108bba0>] cpu_startup_entry+0x330/0x360
[<ffffffff8158fb51>] rest_init+0xc1/0xd0
[<ffffffff8158fa90>] ? csum_partial_copy_generic+0x170/0x170
[<ffffffff81af3ff2>] start_kernel+0x44f/0x45a
[<ffffffff81af399c>] ? set_init_arg+0x53/0x53
[<ffffffff81af35ad>] x86_64_start_reservations+0x2a/0x2c
[<ffffffff81af36a0>] x86_64_start_kernel+0xf1/0xf4
Cc: Vincent Donnefort <vdonnefort@gmail.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
2014-09-02 17:03:12 +08:00
|
|
|
mod_timer(&led_cdev->blink_timer, jiffies + 1);
|
2012-03-24 06:02:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-15 22:15:27 +08:00
|
|
|
static void led_blink_setup(struct led_classdev *led_cdev,
|
2012-05-27 07:19:22 +08:00
|
|
|
unsigned long *delay_on,
|
|
|
|
unsigned long *delay_off)
|
2012-03-24 06:02:14 +08:00
|
|
|
{
|
2016-11-08 21:38:46 +08:00
|
|
|
if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
|
2012-05-27 07:19:22 +08:00
|
|
|
led_cdev->blink_set &&
|
2012-03-24 06:02:14 +08:00
|
|
|
!led_cdev->blink_set(led_cdev, delay_on, delay_off))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* blink with 1 Hz as default if nothing specified */
|
|
|
|
if (!*delay_on && !*delay_off)
|
|
|
|
*delay_on = *delay_off = 500;
|
|
|
|
|
|
|
|
led_set_software_blink(led_cdev, *delay_on, *delay_off);
|
|
|
|
}
|
2012-05-27 07:19:22 +08:00
|
|
|
|
2015-09-28 20:38:04 +08:00
|
|
|
void led_init_core(struct led_classdev *led_cdev)
|
|
|
|
{
|
|
|
|
INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
|
|
|
|
|
|
|
|
setup_timer(&led_cdev->blink_timer, led_timer_function,
|
|
|
|
(unsigned long)led_cdev);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(led_init_core);
|
|
|
|
|
2012-05-27 07:19:22 +08:00
|
|
|
void led_blink_set(struct led_classdev *led_cdev,
|
|
|
|
unsigned long *delay_on,
|
|
|
|
unsigned long *delay_off)
|
|
|
|
{
|
Revert "leds: convert blink timer to workqueue"
This reverts commit 8b37e1bef5a6b60e949e28a4db3006e4b00bd758.
It's broken as it changes led_blink_set() in a way that it can now sleep
(while synchronously waiting for workqueue to be cancelled). That's a
problem, because it's possible that this function gets called from atomic
context (tpt_trig_timer() takes a readlock and thus disables preemption).
This has been brought up 3 weeks ago already [1] but no proper fix has
materialized, and I keep seeing the problem since 3.17-rc1.
[1] https://lkml.org/lkml/2014/8/16/128
BUG: sleeping function called from invalid context at kernel/workqueue.c:2650
in_atomic(): 1, irqs_disabled(): 0, pid: 2335, name: wpa_supplicant
5 locks held by wpa_supplicant/2335:
#0: (rtnl_mutex){+.+.+.}, at: [<ffffffff814c7c92>] rtnl_lock+0x12/0x20
#1: (&wdev->mtx){+.+.+.}, at: [<ffffffffc06e649c>] cfg80211_mgd_wext_siwessid+0x5c/0x180 [cfg80211]
#2: (&local->mtx){+.+.+.}, at: [<ffffffffc0817dea>] ieee80211_prep_connection+0x17a/0x9a0 [mac80211]
#3: (&local->chanctx_mtx){+.+.+.}, at: [<ffffffffc08081ed>] ieee80211_vif_use_channel+0x5d/0x2a0 [mac80211]
#4: (&trig->leddev_list_lock){.+.+..}, at: [<ffffffffc081e68c>] tpt_trig_timer+0xec/0x170 [mac80211]
CPU: 0 PID: 2335 Comm: wpa_supplicant Not tainted 3.17.0-rc3 #1
Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008
ffff8800360b5a50 ffff8800751f76d8 ffffffff8159e97f ffff8800360b5a30
ffff8800751f76e8 ffffffff810739a5 ffff8800751f77b0 ffffffff8106862f
ffffffff810685d0 0aa2209200000000 ffff880000000004 ffff8800361c59d0
Call Trace:
[<ffffffff8159e97f>] dump_stack+0x4d/0x66
[<ffffffff810739a5>] __might_sleep+0xe5/0x120
[<ffffffff8106862f>] flush_work+0x5f/0x270
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffff810945ca>] ? mark_held_locks+0x6a/0x90
[<ffffffff81068a5f>] ? __cancel_work_timer+0x6f/0x100
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffffc081ecdd>] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211]
[<ffffffffc07e4278>] __ieee80211_recalc_idle+0x98/0x140 [mac80211]
[<ffffffffc07e59ce>] ieee80211_idle_off+0xe/0x10 [mac80211]
[<ffffffffc0804e5b>] ieee80211_add_chanctx+0x3b/0x220 [mac80211]
[<ffffffffc08062e4>] ieee80211_new_chanctx+0x44/0xf0 [mac80211]
[<ffffffffc080838a>] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211]
[<ffffffffc0817df8>] ieee80211_prep_connection+0x188/0x9a0 [mac80211]
[<ffffffffc081c246>] ieee80211_mgd_auth+0x256/0x2e0 [mac80211]
[<ffffffffc07eab33>] ieee80211_auth+0x13/0x20 [mac80211]
[<ffffffffc06cb006>] cfg80211_mlme_auth+0x106/0x270 [cfg80211]
[<ffffffffc06ce085>] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211]
[<ffffffffc06cf670>] cfg80211_connect+0x3f0/0x540 [cfg80211]
[<ffffffffc06e6148>] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211]
[<ffffffffc06e651e>] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211]
[<ffffffffc06e36c0>] ? cfg80211_wext_giwessid+0x50/0x50 [cfg80211]
[<ffffffffc06e36dd>] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211]
[<ffffffff81584d0c>] ioctl_standard_iw_point+0x14c/0x3e0
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff8158502a>] ioctl_standard_call+0x8a/0xd0
[<ffffffff81584fa0>] ? ioctl_standard_iw_point+0x3e0/0x3e0
[<ffffffff81584b76>] wireless_process_ioctl.constprop.10+0xb6/0x100
[<ffffffff8158521d>] wext_handle_ioctl+0x5d/0xb0
[<ffffffff814cfb29>] dev_ioctl+0x329/0x620
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff8149c7f2>] sock_ioctl+0x142/0x2e0
[<ffffffff811b0140>] do_vfs_ioctl+0x300/0x520
[<ffffffff815a67fb>] ? sysret_check+0x1b/0x56
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff811b03e1>] SyS_ioctl+0x81/0xa0
[<ffffffff815a67d6>] system_call_fastpath+0x1a/0x1f
wlan0: send auth to 00:0b:6b:3c:8c:e4 (try 1/3)
wlan0: authenticated
wlan0: associate with 00:0b:6b:3c:8c:e4 (try 1/3)
wlan0: RX AssocResp from 00:0b:6b:3c:8c:e4 (capab=0x431 status=0 aid=2)
wlan0: associated
IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
cfg80211: Calling CRDA for country: NA
wlan0: Limiting TX power to 27 (27 - 0) dBm as advertised by 00:0b:6b:3c:8c:e4
=================================
[ INFO: inconsistent lock state ]
3.17.0-rc3 #1 Not tainted
---------------------------------
inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage.
swapper/0/0 [HC0[0]:SC1[1]:HE1:SE0] takes:
((&(&led_cdev->blink_work)->work)){+.?...}, at: [<ffffffff810685d0>] flush_work+0x0/0x270
{SOFTIRQ-ON-W} state was registered at:
[<ffffffff81094dbe>] __lock_acquire+0x30e/0x1a30
[<ffffffff81096c81>] lock_acquire+0x91/0x110
[<ffffffff81068608>] flush_work+0x38/0x270
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffffc081ecdd>] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211]
[<ffffffffc07e4278>] __ieee80211_recalc_idle+0x98/0x140 [mac80211]
[<ffffffffc07e59ce>] ieee80211_idle_off+0xe/0x10 [mac80211]
[<ffffffffc0804e5b>] ieee80211_add_chanctx+0x3b/0x220 [mac80211]
[<ffffffffc08062e4>] ieee80211_new_chanctx+0x44/0xf0 [mac80211]
[<ffffffffc080838a>] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211]
[<ffffffffc0817df8>] ieee80211_prep_connection+0x188/0x9a0 [mac80211]
[<ffffffffc081c246>] ieee80211_mgd_auth+0x256/0x2e0 [mac80211]
[<ffffffffc07eab33>] ieee80211_auth+0x13/0x20 [mac80211]
[<ffffffffc06cb006>] cfg80211_mlme_auth+0x106/0x270 [cfg80211]
[<ffffffffc06ce085>] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211]
[<ffffffffc06cf670>] cfg80211_connect+0x3f0/0x540 [cfg80211]
[<ffffffffc06e6148>] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211]
[<ffffffffc06e651e>] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211]
[<ffffffffc06e36dd>] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211]
[<ffffffff81584d0c>] ioctl_standard_iw_point+0x14c/0x3e0
[<ffffffff8158502a>] ioctl_standard_call+0x8a/0xd0
[<ffffffff81584b76>] wireless_process_ioctl.constprop.10+0xb6/0x100
[<ffffffff8158521d>] wext_handle_ioctl+0x5d/0xb0
[<ffffffff814cfb29>] dev_ioctl+0x329/0x620
[<ffffffff8149c7f2>] sock_ioctl+0x142/0x2e0
[<ffffffff811b0140>] do_vfs_ioctl+0x300/0x520
[<ffffffff811b03e1>] SyS_ioctl+0x81/0xa0
[<ffffffff815a67d6>] system_call_fastpath+0x1a/0x1f
irq event stamp: 493416
hardirqs last enabled at (493416): [<ffffffff81068a5f>] __cancel_work_timer+0x6f/0x100
hardirqs last disabled at (493415): [<ffffffff81067e9f>] try_to_grab_pending+0x1f/0x160
softirqs last enabled at (493408): [<ffffffff81053ced>] _local_bh_enable+0x1d/0x50
softirqs last disabled at (493409): [<ffffffff81054c75>] irq_exit+0xa5/0xb0
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
----
lock((&(&led_cdev->blink_work)->work));
<Interrupt>
lock((&(&led_cdev->blink_work)->work));
*** DEADLOCK ***
2 locks held by swapper/0/0:
#0: (((&tpt_trig->timer))){+.-...}, at: [<ffffffff810b4c50>] call_timer_fn+0x0/0x180
#1: (&trig->leddev_list_lock){.+.?..}, at: [<ffffffffc081e68c>] tpt_trig_timer+0xec/0x170 [mac80211]
stack backtrace:
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.17.0-rc3 #1
Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008
ffffffff8246eb30 ffff88007c203b00 ffffffff8159e97f ffffffff81a194c0
ffff88007c203b50 ffffffff81599c29 0000000000000001 ffffffff00000001
ffff880000000000 0000000000000006 ffffffff81a194c0 ffffffff81093ad0
Call Trace:
<IRQ> [<ffffffff8159e97f>] dump_stack+0x4d/0x66
[<ffffffff81599c29>] print_usage_bug+0x1f4/0x205
[<ffffffff81093ad0>] ? check_usage_backwards+0x140/0x140
[<ffffffff810944d3>] mark_lock+0x223/0x2b0
[<ffffffff81094d60>] __lock_acquire+0x2b0/0x1a30
[<ffffffff81096c81>] lock_acquire+0x91/0x110
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff81068608>] flush_work+0x38/0x270
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffff810945ca>] ? mark_held_locks+0x6a/0x90
[<ffffffff81068a5f>] ? __cancel_work_timer+0x6f/0x100
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff8109469d>] ? trace_hardirqs_on_caller+0xad/0x1c0
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffff810b4cc5>] call_timer_fn+0x75/0x180
[<ffffffff810b4c50>] ? process_timeout+0x10/0x10
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff810b50ac>] run_timer_softirq+0x1fc/0x2f0
[<ffffffff81054805>] __do_softirq+0x115/0x2e0
[<ffffffff81054c75>] irq_exit+0xa5/0xb0
[<ffffffff810049b3>] do_IRQ+0x53/0xf0
[<ffffffff815a74af>] common_interrupt+0x6f/0x6f
<EOI> [<ffffffff8147b56e>] ? cpuidle_enter_state+0x6e/0x180
[<ffffffff8147b732>] cpuidle_enter+0x12/0x20
[<ffffffff8108bba0>] cpu_startup_entry+0x330/0x360
[<ffffffff8158fb51>] rest_init+0xc1/0xd0
[<ffffffff8158fa90>] ? csum_partial_copy_generic+0x170/0x170
[<ffffffff81af3ff2>] start_kernel+0x44f/0x45a
[<ffffffff81af399c>] ? set_init_arg+0x53/0x53
[<ffffffff81af35ad>] x86_64_start_reservations+0x2a/0x2c
[<ffffffff81af36a0>] x86_64_start_kernel+0xf1/0xf4
Cc: Vincent Donnefort <vdonnefort@gmail.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
2014-09-02 17:03:12 +08:00
|
|
|
del_timer_sync(&led_cdev->blink_timer);
|
2012-05-27 07:19:22 +08:00
|
|
|
|
2016-11-08 21:38:46 +08:00
|
|
|
clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
|
|
|
|
clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
|
2012-05-27 07:19:22 +08:00
|
|
|
|
|
|
|
led_blink_setup(led_cdev, delay_on, delay_off);
|
|
|
|
}
|
2015-09-28 21:07:10 +08:00
|
|
|
EXPORT_SYMBOL_GPL(led_blink_set);
|
2012-03-24 06:02:14 +08:00
|
|
|
|
2012-05-27 07:19:22 +08:00
|
|
|
void led_blink_set_oneshot(struct led_classdev *led_cdev,
|
|
|
|
unsigned long *delay_on,
|
|
|
|
unsigned long *delay_off,
|
|
|
|
int invert)
|
|
|
|
{
|
2016-11-08 21:38:46 +08:00
|
|
|
if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
|
Revert "leds: convert blink timer to workqueue"
This reverts commit 8b37e1bef5a6b60e949e28a4db3006e4b00bd758.
It's broken as it changes led_blink_set() in a way that it can now sleep
(while synchronously waiting for workqueue to be cancelled). That's a
problem, because it's possible that this function gets called from atomic
context (tpt_trig_timer() takes a readlock and thus disables preemption).
This has been brought up 3 weeks ago already [1] but no proper fix has
materialized, and I keep seeing the problem since 3.17-rc1.
[1] https://lkml.org/lkml/2014/8/16/128
BUG: sleeping function called from invalid context at kernel/workqueue.c:2650
in_atomic(): 1, irqs_disabled(): 0, pid: 2335, name: wpa_supplicant
5 locks held by wpa_supplicant/2335:
#0: (rtnl_mutex){+.+.+.}, at: [<ffffffff814c7c92>] rtnl_lock+0x12/0x20
#1: (&wdev->mtx){+.+.+.}, at: [<ffffffffc06e649c>] cfg80211_mgd_wext_siwessid+0x5c/0x180 [cfg80211]
#2: (&local->mtx){+.+.+.}, at: [<ffffffffc0817dea>] ieee80211_prep_connection+0x17a/0x9a0 [mac80211]
#3: (&local->chanctx_mtx){+.+.+.}, at: [<ffffffffc08081ed>] ieee80211_vif_use_channel+0x5d/0x2a0 [mac80211]
#4: (&trig->leddev_list_lock){.+.+..}, at: [<ffffffffc081e68c>] tpt_trig_timer+0xec/0x170 [mac80211]
CPU: 0 PID: 2335 Comm: wpa_supplicant Not tainted 3.17.0-rc3 #1
Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008
ffff8800360b5a50 ffff8800751f76d8 ffffffff8159e97f ffff8800360b5a30
ffff8800751f76e8 ffffffff810739a5 ffff8800751f77b0 ffffffff8106862f
ffffffff810685d0 0aa2209200000000 ffff880000000004 ffff8800361c59d0
Call Trace:
[<ffffffff8159e97f>] dump_stack+0x4d/0x66
[<ffffffff810739a5>] __might_sleep+0xe5/0x120
[<ffffffff8106862f>] flush_work+0x5f/0x270
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffff810945ca>] ? mark_held_locks+0x6a/0x90
[<ffffffff81068a5f>] ? __cancel_work_timer+0x6f/0x100
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffffc081ecdd>] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211]
[<ffffffffc07e4278>] __ieee80211_recalc_idle+0x98/0x140 [mac80211]
[<ffffffffc07e59ce>] ieee80211_idle_off+0xe/0x10 [mac80211]
[<ffffffffc0804e5b>] ieee80211_add_chanctx+0x3b/0x220 [mac80211]
[<ffffffffc08062e4>] ieee80211_new_chanctx+0x44/0xf0 [mac80211]
[<ffffffffc080838a>] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211]
[<ffffffffc0817df8>] ieee80211_prep_connection+0x188/0x9a0 [mac80211]
[<ffffffffc081c246>] ieee80211_mgd_auth+0x256/0x2e0 [mac80211]
[<ffffffffc07eab33>] ieee80211_auth+0x13/0x20 [mac80211]
[<ffffffffc06cb006>] cfg80211_mlme_auth+0x106/0x270 [cfg80211]
[<ffffffffc06ce085>] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211]
[<ffffffffc06cf670>] cfg80211_connect+0x3f0/0x540 [cfg80211]
[<ffffffffc06e6148>] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211]
[<ffffffffc06e651e>] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211]
[<ffffffffc06e36c0>] ? cfg80211_wext_giwessid+0x50/0x50 [cfg80211]
[<ffffffffc06e36dd>] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211]
[<ffffffff81584d0c>] ioctl_standard_iw_point+0x14c/0x3e0
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff8158502a>] ioctl_standard_call+0x8a/0xd0
[<ffffffff81584fa0>] ? ioctl_standard_iw_point+0x3e0/0x3e0
[<ffffffff81584b76>] wireless_process_ioctl.constprop.10+0xb6/0x100
[<ffffffff8158521d>] wext_handle_ioctl+0x5d/0xb0
[<ffffffff814cfb29>] dev_ioctl+0x329/0x620
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff8149c7f2>] sock_ioctl+0x142/0x2e0
[<ffffffff811b0140>] do_vfs_ioctl+0x300/0x520
[<ffffffff815a67fb>] ? sysret_check+0x1b/0x56
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff811b03e1>] SyS_ioctl+0x81/0xa0
[<ffffffff815a67d6>] system_call_fastpath+0x1a/0x1f
wlan0: send auth to 00:0b:6b:3c:8c:e4 (try 1/3)
wlan0: authenticated
wlan0: associate with 00:0b:6b:3c:8c:e4 (try 1/3)
wlan0: RX AssocResp from 00:0b:6b:3c:8c:e4 (capab=0x431 status=0 aid=2)
wlan0: associated
IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
cfg80211: Calling CRDA for country: NA
wlan0: Limiting TX power to 27 (27 - 0) dBm as advertised by 00:0b:6b:3c:8c:e4
=================================
[ INFO: inconsistent lock state ]
3.17.0-rc3 #1 Not tainted
---------------------------------
inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage.
swapper/0/0 [HC0[0]:SC1[1]:HE1:SE0] takes:
((&(&led_cdev->blink_work)->work)){+.?...}, at: [<ffffffff810685d0>] flush_work+0x0/0x270
{SOFTIRQ-ON-W} state was registered at:
[<ffffffff81094dbe>] __lock_acquire+0x30e/0x1a30
[<ffffffff81096c81>] lock_acquire+0x91/0x110
[<ffffffff81068608>] flush_work+0x38/0x270
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffffc081ecdd>] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211]
[<ffffffffc07e4278>] __ieee80211_recalc_idle+0x98/0x140 [mac80211]
[<ffffffffc07e59ce>] ieee80211_idle_off+0xe/0x10 [mac80211]
[<ffffffffc0804e5b>] ieee80211_add_chanctx+0x3b/0x220 [mac80211]
[<ffffffffc08062e4>] ieee80211_new_chanctx+0x44/0xf0 [mac80211]
[<ffffffffc080838a>] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211]
[<ffffffffc0817df8>] ieee80211_prep_connection+0x188/0x9a0 [mac80211]
[<ffffffffc081c246>] ieee80211_mgd_auth+0x256/0x2e0 [mac80211]
[<ffffffffc07eab33>] ieee80211_auth+0x13/0x20 [mac80211]
[<ffffffffc06cb006>] cfg80211_mlme_auth+0x106/0x270 [cfg80211]
[<ffffffffc06ce085>] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211]
[<ffffffffc06cf670>] cfg80211_connect+0x3f0/0x540 [cfg80211]
[<ffffffffc06e6148>] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211]
[<ffffffffc06e651e>] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211]
[<ffffffffc06e36dd>] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211]
[<ffffffff81584d0c>] ioctl_standard_iw_point+0x14c/0x3e0
[<ffffffff8158502a>] ioctl_standard_call+0x8a/0xd0
[<ffffffff81584b76>] wireless_process_ioctl.constprop.10+0xb6/0x100
[<ffffffff8158521d>] wext_handle_ioctl+0x5d/0xb0
[<ffffffff814cfb29>] dev_ioctl+0x329/0x620
[<ffffffff8149c7f2>] sock_ioctl+0x142/0x2e0
[<ffffffff811b0140>] do_vfs_ioctl+0x300/0x520
[<ffffffff811b03e1>] SyS_ioctl+0x81/0xa0
[<ffffffff815a67d6>] system_call_fastpath+0x1a/0x1f
irq event stamp: 493416
hardirqs last enabled at (493416): [<ffffffff81068a5f>] __cancel_work_timer+0x6f/0x100
hardirqs last disabled at (493415): [<ffffffff81067e9f>] try_to_grab_pending+0x1f/0x160
softirqs last enabled at (493408): [<ffffffff81053ced>] _local_bh_enable+0x1d/0x50
softirqs last disabled at (493409): [<ffffffff81054c75>] irq_exit+0xa5/0xb0
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
----
lock((&(&led_cdev->blink_work)->work));
<Interrupt>
lock((&(&led_cdev->blink_work)->work));
*** DEADLOCK ***
2 locks held by swapper/0/0:
#0: (((&tpt_trig->timer))){+.-...}, at: [<ffffffff810b4c50>] call_timer_fn+0x0/0x180
#1: (&trig->leddev_list_lock){.+.?..}, at: [<ffffffffc081e68c>] tpt_trig_timer+0xec/0x170 [mac80211]
stack backtrace:
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.17.0-rc3 #1
Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008
ffffffff8246eb30 ffff88007c203b00 ffffffff8159e97f ffffffff81a194c0
ffff88007c203b50 ffffffff81599c29 0000000000000001 ffffffff00000001
ffff880000000000 0000000000000006 ffffffff81a194c0 ffffffff81093ad0
Call Trace:
<IRQ> [<ffffffff8159e97f>] dump_stack+0x4d/0x66
[<ffffffff81599c29>] print_usage_bug+0x1f4/0x205
[<ffffffff81093ad0>] ? check_usage_backwards+0x140/0x140
[<ffffffff810944d3>] mark_lock+0x223/0x2b0
[<ffffffff81094d60>] __lock_acquire+0x2b0/0x1a30
[<ffffffff81096c81>] lock_acquire+0x91/0x110
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff81068608>] flush_work+0x38/0x270
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffff810945ca>] ? mark_held_locks+0x6a/0x90
[<ffffffff81068a5f>] ? __cancel_work_timer+0x6f/0x100
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff8109469d>] ? trace_hardirqs_on_caller+0xad/0x1c0
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffff810b4cc5>] call_timer_fn+0x75/0x180
[<ffffffff810b4c50>] ? process_timeout+0x10/0x10
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff810b50ac>] run_timer_softirq+0x1fc/0x2f0
[<ffffffff81054805>] __do_softirq+0x115/0x2e0
[<ffffffff81054c75>] irq_exit+0xa5/0xb0
[<ffffffff810049b3>] do_IRQ+0x53/0xf0
[<ffffffff815a74af>] common_interrupt+0x6f/0x6f
<EOI> [<ffffffff8147b56e>] ? cpuidle_enter_state+0x6e/0x180
[<ffffffff8147b732>] cpuidle_enter+0x12/0x20
[<ffffffff8108bba0>] cpu_startup_entry+0x330/0x360
[<ffffffff8158fb51>] rest_init+0xc1/0xd0
[<ffffffff8158fa90>] ? csum_partial_copy_generic+0x170/0x170
[<ffffffff81af3ff2>] start_kernel+0x44f/0x45a
[<ffffffff81af399c>] ? set_init_arg+0x53/0x53
[<ffffffff81af35ad>] x86_64_start_reservations+0x2a/0x2c
[<ffffffff81af36a0>] x86_64_start_kernel+0xf1/0xf4
Cc: Vincent Donnefort <vdonnefort@gmail.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
2014-09-02 17:03:12 +08:00
|
|
|
timer_pending(&led_cdev->blink_timer))
|
2012-05-27 07:19:22 +08:00
|
|
|
return;
|
|
|
|
|
2016-11-08 21:38:46 +08:00
|
|
|
set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
|
|
|
|
clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
|
2012-05-27 07:19:22 +08:00
|
|
|
|
|
|
|
if (invert)
|
2016-11-08 21:38:46 +08:00
|
|
|
set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
|
2012-05-27 07:19:22 +08:00
|
|
|
else
|
2016-11-08 21:38:46 +08:00
|
|
|
clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
|
2012-05-27 07:19:22 +08:00
|
|
|
|
|
|
|
led_blink_setup(led_cdev, delay_on, delay_off);
|
|
|
|
}
|
2015-09-28 21:07:10 +08:00
|
|
|
EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
|
2012-05-27 07:19:22 +08:00
|
|
|
|
2012-08-15 21:44:34 +08:00
|
|
|
void led_stop_software_blink(struct led_classdev *led_cdev)
|
2012-03-24 06:02:14 +08:00
|
|
|
{
|
Revert "leds: convert blink timer to workqueue"
This reverts commit 8b37e1bef5a6b60e949e28a4db3006e4b00bd758.
It's broken as it changes led_blink_set() in a way that it can now sleep
(while synchronously waiting for workqueue to be cancelled). That's a
problem, because it's possible that this function gets called from atomic
context (tpt_trig_timer() takes a readlock and thus disables preemption).
This has been brought up 3 weeks ago already [1] but no proper fix has
materialized, and I keep seeing the problem since 3.17-rc1.
[1] https://lkml.org/lkml/2014/8/16/128
BUG: sleeping function called from invalid context at kernel/workqueue.c:2650
in_atomic(): 1, irqs_disabled(): 0, pid: 2335, name: wpa_supplicant
5 locks held by wpa_supplicant/2335:
#0: (rtnl_mutex){+.+.+.}, at: [<ffffffff814c7c92>] rtnl_lock+0x12/0x20
#1: (&wdev->mtx){+.+.+.}, at: [<ffffffffc06e649c>] cfg80211_mgd_wext_siwessid+0x5c/0x180 [cfg80211]
#2: (&local->mtx){+.+.+.}, at: [<ffffffffc0817dea>] ieee80211_prep_connection+0x17a/0x9a0 [mac80211]
#3: (&local->chanctx_mtx){+.+.+.}, at: [<ffffffffc08081ed>] ieee80211_vif_use_channel+0x5d/0x2a0 [mac80211]
#4: (&trig->leddev_list_lock){.+.+..}, at: [<ffffffffc081e68c>] tpt_trig_timer+0xec/0x170 [mac80211]
CPU: 0 PID: 2335 Comm: wpa_supplicant Not tainted 3.17.0-rc3 #1
Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008
ffff8800360b5a50 ffff8800751f76d8 ffffffff8159e97f ffff8800360b5a30
ffff8800751f76e8 ffffffff810739a5 ffff8800751f77b0 ffffffff8106862f
ffffffff810685d0 0aa2209200000000 ffff880000000004 ffff8800361c59d0
Call Trace:
[<ffffffff8159e97f>] dump_stack+0x4d/0x66
[<ffffffff810739a5>] __might_sleep+0xe5/0x120
[<ffffffff8106862f>] flush_work+0x5f/0x270
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffff810945ca>] ? mark_held_locks+0x6a/0x90
[<ffffffff81068a5f>] ? __cancel_work_timer+0x6f/0x100
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffffc081ecdd>] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211]
[<ffffffffc07e4278>] __ieee80211_recalc_idle+0x98/0x140 [mac80211]
[<ffffffffc07e59ce>] ieee80211_idle_off+0xe/0x10 [mac80211]
[<ffffffffc0804e5b>] ieee80211_add_chanctx+0x3b/0x220 [mac80211]
[<ffffffffc08062e4>] ieee80211_new_chanctx+0x44/0xf0 [mac80211]
[<ffffffffc080838a>] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211]
[<ffffffffc0817df8>] ieee80211_prep_connection+0x188/0x9a0 [mac80211]
[<ffffffffc081c246>] ieee80211_mgd_auth+0x256/0x2e0 [mac80211]
[<ffffffffc07eab33>] ieee80211_auth+0x13/0x20 [mac80211]
[<ffffffffc06cb006>] cfg80211_mlme_auth+0x106/0x270 [cfg80211]
[<ffffffffc06ce085>] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211]
[<ffffffffc06cf670>] cfg80211_connect+0x3f0/0x540 [cfg80211]
[<ffffffffc06e6148>] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211]
[<ffffffffc06e651e>] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211]
[<ffffffffc06e36c0>] ? cfg80211_wext_giwessid+0x50/0x50 [cfg80211]
[<ffffffffc06e36dd>] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211]
[<ffffffff81584d0c>] ioctl_standard_iw_point+0x14c/0x3e0
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff8158502a>] ioctl_standard_call+0x8a/0xd0
[<ffffffff81584fa0>] ? ioctl_standard_iw_point+0x3e0/0x3e0
[<ffffffff81584b76>] wireless_process_ioctl.constprop.10+0xb6/0x100
[<ffffffff8158521d>] wext_handle_ioctl+0x5d/0xb0
[<ffffffff814cfb29>] dev_ioctl+0x329/0x620
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff8149c7f2>] sock_ioctl+0x142/0x2e0
[<ffffffff811b0140>] do_vfs_ioctl+0x300/0x520
[<ffffffff815a67fb>] ? sysret_check+0x1b/0x56
[<ffffffff810946ed>] ? trace_hardirqs_on_caller+0xfd/0x1c0
[<ffffffff811b03e1>] SyS_ioctl+0x81/0xa0
[<ffffffff815a67d6>] system_call_fastpath+0x1a/0x1f
wlan0: send auth to 00:0b:6b:3c:8c:e4 (try 1/3)
wlan0: authenticated
wlan0: associate with 00:0b:6b:3c:8c:e4 (try 1/3)
wlan0: RX AssocResp from 00:0b:6b:3c:8c:e4 (capab=0x431 status=0 aid=2)
wlan0: associated
IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
cfg80211: Calling CRDA for country: NA
wlan0: Limiting TX power to 27 (27 - 0) dBm as advertised by 00:0b:6b:3c:8c:e4
=================================
[ INFO: inconsistent lock state ]
3.17.0-rc3 #1 Not tainted
---------------------------------
inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage.
swapper/0/0 [HC0[0]:SC1[1]:HE1:SE0] takes:
((&(&led_cdev->blink_work)->work)){+.?...}, at: [<ffffffff810685d0>] flush_work+0x0/0x270
{SOFTIRQ-ON-W} state was registered at:
[<ffffffff81094dbe>] __lock_acquire+0x30e/0x1a30
[<ffffffff81096c81>] lock_acquire+0x91/0x110
[<ffffffff81068608>] flush_work+0x38/0x270
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffffc081ecdd>] ieee80211_mod_tpt_led_trig+0x9d/0x160 [mac80211]
[<ffffffffc07e4278>] __ieee80211_recalc_idle+0x98/0x140 [mac80211]
[<ffffffffc07e59ce>] ieee80211_idle_off+0xe/0x10 [mac80211]
[<ffffffffc0804e5b>] ieee80211_add_chanctx+0x3b/0x220 [mac80211]
[<ffffffffc08062e4>] ieee80211_new_chanctx+0x44/0xf0 [mac80211]
[<ffffffffc080838a>] ieee80211_vif_use_channel+0x1fa/0x2a0 [mac80211]
[<ffffffffc0817df8>] ieee80211_prep_connection+0x188/0x9a0 [mac80211]
[<ffffffffc081c246>] ieee80211_mgd_auth+0x256/0x2e0 [mac80211]
[<ffffffffc07eab33>] ieee80211_auth+0x13/0x20 [mac80211]
[<ffffffffc06cb006>] cfg80211_mlme_auth+0x106/0x270 [cfg80211]
[<ffffffffc06ce085>] cfg80211_conn_do_work+0x155/0x3b0 [cfg80211]
[<ffffffffc06cf670>] cfg80211_connect+0x3f0/0x540 [cfg80211]
[<ffffffffc06e6148>] cfg80211_mgd_wext_connect+0x158/0x1f0 [cfg80211]
[<ffffffffc06e651e>] cfg80211_mgd_wext_siwessid+0xde/0x180 [cfg80211]
[<ffffffffc06e36dd>] cfg80211_wext_siwessid+0x1d/0x40 [cfg80211]
[<ffffffff81584d0c>] ioctl_standard_iw_point+0x14c/0x3e0
[<ffffffff8158502a>] ioctl_standard_call+0x8a/0xd0
[<ffffffff81584b76>] wireless_process_ioctl.constprop.10+0xb6/0x100
[<ffffffff8158521d>] wext_handle_ioctl+0x5d/0xb0
[<ffffffff814cfb29>] dev_ioctl+0x329/0x620
[<ffffffff8149c7f2>] sock_ioctl+0x142/0x2e0
[<ffffffff811b0140>] do_vfs_ioctl+0x300/0x520
[<ffffffff811b03e1>] SyS_ioctl+0x81/0xa0
[<ffffffff815a67d6>] system_call_fastpath+0x1a/0x1f
irq event stamp: 493416
hardirqs last enabled at (493416): [<ffffffff81068a5f>] __cancel_work_timer+0x6f/0x100
hardirqs last disabled at (493415): [<ffffffff81067e9f>] try_to_grab_pending+0x1f/0x160
softirqs last enabled at (493408): [<ffffffff81053ced>] _local_bh_enable+0x1d/0x50
softirqs last disabled at (493409): [<ffffffff81054c75>] irq_exit+0xa5/0xb0
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
----
lock((&(&led_cdev->blink_work)->work));
<Interrupt>
lock((&(&led_cdev->blink_work)->work));
*** DEADLOCK ***
2 locks held by swapper/0/0:
#0: (((&tpt_trig->timer))){+.-...}, at: [<ffffffff810b4c50>] call_timer_fn+0x0/0x180
#1: (&trig->leddev_list_lock){.+.?..}, at: [<ffffffffc081e68c>] tpt_trig_timer+0xec/0x170 [mac80211]
stack backtrace:
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.17.0-rc3 #1
Hardware name: LENOVO 7470BN2/7470BN2, BIOS 6DET38WW (2.02 ) 12/19/2008
ffffffff8246eb30 ffff88007c203b00 ffffffff8159e97f ffffffff81a194c0
ffff88007c203b50 ffffffff81599c29 0000000000000001 ffffffff00000001
ffff880000000000 0000000000000006 ffffffff81a194c0 ffffffff81093ad0
Call Trace:
<IRQ> [<ffffffff8159e97f>] dump_stack+0x4d/0x66
[<ffffffff81599c29>] print_usage_bug+0x1f4/0x205
[<ffffffff81093ad0>] ? check_usage_backwards+0x140/0x140
[<ffffffff810944d3>] mark_lock+0x223/0x2b0
[<ffffffff81094d60>] __lock_acquire+0x2b0/0x1a30
[<ffffffff81096c81>] lock_acquire+0x91/0x110
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff81068608>] flush_work+0x38/0x270
[<ffffffff810685d0>] ? mod_delayed_work_on+0x80/0x80
[<ffffffff810945ca>] ? mark_held_locks+0x6a/0x90
[<ffffffff81068a5f>] ? __cancel_work_timer+0x6f/0x100
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff8109469d>] ? trace_hardirqs_on_caller+0xad/0x1c0
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff81068a6b>] __cancel_work_timer+0x7b/0x100
[<ffffffff81068b0e>] cancel_delayed_work_sync+0xe/0x10
[<ffffffff8147cf3b>] led_blink_set+0x1b/0x40
[<ffffffffc081e6b0>] tpt_trig_timer+0x110/0x170 [mac80211]
[<ffffffff810b4cc5>] call_timer_fn+0x75/0x180
[<ffffffff810b4c50>] ? process_timeout+0x10/0x10
[<ffffffffc081e5a0>] ? __ieee80211_get_rx_led_name+0x10/0x10 [mac80211]
[<ffffffff810b50ac>] run_timer_softirq+0x1fc/0x2f0
[<ffffffff81054805>] __do_softirq+0x115/0x2e0
[<ffffffff81054c75>] irq_exit+0xa5/0xb0
[<ffffffff810049b3>] do_IRQ+0x53/0xf0
[<ffffffff815a74af>] common_interrupt+0x6f/0x6f
<EOI> [<ffffffff8147b56e>] ? cpuidle_enter_state+0x6e/0x180
[<ffffffff8147b732>] cpuidle_enter+0x12/0x20
[<ffffffff8108bba0>] cpu_startup_entry+0x330/0x360
[<ffffffff8158fb51>] rest_init+0xc1/0xd0
[<ffffffff8158fa90>] ? csum_partial_copy_generic+0x170/0x170
[<ffffffff81af3ff2>] start_kernel+0x44f/0x45a
[<ffffffff81af399c>] ? set_init_arg+0x53/0x53
[<ffffffff81af35ad>] x86_64_start_reservations+0x2a/0x2c
[<ffffffff81af36a0>] x86_64_start_kernel+0xf1/0xf4
Cc: Vincent Donnefort <vdonnefort@gmail.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
2014-09-02 17:03:12 +08:00
|
|
|
del_timer_sync(&led_cdev->blink_timer);
|
2012-06-07 03:12:34 +08:00
|
|
|
led_cdev->blink_delay_on = 0;
|
|
|
|
led_cdev->blink_delay_off = 0;
|
2016-11-08 21:38:46 +08:00
|
|
|
clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
|
2012-08-15 21:44:34 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(led_stop_software_blink);
|
|
|
|
|
|
|
|
void led_set_brightness(struct led_classdev *led_cdev,
|
|
|
|
enum led_brightness brightness)
|
|
|
|
{
|
2015-10-07 17:10:39 +08:00
|
|
|
/*
|
leds: core: Fix brightness setting upon hardware blinking enabled
Commit 76931edd54f8 ("leds: fix brightness changing when software blinking
is active") changed the semantics of led_set_brightness() which according
to the documentation should disable blinking upon any brightness setting.
Moreover it made it different for soft blink case, where it was possible
to change blink brightness, and for hardware blink case, where setting
any brightness greater than 0 was ignored.
While the change itself is against the documentation claims, it was driven
also by the fact that timer trigger remained active after turning blinking
off. Fixing that would have required major refactoring in the led-core,
led-class, and led-triggers because of cyclic dependencies.
Finally, it has been decided that allowing for brightness change during
blinking is beneficial as it can be accomplished without disturbing
blink rhythm.
The change in brightness setting semantics will not affect existing
LED class drivers that implement blink_set op thanks to the LED_BLINK_SW
flag introduced by this patch. The flag state will be from now on checked
in led_set_brightness() which will allow to distinguish between software
and hardware blink mode. In the latter case the control will be passed
directly to the drivers which apply their semantics on brightness set,
which is disable the blinking in case of most such drivers. New drivers
will apply new semantics and just change the brightness while hardware
blinking is on, if possible.
The issue was smuggled by subsequent LED core improvements, which modified
the code that originally introduced the problem.
Fixes: f1e80c07416a ("leds: core: Add two new LED_BLINK_ flags")
Signed-off-by: Tony Makkiel <tony.makkiel@daqri.com>
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
2016-05-19 00:22:45 +08:00
|
|
|
* If software blink is active, delay brightness setting
|
2015-10-07 17:10:39 +08:00
|
|
|
* until the next timer tick.
|
|
|
|
*/
|
2016-11-08 21:38:46 +08:00
|
|
|
if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
|
2015-10-07 17:10:39 +08:00
|
|
|
/*
|
|
|
|
* If we need to disable soft blinking delegate this to the
|
|
|
|
* work queue task to avoid problems in case we are called
|
|
|
|
* from hard irq context.
|
|
|
|
*/
|
|
|
|
if (brightness == LED_OFF) {
|
2016-11-08 21:38:46 +08:00
|
|
|
set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
|
2015-05-14 23:24:02 +08:00
|
|
|
schedule_work(&led_cdev->set_brightness_work);
|
2015-10-07 17:10:39 +08:00
|
|
|
} else {
|
2016-11-08 21:38:46 +08:00
|
|
|
set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
|
|
|
|
&led_cdev->work_flags);
|
2015-10-07 17:10:39 +08:00
|
|
|
led_cdev->blink_brightness = brightness;
|
|
|
|
}
|
2012-08-15 21:44:34 +08:00
|
|
|
return;
|
|
|
|
}
|
2012-06-07 03:12:34 +08:00
|
|
|
|
2015-10-07 17:10:43 +08:00
|
|
|
led_set_brightness_nosleep(led_cdev, brightness);
|
2012-03-24 06:02:14 +08:00
|
|
|
}
|
2015-09-28 21:07:10 +08:00
|
|
|
EXPORT_SYMBOL_GPL(led_set_brightness);
|
2014-08-20 21:41:55 +08:00
|
|
|
|
2015-10-07 17:10:41 +08:00
|
|
|
void led_set_brightness_nopm(struct led_classdev *led_cdev,
|
|
|
|
enum led_brightness value)
|
|
|
|
{
|
|
|
|
/* Use brightness_set op if available, it is guaranteed not to sleep */
|
2016-02-17 03:21:56 +08:00
|
|
|
if (!__led_set_brightness(led_cdev, value))
|
2015-10-07 17:10:41 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* If brightness setting can sleep, delegate it to a work queue task */
|
|
|
|
led_cdev->delayed_set_value = value;
|
|
|
|
schedule_work(&led_cdev->set_brightness_work);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
|
|
|
|
|
|
|
|
void led_set_brightness_nosleep(struct led_classdev *led_cdev,
|
|
|
|
enum led_brightness value)
|
|
|
|
{
|
|
|
|
led_cdev->brightness = min(value, led_cdev->max_brightness);
|
|
|
|
|
|
|
|
if (led_cdev->flags & LED_SUSPENDED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
led_set_brightness_nopm(led_cdev, led_cdev->brightness);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(led_set_brightness_nosleep);
|
|
|
|
|
2015-10-07 17:10:43 +08:00
|
|
|
int led_set_brightness_sync(struct led_classdev *led_cdev,
|
|
|
|
enum led_brightness value)
|
|
|
|
{
|
|
|
|
if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
led_cdev->brightness = min(value, led_cdev->max_brightness);
|
|
|
|
|
|
|
|
if (led_cdev->flags & LED_SUSPENDED)
|
|
|
|
return 0;
|
|
|
|
|
2016-02-17 03:21:56 +08:00
|
|
|
return __led_set_brightness_blocking(led_cdev, led_cdev->brightness);
|
2015-10-07 17:10:43 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(led_set_brightness_sync);
|
|
|
|
|
2014-08-20 21:41:55 +08:00
|
|
|
int led_update_brightness(struct led_classdev *led_cdev)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (led_cdev->brightness_get) {
|
|
|
|
ret = led_cdev->brightness_get(led_cdev);
|
|
|
|
if (ret >= 0) {
|
|
|
|
led_cdev->brightness = ret;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2015-09-28 21:07:10 +08:00
|
|
|
EXPORT_SYMBOL_GPL(led_update_brightness);
|
2014-09-22 23:21:04 +08:00
|
|
|
|
|
|
|
/* Caller must ensure led_cdev->led_access held */
|
|
|
|
void led_sysfs_disable(struct led_classdev *led_cdev)
|
|
|
|
{
|
|
|
|
lockdep_assert_held(&led_cdev->led_access);
|
|
|
|
|
|
|
|
led_cdev->flags |= LED_SYSFS_DISABLE;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(led_sysfs_disable);
|
|
|
|
|
|
|
|
/* Caller must ensure led_cdev->led_access held */
|
|
|
|
void led_sysfs_enable(struct led_classdev *led_cdev)
|
|
|
|
{
|
|
|
|
lockdep_assert_held(&led_cdev->led_access);
|
|
|
|
|
|
|
|
led_cdev->flags &= ~LED_SYSFS_DISABLE;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(led_sysfs_enable);
|