linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] led: core: Fix race on software blink cancellation
@ 2018-01-17 21:12 Jacek Anaszewski
  2018-02-08 20:55 ` Jacek Anaszewski
  0 siblings, 1 reply; 4+ messages in thread
From: Jacek Anaszewski @ 2018-01-17 21:12 UTC (permalink / raw)
  To: linux-leds; +Cc: linux-kernel, pavel, craig.mcqueen, jacek.anaszewski

Commit d23a22a74fde ("leds: delay led_set_brightness if stopping soft-blink")
made a modifications to the LED core allowing for led_set_brightness() to be
called from hard-irq context when soft blink is being handled in soft-irq.

Since that time LED core has undergone modifications related to addition of
generic support for delegating brightness setting to a workqueue as well as
subsequent fixes for blink setting use cases.

After that the LED core code became hard to maintain and analyze, especially
due to the imposed hard-irq context compatibility. It also turned out that
in some cases a LED remained off after executing following sequence of commands:

1. echo timer > trigger
2. echo 0 > brightness
3. echo 100 > brightness

The reason was LED_BLINK_DISABLE operation delegated to a set_brightness_work,
triggered in 2., which was handled after 3. took effect.

In order to serialize above operations and in the same time avoid
code overcomplication the hard-irq context compatibility is being removed,
which allows to use spin_lock_bh() for LED blink setting serialization.

>From now, if in hard-irq context, users need to delegate led_set_brightness()
to a workqueue in the place of use.

Reported-by: Craig McQueen <craig.mcqueen@innerrange.com.au>
Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
---
Craig,

It would be great if you could confirm if this fixes your use case.

 drivers/leds/led-core.c                  | 79 +++++++++++++++++++-------------
 drivers/leds/trigger/ledtrig-activity.c  |  3 --
 drivers/leds/trigger/ledtrig-heartbeat.c |  3 --
 include/linux/leds.h                     |  5 +-
 4 files changed, 49 insertions(+), 41 deletions(-)

diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index ede4fa0..25d711b 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -22,6 +22,9 @@
 DECLARE_RWSEM(leds_list_lock);
 EXPORT_SYMBOL_GPL(leds_list_lock);
 
+DEFINE_SPINLOCK(leds_soft_blink_lock);
+EXPORT_SYMBOL_GPL(leds_soft_blink_lock);
+
 LIST_HEAD(leds_list);
 EXPORT_SYMBOL_GPL(leds_list);
 
@@ -51,26 +54,31 @@ static void led_timer_function(struct timer_list *t)
 	unsigned long brightness;
 	unsigned long delay;
 
+	spin_lock_bh(&leds_soft_blink_lock);
+
+	/*
+	 * Check if soft blinking wasn't disabled via led_set_brightness()
+	 * in the meantime.
+	 */
+	if (!test_bit(LED_BLINK_SW, &led_cdev->work_flags))
+		goto unlock;
+
 	if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
 		led_set_brightness_nosleep(led_cdev, LED_OFF);
 		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
-		return;
+		goto unlock;
 	}
 
 	if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
 			       &led_cdev->work_flags)) {
 		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
-		return;
+		goto unlock;
 	}
 
 	brightness = led_get_brightness(led_cdev);
 	if (!brightness) {
 		/* Time to switch the LED on. */
-		if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
-					&led_cdev->work_flags))
-			brightness = led_cdev->new_blink_brightness;
-		else
-			brightness = led_cdev->blink_brightness;
+		brightness = led_cdev->blink_brightness;
 		delay = led_cdev->blink_delay_on;
 	} else {
 		/* Store the current brightness value to be able
@@ -100,6 +108,9 @@ static void led_timer_function(struct timer_list *t)
 	}
 
 	mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
+
+unlock:
+	spin_unlock_bh(&leds_soft_blink_lock);
 }
 
 static void set_brightness_delayed(struct work_struct *ws)
@@ -108,11 +119,6 @@ static void set_brightness_delayed(struct work_struct *ws)
 		container_of(ws, struct led_classdev, set_brightness_work);
 	int ret = 0;
 
-	if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
-		led_cdev->delayed_set_value = LED_OFF;
-		led_stop_software_blink(led_cdev);
-	}
-
 	ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
 	if (ret == -ENOTSUPP)
 		ret = __led_set_brightness_blocking(led_cdev,
@@ -131,6 +137,8 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
 {
 	int current_brightness;
 
+	spin_lock_bh(&leds_soft_blink_lock);
+
 	current_brightness = led_get_brightness(led_cdev);
 	if (current_brightness)
 		led_cdev->blink_brightness = current_brightness;
@@ -143,18 +151,21 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
 	/* never on - just set to off */
 	if (!delay_on) {
 		led_set_brightness_nosleep(led_cdev, LED_OFF);
-		return;
+		goto unlock;
 	}
 
 	/* never off - just set to brightness */
 	if (!delay_off) {
 		led_set_brightness_nosleep(led_cdev,
 					   led_cdev->blink_brightness);
-		return;
+		goto unlock;
 	}
 
 	set_bit(LED_BLINK_SW, &led_cdev->work_flags);
 	mod_timer(&led_cdev->blink_timer, jiffies + 1);
+
+unlock:
+	spin_unlock_bh(&leds_soft_blink_lock);
 }
 
 
@@ -217,40 +228,46 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
 }
 EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
 
-void led_stop_software_blink(struct led_classdev *led_cdev)
+void led_stop_software_blink_nolock(struct led_classdev *led_cdev)
 {
-	del_timer_sync(&led_cdev->blink_timer);
 	led_cdev->blink_delay_on = 0;
 	led_cdev->blink_delay_off = 0;
 	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
 }
+
+void led_stop_software_blink(struct led_classdev *led_cdev)
+{
+	spin_lock_bh(&leds_soft_blink_lock);
+	led_stop_software_blink_nolock(led_cdev);
+	spin_unlock_bh(&leds_soft_blink_lock);
+}
 EXPORT_SYMBOL_GPL(led_stop_software_blink);
 
 void led_set_brightness(struct led_classdev *led_cdev,
 			enum led_brightness brightness)
 {
-	/*
-	 * If software blink is active, delay brightness setting
-	 * until the next timer tick.
-	 */
+	if (in_irq()) {
+		dev_err(led_cdev->dev,
+			"Cannot set LED brightness from hard irq context!");
+		WARN_ON(1);
+		return;
+	}
+
+	spin_lock_bh(&leds_soft_blink_lock);
+
 	if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
-		/*
-		 * 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) {
-			set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
-			schedule_work(&led_cdev->set_brightness_work);
+			led_stop_software_blink_nolock(led_cdev);
 		} else {
-			set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
-				&led_cdev->work_flags);
-			led_cdev->new_blink_brightness = brightness;
+			led_cdev->blink_brightness = brightness;
+			goto unlock;
 		}
-		return;
 	}
 
 	led_set_brightness_nosleep(led_cdev, brightness);
+
+unlock:
+	spin_unlock_bh(&leds_soft_blink_lock);
 }
 EXPORT_SYMBOL_GPL(led_set_brightness);
 
diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
index 5081894..6f62da7 100644
--- a/drivers/leds/trigger/ledtrig-activity.c
+++ b/drivers/leds/trigger/ledtrig-activity.c
@@ -48,9 +48,6 @@ static void led_activity_function(struct timer_list *t)
 	int cpus;
 	int i;
 
-	if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
-		led_cdev->blink_brightness = led_cdev->new_blink_brightness;
-
 	if (unlikely(panic_detected)) {
 		/* full brightness in case of panic */
 		led_set_brightness_nosleep(led_cdev, led_cdev->blink_brightness);
diff --git a/drivers/leds/trigger/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c
index f0896de..1453352 100644
--- a/drivers/leds/trigger/ledtrig-heartbeat.c
+++ b/drivers/leds/trigger/ledtrig-heartbeat.c
@@ -47,9 +47,6 @@ static void led_heartbeat_function(struct timer_list *t)
 		return;
 	}
 
-	if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
-		led_cdev->blink_brightness = led_cdev->new_blink_brightness;
-
 	/* acts like an actual heart beat -- ie thump-thump-pause... */
 	switch (heartbeat_data->phase) {
 	case 0:
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 5579c64..e520503 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -51,15 +51,13 @@ struct led_classdev {
 #define LED_BRIGHT_HW_CHANGED	BIT(21)
 #define LED_RETAIN_AT_SHUTDOWN	BIT(22)
 
-	/* set_brightness_work / blink_timer flags, atomic, private. */
+	/* blink_timer flags, atomic, private. */
 	unsigned long		work_flags;
 
 #define LED_BLINK_SW			0
 #define LED_BLINK_ONESHOT		1
 #define LED_BLINK_ONESHOT_STOP		2
 #define LED_BLINK_INVERT		3
-#define LED_BLINK_BRIGHTNESS_CHANGE 	4
-#define LED_BLINK_DISABLE		5
 
 	/* Set LED brightness level
 	 * Must not sleep. Use brightness_set_blocking for drivers
@@ -97,7 +95,6 @@ struct led_classdev {
 	unsigned long		 blink_delay_on, blink_delay_off;
 	struct timer_list	 blink_timer;
 	int			 blink_brightness;
-	int			 new_blink_brightness;
 	void			(*flash_resume)(struct led_classdev *led_cdev);
 
 	struct work_struct	set_brightness_work;
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] led: core: Fix race on software blink cancellation
  2018-01-17 21:12 [PATCH] led: core: Fix race on software blink cancellation Jacek Anaszewski
@ 2018-02-08 20:55 ` Jacek Anaszewski
  2018-02-08 21:50   ` Pavel Machek
  0 siblings, 1 reply; 4+ messages in thread
From: Jacek Anaszewski @ 2018-02-08 20:55 UTC (permalink / raw)
  To: linux-leds
  Cc: linux-kernel, pavel, craig.mcqueen, Hans de Goede, Sakari Ailus,
	Matthieu CASTET, Dan Murphy, Andy Shevchenko, Ben Whitten,
	Bjorn Andersson, Andrew Lunn, Alan Mizrahi, Vadim Pasternak,
	David Lin, Joel Stanley, Cédric Le Goater, Willy Tarreau,
	Andrew Jeffery, Javier Martinez Canillas, Colin King

Any comments? I'd like to have some acks before applying
this patch.

Adding more people showing recently interest in the LED subsystem
related development.

Thanks,
Jacek Anaszewski

On 01/17/2018 10:12 PM, Jacek Anaszewski wrote:
> Commit d23a22a74fde ("leds: delay led_set_brightness if stopping soft-blink")
> made a modifications to the LED core allowing for led_set_brightness() to be
> called from hard-irq context when soft blink is being handled in soft-irq.
> 
> Since that time LED core has undergone modifications related to addition of
> generic support for delegating brightness setting to a workqueue as well as
> subsequent fixes for blink setting use cases.
> 
> After that the LED core code became hard to maintain and analyze, especially
> due to the imposed hard-irq context compatibility. It also turned out that
> in some cases a LED remained off after executing following sequence of commands:
> 
> 1. echo timer > trigger
> 2. echo 0 > brightness
> 3. echo 100 > brightness
> 
> The reason was LED_BLINK_DISABLE operation delegated to a set_brightness_work,
> triggered in 2., which was handled after 3. took effect.
> 
> In order to serialize above operations and in the same time avoid
> code overcomplication the hard-irq context compatibility is being removed,
> which allows to use spin_lock_bh() for LED blink setting serialization.
> 
>>From now, if in hard-irq context, users need to delegate led_set_brightness()
> to a workqueue in the place of use.
> 
> Reported-by: Craig McQueen <craig.mcqueen@innerrange.com.au>
> Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
> ---
> Craig,
> 
> It would be great if you could confirm if this fixes your use case.
> 
>  drivers/leds/led-core.c                  | 79 +++++++++++++++++++-------------
>  drivers/leds/trigger/ledtrig-activity.c  |  3 --
>  drivers/leds/trigger/ledtrig-heartbeat.c |  3 --
>  include/linux/leds.h                     |  5 +-
>  4 files changed, 49 insertions(+), 41 deletions(-)
> 
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index ede4fa0..25d711b 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -22,6 +22,9 @@
>  DECLARE_RWSEM(leds_list_lock);
>  EXPORT_SYMBOL_GPL(leds_list_lock);
>  
> +DEFINE_SPINLOCK(leds_soft_blink_lock);
> +EXPORT_SYMBOL_GPL(leds_soft_blink_lock);
> +
>  LIST_HEAD(leds_list);
>  EXPORT_SYMBOL_GPL(leds_list);
>  
> @@ -51,26 +54,31 @@ static void led_timer_function(struct timer_list *t)
>  	unsigned long brightness;
>  	unsigned long delay;
>  
> +	spin_lock_bh(&leds_soft_blink_lock);
> +
> +	/*
> +	 * Check if soft blinking wasn't disabled via led_set_brightness()
> +	 * in the meantime.
> +	 */
> +	if (!test_bit(LED_BLINK_SW, &led_cdev->work_flags))
> +		goto unlock;
> +
>  	if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
>  		led_set_brightness_nosleep(led_cdev, LED_OFF);
>  		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
> -		return;
> +		goto unlock;
>  	}
>  
>  	if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
>  			       &led_cdev->work_flags)) {
>  		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
> -		return;
> +		goto unlock;
>  	}
>  
>  	brightness = led_get_brightness(led_cdev);
>  	if (!brightness) {
>  		/* Time to switch the LED on. */
> -		if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
> -					&led_cdev->work_flags))
> -			brightness = led_cdev->new_blink_brightness;
> -		else
> -			brightness = led_cdev->blink_brightness;
> +		brightness = led_cdev->blink_brightness;
>  		delay = led_cdev->blink_delay_on;
>  	} else {
>  		/* Store the current brightness value to be able
> @@ -100,6 +108,9 @@ static void led_timer_function(struct timer_list *t)
>  	}
>  
>  	mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
> +
> +unlock:
> +	spin_unlock_bh(&leds_soft_blink_lock);
>  }
>  
>  static void set_brightness_delayed(struct work_struct *ws)
> @@ -108,11 +119,6 @@ static void set_brightness_delayed(struct work_struct *ws)
>  		container_of(ws, struct led_classdev, set_brightness_work);
>  	int ret = 0;
>  
> -	if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
> -		led_cdev->delayed_set_value = LED_OFF;
> -		led_stop_software_blink(led_cdev);
> -	}
> -
>  	ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
>  	if (ret == -ENOTSUPP)
>  		ret = __led_set_brightness_blocking(led_cdev,
> @@ -131,6 +137,8 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
>  {
>  	int current_brightness;
>  
> +	spin_lock_bh(&leds_soft_blink_lock);
> +
>  	current_brightness = led_get_brightness(led_cdev);
>  	if (current_brightness)
>  		led_cdev->blink_brightness = current_brightness;
> @@ -143,18 +151,21 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
>  	/* never on - just set to off */
>  	if (!delay_on) {
>  		led_set_brightness_nosleep(led_cdev, LED_OFF);
> -		return;
> +		goto unlock;
>  	}
>  
>  	/* never off - just set to brightness */
>  	if (!delay_off) {
>  		led_set_brightness_nosleep(led_cdev,
>  					   led_cdev->blink_brightness);
> -		return;
> +		goto unlock;
>  	}
>  
>  	set_bit(LED_BLINK_SW, &led_cdev->work_flags);
>  	mod_timer(&led_cdev->blink_timer, jiffies + 1);
> +
> +unlock:
> +	spin_unlock_bh(&leds_soft_blink_lock);
>  }
>  
>  
> @@ -217,40 +228,46 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
>  }
>  EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
>  
> -void led_stop_software_blink(struct led_classdev *led_cdev)
> +void led_stop_software_blink_nolock(struct led_classdev *led_cdev)
>  {
> -	del_timer_sync(&led_cdev->blink_timer);
>  	led_cdev->blink_delay_on = 0;
>  	led_cdev->blink_delay_off = 0;
>  	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
>  }
> +
> +void led_stop_software_blink(struct led_classdev *led_cdev)
> +{
> +	spin_lock_bh(&leds_soft_blink_lock);
> +	led_stop_software_blink_nolock(led_cdev);
> +	spin_unlock_bh(&leds_soft_blink_lock);
> +}
>  EXPORT_SYMBOL_GPL(led_stop_software_blink);
>  
>  void led_set_brightness(struct led_classdev *led_cdev,
>  			enum led_brightness brightness)
>  {
> -	/*
> -	 * If software blink is active, delay brightness setting
> -	 * until the next timer tick.
> -	 */
> +	if (in_irq()) {
> +		dev_err(led_cdev->dev,
> +			"Cannot set LED brightness from hard irq context!");
> +		WARN_ON(1);
> +		return;
> +	}
> +
> +	spin_lock_bh(&leds_soft_blink_lock);
> +
>  	if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
> -		/*
> -		 * 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) {
> -			set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
> -			schedule_work(&led_cdev->set_brightness_work);
> +			led_stop_software_blink_nolock(led_cdev);
>  		} else {
> -			set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
> -				&led_cdev->work_flags);
> -			led_cdev->new_blink_brightness = brightness;
> +			led_cdev->blink_brightness = brightness;
> +			goto unlock;
>  		}
> -		return;
>  	}
>  
>  	led_set_brightness_nosleep(led_cdev, brightness);
> +
> +unlock:
> +	spin_unlock_bh(&leds_soft_blink_lock);
>  }
>  EXPORT_SYMBOL_GPL(led_set_brightness);
>  
> diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
> index 5081894..6f62da7 100644
> --- a/drivers/leds/trigger/ledtrig-activity.c
> +++ b/drivers/leds/trigger/ledtrig-activity.c
> @@ -48,9 +48,6 @@ static void led_activity_function(struct timer_list *t)
>  	int cpus;
>  	int i;
>  
> -	if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
> -		led_cdev->blink_brightness = led_cdev->new_blink_brightness;
> -
>  	if (unlikely(panic_detected)) {
>  		/* full brightness in case of panic */
>  		led_set_brightness_nosleep(led_cdev, led_cdev->blink_brightness);
> diff --git a/drivers/leds/trigger/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c
> index f0896de..1453352 100644
> --- a/drivers/leds/trigger/ledtrig-heartbeat.c
> +++ b/drivers/leds/trigger/ledtrig-heartbeat.c
> @@ -47,9 +47,6 @@ static void led_heartbeat_function(struct timer_list *t)
>  		return;
>  	}
>  
> -	if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
> -		led_cdev->blink_brightness = led_cdev->new_blink_brightness;
> -
>  	/* acts like an actual heart beat -- ie thump-thump-pause... */
>  	switch (heartbeat_data->phase) {
>  	case 0:
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index 5579c64..e520503 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -51,15 +51,13 @@ struct led_classdev {
>  #define LED_BRIGHT_HW_CHANGED	BIT(21)
>  #define LED_RETAIN_AT_SHUTDOWN	BIT(22)
>  
> -	/* set_brightness_work / blink_timer flags, atomic, private. */
> +	/* blink_timer flags, atomic, private. */
>  	unsigned long		work_flags;
>  
>  #define LED_BLINK_SW			0
>  #define LED_BLINK_ONESHOT		1
>  #define LED_BLINK_ONESHOT_STOP		2
>  #define LED_BLINK_INVERT		3
> -#define LED_BLINK_BRIGHTNESS_CHANGE 	4
> -#define LED_BLINK_DISABLE		5
>  
>  	/* Set LED brightness level
>  	 * Must not sleep. Use brightness_set_blocking for drivers
> @@ -97,7 +95,6 @@ struct led_classdev {
>  	unsigned long		 blink_delay_on, blink_delay_off;
>  	struct timer_list	 blink_timer;
>  	int			 blink_brightness;
> -	int			 new_blink_brightness;
>  	void			(*flash_resume)(struct led_classdev *led_cdev);
>  
>  	struct work_struct	set_brightness_work;
> 

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] led: core: Fix race on software blink cancellation
  2018-02-08 20:55 ` Jacek Anaszewski
@ 2018-02-08 21:50   ` Pavel Machek
  2018-02-09 21:35     ` Jacek Anaszewski
  0 siblings, 1 reply; 4+ messages in thread
From: Pavel Machek @ 2018-02-08 21:50 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-kernel, craig.mcqueen, Hans de Goede,
	Sakari Ailus, Matthieu CASTET, Dan Murphy, Andy Shevchenko,
	Ben Whitten, Bjorn Andersson, Andrew Lunn, Alan Mizrahi,
	Vadim Pasternak, David Lin, Joel Stanley, Cédric Le Goater,
	Willy Tarreau, Andrew Jeffery, Javier Martinez Canillas,
	Colin King

[-- Attachment #1: Type: text/plain, Size: 1325 bytes --]

Hi!

> Any comments? I'd like to have some acks before applying
> this patch.

I can't say I like it.

> > Commit d23a22a74fde ("leds: delay led_set_brightness if stopping soft-blink")
> > made a modifications to the LED core allowing for led_set_brightness() to be
> > called from hard-irq context when soft blink is being handled in soft-irq.
> > 
> > Since that time LED core has undergone modifications related to addition of
> > generic support for delegating brightness setting to a workqueue as well as
> > subsequent fixes for blink setting use cases.
> > 
> > After that the LED core code became hard to maintain and analyze, especially
> > due to the imposed hard-irq context compatibility. It also turned out that
> > in some cases a LED remained off after executing following sequence of commands:
> > 
> > 1. echo timer > trigger
> > 2. echo 0 > brightness
> > 3. echo 100 > brightness
> > 
> > The reason was LED_BLINK_DISABLE operation delegated to a set_brightness_work,
> > triggered in 2., which was handled after 3. took effect.

Could we wait for delayed work to be done before setting the
brightness to fix this one?

									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] led: core: Fix race on software blink cancellation
  2018-02-08 21:50   ` Pavel Machek
@ 2018-02-09 21:35     ` Jacek Anaszewski
  0 siblings, 0 replies; 4+ messages in thread
From: Jacek Anaszewski @ 2018-02-09 21:35 UTC (permalink / raw)
  To: Pavel Machek
  Cc: linux-leds, linux-kernel, craig.mcqueen, Hans de Goede,
	Sakari Ailus, Matthieu CASTET, Dan Murphy, Andy Shevchenko,
	Ben Whitten, Bjorn Andersson, Andrew Lunn, Alan Mizrahi,
	Vadim Pasternak, David Lin, Joel Stanley, Cédric Le Goater,
	Willy Tarreau, Andrew Jeffery, Javier Martinez Canillas,
	Colin King

On 02/08/2018 10:50 PM, Pavel Machek wrote:
> Hi!
> 
>> Any comments? I'd like to have some acks before applying
>> this patch.
> 
> I can't say I like it.

Please point out the bits you don't like and spot any risks.

>>> Commit d23a22a74fde ("leds: delay led_set_brightness if stopping soft-blink")
>>> made a modifications to the LED core allowing for led_set_brightness() to be
>>> called from hard-irq context when soft blink is being handled in soft-irq.
>>>
>>> Since that time LED core has undergone modifications related to addition of
>>> generic support for delegating brightness setting to a workqueue as well as
>>> subsequent fixes for blink setting use cases.
>>>
>>> After that the LED core code became hard to maintain and analyze, especially
>>> due to the imposed hard-irq context compatibility. It also turned out that
>>> in some cases a LED remained off after executing following sequence of commands:
>>>
>>> 1. echo timer > trigger
>>> 2. echo 0 > brightness
>>> 3. echo 100 > brightness
>>>
>>> The reason was LED_BLINK_DISABLE operation delegated to a set_brightness_work,
>>> triggered in 2., which was handled after 3. took effect.
> 
> Could we wait for delayed work to be done before setting the
> brightness to fix this one?

Without mutually exclusive section you can always be preempted
after wait test succeeds and before requesting new brightness.
Then the other process can jump in, request other brightness
(and change it in struct led_classdev via led_set_brightness_nosleep()),
which causes we end up with non deterministic LED class device
state after that.

Every solution without mutually exclusive section is prone to races
here.

-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2018-02-09 21:36 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-17 21:12 [PATCH] led: core: Fix race on software blink cancellation Jacek Anaszewski
2018-02-08 20:55 ` Jacek Anaszewski
2018-02-08 21:50   ` Pavel Machek
2018-02-09 21:35     ` Jacek Anaszewski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).